This guide is for code written for qesR 0.4.4, such as the
replication scripts of a published paper. Every function of 0.4.4 keeps
its name and its arguments, but one change can stop that code:
get_qes() no longer creates the object for you (first row
below). What else changes is what some of it produces, which depends on
what the code uses:
| Your code uses | Now | What to do |
|---|---|---|
get_qes("qes2018") alone, then the object
qes2018
|
error “object ‘qes2018’ not found” | write qes2018 <- get_qes("qes2018") (below) |
raw codes from get_qes() (as.numeric(),
ranges of valid codes) |
same numbers | nothing |
label text from get_qes()
(haven::as_factor(), text columns) |
some text changes | check the labels you use |
get_qes_master() |
values change | read the column table, or keep 0.4.4 to reproduce |
get_decon() |
values change; one-time notice | as for the master |
| the other helpers of 0.4.4 | same results as their replacements; one-time notice | nothing, or rename them |
Every chunk on this page runs offline, on the synthetic study
qes_demo. The complete list of changes is in the Changelog
(news(package = "qesR")).
Before you upgrade: record the version
A published result should say which qesR produced it. Before upgrading, record the version you have, and keep it with the results:
packageVersion("qesR")
packageDescription("qesR")$RemoteSha # the commit, when installed from GitHubTo reproduce a 0.4.4 result, install 0.4.4, preferably in a separate library or an renv project so that new work can use the current version:
remotes::install_github("ThomasGareau/qesR", ref = "v0.4.4")qesR 0.5.0 and 0.6.0 were development versions and were never
released: a result computed with one of them is reproduced by installing
the commit it was built from, which
packageDescription("qesR")$RemoteSha records for an
installation from GitHub
(remotes::install_github("ThomasGareau/qesR", ref = "<commit>")).
Assign the result yourself
In 0.4.4, get_qes("qes2018") created an object
qes2018 in your workspace. It now returns the data and
writes nothing: assign it.
A script that calls get_qes("qes2018") on its own line
and then uses qes2018 stops with “object ‘qes2018’ not
found”. Look also for such calls inside tryCatch() or
try(): there the error is caught, and the part of the
script that used the object is skipped without a visible error.
assign_global = TRUE still assigns, into the environment
the function is called from: the global environment at top level, the
function’s own frame inside a function. It still assigns the codebook
too, as <code>_codebook.
get_qes(): the same codes, read from the original
files
get_qes() reads each study’s original SPSS or Stata
file, checked before use. Column names, codes and is.na()
counts are those of 0.4.4 for all 11 studies, so code that works on
codes (as.numeric(), ranges of valid codes,
%in%) gives the same numbers and the same N. Text changes
where the old conversion damaged it or took labels from elsewhere:
- accents are repaired (1,805 cells in
qes2018, 2,517 inqes2022); -
qes2012labels keep their original case and length (0.4.4 lowercased them and cut them at 80 characters); - the
qes2022interview dates are date-times, not text; - codes are stored as doubles instead of integers.
haven::as_factor() and anything else built from label
text can therefore give different text. The data record which file they
came from:
qes_provenance(qes_demo)
#> qes_demo: file 0 (qes_demo.sav), synthetic data shipped with qesR. md5
#> e956e315800690cb0894c86ed85c8bea, verified. 60 rows, 11 columns. Retrieved on
#> 2026-10-06 05:13:52 UTC (local_demo). Read with haven::read_sav(user_na =
#> TRUE), haven 2.5.5. Licence: CC0 1.0. qesR catalog 2.4.1.
#>
#> as.data.frame() gives every column.
get_qes_master(): what changed
The master keeps its 30 documented columns, in the same order and with the same types; new columns are appended after them and are never removed. Its values changed in two ways.
Nothing is dropped, and nothing is stacked by name:
- every respondent of every file is kept: 40,987 rows. 0.4.4 dropped
380
qes2007_panelrespondents and 1 CROP respondent as duplicates, and, in a session with French messages, lost 1,208qes2007_paneland 616qes2018_panelrows while reading them; - the 70 columns 0.4.4 appended by stacking variables that share a
name across studies are removed, because they held different questions
in different studies (
attr(, "removed_columns")lists them); -
qes2018turnoutis 0 rather thanNAfor the 336 respondents who said they did not vote (q5codes 1 and 3): its valid N goes from 2,303 to 2,639 and its mean from 0.96 to 0.84; -
qes_name_enuses each study’s own title for the Durand panels, the CROP polls and the 1998 polls (for example “Panel Survey on the 2018 Quebec Election”): select studies withqes_code, not with the name; - label text is fixed:
qes2012religionkeeps its original case, CROPincomeandprovince_territoryread their accented letters correctly (“20 000 $ à 39 999 $”, “Quebec”), and theqes2022interview_*columns lose their trailing.000.
Each column is built from the harmonized variables.
The master is built by qes_harmonize() for all 11 studies:
each column comes from the harmonized variables (“targets”) it needs,
and a code the harmonization rules do not map is NA.
qes2008 has no recommended weight (its weights are
calibrated on the reported vote), and the weights of
qes1998, the 2007 and 2012 Durand panels and the CROP polls
are not validated: their answers are filled, but weight_pre
and weight_post are NA there, and
survey_weight keeps each study’s own weight, as in 0.4.4.
The weights
table describes each study’s weight. The merged
file page describes every column. The columns that change
estimates:
| Column | Now | Changed in |
|---|---|---|
vote_choice |
The vote reported after the election, in every study that asked it
(0.4.4 used the campaign intention for qes2022 and
qes1998). Non-voters, spoiled ballots, “don’t know” and
refusals are NA: the categories “Did not vote / None” and
“Don’t know / Refused” are gone, and turnout says who
voted. The CROP polls asked only intentions: NA, with the
intention in the appended vote_intent. |
every study |
turnout |
Reported turnout, 1 or 0 (qes2022 from its
post-election wave; the 2018 panel from its turnout question). |
qes2022, qes2018,
qes2018_panel, qes2007_panel
|
sovereignty_support, sovereignty
|
The referendum on Quebec becoming an independent country only. Other
wordings are NA; the 1995 sovereignty-partnership question
is in the appended sov_partnership_1995. |
the studies without that question |
ideology |
0-10 with its end points: 0.4.4 lost the 0 and 10 answers of
qes2014 (142 respondents) and found no source in
qes2018. |
qes2014, qes2018
|
political_interest |
The four-point questions as 10, 7, 3 and 0; 0.4.4 left the 2018 codes 1 to 4 unconverted and reversed. Mixes a 4-point and a 0-10 question across studies. |
qes2018, qes2012_panel
|
language |
The mother tongue; 0.4.4 used the interview language in
qes2014 and the interface language in qes2022.
Two first languages are NA. |
qes2014, qes2022, qes2007,
qes2007_panel, qes2018_panel
|
income, religion
|
Each study’s own categories; -99 and raw codes are
NA; qes2012 income is filled. |
income: 9 studies; religion:
qes2012, qes2014, qes2018,
qes2022
|
age_group, education,
born_canada
|
Categories corrected: six age bands in the 2007 and 2012 panels, maîtrise as University, people born elsewhere in Canada as born in Canada, and similar cases listed in NEWS. | a few studies each |
provincial_pid |
Filled where the study asked it (0.4.4 found a source only in
qes2022). |
qes2007, qes2008, qes2012,
qes2014, qes2018
|
party_best, party_lean
|
NA everywhere: each study’s source was another
question. |
party_best: qes2022, qes2018,
qes2014, qes2012, qes2007;
party_lean: every study but qes1998
|
survey_weight |
Unchanged: each study’s own weight, on its own scale. Do not pool weighted estimates across studies with it. | none |
Three consequences for pooled analyses: an estimate that pooled
vote_choice across studies mixed intentions and reported
votes in 0.4.4, and one that pooled ideology across 2012,
2014 and 2022 lost the extreme answers of 2014. Estimates of that kind
move most. Third, a filter on non-missing values now keeps studies that
0.4.4 left empty: columns that were NA for a whole study
are filled, ideology for qes2018,
provincial_pid for qes2007,
qes2008, qes2012, qes2014 and
qes2018, income for qes2012 and
year_of_birth for qes2022. A complete-case
sample such as
subset(master, !is.na(vote_choice) & !is.na(ideology))
now contains qes2018; if the script also reads
qes2018 on its own, those respondents are counted twice.
Select studies explicitly, with qes_code %in% c(...), and
compare table(sample$qes_code) for the analysis sample with
the counts you published.
The attributes of the master say what each column holds, which studies’ values changed and why a column is empty in a study:
master <- get_qes_master(surveys = "qes_demo", quiet = TRUE)
#> Values changed in qesR 0.7.0: get_qes_master() is now rendered from the harmonization engine (qes_harmonize()), so vote_choice and turnout are the reported vote and turnout in every study that asked them (the CROP polls asked only an intention: vote_intent), and codes the spec does not map are NA (language, the mother tongue, is NA for respondents who gave two first languages); attr(, "legacy_column_map") says what each column holds and NEWS lists the changes. Results of an earlier version are reproducible by installing it (for 0.4.4: remotes::install_github("ThomasGareau/qesR", ref = "v0.4.4")). This note is shown once per session.
#> get_qes_master() no longer appends the 70 columns that qesR 0.4.4 built by stacking variables that share a name across studies; attr(, "removed_columns") lists them. Read those items from each study with get_qes(). This note is shown once per session.
map <- attr(master, "legacy_column_map")
map[map$column %in% c("vote_choice", "ideology", "sovereignty_support"),
c("column", "target", "flag")]
#> column target flag
#> 20 ideology lr_self <NA>
#> 22 vote_choice vote_prov_recall <NA>
#> 26 sovereignty_support sov_indep <NA>
attr(master, "legacy_na_columns")[1:5, c("column", "study", "reason")]
#> column study reason
#> 1 interview_start qes_demo no_source
#> 2 interview_end qes_demo na_column
#> 3 interview_recorded qes_demo na_column
#> 4 language qes_demo no_source
#> 5 citizenship qes_demo no_source
unique(master[, c("qes_code", "vote_choice_timing", "sovereignty_item")])
#> qes_code vote_choice_timing sovereignty_item
#> 1 qes_demo post sov_indepmap$studies_changed names, for each column, the studies
whose values differ from 0.4.4, and
attr(master, "source_map") the question each column read in
each study. The Changelog counts the changed cells, column by column and
study by study.
get_decon() is soft-deprecated and built from the same
targets: its categorical columns are factors with English levels, and
for qes2022 its turnout and
votechoice stay the campaign-period likelihood and
intention (attr(, "timing") says "pre").
From the master to qes_harmonize()
For new work, qes_harmonize() gives what the master
cannot: one column per question stimulus (a reported vote and an
intention are different targets, and so are the sovereignty wordings), a
comparability grade for each study’s question, a reason for every
missing value and each wave’s weight. The target column of
the map above names the target of each master column. The master’s
ideology and vote_choice become:
h <- qes_harmonize("qes_demo", targets = c("lr_self", "vote_prov_recall"),
missing = "reasons", quiet = TRUE, lang = "en")
table(h$lr_self__na, useNA = "ifany")[c("dk", "refused")]
#>
#> dk refused
#> 5 0
attr(h, "qes_weight_guide")[, c("target", "study", "weight_column")]
#> target study weight_column
#> 1 vote_prov_recall qes_demo weight_post
#> 2 lr_self qes_demo weight_postCompute each estimate within one study, with the weight of the wave that asked the question. The mean left-right position of each party’s voters in the demonstration study:
ok <- !is.na(h$lr_self) & !is.na(h$vote_prov_recall)
d <- h[ok, ]
means <- sapply(split(d, d$vote_prov_recall, drop = TRUE), function(p) {
weighted.mean(p$lr_self, p$weight_post)
})
round(means, 2)
#> PLQ PQ CAQ QS PVQ Other party
#> 5.39 6.70 5.56 2.53 6.00 9.00The pooled variable vote_choice is the closest to the
master’s vote_choice across studies: with
types = list(vote_choice = "recall") it is the reported
vote, as in the master (whose party labels are those of 0.4.4); by
default it also takes the vote intention of the studies that asked no
reported vote, and vote_choice__type says which.
qes_spec() shows which study has which target and how
comparable its question is, and
vignette("harmonization-reference", package = "qesR") gives
each target’s questions, wordings and grades.
Old names, new names
The eleven helpers of 0.4.4 below keep working and will not be
removed. Each prints a one-time notice naming its replacement
(options(qesR.quiet_deprecated = TRUE) hides the
notices).
| 0.4.4 | Replacement | Note |
|---|---|---|
get_codebook(), get_qes_codebook()
|
qes_codebook() |
same arguments; offline; refresh is ignored |
format_codebook(cb, layout) |
qes_codebook(cb, layout = ) |
a plain data frame (for example a codebook read back from CSV) is an error |
get_value_labels(cb, variable) |
qes_codebook(layout = "long") |
an unknown variable is an error, not an empty list |
get_question(data, q) |
qes_question(study, variables) |
exact names only; full = FALSE no longer changes the
result |
get_codebook_files(),
get_qes_codebook_files()
|
qes_docs() |
lists every document, offline |
download_codebook() |
qes_download(what = "docs") |
file now selects documents by name; files are
md5-checked |
get_preview(srvy, obs) |
head(get_qes(srvy), obs) |
|
get_qescodes() |
qes_studies() |
the 1998 firm files are added after the 11 old codes |
get_decon() |
qes_harmonize(srvy, targets = "decon") |
soft-deprecated; values changed (above) |
get_qes(), get_qes_master() and
qes_codebook() keep their names. A legacy function returns
what its replacement returns. The first call of
get_codebook() in a new session also prints this notice (it
is not shown below, because it appears only once per session):
get_codebook()is soft-deprecated; useqes_codebook(). It keeps working and will not be removed.
cb <- get_codebook("qes_demo")
identical(cb, qes_codebook("qes_demo"))
#> [1] TRUEKeeping new results reproducible
Record what each result was computed from, and cite it:
qes_provenance(h, level = "spec")[, c("spec_version", "spec_hash", "qesR_version")]
#> spec_version spec_hash qesR_version
#> 1 4.7.0 30697daa0042aab3078507b665867748 0.9.2
qes_cite("qes2014")
#> [1] "Gareau-Paquette, Thomas, 2026, \"qesR: Access Quebec Election Study Datasets\", R package version 0.9.2, https://github.com/ThomasGareau/qesR"
#> [2] "Bélanger, Éric; Nadeau, Richard, 2023, \"Étude électorale québécoise 2014\", https://doi.org/10.5683/SP3/64F7WR, Borealis, V1, UNF:6:OoiAJ3ShbycsxmWCefqrjw=="qes_provenance(x) gives the file of each study (DOI,
dataset version), and level = "spec" the version of the
harmonization rules that produced harmonized data. The same qesR version
reproduces a result exactly, since it fixes both.