qesR 0.9.2
Upgrading from qesR 0.4.4? See Upgrading from qesR 0.4.4.
In short: corrections to qes_decon() and to the long layout of qes_harmonize(), clearer messages, and a shorter test run on CRAN. get_qes_master() and get_decon() return the same values as in 0.9.1.
qes_decon()
-
interestis cut at 0.55 and 0.85 of the 0-1 scale instead of 0.35 and 0.75. On a 0-10 question, 0 to 5 is now low, 6 to 8 medium and 9 or 10 high; on a 4-point question nothing changes (not at all or hardly interested is low, quite medium, very high). With the old cuts, the share with high interest nearly doubled between the 4-point studies and the 0-10 studies through the change of scale alone; it now differs by a few points.interest_01does not change. - In
sovereignty, “would not vote” is still missing, now with the reasonnot_votedinstead of a reason meant for answers that straddle two categories. - The summary message now names every column and study left empty because a source is still in review, the previous vote of 2008 and 2018 included (
vote_prev, held until a decision on respondents too young to vote at the previous election). It used to count only the relaxed mappings in review, so these two columns were empty without notice. -
?qes_deconhas a new section, Who was surveyed: the 1998 polls interviewed francophones only; the 2018 study also interviewed people aged 16 and 17, who are missing inage_groupandvote_choicebut present in the other columns; and religion comes from a filter question in 2012-2018 but from one long list in 2022, so its levels are not strictly comparable across that change. - The description of
employmentand?qes_deconsay that in the 2018 panel only the web respondents were asked about employment, so its telephone respondents appear as don’t know or refused; the description ofunionno longer mentions an internal decision date. - A custom specification with two relaxed mappings that both replace the values of one per-respondent column, in two waves of a study, no longer lets the second erase the first: each gives the respondents of its own wave, and the first wave wins. The shipped specification has no such case, so no value changes.
Long layout
-
qes_harmonize(layout = "long")andqes_decon()have one row per respondent and wave, sowaveis neverNA. A respondent who took part in no wave (one interview in the 2007 panel that neither wave counts) used to appear as an empty row withwaveNA; it is now left out. A message says how many (classqesR_message_no_wave, silenced byquiet = TRUE), andattr(x, "qes_no_wave")counts them study by study. The respondent layout keeps every respondent, andattr(qes_decon(), "weights")now covers every row.
Messages and help
-
get_qes(),get_qes_master()andget_decon()word their one-time note neutrally (“returns the data and does not create objects in your workspace: assign the result”), show it also for scripts run withsource(), and never show it inside a function, while knitr renders a page, withquiet = TRUEor whenassign_globalis passed. -
qes_party_lineage()labels its new columns “(filiation)” in French data, and says thatxmust come fromqes_harmonize()orqes_decon()when it does not. - The harmonization reference and the coverage page no longer show internal tags and status codes: notes lose their pointers to the owner’s decisions, a relaxed mapping is marked “approved” or “awaiting sign-off”, and the sentence on rows awaiting sign-off says in plain words what happens to them.
- A rendering step of
get_qes_master()no longer warns under the development version of R; its values do not change.
Tests and checks
- The exhaustive tests (every study, every rule, every view) are skipped on CRAN, so the CRAN test run takes about a quarter of the time; they still run in continuous integration and with
NOT_CRAN=true. -
inst/WORDLISTlists the proper nouns and terms of the documentation for spell checks.
Harmonization rules (specification 4.7.0)
- Relaxed layer: the
interestcuts and thesovereigntyreason above, and text only forunion,employmentand the grade reason of the 2014 gender question. The recorded relaxed results are rebuilt (the keys ofinterestandsovereignty, and the 2007 panel without its respondent in no wave). No strict row, map, marginal or column hash changes. Engine-Min is 0.9.2.
qesR 0.9.1
Upgrading from qesR 0.4.4? See Upgrading from qesR 0.4.4.
In short: in qes_decon(), education now has three groups instead of four, so that the 1998 study can be included; the four groups stay as education4. Two new columns, union and econ_self, are kept although few studies ask them. The legacy functions return the same values as in 0.9.0.
qes_decon()
-
educationis in three groups for every study: no high school diploma, high school to college (CEGEP), and university (in French: sans diplôme d’études secondaires, secondaire au collégial (cégep), université). These are the four groups of 0.9.0 with high school and college joined; trade and vocational diplomas, with college in 0.9.0, are in high school to college. Code that used the four levels ofeducationshould useeducation4. - New column
education4: the four groups of 0.9.0 (no high school diploma, high school, college, university), with the same mappings and values. It isNAforqes1998(reasonnot_mappable), whose middle group, 10 to 15 years of schooling, spans high school and college. It is not the strict targeteducation4ofqes_harmonize(), whose lowest group is primary or less. -
qes1998enterseducationfrom the pooled file’s three groups (1 to 9 years, 10 to 15 years, university or more). The match is approximate for the CROP respondents, who gave years of schooling: in Quebec high school ends after 11 years, so a respondent with 10 years has no diploma but counts as high school to college, and some respondents with 14 or 15 years are university students. For the CREATEC respondents the groups recode the highest level completed. The column’srelaxedattribute and the row’s notes say so. - New column
union(union membership): whether the respondent belongs to a union (qes2022), or whether the respondent or anyone in the household does (qes2014,qes2018; in 2018, respondents who live with their parents were asked about their family). Three studies. - New column
econ_self(personal financial situation over the past year: better, about the same, worse), the counterpart of cesR’secon_self, asked inqes2022only. - Both are kept whatever their number of studies (
essential = TRUEinrelaxed.csv); their coverage is in theirrelaxedattribute, in?qes_deconand in the article’s table of columns. - The five new relaxed mappings (education of
qes1998,unionin three studies,econ_self) were signed off by the same kind of automated double review against the original files and documents as the other 60 (not a human review; theirreviewed_byandreview_notesay so), andqes_decon()applies them by default. -
qes_decon()now has 35 columns.education4comes beforeeducation(a column built on another column must come after it),unionafteremploymentandecon_selfafterecon_retro.
Website
- The article One file for every study: qes_decon() and its French version explain the choice of three education groups and the 1998 match; their table of columns gives each column’s studies,
unionandecon_selfincluded; the example across elections useseducation4, which keeps college apart, and its estimates do not change. The coverage page shows the new columns, and marks a relaxed mapping as in review until it is signed off.
Harmonization rules (specification 4.6.0)
-
relaxed.csvgainseducation4,unionandecon_self;educationtakes its values fromeducation4(basecolumn:education4, a recode that joins high school and college) and a new relaxed row forqes1998. New level setrx_edu3; 5 new relaxed rows, signed off by an automated double review (65 in all, all stable) and 6 newrx_maps (63). The ten formereducationrows are noweducation4rows, unchanged in codes and sign-off. - V-R2 has a second exception: the relaxed column
education4shares the name of the strict targeteducation4without extending it, asreligiondoes. Engine-Min is 0.9.1. -
expected/relaxed_marginals.csvandexpected/relaxed_hashes.csvare rebuilt: the values ofeducationchange, those ofeducation4equal the 0.9.0 values ofeducation, and no other column changes. MINOR: no strict row, map, marginal or column hash changes.
qesR 0.9.0
Upgrading from qesR 0.4.4? See Upgrading from qesR 0.4.4.
In short: qes_decon() returns one data frame for all 11 harmonized studies, with plain column names in the style of cesR. The columns are harmonized in a relaxed way: one column per concept even when the questions differ, with coarse common categories. The legacy functions return the same values as in 0.8.0.
Relaxed harmonization: qes_decon()
-
qes_decon(studies = NULL, lang = c("en", "fr"), weights = TRUE, quiet = FALSE)(new export, 29 in all) returns one flat data frame with one row per respondent and wave and 32 columns:citizenship,yob,age_group,gender,education,income_cat,language,language_fr,language_eng,religion,marital,employment,region,region_admin,born_canada,born_quebec,vote_choice,vote_type,turnout,vote_prev,pid,lr,interest,interest_01,sovereignty,sovereignty_type,satis_democracy,gov_satisfaction,econ_retro,identity,attach_quebecandattach_canada, afterstudy,year,wave,qes_id,weightandweight_var. Factor levels and every text are in English or French (lang); column names stay English.?qes_deconlists the counterpart of each column of cesR’sget_decon(). - What relaxed means: one concept goes in one column for every study, even when the wording or the answer options differ, in coarse common categories: education in four groups (no high school diploma, high school, college or trade school, university); household income in thirds of each study’s own respondents, by the midpoint of whole brackets; interest in politics low, medium or high from the 0-1 scale; a referendum vote of yes or no whatever the question (
sovereignty_typesays which); religion in five groups; marital status in four; employment in five; the 17 administrative regions. It trades exactness for coverage:qes_harmonize()and its grades stay the strict layer, for when the difference between two questions matters. - Relaxed columns carry no comparability grade and never claim that two studies asked the same question. Each column says how it was relaxed (
attr(, "relaxed")) and where each study’s values come from (attr(, "sources"): the source variable, the document that gives its wording and the recode in words);attr(x, "decon_sources")gathers every source with the count of values and of each reason for a missing value, andattr(x, "weights")says why a weight isNA(qes2008has no recommended weight; those ofqes1998,qes2007_panel,qes2012_paneland the CROP polls still need review). - A column reuses a strict target or a pooled variable where one exists (
gender,vote_choice,satis_democracy…), recoded into its categories where needed (born_quebecfrom the birthplace), and adds relaxed mappings of the studies’ own questions where the strict layer has none or leaves a study out: 60 relaxed mappings, every one checked against the pinned file (every observed code has an outcome). An automated double review checked the 60 against the original files and documents and signed them off (spec 4.5.0), soqes_decon()applies them by default. It is not a human review, and each mapping’sreviewed_byandreview_notesay so. Like a crosswalk row, a relaxed mapping still in review is not applied: its cells areNAwith reasonnot_reviewed, and a message (classqesR_message_decon, silenced byquiet) counts them. - Socio-demographic columns hold one value per respondent, repeated on each of their waves; the vote, turnout and attitudes sit on the wave that asked them.
weightis the wave’s recommended weight with mean 1 in each study and wave. -
qes_party_lineage()also works onqes_decon()output (vote_choice,vote_prev,pid), with no grade column. -
qes_spec("relaxed")lists the columns, their base, transform and relaxation rule, and where each study’s values come from;qes_spec("relaxed_maps")lists the relaxed mappings with their recode in words. The generated reference (vignette("harmonization-reference")and its French version) has a chapter on them. -
get_decon()andget_qes_master()are unchanged (their values and column hashes are those of 0.8.0); the help ofget_decon()points toqes_decon(). - In
qes_spec("relaxed"), a column built on another column (language_fr,language_eng) now takes that column’s source study by study, so the 1998 polls show"relaxed"there, where the view first showedNA; the values ofqes_decon()do not change.
Website
- New article pair One file for every study: qes_decon() / Un seul fichier pour toutes les études : qes_decon() (Harmonization menu): what
qes_decon()returns, a table of its columns generated from the relaxed specification (categories, studies with values, how each was relaxed), a weighted estimate within one study, the education gap in the ADQ and CAQ vote among francophones from 2007 to 2022, and when to useqes_harmonize()instead. - The coverage page has a grid of the relaxed columns: for each study, whether its values come from the strict layer or from a relaxed mapping. The home page, its French version and the README show
qes_decon()in the first session.
Harmonization rules (specifications 4.4.0 and 4.5.0)
- Schema 4: two new tables,
relaxed.csv(one row per column ofqes_decon()) andrelaxed_maps.csv(the relaxed mappings, in the crosswalk row format without grades), with 57 value maps invaluemaps.csvunder the prefixrx_, 10 level sets inlevels.csv(prefixrx_) and the recorded results on the pinned files (expected/relaxed_marginals.csv,expected/relaxed_hashes.csv). MINOR: no existing row, map, marginal or column hash changes. - The validator gains V-R1 to V-R12 (form and keys, names, bases and transforms, English and French, at least 3 studies per column unless essential, no claim of comparability, the relaxed rows and their maps, the data checks on the dictionary with the income thirds rule, override against the base, the recorded results, and the legacy renderers never reading the relaxed layer).
- New registered function
fn:amount_bands(an amount in bands, with a bracket question for those who gave none: the 2022 income) and the optionfirst=TRUEoffn:multiselect(the first option ticked wins: French, then English, for several mother tongues). Neither is used by a crosswalk row. -
qes_spec()reads spec directories of schema 2, 3 and 4; Engine-Min is 0.9.0. - Spec 4.5.0 signs off the 60 relaxed mappings (status
stable). The review had two passes, one on the codes and the data and one on the wording, adjudicated where they disagreed. It corrected text only: the notes of education for the 2007 panel and the CROP polls (13 to 15 years of schooling is college, so some university without a degree may fall there), of marital status for 2012 and 2014 (360 and 309 respondents chose civil union, far more than Quebec’s legal civil unions, so common-law partners are split between married and never married), of employment forqes2008and the 2018 panel (the 400 telephone respondents were not askedd4; the file codes them 9) and of the CROP referendum question (its wording is documented for 19 of the 24 polls); the notes of threeqes2007mother tongue maps; and seven wordings or wording references, among them theqes2012employment and region questions, whose English text sat in the French field. MINOR: no code, value map, level set, recorded marginal or column hash changes.
qesR 0.8.0
Upgrading from qesR 0.4.4? See Upgrading from qesR 0.4.4.
In short: one column per concept across all 11 harmonized studies (vote_choice, sov_support, pol_interest, turnout), 20 new harmonized variables, and new example pages on the website. The legacy functions return the same values as in 0.7.1.
The harmonization engine gains pooled variables: one column for every study that pools several targets, with the question each value comes from recorded row by row. vote_choice is the provincial vote choice of all 11 harmonized studies; sov_support, pol_interest and turnout do the same for support for sovereignty, interest in politics and turnout. The harmonization rules (specification 4.3.0) also add 20 targets, from satisfaction with democracy to leader ratings, and every study now has an age group. 101 of the 104 new rows are signed off, so qes_harmonize() applies them by default; three are not signed off yet. get_qes_master() and get_decon() return the values of 0.7.1, and no count recorded in 0.7.1 changed; the columns of 0.7.1 gain values only where a new row fills a study that had none, as noted below.
Pooled variables
-
qes_harmonize(targets = "vote_choice")returns one provincial vote choice for every study: the reported vote (recall) where the study asked it, else the vote intention with those who named no party (the undecided and, in some studies, those who would not vote or refused) pushed toward the party they lean to, else the vote intention at the first question.vote_choice__typesays which (recall,intention_push,intention),vote_choice__gradegives the grade of that study’s question (never raised), andvote_choice__itemits source (qes2012:post:q25). Wordings differ between studies; the types and grades say how. -
sov_supportpools the referendum questions on sovereignty (an independent country, a sovereign country, the 1995 question with the undecided pushed and without, and, collapsed to yes or no and graded approximate, being favourable or opposed to independence): 10 studies.pol_interestputs interest in politics on 0 to 1 (four-point items scored 1, 0.7, 0.3 and 0, the legacypolitical_interestscores divided by 10; 0-10 items divided by 10; scored items graded approximate at most): 7 studies.turnoutis the reported turnout;types = list(turnout = c("recall", "intention"))adds, where a study asked no reported turnout, the likelihood of voting collapsed to yes or no. - How a row gets its value: the members are tried in order of precedence, and the first that asked the respondent sets the row, with a value or with a missing value that is an answer (don’t know, refused, did not vote). A member that did not ask the respondent (another wave, not asked in the study, a row not signed off, below
min_grade) passes to the next. So a respondent who did not vote isNAwith reasonnot_voted, never given their earlier intention. A row that every member passes isNAwith the reason of the first usable member that has a row in the study and wave (for examplesysmis), else of the first member that has one (not_reviewed,below_grade). - The new argument
typesofqes_harmonize()keeps some members only (types = list(vote_choice = "recall")); the precedence stays the spec’s. It is the last argument, so calls by position are unchanged. - In the respondent layout a study’s pooled values come from one wave (the post-election recall of a pre/post study), so that one weight fits them; the long layout keeps every wave.
qes_design()now weights each study with the column of its own wave when the studies ofxcall for different columns (a new columnweight_auto), and asks to choose only when one study mixes them. -
vote_choice,sov_supportandpol_interestare in the default set"core", soqes_harmonize()returns them by default; the set"pooled"names all four. Results change: the default result ofqes_harmonize()has these 3 columns and their 9 companions more. The members are harmonized too, but are columns only whentargetsnames them. -
qes_spec("pooled")lists the members of each pooled variable, their precedence, transform and grade cap, and each member’s grade in each study;qes_provenance(x, level = "pooled")counts, per study, the rows each member gave. A message (classqesR_message_pooled, silenced byquiet) andprint()say which member each study used. The generated reference (vignette("harmonization-reference")and its French version) has a chapter on them, and the coverage grids a row each. -
qes_party_lineage()(new export) adds, for each party column, a column that joins the ADQ and the CAQ (into which it merged on 2012-01-21) as “ADQ/CAQ”, and optionally Option nationale and Quebec solidaire, for time series of the Quebec parties; rows fielded before 2012 are graded approximate there. The harmonized columns keep the parties apart.
Example pages of the website
The three example pages and their French versions (analysis-descriptive, analysis-sovereignty, analysis-vote-choice) are replaced by six analyses and a page of recipes, in English and French, each built on the pooled variables with qes_design(), the reviewed weights and 95% confidence intervals, every estimate made within one study and wave. The old addresses redirect to the new pages. The pages are website-only (vignettes/articles is not in the package build); nothing changes in the package.
-
From two parties to four (
realignment,fr-realignement): the effective number of parties and each party’s vote among francophones and non-francophones, and the age gap party by party, 1998 to 2022. -
A generation gap that reversed (
sovereignty-generations,fr-souverainete-generations): support for sovereignty by study and wording (sov_support__type), and by birth cohort; the youngest francophones go from the most to the least sovereigntist. -
Two dimensions of competition (
dimensions,fr-dimensions): each party’s voters on sovereignty and on the left-right scale from 2012 to 2022, and where the Yes and No voters cast their vote. -
Who votes? (
turnout,fr-participation): reported against official turnout, the age gap election by election, and turnout by interest in politics (pol_interest). -
Changing minds during the campaign (
transitions,fr-transitions): the panels’ transitions from intention to reported vote, loyalty by party, and where the undecided went. -
How far off are surveys? (
survey-vs-official,fr-enquetes-resultats): reported vote against the official results party by party, what weighting changes (index of dissimilarity), and the CROP polls of 2007 to 2010. -
Recipes (
recipes,fr-recettes): short, copyable code for the pooled variables,types, sources and grades, weighted estimates by study,pool = "equal"andqes_party_lineage(). - The charts share one system (
vignettes/articles/_theme.R,_data.R,_setup.R; not exported): a party palette checked with a colour-vision validator in the light and the dark theme (the output is recorded in_theme.R), each chart drawn for both themes, direct labels, hollow markers for studies whose weight is under review, a title and a note (source, weights, intervals) in HTML, alt text with the chart’s numbers, and a table view with every value behind the chart. The two charts of the validation pages use it too, and the home page shows two of the new figures. -
Config/Needs/websitegains ragg, survey and systemfonts (website builds only).
New targets and rows (specification 4.3.0)
- 20 targets: satisfaction with democracy in Quebec (
satis_demo_qc, 6 studies) and with the Quebec government (gov_satisfaction, 5), Quebec’s economy over the past year (econ_retro_qc, 6), attachment to Quebec and to Canada (attach_qc,attach_ca, 4 each), Québécois or Canadian identity (identity_qc_ca, 5), strength of provincial party identification (pid_prov_strength, 6), the provincial vote at the previous election (vote_prov_prev, 4) and the last federal vote (vote_fed_recall, 4), the most important issue (mip_issue, 5), the ratings of the PLQ, PQ, CAQ, QS and ADQ leaders on 0-100 (therm_leader_*), the region (Montreal CMA, Quebec CMA, rest;region_cma3, 8), the language spoken most often at home (lang_home, 8), attendance at religious services (relig_attend, 5), the birthplace (Quebec, rest of Canada, abroad;birthplace3, 3) and the 1995 referendum question with the undecided pushed (sov_partnership_1995_push, 4). The new sets"attitudes"and"socio"name them;"vote"gainsvote_prov_prevandvote_fed_recall. - 104 crosswalk rows, checked against the pinned files (every code and filter count) and then by an automated double review against the original files and documents (codes and data, wording and comparability; adjudicated where the two reviewers disagreed; not a human review). 101 are signed off (
stable,reviewed_bysays by what), 45 of them corrected or completed, mostly in their text: English wordings and questionnaire citations (qes2008,qes2014), grade reasons that name the option wordings that differ from the anchor, don’t-know offers (dk_offered), and counts in the evidence. Among them, three rows on existing targets: the pushed vote intention ofqes2022(the first question, the “if you decide to vote” question of those unlikely to vote, and the lean question of the undecided), the federal party identification ofqes2012, and the mother tongue and home language ofqes2022, two select-all questions. Results change: these fill cells that wereNA(reasonnot_asked) in 0.7.1. - The leader ratings of
qes2022read -99 as don’t know (dk, the “Don’t know leader” option of the codebook), notno_answer: the reason changes, no value. - Three rows are not signed off yet: the strength of provincial party identification of
qes2022, graded approximate because its French version asks closeness, and the previous provincial vote ofqes2008andqes2018, whose respondents under 18 at that election were asked (the spec has no rule that sets an answer to ineligible by age). - A new crosswalk rule,
coalesce, reads a question across several variables (a question and its push, the two halves of a split ballot), each with its own value map; a new registered function,fn:multiselect, reads a select-all question stored as one variable per option (one level ticked gives it; options of two levels arenot_mappable). - The age groups
age_group3andage_group6are derived from the age, else the year of birth, where a study has no banded question (qes2007,qes2012,qes2014,qes2018,qes2022): every study now has an age group. A band computed from the year of birth only is graded approximate (an age at a band edge can be one year off); cell provenance saysrule = "derive:age_band". Results change:age_group3andage_group6of these studies wereNA(reasonnot_asked) and now have values by default (their sources are signed off). - Not changed: the codes and display order of the party levels, and every existing row, map, gate and grade. The definitions of the targets that said “never pooled” now name the pooled variable that uses them.
- Specification 4.3.1 (text only) rewords a grade reason and a note of the crosswalk, the description of
pol_interestand two notes of pooled members; no row, code, grade or count changed. - The catalog version is now 2.4.1: the vocabulary of crosswalk rules gains
coalesce, and the notes ofqes2007andqes2008are reworded (text only). - The dictionary version is now 1.1.1 (text only): the question text of the 2018 panel’s
rv1abgains the stem’s sentence on advance voting, asrv1ahas it.
The legacy functions are frozen
-
get_qes_master()andget_decon()never read a crosswalk row that nobody has reviewed yet, nor a derived cell: their columns are those of 0.7.1. A validator note lists the rows that a legacy column would read once signed off. Two such rows are signed off in 0.8.0, theqes2012federal party identification and theqes2022mother tongue: the legacy columnsfederal_pid(andfed_pidofget_decon()) ofqes2012andlanguageofqes2022keep theNAof 0.7.1, with the new causelegacy_frozeninattr(, "legacy_na_columns")(the reason staysno_source; the legacy renderer reads these targets as if the study had no row); their answers are inqes_harmonize(). A test builds both functions with the specification frozen before the rows of 4.3.0 and checks that the values are identical.
Validation
- The recorded validation report (
inst/extdata/validation/validation_report.csv, source repository only) gains the census margin of theqes2022mother tongue, which the newlang_motherrow makes possible (weighted index 0.13), and itsqes2018age margin now uses the derived age groups, exact to the month of birth (weighted index 8.32 to 7.91). No other recorded value moved.
For developers: specification format
- Schema 3: the optional tables
pooled.csvandpooled_members.csv, and the rulecoalesce; qesR still reads a schema 2 directory (with no pooled variables).Engine-Minis 0.8.0. - The validator adds checks on the pooled tables (keys and a strict precedence, names disjoint from targets, families, sets, study codes and the leading columns of harmonized data, members that are live targets, a default member, level sets or ranges that fit, a grade cap of approximate on every lossy transform, English and French), and now also forbids a target named like a leading column.
expected/hashes.csvgains the column hash of each pooled variable in each study (wave*, the member types applied assource_var).
Documentation
-
?qes_harmonizehas a section on pooled variables and an example ofvote_choice;?qes_designexplains the per-study weight;?qes_speclists the pooled variables. The articles on harmonization (harmonization,fr-harmonisation) showsov_supportafter the sovereignty targets. - The migration guides (
migrating-0.7,fr-migrer-0.7) say howvote_choicerelates to the master’s column of the same name (types = list(vote_choice = "recall")is the reported vote, as in the master).
qesR 0.7.1
In short: the codebook of the 2022 study now ships with qesR and works offline, like those of the other studies; the data and the harmonized results are unchanged.
qesR 0.7.1 ships the metadata of the 2022 Quebec Election Study (qes2022), as it does for the other studies: its codebook, question text, value labels and missing codes work offline, with no download. Until now qesR shipped none of it, because the study is licensed CC BY-NC 4.0, and built it at runtime from the user’s own copy of the data file. These files keep the study’s licence: the qes2022 content of the files listed in inst/COPYRIGHTS (section 2) is CC BY-NC 4.0, with the attribution given there, and is not covered by the MIT licence of qesR, which covers the package code only. get_qes() returns the same data as in 0.7.0, and the harmonized results are unchanged (specification 4.2.0: no row, map, gate, grade, marginal or column hash changed). The dictionary no longer lists the values of continuous columns such as weights, whose text differed across platforms and failed the live check of 0.7.0 on Linux.
qes2022 metadata ships with the package
-
qes_codebook("qes2022"),qes_question(),qes_search(),qes_missing(), the codebook attached byget_qes()and the soft-deprecated codebook functions (get_codebook(),format_codebook(),get_value_labels(),get_question()) work offline forqes2022: no data file is read or downloaded to describe it, and nothing is written to the cache. The shipped dictionary (version 1.1.0) holds its 718 variables with the labels of the data file, the value labels, the unweighted count of each code and the missing type of each missing-value code, and the question text of 464 variables (448 of them also in French), quoted from the study’s bilingual codebook (question_source = "codebook",doc_ref = "7449514:<question>"). The question of an item of a grid is the grid’s stem followed by the item in brackets (forcps_leadertherm_1, the leader thermometer’s stem followed by the leader’s name), and that of a typed-text box the question followed by the option that opens it (forcps_votechoice1_8_TEXT, “Which party do you think you will vote for? [Another party (please specify)]”, as in the harmonization specification). The 16 variables worded in English only are the codebook’s technical variables (start and end dates, durations, response id, language, riding). 1,294 value labels have their French wording from the codebook (label_fr): every labelled answer of a variable whose codebook entry gives the French options, soqes_search(lang = "fr")findsqes2022items by their French question text and French answer labels. - Results change: the question text of
qes2022is the codebook’s, never cut. Until 0.7.0 it was the variable label of the data file for every labelled variable, which the Stata format cuts at 80 characters (question_truncated = TRUEfor 461 of its 716 labelled variables,question_source = "file",doc_ref = "7449514"). The variables the codebook does not word (254: the display-order variables*_DO_*and*_ADO_*, the click timerstimer_govresp_*, the derivedpid_frandfr_pid_pr, and the four weights) havequestion = NA, as the variables of the other studies whose questionnaire does not word them; their label is unchanged. The labels, value labels, missing types and counts are those 0.5.0 to 0.7.0 built from the data file (identical, checked on the pinned file). -
qes_search()searchesqes2022like every other study: no study is “not searchable yet” any more, and the message for a study whose description does not ship (only a test catalog has one) is reworded. - The runtime “shard” is gone: qesR no longer builds
qes2022metadata from the data file or keeps it in the download cache.qes_cache_info()lists downloaded files only (itskindcolumn is always"file"). Theshardsfolder that 0.5.0 to 0.7.0 wrote into a disk cache is ignored, andqes_cache_clear()deletes it (all of it, or withstudies = "qes2022"orolder_than). The shippedinst/extdata/dict/shard_rules.csvis removed: its rules are applied to the shipped tables. - A printed codebook of
qes2022(print(qes_codebook("qes2022"))) and the harmonization reference (vignette("harmonization-reference", package = "qesR")and its French version) end their header with the licence notice of its metadata: the study, its authors, year and DOI, CC BY-NC 4.0 and the licence’s URL, that qesR adapted it (whereCOPYRIGHTSlists the changes and the files), and that it is not covered by qesR’s MIT licence. Printedqes_search()results that includeqes2022end with it. The same notice is the attributelicence_noticeof aqes2022codebook and ofqes_question()andqes_search()results that includeqes2022, so that a saved or exported copy keeps it, andqes_cite("qes2022")ends its text citation with the licence and its URL. - The catalog version is now 2.3.0:
metadata_shippedisTRUEforqes2022(and for every study of the catalog). A study’s metadata ships when it is released under CC0 or under CC BY-NC 4.0.
The dictionary lists codes, not the values of continuous columns
- The values table of the shipped dictionary (version 1.1.0) lists every labelled code of a variable, and every observed code of a numeric variable with at most 50 distinct codes only when they are all whole numbers and the variable is not a weight or an id. Until 0.7.0 it also listed the observed values of continuous columns with at most 50 distinct values, keyed by their text with 15 significant digits (
"1.43011282409"): the weights ofqes2007_panel(pond,pondam1,pond_tot_am1, …),qes1998andqes1998_createc(poids) andqes1998_crop(ponderc), and the proportions and residual ofqes2007_panel(prop_*,s_res_ML), 564 rows in all. That text depends on the platform and the R version, so the live check that the shipped dictionary describes the pinned files failed on Linux with R 4.6.1 (theliveworkflow on tag v0.7.0). Every listed code is now a whole number written without decimals, the same text everywhere; a test checks it, offline and on the pinned files. - Results change:
qes_codebook(layout = "long")gives these 23 continuous variables one row withvalue = NA, like any variable without value labels, instead of one row per distinct value. Their labels and the data are unchanged.
Harmonization specification 4.2.0
- The 18 crosswalk rows of
qes2022give their question wording in English and French (wording_en,wording_fr), quoted from its codebook, and its 65 value-map rows the value labels of the data file (source_label) instead of the md5 hashes of those labels.qes_spec("crosswalk")and the harmonization reference show them. -
gates.csvandexpected/marginals.csvgain the counts ofqes2022(252 gate cells, 225 marginal cells), which the offline checks now run on with the other studies, in the tests and in CI. They are the counts the build-ignored folderdata-raw/nc/of the source repository held until now, recomputed on the pinned file (identical). - The release check no longer forbids wording and labels for a study whose metadata does not ship. MINOR: keys are only added to
expected/.
Validation
- The recorded validation report (
inst/extdata/validation/validation_report.csv, in the source repository only, with the official results it is computed from) now holds the rows ofqes2022, and the weekly live test gates them like those of the other studies: its weighted recall index is 7.96. The weekly job uploadsinst/COPYRIGHTSwith the report, as the licence notice of itsqes2022rows. - The test fixture of the columns of qesR 0.4.4 (
tests/testthat/fixtures/v044-get-qes-names.csv) gives theNAcount of eachqes2022column, as it does for the other studies. The “What changed” table of 0.7.0 below now listsqes2022too.
Licence and CRAN
- The MIT licence covers the package code only.
inst/COPYRIGHTSsays so first, then lists the third-party content by source and licence: the CC0 study metadata; in section 2 the files that holdqes2022metadata, licensed CC BY-NC 4.0 and not MIT, with the attribution the licence requires (authors, title, DOI, licence and its URL, the changes qesR made); and the Statistics Canada census counts, with the notice of the Statistics Canada Open Licence. A test checks that the list matches the shipped files.DESCRIPTIONkeepsLicense: MIT + file LICENSE(andLICENSEits two lines); itsCopyrightfield and the end of itsDescriptionsay that the MIT licence covers the code only and point toinst/COPYRIGHTS, andLicense_restricts_use: yesandLicense_is_FOSS: norecord that the package as distributed holds CC BY-NC 4.0 content (which R’s licence database classes as restricting use and not FOSS).LICENSE.md, the licence page of GitHub and of the website, opens with the same statement before the MIT text. The README, the website’s home page,?qesR-packageand?qesR-frhave a short Licence section, in English and French. -
cran-comments.mdtells the CRAN team that these files carry CC BY-NC 4.0 and asks whether that is acceptable; if it is not, qesR can fetch them at runtime instead.
Documentation
- Installation: the README (in English and French), the website’s home page and the getting-started vignettes (
get-started,fr-demarrage) now give the GitHub installation first,remotes::install_github("ThomasGareau/qesR"), and noteinstall.packages("qesR")for once qesR is accepted on CRAN. - The website articles on support for sovereignty (
analysis-sovereignty,fr-analyse-souverainete) say that the wording ofqes2022in their Table 1 is quoted, in English and French and not truncated, from its bilingual codebook, under the study’s licence (until 0.7.0 it was its variable label, in English only, cut at 80 characters).
Development
- The weekly
liveworkflow keeps the originals in an absolute directory outside the checkout (${{ runner.temp }}/qesr-cache):actions/cacherejects the relative path../qesr-cacheof 0.7.0 (“Relative pathing ‘.’ and ‘..’ is not allowed”), so its cache was never saved. - The folder
data-raw/nc/is removed: the aggregates ofqes2022are in the shipped files.data-raw/build_sources.R,project_marginals.R,spec_check.R,build_hashes.Randbuild_validation.Rtreatqes2022like every study (QESR_REQUIRE_NCis gone), anddata-raw/compare_legacy.Rcounts it in NEWS. -
data-raw/extract_questions.Rdrafts the wording ofqes2022(data-raw/questions/qes2022.csv) and the French labels of its codes and its select-all items (qes2022_values.csv) from apdftotext -layoutcopy of the codebook (the item of a grid or the answer option that wraps onto the next line is read whole, an option whose “)” the PDF lost or that carries a footnote marker is read, a compound word cut after its hyphen at a line end, “ci-dessous”, is joined, andcps_Duration__in_seconds_, which the codebook spellscps__Duration_in_seconds_, is found); the drafts are markedreviewed = FALSEuntil checked by hand.data-raw/questions/missing_codes.csvholds the study-wide rule that-99is item nonresponse (the codebook, p. 7), andmissing_labels.csvthe labels that typeqes2022’s “don’t know” and “refused” codes.
qesR 0.7.0
In short: get_qes_master() and get_decon() are now built from the harmonized variables, so some of their values change; the harmonization covers all 11 studies and is checked against the official results and the census.
qesR 0.7.0 switches the legacy functions to the harmonization engine: get_qes_master() and get_decon() are now rendered from qes_harmonize() for all 11 studies of qesR 0.4.4, with the same arguments, the same columns in the same order and (for the master) the same types. The harmonization specification is version 4.1.0: 13 new targets, the remaining studies (qes2007, qes2008, the CROP polls and the 1998 panel), the renderer of the legacy columns, the weights documented for the studies that lacked them, and the sign-off of its rows after an automated double review against the original files and documents (not a human review). All 132 crosswalk rows are signed off and applied by default. The sign-off of a row is about its content: the recommended weights of qes1998, qes2007_panel, qes2012_panel and the CROP polls still need review: they are registered but NA until they are accepted, and they no longer hold back the answers of these studies. The harmonized studies are checked against the official results of Élections Québec and the census margins of Statistics Canada. get_decon() is soft-deprecated. get_qes() returns the same data as in 0.5.0.
Legacy switch: get_qes_master() and get_decon() from the engine
-
get_qes_master()keeps its arguments, its 30 documented columns in the order and with the types of qesR 0.4.4, and the columns appended in 0.5.0; each column is now rendered from the targets of the spec, study by study, as the new tablelegacy.csvof the spec says (qes_spec("spec")$tables$legacy: the target(s) and render of each column). The frozen 0.4.4 sources and the blank rules of the interim builders of 0.5.0 are gone. A code the spec does not map isNA, never passed through, and every row of every file is kept (40,987 rows). A message says so once per session (classqesR_message_values_changed). The engine applies only the crosswalk rows signed off by a reviewer, asqes_harmonize()does by default (see the sections on specifications 4.0.0 and 4.1.0): a column whose question is in a row still in review isNA, with the reasonnot_reviewedinattr(, "legacy_na_columns")and a message (classqesR_message_values_changed) naming it;attr(, "source_map")gives each column’s question, target, value map, grade and review status in each study. -
vote_choiceandturnoutare the reported vote and turnout in every study that asked them:qes2022from its post-election wave,qes1998from its post-election recontact, the 2018 panel’sturnoutfrom its turnout question (rts_q1) rather than the vote question. Nonvoters, spoiled ballots, “don’t know” and refusals areNAinvote_choice: the 0.4.4 categories “Did not vote / None” and “Don’t know / Refused” are gone (turnoutsays who voted). The pooled CROP polls asked only vote intentions and stayNA;vote_intenthas them. - Columns filled where the spec has the question:
ideologyforqes2014(with its answers 0 and 10) andqes2018;political_interestforqes2018(the four-point item as 10, 7, 3 and 0);born_canadaforqes2018;provincial_pidforqes2007,qes2008,qes2012,qes2014andqes2018;languageforqes2014(the mother tongue, not the language of the interview);incomeforqes2012;year_of_birthforqes2022;ageandage_groupfor theqes2018respondents who gave their age but not their year of birth, andage_groupfor theqes2008respondents without a year of birth. - Values corrected:
age_grouphas six bands in the 2007 and 2012 panels, whose questions have them (0.4.4 used a three-band recode);educationis University for maîtrise (qes2007,qes2008) and College/CEGEP/Technical for the 2018 panel’s trade certificates, labels 0.4.4 left as they were, and forqes2012puts code 8 (the French questionnaire’s cours technique) in College and leaves code 10 (Certificate and diploma, in neither questionnaire)NA;languageisNAfor the respondents ofqes2007who report two first languages (they are assigned to neither);province_territoryis “Quebec” for the 2018 panel (0.4.4 put its region there);incomeamounts ofqes2022are written in full (100000, not1e+05). -
respondent_idjoins each study’s identifier variables: inqes2007_panelthe project code and the questionnaire number (nompn-quest, such as1-3232, unique;questalone repeats across its two subsamples), inqes2018_panelthe interview mode and id (method-id, such as1-20012; 0.5.0 made upqes2018_panel_<row>). The oneqes2007_panelrow that belongs to no wave (neither interview completed, by its disposition codes) is nowNAin every answer column. - Appended columns (never removed):
family,study_design,waves,subsample,source_row(the row in the study’s file, to join raw variables fromget_qes()),weight_preandweight_post(the spec’s recommended weights, as deposited;NAwhere they are not reviewed yet),vote_intent(vote intention at the first question),turnout_intent(likelihood of voting,qes2022) andsov_partnership_1995(the 1995 referendum question, 1 or 0). Theregion_cma3,region_adminandincome_rankcolumns of the design are not appended yet: their targets are not in the spec. - New attributes and changed ones:
legacy_column_mapgainsrenderand gives instudies_changedthe studies whose values differ from qesR 0.4.4;legacy_na_columnsgives, for each column that is allNAin a study, why (no_source,not_reviewed,na_columnorall_missing, with the rule behind it incause(reported_vote_only,independence_question_only,no_valid_source,not_harmonized_yet,invalid_044_source,not_comparable_source,not_signed_offorweight_needs_review) and why in words inbasis; 0.5.0’s reasonblankedis gone, since no value is blanked after it is read);source_mapgainstarget,map_id,grade,status,renderandspec_version;qes_specrecords the spec version and content hash;qes_provenancehas the cell and spec levels ofqes_provenance(). -
get_decon()is soft-deprecated: it keeps working and will not be removed, and a message names its replacement,qes_harmonize(srvy, targets = "decon"), once per session (classqesR_message_deprecated). Its 19 columns are rendered from the same targets: categorical columns are factors with the targets’ English levels (the same levels in every study:"Man"rather than"A man"),educationhas the four groups of the master,province_territoryis"Quebec",yobis a number,incomeandreligionare each study’s own categories as text (theqes2022amount stays a number),ageis in years (the age band, a factor, for the panels andqes1998, which asked bands), and codes the spec does not map (the-99ofqes2022) areNA.turnoutandvotechoiceare the reported turnout and vote for every study that asked them (qes2018fromq5andq6); forqes2022they are still the campaign-period likelihood of voting and vote intention, whichattr(, "timing")says ("pre").get_decon("qes1998")returns its rows, with its answers from the specification.
What changed, by column and study
Values (non-missing cells) of the get_qes_master() columns that changed in 0.7.0, by study, in qesR 0.4.4, 0.5.0 and 0.7.0 (the counts of qes2022, published since 0.7.1, are CC BY-NC 4.0 like its other metadata). The 0.4.4 counts are over the rows 0.4.4 kept (it dropped 380 qes2007_panel rows and 1 qes_crop_2007_2010 row). Columns and studies not listed are unchanged since 0.5.0.
| Column | Study | 0.4.4 | 0.5.0 | 0.7.0 | Why |
|---|---|---|---|---|---|
year_of_birth |
qes2022 |
0 | 0 | 1,519 | cps_yob read as the year its value label gives (codes 1-91 are labelled with years) |
income |
qes2022 |
1,521 | 1,482 | 1,444 | An amount of 0 is a blank that the survey sent to the bracket follow-up cps_income2: a missing value (spec 4.0.0); amounts written in full (100000, not 1e+05) |
turnout |
qes2022 |
1,322 | 0 | 1,215 | Reported turnout from the post-election wave (pes_turnout) |
vote_choice |
qes2022 |
1,432 | 0 | 1,101 | The reported vote of the post-election wave (pes_votechoice) |
federal_pid |
qes2022 |
1,521 | 1,521 | 1,520 | -99 (item not answered) is a missing value (0.4.4 category Don’t know / Refused) |
vote_choice_timing |
qes2022 |
0 | 1,521 | vote_choice now holds the reported vote in this study | |
age |
qes2018 |
3,031 | 3,031 | 3,072 | The age item (agenum) of the respondents who gave no year of birth |
age_group |
qes2018 |
2,802 | 2,802 | 2,839 | Age bands of the respondents who gave their age (agenum) but no year of birth |
born_canada |
qes2018 |
3,072 | 0 | 3,047 | q69 with Canadian-born people outside Quebec as Yes (0.5.0 blanked the 0.4.4 coding) |
political_interest |
qes2018 |
3,012 | 0 | 3,012 | q27 on 0-10 with the corrected conversion (10, 7, 3, 0) |
ideology |
qes2018 |
0 | 0 | 2,490 | q36_1 (0.4.4 found no source) |
vote_choice |
qes2018 |
2,207 | 2,207 | 2,016 | Nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
provincial_pid |
qes2018 |
0 | 0 | 2,655 | Provincial party identification of the spec (0.4.4 found a source only in qes2022) |
respondent_id |
qes2018_panel |
1,250 | 1,250 | 1,250 | The file’s identifier (interview mode and id, qes_id) instead of a made-up |
province_territory |
qes2018_panel |
1,250 | 1,250 | 1,250 | Quebec for every respondent; 0.4.4 put the region (Couronne, ROQ) in the province column |
education |
qes2018_panel |
1,250 | 1,247 | 1,247 | The trade certificate (d3 code 4), left as a label by 0.4.4, is College/CEGEP/Technical |
turnout |
qes2018_panel |
828 | 828 | 842 | The turnout question (rts_q1) instead of the vote question: 14 who went to vote but did not know for whom are 1 and 13 who went to vote and spoiled their ballot are 1, not 0 |
vote_choice |
qes2018_panel |
842 | 842 | 704 | Nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
language |
qes2014 |
1,517 | 0 | 1,402 | The mother tongue (QLANG); 0.5.0 blanked the interview language (LANG) |
ideology |
qes2014 |
1,053 | 0 | 1,195 | Q32 with its 0 and 10 answers (0.5.0 blanked the truncated 0.4.4 column) |
vote_choice |
qes2014 |
1,352 | 1,352 | 1,283 | Nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
provincial_pid |
qes2014 |
0 | 0 | 1,383 | Provincial party identification of the spec (0.4.4 found a source only in qes2022) |
education |
qes2012 |
1,500 | 1,500 | 1,400 | scol code 10 (Certificate and diploma) is in neither questionnaire: not mappable; scol code 8 (the French questionnaire’s cours technique) is College, not University |
income |
qes2012 |
0 | 0 | 1,357 | The income brackets of reven (0.4.4 found no source) |
vote_choice |
qes2012 |
1,369 | 1,369 | 1,274 | Nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
provincial_pid |
qes2012 |
0 | 0 | 1,361 | Provincial party identification of the spec (0.4.4 found a source only in qes2022) |
age_group |
qes2012_panel |
844 | 844 | 844 | Six age bands, which the question has, instead of the producer’s three-band recode |
vote_choice |
qes2012_panel |
844 | 844 | 633 | Nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
age_group |
qes2008 |
1,125 | 1,125 | 1,151 | The study’s own age bands (q0age) where the year of birth is missing |
education |
qes2008 |
1,141 | 1,141 | 1,141 | maîtrise (q77 code 10), left as a label by 0.4.4, is University |
vote_choice |
qes2008 |
986 | 986 | 898 | Nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
provincial_pid |
qes2008 |
0 | 0 | 1,083 | Provincial party identification of the spec (0.4.4 found a source only in qes2022) |
language |
qes2007 |
2,172 | 2,172 | 2,119 | Two first languages including French or English (codes 4, 5 and 7) are not mapped to one of them |
education |
qes2007 |
2,160 | 2,158 | 2,158 | maîtrise (q77 code 10), left as a label by 0.4.4, is University |
vote_choice |
qes2007 |
1,990 | 1,990 | 1,727 | Nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
provincial_pid |
qes2007 |
0 | 0 | 2,069 | Provincial party identification of the spec (0.4.4 found a source only in qes2022) |
respondent_id |
qes2007_panel |
2,062 | 2,442 | 2,442 | The project and questionnaire number (nompn-quest, qes_id), unique, instead of quest, which repeats across the two subsamples |
language |
qes2007_panel |
2,057 | 2,435 | 2,434 | The one row in no wave (neither interview completed, by its disposition codes) is not_in_wave |
age_group |
qes2007_panel |
2,062 | 2,441 | 2,440 | The one row in no wave (neither interview completed, by its disposition codes) is not_in_wave; six age bands, which the question has, instead of the producer’s three-band recode |
gender |
qes2007_panel |
2,062 | 2,441 | 2,440 | The one row in no wave (neither interview completed, by its disposition codes) is not_in_wave |
education |
qes2007_panel |
2,053 | 2,430 | 2,429 | The one row in no wave (neither interview completed, by its disposition codes) is not_in_wave |
income |
qes2007_panel |
2,062 | 2,033 | 2,032 | The one row in no wave (neither interview completed, by its disposition codes) is not_in_wave |
turnout |
qes2007_panel |
1,735 | 2,055 | 2,054 | The one row in no wave (neither interview completed, by its disposition codes) is not_in_wave |
vote_choice |
qes2007_panel |
2,062 | 2,055 | 1,494 | The one row in no wave (neither interview completed, by its disposition codes) is not_in_wave; nonvoters, spoiled ballots, don’t know and refusals are missing values of the reported vote (0.4.4 categories Did not vote / None and Don’t know / Refused) |
vote_choice |
qes1998 |
1,483 | 0 | 1,126 | The reported vote of the post-election recontact (q3post) |
vote_choice_timing |
qes1998 |
0 | 1,483 | vote_choice now holds the reported vote in this study |
The comparison with qesR 0.4.4 in the source repository, generated by data-raw/compare_legacy.R, gives every difference from qesR 0.4.4 and 0.5.0, cell counts by column and study for both functions, and its cause; the gate of this release is that each one is explained. Results of the released 0.4.4 are reproducible by installing it (remotes::install_github("ThomasGareau/qesR", ref = "v0.4.4")); 0.5.0 and 0.6.0 were development versions and were never released, so a result computed with one of them is reproducible by installing the commit it was built from (packageDescription("qesR")$RemoteSha records it for an installation from GitHub).
Harmonization specification 4.1.0: content sign-off apart from the weights
- The 38 crosswalk rows of
qes1998,qes2007_panel,qes2012_paneland the CROP polls that the automated double review of spec 4.0.0 signed off on their content arestable, andqes_harmonize(),get_qes_master()andget_decon()apply them by default. They had been held in review only because the recommended weights of their waves need review. Theirreview_notestill says the review was automated, not human, and that the weight is tracked apart. No value map, gate, level set or column hash changed (MINOR: rows are only added to the default output). - The release check no longer fails a stable row on a study-wave whose recommended weight needs review: the sign-off of a row is about its content, and the weight is reviewed on its own. It still requires that a recommended weight is never calibrated on vote or turnout, and that each study-wave has exactly one (none where every weight is calibrated).
- The weights that still need review are still not applied:
weight_preandweight_postofqes_harmonize()areNAfor these waves, with the messageqesR_message_weight_review, andqes_design()leaves their rows out (or stops when no row has the weight). Inget_qes_master(),weight_preandweight_postof these studies areNAwith the reasonnot_reviewedand the new causeweight_needs_reviewinattr(, "legacy_na_columns"), whosebasisnames the weight (they wereno_source).survey_weightkeeps each study’s own weight, as in 0.4.4, and is not reviewed: it is theneeds_reviewweightXPONDfor the CROP polls andpond(registeredneeds_review) forqes2012_panel, the vote-calibratedpondforqes2008,pond, which is not in the weights registry, forqes2007_panel, andponderc, the deposit’s advice (registeredneeds_review; the spec’s candidate isponder3), forqes1998.qes_design()never uses it; the note of each study’ssurvey_weightrow inqes_spec("spec")$tables$legacysays which weight it is. -
qes2014QSEXE(gender) isstableatcomparable, the grade it had before the review; the English stem the review filled is kept. The review’sidentical(the anchor’s stems in both languages) waits for a human second reviewer. - Every legacy column that was
NAbecause its row was held has its values again (the table above);genderofqes2014and ofqes_demotoo. - The message
qesR_message_weight_reviewand thebasisofweight_needs_reviewsay what is pending (the weight is registered but not accepted yet), not that its method is undocumented:XPOND’s method is in CROP’s reports.qesR_message_weight_timing(useweight_prefor pre-election targets andweight_postfor post-election ones) is no longer shown for a study whose weights all need review, since both columns areNAthere. - Text only (
legacy.csv): the note of each study’ssurvey_weightrow names the weight and its registry status, and the intended blanks ofget_qes_master()have their own cause andbasisinattr(, "legacy_na_columns"):invalid_044_sourceforqes1998educationandqes2018incomeandreligion(the source 0.4.4 read was verified wrong, and the spec has no row for them since),not_comparable_sourceforqes2012_panelpolitical_interest(its only source,interetrec, is a score derived from debate viewing, gradednot_comparable), andnot_harmonized_yetforqes2022language.
Harmonization specification 4.0.0: review sign-off
- An automated double review checked the 132 crosswalk rows against the original files and documents: one pass on the codes and the data, one on the wording and comparability, adjudicated where the two disagreed. It is not a human review, and the crosswalk says so:
qes_spec("crosswalk")showsreviewed_by,reviewed_onand the new columnreview_note(what the review corrected, and why a row is held). 93 rows are signed off (statusstable) andqes_harmonize()applies them by default. 39 stay in review, applied only withinclude_draft = TRUE: the 38 rows ofqes1998,qes2007_panel,qes2012_paneland the CROP polls, whose recommended weights still need review (a stable row on such a study failed the release check of spec 4.0.0), and theqes2014gender row, whose grade the review raised toidentical, which needs a second reviewer. Spec 4.1.0 (above) signs off all 39. The validator requires areview_noteon a reviewed row left in review. -
get_qes_master()andget_decon()apply the signed-off rows only. In spec 4.0.0 (not in 4.1.0), every column thatqes1998,qes2007_panel,qes2012_paneland the CROP polls take from the specification wasNA(reasonnot_reviewed, causenot_signed_off), as wasgenderofqes2014; their identifiers, dates and weights, read from the files, are kept. A render no longer falls back on a later target when the first one’s row is held. - MAJOR (changed column hashes): in
qes2022, an income amount of 0 is a blank that the survey sent to the bracket follow-upcps_income2, and is nowNA(reasonno_answer; also in theincomeofget_qes_master()andget_decon()); the typed text of another party (vote_prov_intent_other) is gated oncps_turnoutlike its parent row, so the respondents routed past the question areinapplicable(ineligiblefor code 6) rather thansysmis. - Grades: the 2018 panel’s vote intention
rv1aand its pushed versionrv1abgo fromcomparabletoapproximate(the question also asks those who already voted in advance for that vote, as Ipsos’s report on the same poll prints it, and the push was not put to those who would not vote);qes2014QSEXEwent fromcomparabletoidentical(held for a second reviewer; back tocomparablein spec 4.1.0). With the defaultmin_grade = "approximate"no cell is dropped. - Metadata:
qes2014QSCOLoffered no don’t-know option (dk_offered = none);qes2022cps_ideoself_1is a plain 0-10 item (no slider is documented: instrumentlr_0_10); the CROP intentionsintvoteprovaandintvoteprovhave their French wording anddk_offered = volunteeredfrom CROP’s own reports for La Presse. The dictionary (version 1.0.3) has the same wording, and the 2018 panel’srv1athe stem’s second sentence on advance voting. - Text only: the wording, grade reasons, evidence and notes of 36 rows are corrected, each named in its
review_note: among them the English stems of items ofqes2007,qes2008,qes2014andqes2018, the counts of wave members inqes2007_panel(its system-missing respondent is in both waves, and the one row in no wave has values) and where its time-invariant items were asked (the CROP campaign interviews, recorded for every row). - Schema version 2: the crosswalk’s new column
review_note. Ingates.csv, typed text counts as one token and an empty text as system missing.
Harmonization specification 3.0.0
- MAJOR (a corrected weight role and changed recommended weights).
qes2008’spondreproduces the official 2008 vote among voters (to within 0.1 point) and is registered as calibrated on the vote (vote_calibrated), soqes2008has no recommended weight: both of its weights are calibrated on vote or turnout, and its harmonized weights (weight_post, andweight_postofget_qes_master()) stayNA. The validator now allows a study-wave with no recommended weight in that case only. - Reviewed weights, so
qes_harmonize()andget_qes_master()(weight_pre,weight_post) now return them, where they wereNA:qes2007pond(sex, age, mother tongue and region, to the census, applied separately to the web and telephone samples, as the study’s authors describe it in Le comportement électoral des Québécois, 2009, and as the file confirms), and the 2018 panel’sweightandweight_rts(sex, age, region, mother tongue and university education, as Ipsos’s report and Durand and Blais (2020) describe them, each wave weighted separately; the weighted margins reproduce the published targets). The 2007 panel’s post-election wave has a recommended weight,pond_tot_am1, which still needs review. - The weights that still need review say what the files and the documents found show: for
qes1998,ponder3 = ponderc x poids / c(one constant per firm) andpoidsis the design factor of the stratified recontact;ponder3reproduces the authors’ published tables, but the deposit advisesponderc, which does not undo the over-selection, so the choice waits for the producer; CROP’sXPOND(weighted to the 2006 Census by sex, age, region and home language, from CROP’s reports); the cells of the 2012 panel’s weights; the margins ofqes2012pond(probably the 2006 Census, not the 2011 one). - Waves (
qes_spec("spec")$tables$waves): the fieldwork dates of each of the 24 CROP polls (CROP’s reports for La Presse and the archive of quebecpolitique.com, instead of the month’s bounds), the election their previous-vote questionQP4refers to (2007-03-26 until November 2008, 2008-12-08 from January 2009), and what is known of the wording of their first referendum questionintvoterefa(a sovereign country, documented for 19 of the 24 polls; it stays unmapped, since a crosswalk row applies to every poll and five polls are not documented); the 2018 panel’s fieldwork dates (2018-09-26 to 09-28 and 2018-10-12 to 10-19); theqes1998definition of francophone, now confirmed by linking the pooled rows to the firms’ files (CREATEC: mother tongue French; CROP: French spoken most often in the household); what the questionnaires say about theqes2008mode (still telephone, as the deposit says, until the authors confirm it). - Text only:
qes2018mother tongue codes 2 (English) and 96 (other) are not swapped (checked against home language, birthplace and open answers); the English share over the census comes from the sample and a weight that calibrates French against the rest only. Thesov_sovereign_countrydescription says the push question put to the undecided is not part of the target. The notes ofget_qes_master()andget_decon()for the CROP sovereignty, turnout and vote columns, and forqes1998languageandsurvey_weight, say the same. No value map, gate, grade, expected marginal or column hash changed. - Validation: the census margins of 2006 and 2011 are now at 18 and over for gender and the six age bands (the 2011 Census Profile, 98-316-XWE2011001, and the 2006 table 97-551-XCB2006009), as for 2016 and 2021, instead of 20 and over and five bands from 25; the report adds weighted rows for
qes2007and the 2018 panel.
Harmonization specification 2.0.1
- Text only. The
causecolumn ofattr(, "legacy_na_columns")ofget_qes_master()andget_decon()names the rule behind an all-NAcolumn in words:reported_vote_only(the CROP polls asked only a vote intention),independence_question_only(the study did not ask the referendum question on an independent country),no_valid_source(party_best,party_lean,partylean) andnot_harmonized_yet(vote_choice_text), instead of internal decision ids, which were not documented anywhere a user could look them up. The definitions and notes ofattr(, "legacy_column_map")and the text of the spec say each decision in words as well. - The reason
sovereignty_supportandsovereigntyareNAis corrected for two studies:qes2007_panelasked the 1995 question (sovereignty with an offer of partnership, insov_partnership_1995), asqes2007,qes2008andqes1998did; for the CROP polls, the first referendum question (intvoterefa) has only a truncated label and no questionnaire is deposited, so its wording is unknown and it is not mapped. The values are unchanged (NAin both).
Harmonization specification 2.0.0
- MAJOR (a corrected gate): the
religionrows ofqes2012(q103) andqes2014(Q63) are gated on their filter question (q102,Q62). The respondents who said they belong to no religion areNAwith reasoninapplicable(842 inqes2012, 845 inqes2014) and those who preferred not to answer the filter questionrefused(32 and 44, with the 4 and 9 who refused the religion question itself), where every one of them wassysmis. The values and the other NA reasons are unchanged, andget_qes_master()andget_decon()are unchanged (theirreligionisNAfor these respondents, as before). A gate now applies to rulesweight,dateandstringas it does tomapandnumeric(it was ignored there), and a gate on a rule that cannot apply it (constant,fn:) is a validation error;gates.csvcounts the cells of these gated rows for the universe check. - The reported turnout of
qes2007_panelreads the post-election turnout question itself (voteoui: yes on election day, yes in advance, no), in place of the producer’s recodeavote, which gives the same values; the row is gradedcomparable(the same yes/no question with a probe asqes2012_panel), notapproximate, somin_grade = "comparable"keeps it. - Text only: the
education4rows ofqes2018andqes2018_paneland the target’s description (EN/FR) say that a vocational or trade credential (the DEP) is secondary inqes2018and college inqes2018_paneland, probably, in the studies with no DEP option; the note of theqes1998languagecolumn ofget_qes_master()says the constant French is a mother tongue only for the CREATEC rows (the home language for the 426 CROP rows; the pooled definition is not confirmed yet); theqes2022evidence notes give codes and codebook pages only.
Harmonization specification 1.0.0
- 13 new targets, with 58 crosswalk rows checked against the original files and documents (all in review):
gender,education4(primary or less, secondary, college, university),lang_mother(a respondent who reports two first languages isNAwith reasonnot_mappable: assigned to neither),born_canada,income_nativeandreligion(each study’s own categories, as text: they are not comparable across studies),pid_fed,interest_0_10,interest_election_0_10andinterest_campaign_4pt(interest in politics on 0-10, in the election, and in the campaign on four points: never pooled withinterest_4ptor with each other),age_group6,turnout_prov_likelyandvote_prov_intent_other(qes2022); and the 2007 panel’s reported turnout. The new setdeconnames the targets ofget_decon():qes_harmonize(targets = "decon"). - A crosswalk row of rule
stringmay takefrom_label = TRUE: the value is the text of the code’s value label in the file (a code without a label is unmapped, never passed through as a number). - A crosswalk row may name the wave
*for a target of timingstaticorany: it applies to the members of any wave of the study, for a question asked in whichever wave the respondent took part. The 2007 panel’s time-invariant items (gender, education, mother tongue, income, age group) use it, so its 391 respondents reached only after the election now have them; this moves its existingage_group3row from the pre-election wave to*, which makes the specification a major version (1.0.0): theage_group3column ofqes2007_paneland itseligible_voterchange for those respondents. No other existing row, code, grade, marginal or column hash changed. - The table
legacy.csv(the renderer ofget_qes_master()andget_decon()) is part of the specification and of its content hash, checked by the validator.Engine-Minis 0.7.0. - The check that a row graded
identicalmatches its target’s anchor now finds the anchor when two targets read the same question (the three and six age bands of one question); it skipped those targets before.
New: validation against official results and the census
- Benchmarks are in
inst/extdata/validation/of the source repository, each row with its source: province-wide votes and shares of valid votes by party and ballots cast for the general elections of 1998 to 2022, from Élections Québec’s archived results files (official_results.csv,official_turnout.csv; the parties are in the spec’s party levels, every other party and the independents one row “other”); and the Quebec margins of gender, age, mother tongue and education of the censuses of 2006, 2011, 2016 and 2021, from Statistics Canada tables 98-10-0020-01, 98-10-0218-01 and 98-10-0384-01, the 2011 Census Profile and the 2006 table 97-551-XCB2006009 (census_margins.csv). Gender and age are compared at 18 and over (from specification 3.0.0 for 2006 and 2011, with the 2011 Census Profile and the 2006 table 97-551-XCB2006009); mother tongue at 20 and over and education at 25 and over, the nearest published cuts, and respondents are cut the same way when their age is known.data-raw/build_benchmarks.Rrebuilds them from the downloaded source files. Only the census margins ship with the package (system.file("extdata", "validation", package = "qesR"); Statistics Canada Open Licence): Élections Québec’s open-data licence covers only its 2014, 2018 and 2022 files and can be withdrawn, and its terms of use need its written permission for the other years, so the official results and the validation report computed from them are excluded from the package build and used only on the source tree (inst/COPYRIGHTS, section 3). Without them the internal checks skip the recall and turnout comparisons. - The validation report (
inst/extdata/validation/validation_report.csv, written bydata-raw/build_validation.R) records, for every study the spec covers whose metadata ships: the dissimilarity index between the reported vote and the official results, the reported minus the official turnout, the dissimilarity index of each census margin, and the construct-validity directions, weighted with the recommended weight where it is reviewed (qes2012,qes2014,qes2018, and from specification 3.0.0qes2007andqes2018_panel) and unweighted otherwise. The weighted recall indices are 8.0 points (qes2012), 5.6 (qes2014) and 3.2 (qes2018), the provisional values of the design now confirmed on the official figures;qes1998(francophones only) is not checked. Turnout is over-reported in every study, by 14 to 30 points. The rows ofqes2022(CC BY-NC) do not ship; they are kept with the build-ignored aggregates indata-raw/nc/. - Live checks (never on CRAN;
QESR_TEST_DATA_DIRorQESR_LIVE=true):tests/testthat/test-validation-live.Rfails when a weighted recall or census index rises more than 2.0 points above its recorded value, when turnout over-reporting leaves 0 to 35 points, when a construct-validity direction fails (voters more interested than nonvoters; QS voters left of CAQ voters; independence support of PQ voters more than 40 points above PLQ voters; at least 55% of partisans voting for their party), or when a deposit has a new version or a changed data file on Dataverse. A weekly workflow (.github/workflows/live.yml) runs every live test on the pinned originals, restored from the Actions cache, and uploads an aggregates-only report; itsqes2022rows (CC BY-NC 4.0) go with their licence and attribution notice,data-raw/nc/README.md, which also covers the build-ignoredqes2022aggregates of the repository (inst/COPYRIGHTS, section 2). A recorded value moves only throughdata-raw/build_validation.R, which refuses to record a gated row more than 2.0 points above the value it replaces unless run with--accept-regressions. - New website article “Validation against official results and the census” (French: “Validation par les résultats officiels et le recensement”), built from the downloaded files: the tables and figures of the report for every study,
qes2022included, and what the differences mean (turnout over-reporting, recall and differential nonresponse, unlisted parties, population and question differences).
New: the remaining studies (experimental; specification 0.3.0, now part of 1.0.0)
-
qes2007andqes2008(Quebec Election Studies, so they are in the defaultstudies = NULL): reported vote and turnout, party identification, year of birth, the 1995 sovereignty-partnership question and, for 2008, age group. In both, code 3 of the vote question is the ADQ, never the CAQ, and “none” among those who said they voted is a spoiled or blank ballot (reasonspoiled).qes2007mixed telephone and web interviews; itssurvey_modeis each respondent’s (1,003 by telephone, 1,172 on the web). -
qes2012_panelgains vote intention (first question and pushed; gradedapproximate, since its first question already asks which party the respondent “would be tempted to vote for”), reported vote (unprompted,approximate) and turnout; its post-election interviews are dated (interview_datein the long layout, fromResLastCallDate_last). - The pooled CROP polls of 2007-2010 (
qes_crop_2007_2010) have one wave per monthly poll, 24 in all (poll_2007_06topoll_2010_01, about 1,000 respondents each), each a stratum of its own (the new leading columnstratumgives the poll’s wave name) and each referring to the next general election, whichelection_dategives row by row (the election of 2008-12-08 up to the poll of November 2008, that of 2012-09-04 after);yearis the year each poll began.qes_design(pool = "equal")counts the 24 polls as one study in the long layout, as in the respondent layout. The spec maps their questions once for every poll: a crosswalk or weight row may name the wave*in a study whose waves are all poll waves, and each poll keeps its own weight normalization. Vote intention (first question and pushed) and age group are mapped; the pushed intention’s code for “none” also holds 74 answers “another party” and isnot_mappable. The referendum item and the previous-vote item are not mapped: their wording and reference election are not documented. - The 1998 panel (
qes1998) pools the CREATEC (1,057) and CROP (426) polls of francophones, with a pre- and a post-election wave of the same 1,483 respondents. The firm is a stratum: the new leading columnstratumgives it as the file’sfirme_postcode ("1"= CREATEC,"2"= CROP;NAfor a study drawn as one sample), andqes_design()now makes each study’s strata strata of the design. Reported vote and turnout, the pushed vote intention (the firms’ first questions are separate variables, not mapped), the 1995 question (asked by CROP only;inapplicablefor CREATEC) and age group are mapped;intvote2, whose value labels are shifted in the source, is documented asnot_comparableand never mapped. The firms’ own files (qes1998_crop,qes1998_createc) hold the same respondents and are not harmonized separately. - New target
sov_partnership_1995: the 1995 referendum question, sovereignty with an offer of partnership to the rest of Canada (qes2007,qes2008,qes2007_panel,qes1998), never pooled with the other sovereignty targets. - The deposits of these studies do not document which weight to apply: all their weights are registered with status
needs_reviewand areNAuntil reviewed. Forqes1998this matters: both firms’ codebooks (files 332049 and 332050) say that the undecided, those who would spoil their ballot and those who refused to reveal their vote were over-selected, so unweighted 1998 estimates over-represent the undecided, would-spoil and refusers. The coverage page and the weight message give the 24 polls as one range. - The dictionary (version 1.0.2) has the question text of the new items of
qes2012_panel,qes1998and the CROP polls (the CROP codebook gives only variable labels cut after a few words, marked as truncated).
Documentation and website
- The vignette on moving from qesR 0.4.4 is now
vignette("migrating-0.7", package = "qesR")(French:vignette("fr-migrer-0.7", package = "qesR"); formerlymigrating-0.5andfr-migrer-0.5) and covers 0.4.4 to 0.7.0, for scripts and papers written with 0.4.4: what changes by use (raw codes, label text, the master,get_decon()), how to record and pin the version a result used, the columns of the engine-renderedget_qes_master()one by one, and how to move master code toqes_harmonize(). - The website’s home page leads with harmonization: an example, what the engine guarantees, the full grid of the grade of each study’s question for each target across the 11 studies (generated from the specification by
data-raw/readme_coverage.R, each target linked to its reference section;data-raw/spec_check.Rchecks it is current, as for the README), and links to the validation and migration articles, in English and French. - The reference index has its final groups, one per help-page family: Package overview, Data (
get_qes(),get_qes_master()), Studies and documents, Codebooks and search, Harmonization (experimental), Reproducibility, Cache, and Legacy and deprecated, headed by the table of replacements.?qesRand?qesR-frlist the functions in the same groups.get_preview()andget_qescodes()join thelegacyfamily, andget_qes()andget_qes_master()form thedatafamily.data-raw/check_site.Rfails when a family is split across sections or a section mixes families. - The example articles use the master of 0.7.0: the reported vote now includes
qes2022,qes1998(shown as the 1998 polls) and the Conservative Party (PCQ); which parties ran comes from the official results of the source repository and which answers each question offered from the specification, instead of lists typed into the article, so the 1998 row no longer shows every party as not running.
Changes to messages and printing
-
qes_harmonize()with the defaultinclude_draft = FALSE, when no cell of the result is applied because every crosswalk row it needs is not yet signed off (a call onqes1998alone, for example), now warns (classqesR_warning_all_unreviewed) instead of only sending a message: the warning gives the number of values leftNA, names the targets and says to passinclude_draft = TRUE. When some cells are applied, the message (qesR_message_unreviewed_skipped) and the printed summary count the values leftNAas well as the cells. Which rows apply by default is unchanged. -
qes_studies()returns a data frame of classqes_studiesthat prints compactly (study, year, family, design, licence and the title in the session language, plusstatusaftercheck_updates = TRUE);qes_cache_info()returns one of classqes_cache_infothat prints the mode, the directory and the total size once, then the paths relative to the cache directory. Columns and values are unchanged,as.data.frame()gives the plain data frame, and a subset of the columns is a plain data frame. -
print()of a codebook names, on itsData file:line, the original file thatget_qes()reads andqes_provenance()reports (Quebec Election Study 2014.sav), then the Dataverse name of the ingested copy that the attributeselected_data_filekeeps, as in qesR 0.4.4. - The once-per-session note on changed values names the columns and attributes of the function that shows it: for
get_decon(),turnoutandvotechoiceand the attributessource_mapandtiming(get_decon()has nolegacy_column_map). - The notice for
get_decon()names its replacement with the study,qes_harmonize(srvy, targets = "decon"), as?qesR-deprecatedand the migration vignettes now do (withoutsrvy,qes_harmonize()harmonizes every study). -
qes_harmonize(data = )given a data frame without the study’s identifier column (such asresponseidforqes2018) says that the column was dropped fromdata$<study>and is needed to buildqes_id, instead of saying that the study lacks the variable (still classqesR_error_unknown_variable).
Bug fixes
-
qes_missing()onqes2022now sets-99(“no answer”) toNAin every numeric column, as documented, not only in columns whose-99is labelled or that have at most 50 distinct values: the unlabelled thermometers,cps_income,cps_yoband the like kept-99as a number (somean(qes_missing(x)$cps_income)disagreed withget_decon()).qes_codebook("qes2022")lists the code inmissing_codes, and a select-all item keeps its-99(“not selected”) by default. The metadata shard has a new schema version, so a shard already in the cache is rebuilt. - Numbers written to CSV (
get_qes_master(save_path = "x.csv")and its_provenance.csv, the metadata shards) always use.as the decimal mark: underoptions(OutDec = ",")a weight was written as0,67...and split into two fields. -
rbind()ofqes_harmonize()results refuses (an error of classqesR_error_input) parts built with differentlang,layout,values,missingorweights, which it stacked silently (a French and an English result gave every category twice), and parts with different columns (differenttargets), naming what each part lacks, instead of a bare base-R error. -
get_qes(),get_qes_master()andget_decon()check their logical arguments (assign_global,with_codebook,quiet,strict): a value that is notTRUEorFALSE, such asassign_global = "TRUE", is an error of classqesR_error_inputinstead of being taken asFALSE. -
qes_search(regex = TRUE)checks the pattern with the engine that matches it (Perl-compatible): lookaheads and named groups are accepted, and a pattern PCRE cannot compile is an error of classqesR_error_input, not a bare regex error. - A bad
styleinqes_cite()orlayoutinformat_codebook()is an error of classqesR_error_input. -
qes_cache_clear(older_than = )also forgets the metadata held in memory for the files it removes, soqes_search()andqes_codebook()no longer use a deletedqes2022shard. - A
qes_specobject edited in memory that fails validation is named as such in the error (spec_invalid_edited), instead of the directory of the shipped spec it was loaded from. -
qes_question("qes2022", ...)announces the download of an uncached data file, asqes_codebook()does. -
get_question("name", q)looks the named data frame up in the calling environment and its enclosing environments (read only), as qesR 0.4.4 found it in the workspace: a call from a function or ansapply()/lapply()lambda defined at top level finds an object created at top level (for example byget_qes(assign_global = TRUE)), which since 0.5.0 was an error of classqesR_error_input. Only data frames and other lists are matched, so a function or a string of the same name is skipped. -
get_codebook_files("qes_demo"),get_codebook_files(codebook = qes_codebook("qes_demo"))anddownload_codebook("qes_demo")return an empty table (the synthetic study has no documents;download_codebook()says so) instead of an unknown-study error, andqes_docs("qes_demo")returns zero rows. - In
attr(get_qes_master(), "source_map"), a raw variable the file lacks (SDATinqes_demo) has nosource_variable, matching itsno_sourcerow inlegacy_na_columns. # qesR 0.6.0
qesR 0.6.0 adds an experimental engine that harmonizes variables across studies from a reviewed specification only, with each study’s waves, weights and comparability grades, and hands the result to the survey and srvyr packages. The functions of 0.5.0 are unchanged: get_qes() returns the same data, and get_qes_master() and get_decon() the same values.
New: harmonization (experimental)
- New
qes_harmonize(), marked experimental, builds one data frame from several studies, one row per respondent (no row is ever dropped) and one column per harmonized variable (“target”), from a reviewed specification only: for each study and target, one question, its codes mapped one by one to the target’s levels. Nothing is matched by name, and a code the specification does not map is an error (classqesR_error_unmapped;unmapped = "warn"or"na"sets it toNAwith reasonunmapped), so no raw code or sentinel such as-99can pass through. It coversqes2012,qes2014,qes2018,qes2022,qes2007_panel,qes2018_panelandqes2012_panelfor 14 core targets: reported vote and turnout, vote intention (first question, and with the undecided pushed), three referendum and sovereignty items that are never pooled (“independent country”, “sovereign country”, a four-point favour scale), left-right self-placement, provincial party identification, four-point interest in politics, year of birth, age, age group and citizenship (and the month of birth, outside the core set). Intention and recall are separate targets, each from its own wave; a respondent outside the wave that asked a question (for example the 301qes2022respondents not in the post-election wave) isNAwith reasonnot_in_wave. - Every missing value has a reason (
dk,refused,dk_refused,no_answer,not_selected,inapplicable,not_voted,spoiled,ineligible,not_registered,not_in_wave,not_mappable,sysmis,not_asked,not_reviewed,below_grade,unmapped;?qes_harmonizelists them, generated from the catalog vocabulary);missing = "reasons"adds a factor column<target>__na. Each study’s question has a comparability grade against the target’s anchor question (identical,comparable,approximate), andmin_gradesets the cells below a grade toNA(reasonbelow_grade); approximate cells included are listed in a message. Levels a study’s question did not offer (the Conservative party before 2022, say) are flagged as structural zeros, in a message, when printing, and inqes_provenance(x, level = "cell"). - Categorical targets are factors with the same levels in every study (
values = "factor"),haven::labelled()integer codes ("labelled") or ASCII level names ("code");lang = "fr"gives French labels and the same codes.keep_source = TRUEadds each value’s source code.data =harmonizes frames you already read (their origin is then recorded as unverified, with a warning);on_fail = "skip"leaves out a study that cannot be read or checked. - A crosswalk row is applied only once a reviewer has signed it off (status
stable). The rows of this first specification were checked against the original files and documents but are still in review, so the default call givesNAwith reasonnot_reviewed(a question the study asked, whose row is not signed off;not_askedis kept for a study with no question for the target), and a message;include_draft = TRUEapplies them, with another message.qes_provenance(x, level = "cell")$excludedsays why each cell left out was left out (no_row,not_reviewed,not_in_data,below_grade). - New
qes_spec()shows the specification:view = "targets"(default), one row per target with each study’s grade;view = "crosswalk", one row per study and target (source variable, grade and its reason, instrument, levels offered and not offered, filter question, wording or the document that gives it, recommended weight, review status), or one row per code (level = "code", also under the column names of the retroharmonize package’s crosswalk tables withformat = "retroharmonize"); printing the crosswalk of one target shows its reference section;view = "spec", the specification itself, checked. -
qes_provenance(x, level = "cell")gives, for harmonized data, the crosswalk row, grade, instrument, recommended weight, levels not offered and the count of values and of each missing reason of every study and target;level = "spec"the specification version and content hash, the qesR version and the call’s arguments. Harmonized data also record the specification inattr(, "qes_spec"): the same specification version and hash, the same pinned files and the same qesR version give the same values and the same study and cell provenance (only thecreatedtime of the spec-level record differs).rbind()of results for different studies built with the same specification combines their provenance too.qes_cite()of harmonized data adds the specification version and content hash to the qesR citation. - New vignettes
vignette("harmonization-reference")andvignette("fr-reference-harmonisation"), generated from the specification when they are built (nothing in them is written by hand): each target’s definition and levels, then, study by study, the source question, grade and reason, instrument, levels offered, wording, filter, weight and whether “don’t know” was offered.?qes_speclists the targets, also generated from the specification. -
qes_search()andqes_codebook()now fill theirtargetscolumn with the harmonized targets each variable feeds, andqes_search(fields = "target")searches the names and labels of those targets, in the language(s)langasks for. - The specification is version 0.2.0 (
qes_spec("spec")). Its checks run offline on the shipped metadata (gates.csv, the joint counts of filter and source codes, andexpected/marginals.csv, the projected marginals of every row), andexpected/hashes.csvholds the md5 of each harmonized column on the pinned files, which the live tests compare with the engine’s output. Version 0.2.0 adds the targetsage,birth_month,age_group3andcitizenand the interview-mode targetsurvey_mode, with their rows for the seven studies; no earlier row, code or grade changed.
New: waves, weights and survey designs (experimental)
- Waves: every study of the specification has one or more waves (a post-election survey, the two waves of a panel, the campaign-period and post-election surveys of
qes2022), each with the rule that says who took part: a disposition or date variable, never an answer (qes2007_panel:resultat = "C", 2,050 respondents, andresultat_pst = "CO", 2,054, of whom 1,663 took part in both;qes2018_panelhas a pre-election wave of all 1,250 respondents and a post-election wave of 842).qes_harmonize(layout = "long")gives one row per respondent and wave, withwave,wave_timingandwave_design: a value sits on the row of the wave that asked the question and the respondent’s other rows areNAwith reasonnot_in_wave, except the year and month of birth, which are repeated. The respondent layout (the default) keeps one row per respondent, withwaveslisting their waves.qes_studies()$waveslists the waves of each study. - Weights: each wave has at most one recommended weight in the specification’s registry, never one calibrated on the vote or turnout (for
qes2022, the untrimmedcps_weight_generalandpes_weight_general).qes_harmonize()returns them asweight_preandweight_post(the respondent’s pre- and post-election waves;NAoutside them), with their source variables inweight_pre_varandweight_post_var, or asweightandweight_varin the long layout.weights = "normalized"(default) divides each wave’s weight by its mean over the wave’s members, so it has mean 1 in each study and wave;weights = "raw"keeps the deposited values. The weights ofqes2007_panel,qes2012_panelandqes2018_panelare not documented yet (their registry rows need review; the 2007 panel’s post-election wave has none) and areNA, with a message (classqesR_message_weight_review), until they are.attr(, "qes_weight_guide")gives, for each target and study, the wave that asked the question and the weight that fits it, and, in the respondent layout, a message (classqesR_message_weight_timing) says when the targets of a study (static ones aside) need different weights, such as vote intention (weight_pre) and reported vote (weight_post) inqes2022. Results with different layouts cannot be combined withrbind()(an error of classqesR_error_input). - New leading columns:
survey_mode("web","phone"or"mixed": the wave’s mode, or the respondent’s own in the first wave of the 2018 panel, where 400 were interviewed by telephone and 850 on the web),interview_date(where the file has one:qes2022andqes2007_panel),days_to_election(negative after the election) andeligible_voter: whether the respondent could vote in the study’s election, 18 or older on election day and, where asked, a Canadian citizen. It is read from the year and month of birth, age and age group, and isNAwhen the answers do not settle it (born 18 years before the election year with no month of birth, say) or when a code of these design inputs has no mapping and they were not requested as targets. Inqes2018, which sampled people aged 16 and over, 255 respondents could not vote (among them the 229 born in 2001 or 2002) and 18 areNA; its weights target the population aged 16 and over, and keeping eligible voters only does not recalibrate them. Inqes2007_panelthe 391 respondents reached only after the election areNA: the age question of the specification belongs to the first wave. - New
qes_design()turns harmonized data into a survey design: asurvey.design2object of thesurveypackage, or atbl_svyofsrvyr(engine = "srvyr"), both suggested and not imported. Each study is a stratum; in the long layout the respondent is the sampling unit. Withweight = NULLit uses the weight column of the waves the targets came from (static targets such as the year of birth do not count), and asks you to choose when they call for bothweight_preandweight_post. Rows without a weight are left out of the design, with a message.pool = "equal"gives each study (each study and wave in the long layout) the same total; it is the only place where qesR changes the relative size of studies. A missingsurveyorsrvyris an error of classqesR_error_dependency. -
qes_provenance(x, level = "cell")also gives the registry status and raw mean of each cell’s weight (weight_status,weight_mean_raw) and, for targets about an election, the number of the wave’s members who could not vote in it (n_outside_universe;NAwhen eligibility is not available for the wave, for example when its age rows are not signed off). - DESCRIPTION:
surveyandsrvyrare suggested.
Website
- The website has a Harmonization (experimental) menu, in English and French: Harmonizing across studies (Harmoniser entre études) goes from
qes_harmonize()to weighted estimates by study withqes_design(), and shows the comparability grades, the reason for every missing value, the weight of each wave, structural zeros and the provenance of each cell, on the full data files; Coverage by study (Couverture par étude) is a grid of every target and study, with the grade of each study’s question and each study’s waves and recommended weights, generated from the specification when the site is built; the Harmonization reference is listed with them. Each target of the reference now has a fixed anchor,#target-<name>, which the grid links to. - The README, in English and French, presents
qes_harmonize(),qes_design()andqes_spec(), with a compact grid of the grade of each study’s question for each target, generated from the specification.
qesR 0.5.0
qesR 0.5.0 reads each study from its original data file, pinned and checked by md5, and ships an offline catalog, codebooks, question text and search in English and French. Every function of 0.4.4 keeps its name and its arguments, and get_qes() returns the same codes as before. Three changes alter what 0.4.4 code produces: functions no longer write into your workspace by default; text that the old reader damaged is repaired; and get_qes_master() and get_decon() keep every respondent and set values verified to be wrong to NA. vignette("migrating-0.7", package = "qesR") walks through the changes.
Breaking default: workspace assignment
-
get_qes(),get_qes_master()andget_decon()no longer write into your workspace by default (assign_globalnow defaults toFALSE; it wasTRUEin 0.4.4). They always return the data, visibly: writeqes2018 <- get_qes("qes2018"). The first call in a session that leavesassign_globalunset prints a one-time note (classqesR_message_assign_default); passingassign_globalexplicitly avoids it. - With
assign_global = TRUE, every function now assigns into the environment it was called from: the global environment at top level, the function’s own frame when called inside a function.get_qes_codebook()andqes_codebook()previously assigned into a frame of their own, so the object never reached you. -
get_qes(assign_global = TRUE)still assigns both<code>and, withwith_codebook = TRUE,<code>_codebook. - Study codes are trimmed and case-insensitive:
get_qes(" QES2018 ")readsqes2018, assignsqes2018and stores"qes2018"inattr(, "qes_survey_code")(0.4.4 stored the code as typed).get_qes_master(surveys = "all")means the 11 studies of 0.4.4. Codes are never matched fuzzily:"2018"is an error that suggestsqes2018. -
get_codebook(assign_global = TRUE)and its aliases now assign exactly the object they return, in the requestedlayout(0.4.4 assigned the compact codebook whatever the layout). -
get_qes_master(save_path =, assign_global = TRUE)now setssaved_tobefore assigning, so the assigned and returned objects are identical.
Results change: what this means for code written for 0.4.4
Scripts written for 0.4.4, such as the analyses of a paper that installed v0.4.4, keep running, but some of their results change. What changes depends on what they use:
-
Raw codes from
get_qes(): unchanged. Codes, column names andis.na()counts are those of 0.4.4 for all 11 studies (checked against 0.4.4 output). Code that works on codes, such asas.numeric()with windows of valid codes, gives the same numbers. -
Label text, factors and types from
get_qes(): some change.haven::as_factor()and anything else built from label text sees the repaired accents, theqes2012labels in their original case and up to 256 characters (0.4.4 lowercased them and cut them at 80), theqes1998panel file’s own labels and the corrected CROP letters;qes2022dates are date-times, and codes are doubles instead of integers (see Changed outputs: reading data). -
get_qes_master()andget_decon(): values change. The master has 40,987 rows instead of 40,606, drops the 70 stacked columns and sets the cells listed under Changed outputs: legacy master andget_decon()toNA. Any estimate that usesvote_choice,turnout,sovereignty_support,political_interest,ideology,language,income,religion,born_canada,education,party_bestorparty_leancan change. Under French messages (for example withLANG=fr_CA.UTF-8), 0.4.4 also lost 1,208 of the 2,442qes2007_panelrows and 616 of the 1,250qes2018_panelrows while reading them (a master of 39,132 rows); 0.5.0 reads every row whatever the language. - Codebooks come from the shipped dictionary, not from Dataverse’s DDI: labels and question text differ where 0.4.4 used the variable name, a lowercased label or a question guessed from a PDF.
The comparison with qesR 0.4.4 in the source repository (generated by data-raw/compare_legacy.R against a clean 0.4.4 build) gives, column by column and study by study, how many cells changed and why; every difference is an intended deletion, a blanked value or one of the reader changes listed here. To reproduce a 0.4.4 result exactly, install that version, for example in a separate library or an renv project: remotes::install_github("ThomasGareau/qesR", ref = "v0.4.4") (the same code as the 0.4.4 baseline of that comparison), and record packageDescription("qesR")$RemoteSha with the results.
Changed outputs: legacy master and get_decon()
get_qes_master() keeps its arguments, its 30 documented columns in the same order and types, and its attributes; get_decon() keeps its 19 columns. Their values change by deletion, apart from the reader changes listed under Changed outputs: reading data (for example qes2018 turnout): each column reads the variable 0.4.4 read, converted as 0.4.4 converted it, and values verified to be wrong are set to NA. A message says so once per session (classes qesR_message_values_changed and qesR_message_legacy_columns). Results from 0.4.4 can be reproduced only by installing it (remotes::install_github("ThomasGareau/qesR", ref = "v0.4.4")). The comparison with qesR 0.4.4 in the source repository gives the counts, column by column and study by study, against a clean 0.4.4 build.
-
Frozen sources. The source variable of every column of every study is the one qesR 0.4.4 chose, frozen from a clean 0.4.4 build (
inst/extdata/legacy/sources.csv); nothing is matched by name at run time, so a label or name read differently can never change which variable a column reads.attr(, "source_map")gives them, with the md5 of each file read;get_decon()now has one too. -
No row is dropped. There is no de-duplication and no removal of empty rows: every study contributes exactly the rows of its file, which is checked. 0.4.4 dropped 380
qes2007_panelrespondents (itsquestnumber repeats across the two subsamples) and 1qes_crop_2007_2010respondent as duplicates; the master now has 40,987 rows (40,606 in 0.4.4).duplicates_removedandempty_rows_removedare always 0.respondent_idstill repeats inqes2007_panel. -
Columns removed. The 70 columns 0.4.4 appended after the 30 documented ones, by stacking raw variables that share a name across studies and renaming them from the 2007 questionnaire, are gone: they held different questions in different studies (
vote_federal_2006was satisfaction with democracy inqes2012,feeling_david_0_100an abortion item inqes2018,provincial_pid_itemthe home language).attr(, "removed_columns")lists them;crossstudy_variables_addedis empty andvariable_name_maphas no rows. Read those items from each study withget_qes().save_pathno longer writes<stem>_variable_name_map.csv, andattr(, "variable_name_map_path")isNULL; the provenance sidecar<stem>_provenance.csvreplaces it. -
Values set to
NAinget_qes_master(), each listed with its number of cells and reason in the new attributelegacy_na_columns:-
party_bestandparty_leanin every study (each study’s source was another question or construct); -
vote_choiceandturnoutwhere the source is a vote intention or likelihood:qes2022(cps_votechoice1,cps_turnout),qes_crop_2007_2010andqes1998(vote_choiceonly;qes1998turnoutis the reported vote);vote_choiceof the 387qes2007_panelrespondents not reached after the election (code 10, which 0.4.4 counted as “did not vote”);vote_choice_textofqes2022(the text of the intention item) andqes2018(the text of the most important issue); -
sovereignty_supportandsovereigntywherever the question is not the referendum on Quebec becoming an independent country:qes2007,qes2008andqes1998(the 1995 partnership question),qes2012_panel(a sovereign country),qes2007_panelandqes_crop_2007_2010(follow-up push questions),qes2018_panel(a favourable/opposed scale). They keepqes2012,qes2014,qes2018andqes2022; -
languageofqes2014andqes2022, which held the interview or interface language, not the mother tongue; -
political_interestofqes2018(raw 1-4 codes in reverse order on the 0-10 column; filled in 0.7.0) andqes2012_panel(a 0-3 index derived from debate viewing);ideologyofqes2014, whose 0 and 10 answers were lost; -
born_canadaofqes2018(people born elsewhere in Canada coded No, raw codes left in);incomeandreligionofqes2018(raw codes) and the-99codes ofqes2022;educationofqes1998(10-15 years of schooling coded as college); - “don’t know” and refusal labels left as values:
born_canadainqes2012andqes2014,languageandeducationinqes2018_panel,educationinqes2007,incomeinqes2014,qes2018_panel,qes_crop_2007_2010,qes2008,qes2007andqes2007_panel,religioninqes2014andqes2012.vote_choicekeeps its 0.4.4 category “Don’t know / Refused”, a category 0.4.4 coded on purpose, until the harmonizedvote_choiceof 0.7.0.
-
-
Columns appended.
vote_choice_timing("post": the vote reported after the election) andsovereignty_item("sov_indep": the independent-country referendum question) say whatvote_choiceandsovereignty_supporthold in each study, with one value per study; they areNAin the studies where those columns are blanked. Appended columns are never removed. -
New attributes of
get_qes_master():legacy_na_columns,legacy_column_map(what each column means, the harmonized target that will fill it, the studies whose values changed, and a flag for the columns that mix instruments:political_interest,age_group,education),removed_columns,qes_provenance(the file read for each study, soqes_provenance()andqes_cite()accept the master) andqes_spec.save_pathwrites UTF-8 (CSV) and, next to the file,<stem>_provenance.csv. - Each study’s rows are converted on their own, the way the full 11-study build of 0.4.4 converted them (0.4.4 converted the stacked columns, so a study’s values could depend on the studies built with it):
get_qes_master(surveys = "qes2018")gives exactly theqes2018rows of the full master. -
get_qes_master(surveys = "qes_demo")andget_decon("qes_demo")build the synthetic demonstration study, offline; the help pages’ examples use it. -
get_decon():turnoutandvotechoiceareNAin every study butqes2022, because 0.4.4 read other questions for them (q1andq2: forqes2018, satisfaction with democracy and the most important issue);party_bestandpartyleanareNAeverywhere; the-99item nonresponse codes ofqes2022areNAin every column (the mean ofideologyis about 4.96, not 2.64).qes2022turnoutandvotechoicestay the campaign-period likelihood and intention of 0.4.4, and the new attributetimingsays so ("pre").get_decon()now refuses the studies added since 0.4.4 (qes1998_crop,qes1998_createc) with an error of classqesR_error_input.
Changed outputs: reading data
-
get_qes()now reads the original upload of each study’s pinned data file (SPSS.savor Stata.dta), not the tab-delimited copy Dataverse makes of it. The file is checked against the md5 in the catalog (qesR_error_checksum) and its rows and columns against the catalog (qesR_error_rowcount) before use, and is kept in the download cache. Within a session the parsed data, and the Dataverse metadata used for the codebook, are also kept in memory, so a second call (orget_preview()) makes no request (options(qesR.memo = FALSE)turns this off;qes_cache_clear()empties it). qesR no longer reads text, CSV or zip files, and never guesses which file to read. - Column names, codes and
is.na()counts are those of 0.4.4 for all 11 studies (checked against 0.4.4 output), except in the columns listed here:- text damaged by Dataverse’s conversion is repaired: 1,805 cells with a U+FFFD replacement character in
qes2018(for exampledistrict,NM_MUNCP), 2,517 inqes2022, 94 inqes2018_panel, 96 inqes2007_paneland 2 inqes1998; -
qes2022: the sixcps_/pes_StartDate,EndDateandRecordedDatecolumns are date-times (POSIXct) instead of text, so post-election dates of non-respondents areNAinstead of"";cps_votechoice3_5_TEXTandpes_maildifficult_11_TEXTare text, as in the file, so their blank answers are""instead ofNA(0.4.4 returned integer and logical columns); - Stata
floatcolumns (weights such aspondinqes2012andqes2018,cps_timeinqes2022) keep their stored precision instead of 7 printed digits (relative differences below 1e-7); - open-text answers no longer carry stray quote characters (
qes2007_panel,qes2018_panel), and blank answers are""instead of" "; - codes are stored as doubles instead of integers.
- text damaged by Dataverse’s conversion is repaired: 1,805 cells with a U+FFFD replacement character in
-
qes2007now reads the SPSS file of its deposit instead of the Stata twin: both hold the same data, and the SPSS file has the complete labels. Both files store most answer codes as zero-padded text ("01"); qesR reads them as numbers, blanks asNA, in both files, soget_qes("qes2007", file = "STATA")also gives the numbers, names andNAcounts of 0.4.4. (Dataverse’s tab-delimited copy keeps these codes as quoted text; 0.4.4 had numbers because its text reader converted them.) The same holds for the three interviewer-number columns ofqes2012_panel. These conversions are listed in the catalog (type_fixes.csv). The catalog version became 2.2.0: theqes2007pin changed, andenums.csvgained the vocabulary of the harmonization spec (block,target_type,election_ref_rule,date_format,spec_changeand thedk_offeredvalueunknown) and the missing-value reasonnot_reviewed, set byqes_harmonize(). - Labels come from the data file itself, never from Dataverse’s DDI metadata or from a variable name:
-
qes2012: labels in their original case and up to 256 characters, from the SPSS twin of the same data (0.4.4 returned the Stata file’s labels, lowercased and cut at 80 characters); names and values are still those of the Stata file; -
qes1998: the panel file’s own labels (0.4.4 attached labels from another file of the deposit); - CROP files (
qes_crop_2007_2010,qes1998_crop,qes1998_createc): letters typed in the DOS character set are corrected, for example “RESTE DU QUÉBEC” and “travaille à temps plein” (listed intext_fixes.csv); - a variable without a label in its file has none (0.4.4 often used the variable name);
-
qes2018: its data file has value labels for 21 variables only. The hand-typed labels 0.4.4 attached to eight others (qscol,q5,q6, …) return, checked against the questionnaire, with the offline dictionary.
-
- Codes that an SPSS file declares as user-missing are still returned as values, as in 0.4.4; the declaration is now kept in the column attributes
qes_na_valuesandqes_na_range. -
get_qes()data carries a new attribute,qes_provenance: the DOI, dataset version, file id and name, md5 (expected, observed), UNF, rows and columns, where the file came from and when, the licence, the source of the labels and the reader. - New
qes_provenance(x)returns that record (classqes_provenance); printing it gives one paragraph per file for a replication log, in the message language. It also accepts the result ofqes_download(), and study codes, for which it shows whatget_qes()would read before anything is downloaded. An object that lost the record (for example throughmerge()) is an error of classqesR_error_no_provenance.qes_cite()accepts the same objects. -
get_preview()is exactlyhead()ofget_qes(): it keeps theqes_survey_code,qes_provenanceandqes_codebookattributes and is served from memory when the study was read before in the session. -
get_qes(file =)chooses among the study’s data files only (0.4.4 took the first match among all files, documentation included), and a pattern that matches several is an error (classqesR_error_ambiguous_file); a pattern that is not a valid regular expression is an error of classqesR_error_input.get_qes("qes2012", file = "SPSS")still reads the SPSS twin.get_qes("qes1998", file = "CROP")reads the studyqes1998_crop, with a message, and its banner, assigned names and codebook are those ofqes1998_crop(so isqes_codebook("qes1998", file = "CROP")). - A label or text value still holding a replacement or control character after reading raises a warning of class
qesR_warning_encoding. - The codebook attached by
get_qes()is built offline (see New: codebooks, questions and search):get_qes()makes one request per study, for its data file. -
get_qes_master()andget_decon()read through the new reader. Where the original files leave a column unlabelled that 0.4.4 labelled by hand (qes2018education and, inget_decon(), gender;qes1998age group 65+ and education code 9), they keep the 0.4.4 labels, so these columns are unchanged. Changes that come from the reader, compared with 0.4.4 on the same rows (see Changed outputs: legacy master andget_decon()for the rest):-
get_qes_master():qes2012religionlabels in their original case;qes_crop_2007_2010incomereads “20 000 $ à 39 999 $” instead of “20 000 $ … 39 999 $”, andprovince_territoryreads “Quebec” for the 7,203 respondents whose region was typed in the DOS character set;qes2018turnoutis 0 instead ofNAfor the 336 respondents who said they did not vote (q5codes 1 and 3), as its coding intends;survey_weightkeeps its stored precision; theinterview_*columns ofqes2022lose their trailing.000. -
get_decon():qes2012province_territorylabels in their original case;qes1998ageholds the panel file’s labels (“18-24” … “55-64”, with “65+”) instead of another file’s (“DE 18 A 24 ANS” …);qes2022votechoice_textloses 4 replacement characters, and its numeric columns (age,political_interest,ideology,income) are doubles instead of integers.qes2022agestays a number (a column whose labels only repeat its codes is not turned into a factor).
-
Soft-deprecated names (kept indefinitely)
| 0.4.4 function | Use instead | Notice from | What changed in 0.5.0 |
|---|---|---|---|
get_codebook() |
qes_codebook() |
0.5.0 | offline; columns appended; refresh ignored |
get_qes_codebook() |
qes_codebook() |
0.5.0 | as get_codebook()
|
format_codebook() |
qes_codebook(codebook, layout = ) |
0.5.0 | a plain data frame is an error; long layout keeps unlabelled variables |
get_value_labels() |
qes_codebook(layout = "long") |
0.5.0 | an unknown variable is an error; "" labels kept |
get_question() |
qes_question() |
0.5.0 | exact match; full = FALSE no longer changes the result (no-op, with a note) |
get_codebook_files() |
qes_docs() |
0.5.0 | lists every document, offline; file and refresh ignored |
get_qes_codebook_files() |
qes_docs() |
0.5.0 | as get_codebook_files()
|
download_codebook() |
qes_download(what = "docs") |
0.5.0 |
file now selects documents (it selected a data file); md5-checked; refresh ignored |
get_preview() |
head(get_qes(srvy), obs) |
0.5.0 | exactly head() of get_qes(); obs must be a whole number of at least 1 |
get_qescodes() |
qes_studies() |
0.5.0 | accented French names; doi.org links; 1998 firm codes appended |
get_decon() |
none yet (a replacement is planned for 0.7.0) | none | kept as it is; values changed (see above) |
- Eleven 0.4.4 helpers become legacy wrappers. They keep their arguments and output, keep working and will not be removed. Each prints a one-time notice (class
qesR_message_deprecated) naming its replacement, but only once that replacement exists.quiet = TRUEdoes not hide the notice;options(qesR.quiet_deprecated = TRUE)does. The table is in?qesR-deprecated. - Notices:
get_codebook()andget_qes_codebook()(useqes_codebook(), same arguments) andget_preview()(usehead(get_qes(srvy), obs)). - Notices:
get_qescodes()(useqes_studies()), andget_codebook_files()andget_qes_codebook_files()(useqes_docs()). These two now return every document of the study from the offline catalog, with the 0.4.4 columns, and no longer download metadata; theirfileandrefresharguments are ignored, with a one-time note (classqesR_message_arg_ignored). - Notice:
download_codebook()(useqes_download(what = "docs")). It now downloads the documents listed in the offline catalog, the same filesget_codebook_files()lists, checks each against its md5 before giving it its final name, and makes no metadata request. Itsfileargument now selects documents by name (in 0.4.4 it selected a data file),refreshis ignored with a one-time note, anddest_diris created only when there is a file to download. A file already indest_diris still kept unlessoverwrite = TRUE. - Notices:
format_codebook()(useqes_codebook(codebook, layout = )),get_value_labels()(useqes_codebook(layout = "long")) andget_question()(useqes_question()). Their behaviour changes:-
format_codebook()on a plain data frame (for example a codebook read back from a CSV file, which has lost its class) is now an error of classqesR_error_inputthat names the fix,qes_codebook("<code>"); 0.4.4 accepted any data frame. Its long layout keeps the variables that have no value labels (one row withvalueNA), where 0.4.4 dropped them. -
get_value_labels(codebook, variable)with a variable that is not in the codebook is an error of classqesR_error_unknown_variablethat suggests near matches; 0.4.4 returned an empty list. A value label that is""in its file is kept. -
get_question(do, q)matchesqexactly (ignoring case only when that names a single column):get_question(d, "q1")no longer answers forq10, and"q36"no longer forq36_1; an unknown name is an error of classqesR_error_unknown_variablethat suggests near matches. It returns the questionnaire wording ofqes_question()when the study is known, else the variable label; a label that only repeats the variable’s name is no label, and the result is thenNAwith a warning (0.4.4 returned the name, for example"responseid"inqes2018).fullno longer changes the result (the text is always the most complete one qesR has;full = FALSEprints a one-time note), and a question its source file cut at 80 characters (qes2022) is returned as is with a warning of classqesR_warning_truncated, instead of being completed from a downloaded PDF. -
get_codebook()andget_qes_codebook()return whatqes_codebook()returns: the 0.4.4 columns first and in the same order, then new columns (see New: codebooks, questions and search);refreshis ignored with a one-time note.
-
-
get_decon()stays stable until 0.7.0.
New: study catalog, documents and citations
- New
qes_studies()lists every study from a catalog shipped with the package, with no network request: code, family, deposit title and authors, English and French display names, year, design, target population, server, DOI, the pinned dataset version and data file, source language, licence and the dataset UNF.qes_studies(check_updates = TRUE)asks Dataverse, one metadata request per deposit, whether a newer version exists or the pinned data file changed; a study that cannot be checked is reported as"unreachable"instead of failing. - New
qes_docs()lists the codebooks, questionnaires and technical or methodological reports of each study, with their role, language, size, md5 and download URL, offline. - New
qes_cite()returns the citation of qesR and of each dataset (authors, year, verbatim deposit title, DOI, repository, pinned version and UNF) as text, BibTeX orbibentry.citation("qesR")now works (staticinst/CITATION). The citation vignettes are generated from the catalog; the hand-copied tables, which carried a stray[fileUNF]token and non-DOI links, are gone from the README and vignettes. - The 1998 deposit is split into its three surveys:
qes1998still reads the combined CROP-CREATEC panel file (1,483 rows, as in 0.4.4), and the new codesqes1998_crop(450 rows) andqes1998_createc(1,057 rows) read the two firms’ files. All three cover francophones only, each with the definition its codebook gives. - The Durand panels, the CROP polls and the 1998 polls are no longer presented as “Quebec Election Study” panels:
get_qescodes(detailed = TRUE)and theget_qes()banner use their own titles (for example “Panel Survey on the 2018 Quebec Election”), and the same names now appear in theqes_name_encolumn ofget_qes_master()for those studies. French names carry their accents.documentationis the doi.org link. -
get_qes_master()keeps building the 11 studies of 0.4.4 by default, andsurveys = "all"keeps meaning those 11. The new 1998 codes are not in the master yet: naming them is an error (classqesR_error_input), because theqes1998panel file already holds their respondents. Read them withget_qes(). - The website is reworked. Its English and French guides sit in two parallel menus, every guide and example has a partner in the other language that runs the same code, and the home page has a French section. Getting started now goes from a study code to a weighted estimate, stating the population, the weight and the denominator. The Study catalog page (EN and FR) is generated from the shipped catalog when the site is built, one section per study with its deposit, pinned data file and md5, documents and licence, in place of the hand-written survey code table, which still carried the old study names. The examples are built from the full data files through qesR’s cache, compute each estimate within one study with its weight, and mark structural zeros (a party that did not run, or that a study’s question did not list) instead of showing them as 0. The reference is grouped by task (Discover, Get data, Metadata and search, Reproducibility, Cache), with a Harmonization (experimental) section for
qes_spec()andqes_harmonize()and the 0.4.4 functions under Legacy and deprecated;?qesRand?qesR-fropen with the same grouped table of functions. The site has a dark mode, the logo in the navigation bar and favicons drawn from the current logo; nothing on it reads the old 40,606-row master file. - The catalog is versioned (
inst/extdata/VERSIONS), and a synthetic demonstration study,qes_demo, ships in its own tree for offline examples and tests.get_qes("qes_demo")reads it with no download.
New: codebooks, questions and search
- Codebooks are built offline. qesR now ships the description of every variable of every study released under CC0, that is all but
qes2022(inst/extdata/dict/, 165 KB): the variable label of the pinned data file, the question text in English and French from the deposited questionnaires (with a reference to each document that gives it: one or more<file_id>:<question>separated by;, e.g.352010:Q19;352009:Q19for the English and French questionnaires), the value labels, the unweighted count of each code, and the missing type of each “don’t know”, “refused” or declared missing code. A code an SPSS file declares missing but whose label is an answer (“Un autre parti”, “ne voterait pas/annulerait” in a voting-intention item) gets no missing type; “did not vote”, “spoiled” and “not reached in this wave” codes get their own type instead ofuser_na.qes_codebook()andget_qes(with_codebook = TRUE)no longer download Dataverse’s DDI metadata or any PDF or Word document, and no longer run external programs (pdftotext,gs,python3,textutil) to read them. Thexml2package is no longer needed. -
qes2022is licensed CC BY-NC 4.0, so qesR ships none of its labels, question text or answer counts. Its codebook is built the first time you ask for it, from your own md5-verified copy of the data file, and kept in the download cache as two CSV files (listed byqes_cache_info()withkind = "shard", deleted byqes_cache_clear()). Its question text is the file’s variable label, which Stata cut at 80 characters: those questions are flaggedquestion_truncated = TRUE, with the codebook document to read indoc_ref. -
qes_codebook()gainsvariables(exact names; an unknown name is an error that suggests near matches) andlang("en"or"fr"for the question text;NULL, the default, gives the study’s own language). Its other arguments are unchanged.srvyalso accepts a codebook (laid out again) or data returned byget_qes()(a codebook of its columns).refreshis ignored, with a one-time note. Withfile =, the codebook describes the pinned file and uses its variable names, which the new attributevariable_names_filenames (qes2012’s SPSS twin spells them in upper case);qes_codebook(<data>)and the codebookget_qes()attaches use the names of the data. - Codebook columns: the four 0.4.4 columns come first, in the same order (
variable,label,question,n_value_labels), followed bystudy,position,type,question_lang,question_truncated,value_labels("1=Oui | 2=Non | 8=Je ne sais pas"),missing_codes("8=dk | 9=refused"),targets(empty for now),label_source,question_sourceanddoc_ref. The long layout addsstudy,missing_typeandis_declared_naafter its 0.4.4 columns. - Fixed codebook problems of 0.4.4:
- the attributes
survey_code,doi,doi_url,selected_data_file,filesandcodebook_filesare kept in every layout (0.4.4 dropped them, soget_codebook_files(codebook = )anddownload_codebook()found nothing andget_qes()reported “0 support files”); a newqes_provenanceattribute records the pinned file; -
codebook_fileslists every document of the study (0.4.4 missed the Word questionnaires ofqes2007,qes2012,qes2014andqes2018); - a missing label or question is
NA, never the variable name (in 0.4.4, 246 of the 254qes2018labels were the variable name), andquestionis never a copy oflabel; labels no longer carry raw line breaks; -
qes2018’s value labels, absent from its data file for most variables, come from the study’s French questionnaire with its programmed answer codes (label_source = "supplement"), checked against the codes observed in the data; -
print()counts variables, not rows, and acceptsn.
- the attributes
- New
qes_question(x, variables, lang = NULL)returns the exact wording of the questions, with its language, source, document reference and universe (who was asked), and flags a question that its source cut (truncated). - New
qes_search(pattern)searches variable names, labels, question text (English and French) and value labels of every study, offline, ignoring case and accents (qes_search("souverain|sovereign")).qes2022is searchable once its codebook has been built. The result reports, per study, how many variables have question text and how many were checked by hand. - New
qes_missing(x)sets the codes the codebook types as missing (“don’t know”, “refused”, declared SPSS missing values,qes2022’s-99item nonresponse) toNAinget_qes()data, or withaction = "tagged"to taggedNAvalues that keep the reason (haven::na_tag()). Spoiled ballots, options not ticked in a multiple-choice question, “did not vote” and “not registered” are left alone unless asked for (types =). A code that the file declares missing but that the codebook records as an answer is never changed. It records what it did in the attributeqes_missing_log. It works from the codebookget_qes()attached or from the labels on the columns, and never downloads anything.
New: downloads, cache and provenance
- Downloads now use the
curlpackage (new in Imports) instead ofdownload.file().curlgives the HTTP status, the response headers (needed to honourRetry-Afterand to recognise a server that refuses automated requests) and the body from one request, has per-transfer stall timeouts, needs no external program and has no R dependencies. - A request that fails for a passing reason (a timeout, a dropped connection, HTTP 408, 429, 500, 502, 503 or 504) is tried again, up to 4 attempts (
options(qesR.max_tries =)), after a growing pause or the delay the server asks for inRetry-After(at most two minutes; a longer delay is an error of classqesR_error_http). Other errors fail at once. Connections time out after 30 s, and a download slower than 1 KB/s for 60 s (options(qesR.stall_timeout =)) is abandoned. - Network errors have subclasses:
qesR_error_http(fieldsstatus,retry_after,server_message),qesR_error_http_refused(a Harvard Dataverse bot challenge or a 403: never retried or worked around; the message explains how to download the file by hand),qesR_error_tlsandqesR_error_offline. A file is written under a temporary.partname and renamed only once complete, so an interrupted download never leaves a truncated file behind. - New
qes_cache_info()andqes_cache_clear()list and delete the files in qesR’s download cache. The cache keeps each catalog file under its file id and md5 and checks the md5 before keeping it (qesR_error_checksumotherwise). By default it lives in the session’s temporary directory and disappears when R exits;options(qesR.cache = "disk")keeps files between sessions intools::R_user_dir("qesR", "cache"), andoptions(qesR.cache_dir =)in a directory of your choice (qesR works only in aqesRsubfolder that it creates and marks, andqes_cache_clear()refuses any folder without that mark).get_qes()reads its data files through this cache (see Changed outputs: reading data). -
qes_studies(check_updates = TRUE)remembers each answer for the session (qes_cache_clear()forgets it) and tries each deposit at most twice. - New
qes_download(studies, path)saves the original files of one or more studies in a directory you name: the data file as deposited (what = "data", the default) and the codebooks, questionnaires and reports (what = "docs", filtered byroleandlang).pathmust already exist, nothing is written outside it, and nothing at all when no file matches. Before anything is written, a file already there with the expected md5 is kept, and one that differs is an error (classqesR_error_input) unlessoverwrite = TRUE. Each file comes through the download cache, is copied under a temporary.partname, checked against its catalog md5 and only then renamed. It returns, invisibly, one row per file (study,file_id,file_name,role,lang,md5,local_path,from_cache,downloaded,pinned) with aqes_provenanceattribute. -
qes_download(version = "latest")is the only way qesR fetches files it does not pin: it asks Dataverse for the latest published version of each deposit, follows a replaced file to its replacement, checks each file against the md5 Dataverse gives, warns (classqesR_warning_unpinned) and recordspinned = FALSE.get_qes()keeps reading the pinned files.
Security
- Removed the insecure download fallback. qesR 0.4.4 retried a failed download with TLS certificate verification turned off (for
qes2022always, and for any study whose first error mentioned SSL), first throughdownload.file()and then by running thecurl --insecurecommand. A failed download is now an error of classqesR_error_networkthat keeps the original error; qesR never disables certificate checks and never runs external download programs. - Requests now identify themselves only as
qesR/<version> R/<version>and are spaced at least one second apart per server. - qesR no longer reads
.rdsfiles from Dataverse:readRDS()is never applied to downloaded content. - qesR no longer runs external programs (0.4.4 ran
curl,pdftotext,gs,python3andtextutil) and never callsload(),eval()orparse(); a test scans the installed namespace for these calls and for any TLS option.
Messages and errors
- Every error, warning and message that qesR itself raises now has a class, so scripts can handle them with
tryCatch()without matching text. Errors inherit fromqesR_error(qesR_error_input,qesR_error_unknown_study,qesR_error_unknown_variable,qesR_error_ambiguous_file,qesR_error_network,qesR_error_source), warnings fromqesR_warningand messages fromqesR_message. Conditions carry data fields such asstudy,suggestions,urland the root cause inparent. See?qesR. - Messages, warnings and errors are available in English and French. qesR follows
options(qesR.lang =), then theQESR_LANGandLANGUAGEenvironment variables, then the messages locale. The language never changes what functions return: qesR’s own part of the reasons recorded inattr(get_qes_master(), "failed_surveys")is always in English (a root cause reported by R itself, such as a download error, keeps R’s text). - Unknown study codes suggest near matches.
Documentation, licence and package
- Help pages are now generated with roxygen2 from comments in
R/, with the same topics, arguments and defaults as 0.4.4; their examples now run offline, and new topics document the new functions (see below). The package help page is also available as?qesRand now lists the authors and project links. - The test suite is back (testthat 3rd edition). It restores the tests removed before 0.4.4, except one that required the insecure TLS retry, and adds contract tests for the 14 exported functions: their arguments, visible return values, no writes outside
tempdir()or into your workspace by default, opt-in assignment, and the column names thatget_qes(),get_qes_master(),get_decon()and the codebook helpers return. Tests run offline against a simulated Dataverse; the live check against Dataverse runs only whenQESR_LIVE=true. -
R CMD check --as-crannow runs on GitHub Actions for every push and pull request on macOS, Windows and Ubuntu (R devel, release, oldrel-1 and 4.1). - Copyright and licence: the MIT licence now names the author, Thomas Gareau-Paquette, as copyright holder in
LICENSEandLICENSE.md(it named “Quebec Election Study/Étude électorale québécoise rightful owners”), and the “Quebec Election Study” copyright-holder entry is removed fromAuthors@R: it is not a legal person, and no data of the studies ship with the package. The new fileCOPYRIGHTSlists the metadata of each study that ships (CC0 labels and question text) with its DOI. -
inst/extdata/qes_master.csvis removed. It held a 300-row sample per study of an old master build, including rows derived from the 2022 study (CC BY-NC 4.0), and the analysis vignettes read it when installed, so they showed different numbers from the website. No respondent data ship any more; the demonstration studyqes_demois synthetic. - Vignettes: the vignettes that ship with the package run their code offline, on the catalog, the shipped metadata and
qes_demo: Getting started (get-started,fr-demarrage), Study citations (renamedcitations,fr-citations) and the new Moving from qesR 0.4.4 to 0.5.0 (migrating-0.5,fr-migrer-0.5). Each French vignette runs the same code as its English partner. - The analysis examples (descriptive statistics, sovereignty, vote choice) and the merged-dataset guide are now website-only articles. They read
get_qes_master()when the site is built, instead of a CSV file found in the working directory or in the package, compute each estimate within one study with that study’s weight, show their code, and correct three statements: the vote shares are among respondents who reported voting for a party, the CROP polls do have a year for each respondent, and only the independent-country question is used for sovereignty. - Examples run offline, on
qes_demoand the shipped metadata. The only example that uses the network isqes_studies(check_updates = TRUE), run only when a connection is available. - New help page
?qesR-fr: an overview of the package in French, with a table of its functions. - DESCRIPTION: the Description cites each study’s dataset DOI;
LazyDatais dropped;dplyr,ggplot2andpkgdownare no longer suggested (the website listsggplot2andpkgdowninConfig/Needs/website;dplyris not used); aCopyrightfield points toCOPYRIGHTS;xml2is no longer imported, andcurlis (see New: downloads, cache and provenance). -
scripts/build_qes_master.Rmoved todata-raw/legacy_build_master.R; it now needs an explicit--out-dir. The repository no longer tracks the locally builtqes_master.*files.
qesR 0.4.4
- Added
get_qes_master()for harmonized merged datasets across QES studies. - Added merge quality controls: within-year respondent de-duplication and all-empty-row removal.
- Added broader harmonization for age, age group, education, turnout, vote choice, and weights.
- Renamed opaque legacy merged variables (e.g.,
q10,q16,voteprec) to readable names and added transparent old-to-new mapping viavariable_name_map. - Added variable-name mapping sidecar output (
*_variable_name_map.csv) when saving/building merged datasets. - Improved codebook workflows with compact/wide/long layouts and value-label extraction helpers.
- Updated researcher website use-case figures with clearer year-axis labeling, dynamic y-axis bounds, and expandable full code blocks for reproducibility.
- Added researcher website content with merged-dataset documentation, study citations, and analysis examples.
qesR 0.4.3
- Added
get_question(..., full = TRUE)support for richer question-text recovery. - Improved codebook metadata parsing and handling of support files.
qesR 0.4.1
- Added
get_decon()and export updates.