crosstab_banner() no longer stops with "Element ... doesn't exist" when
the same binary-encoded columns (an option label or blank) are named in both
rows and cols. The multi-select block reshaped its own member column away
before grouping by it. Each block is now tabulated from its own columns, which
also keeps an unrelated column that happens to share the prefix out of it.crosstab_banner()
chooses its own variables, whatever max_levels is. Each gives one column of
the respondents who ticked it, and the columns of a multi-select block share
one spanner named after the block. A lone binary column is used across the
top only; as a stub every cell would read 100.save_plot(),
save_data(), save_output(), export_xlsx(), export_summary_xlsx(),
report_save(), report_deck(), scaffold_report(), and dir in
example_report().output_dir option defaults to ".", so a bare file name is written
in the working directory as named. Set it to a folder, in a session or in
your profile, to collect bare names there as before.sample_comments() and sample_comments_diverse() set their seed through
withr, which is now imported, and no longer touch the global environment.crosstab() and crosstab_banner() no longer break on a question called
value or n. Both used those names for their own counting, so such a
column came back as a table of the wrong shape without a word: percentages
for headings, a list-column, a banner group of zeros. The counting now runs
under names no survey uses. The long form of crosstab() still puts its
cells in a column called value, so it refuses a question of that name and
says so; the wide form and the banner handle it.calc_percentage() and crosstab() keep a factor's own level order.
Without a registered order, a factor came back as text in alphabetical order,
so PISA's "Strongly disagree" to "Strongly agree" read Agree, Disagree,
Strongly agree, Strongly disagree. A registered order or levels = still
wins; a factor's levels now come next, and only then data order.crosstab() puts its columns in level order whenever the column
variable has one. tidyr names new columns by first appearance, so a
registered order could still come out scrambled when the first row lacked
the first answer.bin_numeric(), recode_age() and recode_generation() return factors
whose levels follow the bands, where they used to return text. Text sorted
"100-150k" before "40-70k" in every table built from it. Code comparing the
result to a string is unaffected; wrap it in as.character() where plain
text is needed.checkpoint = TRUE made; a path you chose is kept, as
the help page already said.Free-text country questions do not come back tidy. In one real open salary
survey of 26,232 people, nine thousand wrote "United States" and eight thousand
wrote "USA"; the bundled table matched the first and missed the second, so
recode_region() resolved 49% of the column.
recode_region(), recode_subregion() and add_region() now fold the
answer down before looking it up (case, punctuation, accents and a leading
"the" are dropped) and then try a table of the spellings people actually use.
It carries endonyms ("Deutschland", "Brasil", "Espana"), the constituent
countries of the United Kingdom, and ISO 3166-1 alpha-2 and alpha-3 codes, so
a column already coded to the standard needs no preparation. On that same
survey the three functions now resolve 99.3%, and what remains are real typos
worth seeing."NO" is Norway and "no" stays
unmatched. Unambiguous codes match either way.country_region gains iso2 and iso3 columns, so a region breakdown
can be reported against ISO 3166-1 rather than against country names. Every
country carries both except Kosovo, which has no code of its own. Namibia's
alpha-2 code is the two letters "NA", which is worth knowing before
filtering on the column."Ise of Man", "America Samoa" and "Kazakstan". Bermuda was
missing altogether. All four are fixed, and the duplicate rows the
corrections exposed are gone, which takes the table from 182 rows to 180.Satisfaction is often collected on 0 to 100, and seven-point agreement scales are common. The band presets are written on 1-5, 0-10 and -100..100, so until now a chart on any other scale meant dividing the data to meet the thresholds, which left the axis showing a scale nobody had been asked about.
rescale_bands() stretches any band specification onto the scale the
chart actually uses: bands_rating_3() %>% rescale_bands(to = c(0, 100)).
The mapping is proportional, and labels and colours are untouched. It works
on a hand-built band data frame as well as on the presets.register_order() set the levels of the answer column and stopped there. The
levels are what a chart reads, so charts were right; a table printed to the
console or written to CSV carries no levels with it, so every number anybody
actually read came back in alphabetical order. On a recency scale that puts
"In the past month" above "In the past week".
calc_percentage() sorts its rows into the registered order. A by =
breakdown keeps its groups together and orders within each one.crosstab() does the same on both margins. Column order was affected
too, because tidyr names new columns in order of first appearance rather
than by factor level.plot_ipm() pinned its performance axis to 1-5 whatever bands it was
given. A study rated on 0-100 or on a seven-point scale came back as an empty
panel with the bands still drawn across it, and ggplot2's only complaint was
that some rows had been removed. The axis now runs over whatever scale the
bands describe, and a feature that really does fall outside them is named in
a message rather than dropped in silence.Three separate faults kept recode_region() from matching countries that were
spelled perfectly. On the Stack Overflow Developer Survey, whose country column
is coded to ISO 3166-1, 3,126 of 87,973 answers came back NA. They now resolve
to 99.93%, and the only thing left unmatched is the answer "Nomadic".
"Timor-Leste", "Guinea-Bissau", "Cote d'Ivoire", "Korea, North",
"Gambia, The" and some thirty more.country_region held 180 of the 249 entries in ISO 3166-1. What was
missing was not obscure: Ghana, Ethiopia, Senegal, Rwanda, Yemen, Benin,
Botswana, Haiti, Mali, Sierra Leone and South Sudan all returned NA. The
table is now complete at 255 rows, grouped the way the package groups
countries rather than the way the UN does, so no answer already built on it
changes."Viet Nam", "Syrian Arab Republic", "Lao People's Democratic Republic", "Brunei Darussalam",
"Cabo Verde", "Eswatini", "North Macedonia" and the rest are what a
questionnaire built on the standard shows a respondent. They match now, in
ISO's inverted order ("Moldova, Republic of") and in the order the name is
actually said ("Republic of Moldova").Matching a country meant stripping its accents, and that was left to
iconv(to = "ASCII//TRANSLIT"), which three platforms turned out to do
differently. Windows writes a German sharp s as ?; macOS leaves a circumflex
where it found one, so "Cote d'Ivoire" typed with its accent matched on Linux
and came back NA on a Mac. A published figure changed with the machine that
produced it.
recode_region(), recode_subregion() and add_region() now fold the
eighty-eight accented letters and four ligatures a country name can carry by
hand, and reach iconv only for anything outside that range. The answer is
the same on every platform."Estados Unidos", "Etats-Unis", "Vereinigte Staaten", "Stati Uniti", "Reino Unido" and "Royaume-Uni" now match.use_brand() says so when it sets a typeface in a session whose default
device is pdf, which is what a plain Rscript run gets. That device carries
its own short list of font families and fails at drawing time with "invalid
font type" rather than substituting. Charts written with save_plot() were
never affected.calc_percentage(d, gendr) reported replacement has 0 rows, data has 3, which describes an assignment the caller never made; it now
says Column `gendr` not found in the data. Did you mean `gender`?. The
same applies to calc_summary(), calc_nps(), crosstab(), plot_nps() and
split_multi().calc_nps() works on a frame filtered down to no rows instead of failing
with replacement has 1 row, data has 0. Filtering to a segment nobody fell
into is ordinary, and the answer is a zero count, not an error.crosstab_banner() refuses a frame with no rows in a sentence that says
a filter is the usual cause, rather than reporting the same internal mismatch.plot_stacked_rating() names the argument the caller actually omitted.
Leaving out level reported a missing level_sym, which is not an argument
it has.plot_rating_grid() reports two answers that land on the same rank.
recode_likert() falls back to matching on a substring, and "Neither agree
nor disagree" contains "disagree", so a five-point scale handed only four
levels drew two segments both numbered 2. The chart looked plausible and was
wrong; now it says which level is missing.plot_quotes_tree() says which column it cannot find. Its defaults are
the comment and length columns sample_comments() produces, and handed
anything else it failed at drawing time with a ggplot error about an object
of type <function>, because length had resolved to the base function.read_folder() notices when the first row of a file is question wording
rather than an answer, which is how survey platforms export the question text,
and says so. Read as data it becomes respondent number one and shifts every
count by one. The new question_row argument drops that row from every file
(TRUE), keeps it silently (FALSE), or keeps it and warns (NULL, the
default).recode_age() reports answers that held no number at all. "young" and
"prefer not to say" became NA in silence, which made a column that was
never numeric look like a column of missing ages. New quiet argument.README.md had pointed at man/figures/logo.png
since the first release without that file ever existing, so the front page of
the README and of the documentation site both opened with a broken image.Google Forms and most spreadsheet exports put every answer a respondent ticked into a single cell, joined by a delimiter. That shape could not be counted before, because a respondent who ticked three options is one row, not three.
calc_percentage_multi() now detects a packed column and unpacks it. When
the prefix matches exactly one column and a ";", "|" or "," is found
inside it, the answers are separated and the answer text becomes the option
label. split = "," forces a delimiter and split = FALSE turns the detection
off. A block of one-column-per-option behaves exactly as before.split_multi() is the new recode helper for the same shape, widening a
packed column into one column per answer so that crosstab(), the plots and
everything else can use it too.pct_detractors, pct_passives and pct_promoters, with the
counts behind them in detractors, passives and promoters. A single NPS
hides its own composition: +20 from 40% promoters and 20% detractors is a
different picture from +20 with 25% and 5%. Counts are unweighted; the
shares follow nps and so are weighted when a scheme is active.method argument. "rwa" (relative weights analysis) remains the
default and is unchanged. "forest" adds random-forest permutation importance,
which sees non-linear effects and interactions that relative weights cannot, so
a feature ranking high there and low under "rwa" is a signal worth chasing.
"correlation" needs no suggested package at all.plot_ipm().
ipm_model(method = ) passes the choice through.-100 0 30 70 100 and a quality bar reads 1 3 4 5. The bars share one
panel but not one scale, so no axis could serve them both and they carried no
scale at all: a marker two thirds along the bar said nothing about the score it
marked."EXCELLENT" in the
30-point band at the top of the NPS scale was printed as "EXCELLEN".se_prop(pctp = TRUE) returns the standard error in percentage points, the
form report footnotes use, instead of leaving you to multiply by 100.plot_nps() is centred over the promoter bars, like
the detractor and passive callouts beside it. It had been pinned to the right
edge of the panel, which left "Very likely" hanging off to one side of the
"% PROMOTER" line above it.rse_rating() now say exactly what the
Australian Bureau of Statistics does and where ezrsurvey departs from it.library(ezrsurvey) still puts dplyr, ggplot2, tidyr, tibble, readr, stringr
and purrr on your search path, so nothing changes in a script. The package now
names those seven in Depends rather than depending on the tidyverse
meta-package, which had tied it to every package in that tree.crosstab_banner(checkpoint = TRUE) now deletes its checkpoint once the
table is built. A finished run has nothing left to resume from, so the file
it kept under tools::R_user_dir() is removed instead of accumulating.clear_checkpoints() empties that folder, for runs that were interrupted
and never repeated.checkpoint = "my-run.rds") is your
file: it is left exactly where it is, and clear_checkpoints() never touches
it.scaffold_report() now follows the same rule as every other writer: a
bare name lands in ezrsurvey-outputs/, so the .qmd sits where its rendered
output will. Pass a path naming a directory to put it somewhere else.recode_likert() now tries the longest answer wording first. A scale
whose levels nest inside one another ("Good" inside "Very good", "Likeable"
inside "Very likeable") previously resolved any answer the exact pass missed
to the shorter level, so an answer carrying an emoji or a stray character
was quietly downgraded. If your data has such a scale and any untidy answers,
ratings, feature averages and ipm_model() performance scores will change,
and the new numbers are the correct ones. Supplied synonyms follow the same
longest-first rule.bin_numeric() reports values that fell outside breaks instead of
turning them into NA in silence, and takes quiet = TRUE to suppress that.
A closed top band under an open-ended label (35 to 40.5 labelled
"35 to 40+") silently drops everyone above it; Inf is what that label means.NA rather than letting them appear as an unlabelled bar.crosstab_banner(),
export_summary_xlsx(), report_deck(), read_folder(), diagnose() --
now report the question, file or slide they are on, with a measured estimate
of the time remaining. A full every-variable banner is one cross-tab per
question per grouping variable, so it takes minutes on a wide survey and used
to give no sign of life at all.progress option (ezrsurvey_options(progress = )): "auto"
(default) reports in an interactive session and stays silent in scripts,
vignettes and R CMD check; TRUE / FALSE force it either way.ezrsurvey-outputs/, created on
demand: save_plot(p, "nps.png") writes ezrsurvey-outputs/nps.png, and the
same holds for save_data(), save_output(), export_xlsx(),
export_summary_xlsx(), report_save() and report_deck(). Previously only
the path = NULL default used that folder, so any script that named its files
scattered them through the working directory."./nps.png" for the working directory, "charts/nps.png" for a
folder of your own, or any absolute path (so tempfile() is unaffected).output_dir option (default "ezrsurvey-outputs") renames that folder
for a project, or set it to "." to put bare names back in the working
directory.crosstab_banner() and export_summary_xlsx() choose their own variables
when none are named, and that choice decides everything they produce. Both now
print the questions kept, the grouping variables and the columns skipped, and
wait for a yes before a run that takes minutes. Naming rows / cols (or the
variables to summarise) is your own choice and is never questioned.confirm option and argument: "auto" (default) asks in an
interactive session and never asks in a script, so an unattended run cannot
stall on a prompt; TRUE / FALSE force it either way. Answering no computes
nothing and returns NULL.crosstab_banner(checkpoint = ) saves each question as it finishes, so
re-running the identical call after a crash or an interrupt continues instead
of starting over. A checkpoint written from different data or different
arguments is reported and discarded, never blended into the new run. Use
TRUE to have the file managed under tools::R_user_dir(), or name a path to
choose the location. Nothing is written unless you ask.clear_weights_cache() empties the cache; clear_weights() does it too.sample_comments_diverse(max_candidates = ) narrows a large corpus to a
random shortlist (default 500) before comparing every comment with every
other, which is what made it slow and memory-hungry on a big survey.plot_rating_grid() builds a stacked rating chart for a whole block of
prefix-sharing columns in one call: tabulate, tidy the question names, number
the answers by their place on the scale, stack. It replaces an eight-line
block per question block, and it reports answers that are not on the scale
instead of leaving them as an unranked segment.clean_label() is now exported. It is the tidying
calc_percentage_multi() and ipm_model() already applied to their own
output, so a hand-built table can be made to match them: labels that disagree
will not join, which is what silently empties a comparison chart.calc_percentage_batch(clean_names = , prefix = ) applies that same
tidying to its variable column.bands_nps_score() exposes the Net Promoter Score band set
(needs work / good / great / excellent over -100..100) that was previously
locked inside plot_nps_gauge(). Note bands_nps() is the 0-10 answer
scale and is a different thing.from / to to trim themselves to the part of the
scale a chart actually shows, e.g. bands_rating_3(from = 2) on a 2-5 panel.report_new(keep_slides = FALSE) starts from an empty deck, dropping any
boilerplate slides the template carries.sample_comments(by = ) samples n comments per group and keeps the
group as a column, replacing a hand-rolled loop over the groups.ezrintelligence package: ai_chat(), ai_summarise(),
ai_report_sections(), ai_context(), the prompt-template registry
(list_prompts(), get_prompt(), register_prompt()) and key management
(set_llm_key(), get_llm_key(), has_llm_key(), delete_llm_key(),
list_llm_keys()). Install it alongside ezrsurvey and pass it any summary
table ezrsurvey produces. ellmer and keyring are no longer suggested
dependencies.report_deck() loses its ai, ai_titles, chat, provider, model
and context arguments. For AI slide text, call
ezrintelligence::ai_slide_text() on the table behind a slide and place the
title and bullets it returns yourself.ai parameter and
the eval-gated AI chunks; they render with placeholder narrative and their
data parameter as before.export_summary_xlsx() no longer fails with "Failed to save workbook"
when R_ZIPCMD names a zip tool that is not installed. openxlsx2 reaches for
an external zip whenever that variable is set, and R sets it to a bare zip
on Windows under R CMD whether or not a zip.exe exists; the workbook is
now written with openxlsx2's own zipping code whenever the named tool cannot
be found.crosstab_banner() example that crosses every variable against every
other moves to \donttest, where its runtime belongs.crosstab_banner() builds the market-research banner table: a stack of
question variables down the side (rows) and one or more grouping variables
across the top (cols), plus a whole-sample Overall column. Categorical
questions become column-percentage blocks (each column sums to ~100 within a
block); numeric questions turn into a mean/median/sd/quartile block
automatically. cell = "diff" shows each cell's distance from the Overall
column, long = TRUE returns the tidy form, and flextable = TRUE renders the
two-row spanning header for a slide or Word report. It generalises crosstab()
from a single pair to a whole table. Pass only the data frame to cross every
variable against every variable; identifier and free-text columns are skipped,
and check-all-that-apply blocks (e.g. motivations_*) are folded into a single
multi-select stub question with the correct question-level base.export_summary_xlsx() writes a tabbed Excel workbook with one worksheet
per question, each holding that question's summary table and its chart
(percentages and a bar chart for categorical questions, a calc_summary()
table and a histogram for numeric ones, a calc_percentage_multi() table for a
check-all-that-apply block). Called with just the data frame it writes every
question at once, one multi-select block to a single sheet, skipping identifier
and free-text columns. The Excel counterpart of the slide/Word report_deck().
Needs the suggested openxlsx2 package.ezrsurvey-outputs/ (was outputs/).
save_plot(), save_data(), save_output(), export_xlsx(),
report_save() and report_deck() write straight into it with no per-type
subfolders when no path is given; explicit paths behave as before.library(ezrsurvey)
brings dplyr, ggplot2, tidyr, stringr and the rest with it, so scripts
and reports never need to load the packages ezrsurvey wraps.use_dataset() finally saves the typing it promised. With a default set,
columns can be passed positionally with no data and no column =:
calc_percentage(demo_gender), calc_nps(nps_value),
crosstab(demo_gender, region), diagnose(starts_with("ratings_")). The
helpers tell a data frame from a column by looking at the first argument, so
an explicit data frame or a pipe still wins. Previously only the much longer
calc_percentage(column = demo_gender) worked, which saved nothing over just
passing the data.plot_gauges() draws several scores as thin banded gauge bars stacked one
above another -- the summary-slide staple of the Net Promoter Score over the
average feature quality rating, each on its own scale with a marker, instead
of one fat single bar. plot_nps_gauge() remains for a single score.bands_rating_3(). [plot_ipm()] now colours every point by the band it
actually falls in, and pal_rating uses the same thresholds (1-2 bad, 3 ok,
4-5 good). plot_ipm() gains a bands argument.use_brand() adopts an organisation brand from a .pptx / .docx
template: theme accent colours and typefaces are extracted from the file's
OOXML theme and become the session defaults (plot_bars() fill,
theme_ezrsurvey() font when installed locally), and the template is
registered as the default reference document for report_new(),
report_deck() and scaffold_report(). Colours/fonts can also be set
directly (use_brand(colors =, fonts =)) or persisted in the YAML profile
(brand_* options). brand_info() shows and clear_brand() resets the
active brand.pal_brand() and scale_fill_brand() / scale_colour_brand()
expose the brand accents to any plot; semantic palettes (pal_rating,
pal_nps) deliberately keep their red-amber-green meaning.One line per slide. New wrappers collapse calc -> plot -> add onto a
single pipe step so a deck script reads as one line per slide:
report_slide(title, content) starts a titled slide and places its content
(a ggplot becomes a chart, a data frame a table, a character vector a bullet
list); report_section("DEMOGRAPHICS") drops a single-word divider; and
report_title_slide(title, subtitle) opens the deck. The rewritten
example_report() deck is built this way as a full agency read-out -- cover,
chaptered sections and every question block in the questionnaire
(recommendation, the six experience ratings and their drivers, motivations,
the three sponsor brands, the whole respondent profile, favourite drivers,
open-text comments and a methods appendix) -- with each slide title the survey
question it answers.
Slide tables fill the slide. report_add_table() now styles the table
(banded header, centred cells, a readable 12pt font) and widens its columns
to span the content placeholder, so a summary table fills the slide instead
of sitting tiny in a corner.
Headlines and bullets no longer overflow. The bundled templates bring the master title/body font sizes down to deck-appropriate values and give every text placeholder shrink-to-fit, so a descriptive question headline or a long bullet list is scaled to its box rather than spilling out of it.
Decks are 16:9 by default, and properly designed. Without a brand
template, report_new(), report_deck() and the Quarto pptx scaffold use a
built-in widescreen template with a coherent navy/gold identity in place of
the dated default Office colours: a full-bleed navy cover with a large
left-aligned title, gold accent rule and a subtitle strapline; full-bleed
navy section dividers carrying a single large section word; and content
slides with a navy 24pt title over one slim rule (no stray hairlines) and a
live slide number in the corner. report_title_slide() gains a subtitle
argument for the cover strapline. Pass style = "plain" for the same
palette with no decoration, or slide_numbers = FALSE to drop the numbering.
Both templates keep the standard PowerPoint layout names (plus "Content with
Caption"), so Quarto renders against either without falling back to Pandoc's
own template.
Bars are a constant thickness across charts. geom_col() widths are a
fraction of a category slot, so a three-answer chart used to draw bars over
twice as thick as a ten-answer one and a deck looked incoherent as you
flicked through it. [plot_bars()] now scales the fraction with the bar count
so the drawn thickness is the same everywhere (bar_width /
bar_ref_items, see ezrsurvey_options()).
example_report() copies a complete worked report into your project: a
full narrative report over the bundled podracing_survey and the matching
deck script. Both are written the way a real readout is -- a story that runs
headline, then cause, then segment, then evidence; slide titles that state
the finding rather than name the chart; and every figure in the prose
computed from the data rather than typed in, so the narrative cannot drift.
The officer builders now work with any corporate template: slide layouts
are chosen by inspecting placeholder types (not locale-dependent layout
names), slide titles are skipped with a warning when a layout has none, and
plots/tables are rendered at the exact size of the content placeholder with
sensible fallbacks. report_layouts() lists what a template offers.
report_deck(ai = TRUE) drafts 3-4 takeaway bullets per slide (and,
with ai_titles = TRUE, headline titles) from the data behind each item,
reusing one chat across the deck; bullets land in a two-content layout when
the template has one, else in the speaker notes. AI failures degrade to
plain slides.
Quarto scaffolds rewritten as four distinct report skeletons (pptx /
html / docx / pdf) with data and ai parameters, placeholder narrative,
executive summary and a precision appendix; scaffold_report() gains
author and reference_doc (defaulting to the brand template).
README documents the Google Slides workflow: build the branded .pptx
and import it (File > Import slides); what survives and what to watch.
slide_bullets, thematic_analysis, segment_comparison, methodology
and full_report.ai_context() bundles sample size, question wording, base, fieldwork
and precision notes (auto-derived from the raw survey) into every prompt via
the new context argument of ai_summarise() / ai_report_sections() /
report_deck().register_prompt() gains an output_contract field pinning the answer's
format; ai_report_sections() gains chat for connection reuse.outputs/ folder in the working
directory instead of requiring a path: save_plot() (outputs/plot.png),
save_data() (outputs/data.csv), export_xlsx() (outputs/tables.xlsx),
report_save() / report_deck() (outputs/report.pptx / .docx). Missing
directories are created; explicit paths behave as before.annotate_bands() band labels no longer sit on top of the marker line:
label_offset defaults to 4% of the opposite-axis range and labels are
justified into the panel, so nothing clips at the panel edge. text_size
now defaults to the theme's base font size (11 pt) instead of a fixed geom
size, and multi-line band labels get a tighter line height.mark_value() labels are justified inside the panel next to the marker
line instead of being half-clipped at the Inf edge.plot_nps() detractor/passive/promoter callouts no longer clip at the
panel top or overlap each other; the promoter callout right-aligns to the
panel edge.plot_nps_gauge() gains a label_size argument; band labels default to
the theme's base font size.sample_comments() gains a seed argument (matching
sample_comments_diverse()), so quote selections can be reproduced; the RNG
state is restored afterwards.calc_percentage(long = TRUE) no longer triggers the deprecated tidyselect
external-vector warning.plot_bars() now lays itself out. orientation = "auto" (the default)
draws vertical columns for a few short labels and horizontal bars for many or
long ones, wraps long labels with str_wrap(), steps the data-label size down
as bars multiply, and orders bars so the longest sits at the top (bars) or on
the left (cols). An intentional ordinal scale (an ordered factor from a
registered order) keeps its order. orientation / sort / wrap /
label_size and the new bar_* options (see ezrsurvey_options()) override
the automatics; the old flip argument still works.drop_items() plus a drop = argument on calc_percentage(),
calc_percentage_multi(), calc_percentage_batch() and crosstab() remove
unwanted answers (e.g. "Other", "Don't know") before counting, so the kept
answers re-base to ~100%. Set a session default with
ezrsurvey_options(drop_answers = ...).podracing_survey (renamed from
consumer_survey): a Star Wars pod-racing fan survey with
international-standard demographics (ISO/IEC 5218 sex, ISCED education, ILO
labour-force status, ISIC sectors, ISO 3166 countries) and diverse, entity-rich
open-text comments for the text / NER helpers.shopping_survey: a turn-of-the-century (Edwardian) shopping-behaviour
survey -- a second, very different theme for examples.register_order_presets() now ships an education_isced order (was
education_us).set_weights() defines a post-stratification / raking scheme from target
shares, e.g. set_weights(c(variable = "demo_gender", Male = 0.49, Female = 0.50, "Non-binary" = 0.01)), and multiple variables at once. With
one variable it is exact post-stratification; with several it rakes (iterative
proportional fitting) so every margin matches.calc_percentage()
(and calc_percentage_batch()) gain a wpct column beside n/pct, while
calc_nps(), calc_summary() and crosstab() default to weighted
values. Pass weights = FALSE to any of them to opt out, or weights = <spec>
to weight a single call ad hoc.clear_weights(), get_weights(), has_weights() manage the scheme;
weight_vector() returns the per-respondent weights (and set_weights()
reports the Kish design effect and effective sample size).First release. A single-line-helper toolkit for everyday consumer-survey analysis, aimed at research and insights professionals.
read_folder(), select_prefix(), select_suffix(),
parse_filename().na_blank(), ensure_numeric(), bin_numeric(),
recode_age(), recode_generation(), recode_likert(), nps_group(),
recode_region() / add_region().calc_percentage(), calc_percentage_multi(),
calc_percentage_batch(), calc_summary(), crosstab().calc_nps(), calc_importance(), ipm_model().compare_values(), plot_diff().se_mean(), se_prop(), rse(), margin_of_error(),
diagnose().sample_comments(), sample_comments_diverse().convert_currency(), add_currency(), list_currencies().plot_bars(), plot_stacked_rating(), plot_nps(),
plot_nps_gauge(), plot_ipm(), plot_quotes_tree(), with the
theme_ezrsurvey*() family, semantic palettes and annotate_bands().save_plot(), save_data(), save_output(),
export_xlsx(), the report_*() officer builders and scaffold_report().ai_chat(), ai_summarise(), ai_report_sections() with
keyring-backed key management and a prompt-template registry.ezrsurvey_options(), reusable level orders
(register_order()), YAML profiles (use_ezrsurvey_profile()).podracing_survey, country_region, currency_rates.