recommend_test() and fit_recommended() gain
random_slopes to request particular slopes or an intercept-only structure.fit_recommended() reports Welch, Wilcoxon and Kruskal-Wallis test statistics
and p-values in its omnibus table and manuscript sentences. Single-model
engines now report their actual convergence status.fit$data.replace_values() promotes integer columns when a replacement requires a
fraction, a non-finite value or a value outside the integer range.plot_fit() draws the analyzed observations and the tests stored by
fit_recommended(), without running a separate model or significance test.plots target and reuse those plots in
individual PDFs, combined figures and the Quarto report. Re-scaffolded projects
retain existing files unless overwrite = TRUE is requested; update the
pipeline and figure/report scripts together to adopt the new workflow.This release fixes a set of defects that could put a wrong number, a wrong direction or a wrong label into a manuscript without any error. Re-run within-subjects analyses, post-hoc sentences from rank-based tests, mixed-model reports and prefix = questionnaire scoring made with earlier versions.
ggwithinstatsWithPriorNormalityCheck() / plot_within_stats(), the asterisk variant, and analyze_and_report() / report_all() with design = "within" require a new subject = argument naming the participant column. Without it, ggstatsplot paired the k-th row of each condition: shuffling one condition's rows moved p from 3e-7 to 0.30, and one participant missing a condition shifted every later pair. Participants lacking a condition are now dropped with a message naming them, and more than one row per participant and condition is an error (aggregate repeated trials first). subject is the last argument, so existing positional calls are not silently remapped.ggstatsplot (>= 1.0.0) and statsExpressions (>= 2.0.0) need it, so the previous R (>= 4.2.0) could not be satisfied. ggplot2 (>= 3.5.0) is required for the inside-legend placement used by the themes and plots.score_questionnaire() stops with an error when an item column holds text ("Strongly agree", "5 - Strongly agree", "2,5"), names duplicate columns, or gets a prefix whose column names do not identify the items. All three used to score silently and wrongly.use_study_project() no longer initialises renv by default; pass renv = TRUE, which now runs renv::init() in a separate R process so the calling session's library paths, environment variables and options are untouched.not_empty() rejects a data frame without rows.check_normality_by_group() gains subject = and then tests what a repeated-measures analysis assumes to be normal: the per-participant differences (two conditions) or the residuals of y ~ condition + participant (more), not the raw scores per condition. Between subjects, the per-group p-values are now Holm-corrected (six normal groups used to send about one analysis in four to the non-parametric branch), and a group that cannot be tested -- fewer than three values, or a rating item at ceiling -- counts as non-normal instead of passing silently.assumption_methods_text() gains subject and describes the check that was actually run, names untestable groups, labels adjusted p-values as such, and names the variance test as run (Brown-Forsythe, the median-centred Levene test rstatix uses). With subject, include_homogeneity is ignored with a message: equal variances across conditions are not a repeated-measures assumption, and the Welch clause it could add has no meaning there.checkAssumptionsForAnova() treats every factor as categorical (numeric condition codes were fitted as one linear covariate, so identical data got opposite advice depending on the coding), Holm-corrects its per-cell tests, reports cells too small to test instead of crashing, and gains subject = to test the residuals a repeated-measures ANOVA assumes.checkAssumptionsForAnova() and recommend_test() test the per-participant differences, as check_normality_by_group() does, rather than the model residuals: each participant's two residuals are mirror images, a symmetrised sample in which Shapiro-Wilk missed skewed differences about half the time.check_normality_by_group(), checkAssumptionsForAnova(), the post-hoc descriptives and generateEffectPlot()'s within-subject intervals -- now decides who is analysed by one shared rule (one row per participant and condition; participants lacking a condition are left out), so the methods sentence, the test, the figure and the descriptives always describe the same participants.generateEffectPlot()'s main-effect line uses unweighted marginal means (it drew 9 vs 1 where the cell means average 5 vs 5 in an unbalanced design); a new subject gives within-subject (Cousineau-Morey) intervals matching afex.reportDunnTest(), reportArtCon() and reportggstatsplotPostHoc() decided which level "was significantly higher" from the raw means, which contradicts a rank-based test exactly when outliers or skew made one necessary: a Dunn test with Z = -4.05 was reported as "A was significantly higher (M = 10.87)". The direction now comes from the test (sign of Z, the ART-C estimate, mean ranks, within-participant rank sums, or trimmed means), and the descriptives match it by default -- \mdn{}/\iqr{} for rank-based tests -- via a new descriptives argument, with a warning when forced means would contradict the test.\p{} and "p", and the sentences name the test and the correction ("A Dunn post-hoc test (Holm-adjusted) found ...").reportArtCon() crashed (and reportArtConTable() printed NA effect sizes) for numeric-looking levels or levels containing -, +, * or /, which emmeans rewrites in its labels.reportART() and reportNPAV() was one-sided (upper bound always 1.00); it is now two-sided.reportggstatsplotPostHoc() gains subject, so its descriptives describe the participants ggwithinstats() tested; it writes sink_to when nothing is significant, so a stale significant result no longer survives in the manuscript; and it refuses Bayesian tables instead of calling them non-significant. reportggstatsplot() reports Bayes factors and Yuen's t(df), and no longer writes "An A heteroscedastic ...".reportggstatsplotPostHoc() warns whenever a within-subjects plot is reported without subject (equal row counts per level do not show that every participant is complete); its direction warning under descriptives = "auto" no longer recommends the setting already in use. Between-subjects Yuen plots are no longer mistaken for within-subjects ones ("independent samples" contains "dependent samples"). Bayes factors below 1 keep two significant digits (0.014, not 0.01).recommend_test() and fit_recommended()reportGLMM() reported linear mixed models with the residual df: t(116), p = .030 where lmerTest's Satterthwaite test gives t(10), p = .053. LMM coefficients now use Satterthwaite df, and the sentence says so.reportGLMM() / reportCLMM() report a Type III omnibus test for every model term (Satterthwaite F for LMMs, Wald chi-square via emmeans::joint_tests() otherwise) before the coefficients, which are labelled as the treatment contrasts they are ("B vs. A of cond, at b = b1") instead of "the effect of condB" -- in a model with an interaction that coefficient is a simple effect, and a 2x2 example had read "significant, p < .001" where the Type III test gave p = .26. New argument omnibus = TRUE.classify_outcome() no longer silently treats bounded scores as counts (raw NASA-TLX in steps of 5 is continuous, and every count classification is announced) or a two-valued non-0/1 item as binary.recommend_test() tests normality on the residuals of the model it recommends and homogeneity across all design cells; recommends HC3-robust Type III ANOVA instead of a one-way Welch test for heteroscedastic factorial designs; and only recommends models that can be fitted (no ART with covariates, no nparLD without a within factor). A clustered nominal outcome is fitted with mclogit::mblogit() instead of a multinom() that ignored the clustering.y ~ cond * age, the "main effect of cond" was the effect at age 0 -- reported as F = 0.11, p = .74 next to post-hoc contrasts with p < .001 for the same effect at the mean age. The tests now equal those of the model with the covariate centred, for linear mixed models as for GLM(M)s.fit_recommended() fits every engine on the same complete cases (ART, nparLD and mblogit refused data with a missing value), turns a nominal outcome stored as text into a factor (mblogit refused it), fits nparLD on the participants observed in every condition instead of recommending a model it then could not fit, writes ART sentences with plain factor names (a factor called B became the undefined \B{}), and handles an intercept-only model. ART-C simple effects read their cells from the contrast coefficients rather than by splitting labels, so levels such as "low - fast" or "b,1" no longer make them fail; the methods text describes simple effects only when they were computed, and names the correction actually applied (ART-C simple effects fall back from Tukey to Holm). The Kruskal-Wallis route runs the Dunn's test its methods sentence names. Declaring a three-valued outcome binary warns that the binomial model compares the first value with all the others.fit_recommended() uses order-independent Type III sums of squares for unbalanced between-subjects factorials (Type I gave F = 2.03 or 0.14 depending on predictor order); fits by-participant random slopes when trials repeat within a condition, simplifying them when the fit is singular; computes post-hoc contrasts per factor, with simple effects only for a significant interaction in the model; no longer turns a numeric covariate with few values into a factor (new factors = argument declares categorical predictors); handles column names with spaces; records singular and non-converged fits and the function actually called; and escapes its LaTeX methods sentence.score_questionnaire(prefix =) matched columns to items by sort order: NASA-TLX Mental Demand was filled from the Effort column, TiA and IPQ reversals landed on the wrong items, AttrakDiff PQ and ATT swapped, and SUS_10_1 was read as item 2. Columns are now matched by name or by an unambiguous item number running 1..n.
The scoring message shows the observed response range; NASA-TLX data that never exceed 21 (other than the 0-100 sheet's own steps of 5), and responses that fill exactly the assumed range shifted by one point (an IPQ exported 1-7, a SUS exported 0-4), draw a warning unless scale is given. An unused scale end alone does not: nobody choosing the top of a 7-point scale is unremarkable in a small sample.
An item column stored as a factor that mixes numeric and text levels ("0"-"3" plus "no answer") is refused, naming the text levels, instead of being scored by level position -- which put every SSQ answer one point too high and doubled the Total. Blank factor levels are missing responses, as blank text cells are. define_questionnaire() refuses a missing subscale.
reverse_items warns when it un-reverses an item the published key already reverses (passing c(2, 4, 6, 8, 10) for the SUS gave a perfect respondent 50); new unreverse_items does that on purpose.
With min_valid < 1, missing SUS items count as the centre point, as Brooke (1996) instructs; new impute = chooses the rule explicitly.
score_reliability() adds a whole-scale row, a Spearman-Brown coefficient for two-item scales, and a note column; omega comes from an unrotated one-factor solution, so it no longer silently returns NA without 'GPArotation'.
The scaffolded figures follow the design declared in analysis.R: with CLUSTER <- NULL, or a condition that does not vary within participants, they are between-subjects plots (make_figures() used to stop with "subject is required"), and repeated trials are averaged per participant first. The report draws the same figures by reusing the pipeline's own plot_outcome() instead of hard-coding column names, and the combined figure grows with the number of outcomes instead of failing for a long questionnaire battery.
rFromWilcox() and rFromNPAV() no longer halve one-sided p-values, which overstated r; rFromNPAV() warns for omnibus F tests with more than one numerator df.remove_outliers_REI() takes proportions over the items each respondent answered, as Tawa (2021) defines the index; matches variables by exact name rather than as regular expressions; and flags nobody, instead of everybody, when all indices are equal.add_pareto_moocore_column() and add_pareto_emoa_column() agree on tied points (moocore kept only the first copy); emoa accepts integer columns; rows with a missing objective get NA.check_homogeneity_by_group() and the plot wrappers accept column names with spaces and numeric condition codes. debug_contr_error() reported unobserved factor levels as real when a character column came first. reshape_data() stacked the columns before the first marker as a data slice.time, L, small, value, ...) are written as text instead of \time (a compile error), \L (the letter Ł) or \small (a font switch). The list of reserved names is generated from TeX itself (data-raw/latex_reserved.R). Name macros are written as \Name{}, so TeX no longer swallows the following space ("cylon mpg").reportNparLD() output did not compile (nested math in \F{..}{$\infty$}{..}), rounded the ATS df to an integer, and promised a relative treatment effect that real fits never carry. reportART(), reportNPAV() and reportNparLD() decided "interaction" by searching for a capital X (a main effect UX became an interaction) and emitted names like Video_Type as uncompilable macros.emit_overleaf() no longer lets sections overwrite each other (Q1 and Q2 both became Q.tex), checks every path before writing anything, and produces a non-empty bibliography (\nocite{*}, and cite_methods() now gives each BibTeX entry a key). define_result_macro() spells digits out (tlx_1 becomes \tlxOne), refuses names that would redefine a command, and replaces an existing definition instead of duplicating it. cite_methods() cites a method named twice (c("art", "ART")) only once.reportMeanAndSD() wrote only LaTeX comments, and latexify_report() left _ & # ^ < > unescaped.\mdn{} and \iqr{} macros in latex_preamble() and colleyRstats.sty; re-copy the .sty into existing Overleaf projects with use_colleyrstats_sty(overwrite = TRUE).attrakdiff_official: the AttrakDiff 2 word pairs in the order and with the poles of the administered sheet. The existing attrakdiff key (blocked by dimension, negative pole first) is unchanged.showPairwiseComp and plotType work again (ignored since 'ggstatsplot' 0.12.0); non-parametric figures label medians; two-group asterisk brackets use the test shown in the subtitle.generateMoboPlot() / generateMoboPlot2() lines up with its phase guides and fitted equation. numberSamplingSteps counts iteration steps from the first iteration, so an iteration missing from the log no longer moves the sampling boundary, and a value covering every iteration warns instead of stopping.rFromWilcoxAdjusted() is deprecated: inflating p by the number of comparisons shrank r (0.34 to 0.21 with six comparisons) although the effect was unchanged. Effect sizes are not multiplicity-adjusted; use rFromWilcox().generateEffectPlot(numberColors =) is deprecated; it was never used.roxyglobals configuration. Added 'callr', 'MASS', 'mclogit', 'performance' and 'WRS2' to Suggests.reportggstatsplotPostHoc() names the post-hoc test it is reporting, and the multiplicity correction that produced the p-value: "A Games-Howell post-hoc test (Holm-adjusted) found that ...". Both are read from the pairwise table 'ggstatsplot' attaches to the plot rather than assumed, because which test ran is not something a reader can infer from the call: type and the between/within distinction select between Games-Howell, Dunn, Durbin-Conover, Student's t and Yuen's trimmed means, and 'ggstatsplot' may change its defaults between releases. A sentence that says only "A post-hoc test found that ..." is therefore not reproducible, and the methods section it lands in cannot be written from it (#31).p.adjust.method = "None", the p-values are uncorrected and are now emitted with \p{} / \pminor{} instead of \padj{} / \padjminor{}. Labelling a raw p-value $p_{adj}$ claims a correction that was never applied. Both macros are already part of latex_preamble() and the shipped colleyRstats.sty, so no preamble changes are needed. A plot whose table carries no p.adjust.method column says nothing either way and keeps the \padj{} macros it has always used.reportggstatsplot() opened a Welch ANOVA sentence with "An One-way analysis of means ...". The article was chosen from the first letter of the method name, and "One" is a vowel letter read as a consonant ("wun"). It is now chosen from how the name is spoken, which also covers the "yoo" openings ("a unique ...", "a European ..."). The "un-" prefix is deliberately untouched, so "an unpaired Wilcoxon rank sum test" is unchanged.inst/WORDLIST, so the spell-check workflow passes again.score_questionnaire() applies a published instrument's own scoring key to raw item columns: reverse-coding, the recoding it prescribes (centring a semantic differential to -3..+3, zero-basing the SUS), its subscale structure, and its published weights or multipliers. Ten instruments ship: NASA-TLX (raw/RTLX), SUS with the Lewis & Sauro usability/learnability subscales, UEQ-S, the full 26-item UEQ, TiA (Körber), AttrakDiff 2, IPQ, SSQ (with the Kennedy et al. weights and its overlapping subscales), FMS and MISC. The scoring is checked against published reference values in the test suite: the SUS anchors (100/0/50), the SSQ weighting (21 raw on each subscale gives 200.34/159.18/292.32, total 235.62), the TLX rescale from any sheet onto 0-100, and the UEQ's balanced polarity, where answering the positive pole of all 26 pairs must give +3 on all six scales.scale = declares the response range a survey actually used, so a 21-point TLX sheet, a 20-point slider and a 0-100 slider all score onto the conventional 0-100. Responses outside the declared range are an error, not a silent rescale.min_valid = governs incomplete responses. The default scores only complete subscales and returns NA otherwise -- no silent imputation. Relaxing it scales a sum-scored instrument up proportionally so it stays on its published range.score_questionnaire() (once per distinct mapping per session, alongside the mapping itself), and from check_questionnaire(), questionnaire_items() and score_reliability() every time. The wording lives in one place, so the console text and the help pages cannot drift apart. Silence the repeated note in a pipeline with options(colleyRstats.quiet_questionnaires = TRUE); that quiets the note, not the errors.check_questionnaire() prints the mapping that will be used -- which column supplies which item, its subscale, whether it is reverse-coded, and the observed range of each column -- along with the instrument's scoring notes. This is the guard against the failure mode that motivates the whole feature: item order and polarity belong to the sheet a study actually administered, so a shifted or re-ordered survey export scores silently, plausibly, and wrongly. score_questionnaire() also attaches the mapping as an attribute and announces it once per session.score_reliability() gives Cronbach's alpha (and McDonald's omega where 'psych' is installed) per subscale, computed on the same recoded matrix that is aggregated -- so a negative alpha means a real problem rather than a forgotten reversal, and it warns when one appears.define_questionnaire() registers a lab-specific, translated or shortened instrument, which then behaves exactly like a built-in one. Put it in a project's setup script and every analysis in that project scores it identically.reverse_code(x, min, max) flips a response scale using the possible range rather than the observed one, and warns about out-of-range values. Taking the endpoints from the data is the classic reverse-coding bug: if nobody picked the lowest option, every flipped response is off by a point.summarize_sickness() reduces a repeated single-item sickness rating (FMS, MISC) to the measures those studies actually analyse: peak, mean, final value, trapezoidal area under the curve, the time-weighted mean, and time to a threshold.list_questionnaires() and questionnaire_items() describe the registry and one instrument's items.fit_recommended() carries recommend_test() through to a fitted model. It coerces the outcome and predictors into the classes the model family needs (announcing each coercion, since turning a numeric rating into an ordered factor changes what is estimated), builds the random-effect term for a clustered design, fits, computes pairwise post-hoc contrasts with the machinery that matches the fit, and produces the manuscript sentence via this package's reporter for that family. It covers cumulative link models with and without random effects, linear and generalized linear mixed models, GLMs, the aligned rank transform, nparLD, multinomial regression, and the classical ANOVA / Welch / Kruskal-Wallis / Wilcoxon tests.recommend_test() prints advice that is then re-typed by hand.outcome_type = overrides the automatic classification. This matters more than it sounds: an outcome whose scores stay whole numbers is taken for a count and fitted with a Poisson model. That catches the six raw NASA-TLX subscales, a single MISC rating, and item-level ratings; SUS and RTLX escape it only because their multipliers and means make them fractional.add_pareto_moocore_column() and add_pareto_emoa_column() gain maximise. Both minimised unconditionally: moocore::is_nondominated() was called with its maximise = FALSE default and emoa::is_dominated() has no direction argument at all. Since trust, acceptance, perceived safety and most other rating-scale objectives are maximised, using them meant passing negated copies of your own columns and remembering to negate them everywhere else too. Pass maximise = TRUE, or a logical vector with one entry per objective for a mixed problem (c(TRUE, TRUE, FALSE) to maximise two and minimise a workload score). The default stays FALSE, so existing results are unchanged.maximise is validated. moocore::is_nondominated() accepts three flags for two objectives and quietly uses the first two, which would produce a plausible, wrong front; passing the wrong number here is an error naming the objectives instead.maximise = TRUE matches the negate-your-columns workaround it replaces.use_study_project() scaffolds a study analysis as a reproducible pipeline: a 'targets' pipeline that recomputes only what changed, R scripts split along the stages every user study goes through (read, clean, score, model, plot), a Quarto report, a directory the generated LaTeX lands in so a manuscript \input{}s the numbers instead of having them re-typed, renv for version pinning, and a .gitignore that keeps generated output out of the repository.OUTCOMES and OUTCOME_TYPES vectors are derived by scoring a dummy row through the real code path, so the scaffold cannot name a column that score_questionnaire() does not produce.reportggstatsplot() named every effect size r. Each branch pasted a literal , r=, but only the rank-based tests actually produce an r: a t-test yields Hedges' g, an ANOVA Omega2, a Friedman test Kendall's W, a Kruskal-Wallis Epsilon2 (rank). The sentence therefore put a wrong statistic name in the manuscript, and for the standardised mean differences an impossible one -- a paired t-test on 16 pairs emitted r=-1.38, a correlation outside the range r can take. The reporter now reads the effect-size name that statsExpressions supplies in the effectsize column of extract_stats() and renders the matching symbol ($g_{Hedges}$, $d_{Cohen}$, $\eta_{p}^{2}$, $\omega_{p}^{2}$, $\epsilon_{ordinal}^{2}$, $W_{Kendall}$, $V_{Cramer}$), keeping the existing \rankbiserial{} macro where the effect size really is a rank-biserial correlation, so this reporter and reportArtCon() stay consistent and no LaTeX preamble changes. Names it does not recognise -- the robust tests return long descriptive ones -- are printed verbatim rather than guessed at. Any .tex generated by this function before 0.2.0 needs its effect-size label checked.
The same values were formatted with the internal bounded-statistic formatter, which drops the leading zero under options(colleyRstats.leading_zero = FALSE). That is APA-correct only for statistics bounded within [-1, 1]. Hedges' g and Cohen's d are unbounded and now keep their leading zero.
save_paper_figure() resized only the last panel of a multi-panel figure. It applied the type size with plot + .resize_theme(base_size), and on a patchwork object + modifies the last plot only -- so a seven-panel grid came out with six panels at whatever size they were built with and one at the figure size, which reads as a single oversized panel. Resizing now dispatches on the object: & for a patchwork, so it reaches every panel, + for a single plot as before. A test asserts every panel of a grid ends up at one size.
vignette("scoring-questionnaires") covers the scoring key, verifying a mapping before trusting it, incomplete responses, reliability, registering your own instrument, and the sickness time course.pkgdown reference index gains Questionnaires and Starting a study sections.reportNparLD() announced "no significant effects" for every model fitted with 'nparLD' 2.3.0, however strong the effect actually was. That release rewrote the object nparLD::nparLD() returns: it now has class nparld_fit and carries the ANOVA-type statistic in $ATS, where earlier versions used $ANOVA.test. Asking for the old name yielded NULL, and as.data.frame(NULL) is a table with no rows -- which the code that followed could not tell apart from a table in which nothing crossed p < .05. A toy fit with ATS = 92.8 and p < 1e-10 was reported as effect-free. Both layouts are now recognised, and an object carrying neither raises an error instead of silently reporting nothing.?reportNparLD passed description = FALSE to nparLD::nparLD(). 'nparLD' 2.3.0 dropped that argument, so R CMD check --run-donttest failed on CRAN with unused argument (description = FALSE). The call now uses only arguments both versions accept, and the toy data carries a trend over time so the example demonstrates a reported sentence rather than the no-effect message.animate_mobo2(), the video counterpart of generateMoboPlot2(). It draws one frame per iteration and encodes them with av, so an optimisation run can be shown building up rather than only in its finished state; the file extension picks the container (.mp4, .gif, .mov, ...). The plot is built once from the complete data and each frame only hides rows, which is what keeps the axes, the sampling/optimisation guides and the legend still while the points, intervals, fitted line and its equation move. It is also the only way the early frames can be drawn: generateMoboPlot2() requires both phases to be present, and the first iterations are all sampling. av is a Suggests, so nothing changes for installations that do not want it.colleyRstats_setup() made it impossible to attach a meta-package afterwards. library(colleyRstats), colleyRstats_setup(), library(easystats) died on the third line with .onAttach failed in attachNamespace() for 'easystats': object 'quietly' not found, which broke every downstream analysis script that opens that way. The cause was the set_conflicts = TRUE default: conflicted::conflict_prefer() does not merely record a preference, it activates conflicted, which attaches a .conflicts environment carrying its own library() and require() shims. Every later attach in the session then went through those shims, which forward quietly and verbose into base::library() as unevaluated symbols; a package whose .onAttach inspects the calling library() frame -- meta-packages do this to decide whether to print their banner -- evaluated those symbols in a frame that does not bind them, and the attach failed outright. set_conflicts now defaults to FALSE, so nothing takes over library() unless it is asked to. The preference list is unchanged and still takes effect when you opt in.
Preferences that fail to register are no longer discarded silently. The try() around the loop existed so that one unsettable preference could not abort the rest, but it also swallowed the fact that it had failed; with verbose = TRUE the failures are now named.
Figures saved at the column presets came out unreadable. colleyRstats_setup() set text sizes in absolute points (axis text 17 pt, axis titles 20 pt, plot titles 28 pt, strip text 22 pt) while save_paper_figure(columns = 1) writes a 3.33 x 2.22 in PDF. Points do not shrink with the canvas, so on a single-column figure the type came out about three times what fits: axis titles ran off the page entirely, and three-level factor labels such as demonstration/language/reward overprinted into an unreadable smear. Type size is now derived from the width the figure is actually written at.
generateEffectPlot() fixed its legend title at 14 pt, and the four ggstatsplot wrappers fixed text at 16 pt and plot.subtitle at 17 pt. These absolute values overrode whatever theme the caller had chosen, so they survived any attempt to scale a figure down. They are now relative to the theme.
Significance-bracket labels were fixed at 4 mm (about 11 pt). ggsignif measures text in millimetres while themes measure it in points, so the label kept its physical size no matter how small the figure was drawn and ended up larger than the axis text beside it. Bracket text now follows the active theme; at the historical 17 pt base it is still 4 mm, so existing figures do not shift.
colley_theme(base_size, base_family), the package theme as a function. Every text element is a multiple of base_size rather than an absolute point size, so one theme serves a journal column, a full-width figure and a slide -- only the number changes. The multipliers are axis titles 1.15, axis text 1.0, plot title 1.65, subtitle 1.0, caption 0.8, legend text 0.9, strip text 1.3.figure_base_size(width), the rule that turns a figure width into a type size: 3.33 in gives 7 pt and 7 in gives 9 pt, interpolated between and clamped outside. Exported so the choice is inspectable rather than buried.save_paper_figure() gains base_size. The default NULL derives it from the width, which is the fix above; pass a number to choose it, or NA to leave the plot's own text sizes untouched. The confirmation message now names the size used.colleyRstats_setup() gains base_size (default 17), passed through to colley_theme().pkgdown site (_pkgdown.yml plus a deploy workflow) puts the 56 help topics into a grouped reference index -- session setup, pipelines, test selection, assumptions, plots, reporting, effect sizes, LaTeX output, data preparation -- instead of one alphabetical wall.vignette("getting-started") now show the session-setup pattern, including where colleyRstats_setup() belongs relative to your library() calls and what happens if it goes first. Neither had covered this, which is how the conflicted defect above reached three downstream analysis repositories.snake_case spellings (report_art(), plot_effect(), check_assumptions_anova(), ...) are now stated to be the canonical ones, and the documentation uses them throughout: README, vignettes, and the pkgdown reference index. Every affected help page gained a Naming section saying which spelling to prefer and that the other is not going away. Previously the aliases file described snake_case as the discoverable API while the README taught camelCase exclusively, and nothing told a reader which to use.camelCase function names (reportART(), generateEffectPlot(), and the rest) are now marked superseded. Nothing changes at runtime: they are the same function objects, they emit no warnings, and they are not scheduled for removal. Only the documentation's recommendation has changed.colleyRstats_setup(set_options = TRUE) now emits a deprecation warning instead of an easily missed message, and the warning is no longer suppressed by verbose = FALSE. The argument has had no effect since global options() handling was removed for CRAN compliance; set_options = FALSE is unaffected.colleyRstats_setup() no longer sets conflicted preferences by default. Pass set_conflicts = TRUE for them, and place that call after every library() call in the script rather than before. That ordering is a requirement -- once conflicted is active, attaching a meta-package fails -- and it is also where the preferences do most good, since conflicted resolves only those names that are ambiguous among the packages attached at the time.colleyRstats_setup() with no arguments produces the same text sizes as before: the default base_size = 17 reproduces the previous absolute values exactly (17 / 19.55 / 28.05 / 15.3 / 22.1 pt), and there is a regression test pinning them.save_paper_figure() do change, which is the point of the fix -- they become legible at the size they are placed at. To keep a figure exactly as it was, pass base_size = NA.see::theme_lucid(base_size = ...) rather than theme_lucid() with sizes overridden on top, margins and spacing scale with the text instead of staying at the 11 pt defaults. This is part of why elements used to collide.reportART() reported no F statistic and no effect size for mixed ART models. stats::anova() names the F column F value for a between-only (lm) fit but plain F for a mixed (lmer) one, and only the former was read -- so every model containing a random term (+ (1 | participant), i.e. any within-subjects design) produced \F{3}{108}{} with the statistic missing, silently dropped the partial eta-squared, and left the opening parenthesis unclosed. The F column is now resolved by whichever name it carries.reportART() now always closes the statistics parenthesis. It was appended only when an effect size could be computed, so sentences without one shipped unbalanced parentheses into LaTeX.reportggstatsplotPostHoc() emitted a stray ) after the p-value: ... (\m{3.8}, \sd{1.6}); \padj{0.001}). The mean/SD parenthetical closed too early, leaving the p-value outside the parentheses it belongs to and the sentence unbalanced. It now reads ... (\m{3.8}, \sd{1.6}; \padj{0.001})., matching reportArtCon().\F{1.80875305770353}{66.9238631350305}{0.11} and F(2, 180.000000000002). Whole dfs stay whole; fractional ones are rounded to two decimals.reportggstatsplot() chooses the indefinite article from the method name, so it reads "An ANOVA ..." rather than "A ANOVA ...".check_assumptions_anova() guidance message).vignette("analyzing-a-user-study")): a complete walkthrough from raw within-subjects data to manuscript-ready figures and LaTeX text.x/y/iv/dv/factors/objectives up front and report which column is missing plus the available columns, instead of failing later with a cryptic dplyr/rlang error.xlabels does not match the number of observed groups (previously the axis labels silently misaligned).not_empty() now names the offending argument in its default error message.remove_outliers_REI() accepts variables as a character vector (e.g. c("var1", "var2")) in addition to the comma-separated string.add_pareto_emoa_column() / add_pareto_moocore_column() verify that the objective columns exist and are numeric.?replace_values works again: a malformed roxygen @name tag had redirected its documentation to a stray data-the-data-frame help page.reportArtCon() now escapes the dependent variable and condition names for LaTeX and renders the IV via the same name-macro policy as reportDunnTest(); previously names with underscores produced uncompilable LaTeX.reshape_data() example was not runnable (requireNamespace() was called with a vector and a misspelled package name).latex_preamble() no longer documents its path argument twice; rFromNPAV()'s documentation block is no longer split by a stray comment line.reportNPAV() no longer refers to the non-existent reportNPAVChi() when the input lacks a Pr(>F) column.reportNPAVChi(), refreshed the stale reportNPAV() deprecation date, fixed the double anova() call in the reportART() example, and documented the newer API (recommend_test, pipelines, Overleaf output) and all vignettes.ggwithinstatsWithPriorNormalityCheck() no longer runs an unused Levene test (the asterisk variant was already fixed in 0.1.0).classify_outcome() determines a variable's measurement scale (continuous, ordinal, binary, count, nominal) and recommend_test() turns scale x clustering x assumption checks into a concrete recommendation -- including mixed models -- with the matching reporter, a ready-to-edit fit call, a rationale, and an APA-style methods sentence.reportGLMM() (lme4 / glmmTMB / lm / glm; odds and incidence-rate ratios are chosen automatically from the family) and reportCLMM() (ordinal::clmm / clm).use_colleyrstats_sty() ships the macro package, emit_name_macros() / define_result_macro() generate \newcommand stubs and single-source-of-truth result macros, expand_latex_macros() -- with options(colleyRstats.macros = FALSE) -- expands everything to plain math, and emit_overleaf() bundles a whole analysis into a compilable Overleaf project.latex_escape() escapes LaTeX special characters in user-supplied names.ggstatsplot implementationanalyze_and_report(): one-call pipeline per dependent variable -- assumption checks with a ready-made methods sentence, the matching ggstatsplot figure (automatic parametric/non-parametric selection), the omnibus result, and post-hoc comparisons.report_all(): runs analyze_and_report() over many dependent variables (e.g., all questionnaire scales of a study), returns a summary table with Holm-adjusted omnibus p-values across the DVs, and a combined figure when patchwork is installed.latex_preamble(): prints (or writes to a file) the complete set of LaTeX \newcommand definitions required by the report functions -- no more hunting through individual help pages.sink_to to write their output to a .tex file, so manuscripts can \input{} the results and stay up to date when the analysis is re-run. They all also invisibly return their text for programmatic use (e.g., inline in Quarto/R Markdown).save_paper_figure(): saves plots with publication presets (ACM-style single-column 3.33 in / full-width 7 in). PDFs use Cairo for embedded fonts on Windows/Linux; on macOS the default pdf device is used because R's cairo there can crash the session (observed as segfaults on GitHub Actions macOS runners). A device argument allows overriding.assumption_methods_text(): turns the Shapiro-Wilk (and optionally Levene) checks into the methods-section justification sentence reviewers expect, including the test statistics. check_normality_by_group() and check_homogeneity_by_group() now attach their test statistics as attributes.cite_methods(): prints methods boilerplate plus the BibTeX entries for the packages behind the analyses (ART, Dunn, nparLD, ggstatsplot, effectsize).report_art(), report_dunn_test(), plot_between_stats(), check_assumptions_anova()). The original names remain fully supported.options(colleyRstats.leading_zero = FALSE) for APA-style p-values and effect sizes without the leading zero (e.g., p=.033).reportggstatsplot() now recognizes the unpaired non-parametric test ("Wilcoxon rank sum test", reported with its W statistic); previously such results fell through to a generic fallback format. The signed-rank (paired) variant keeps the V statistic. The p-value is also compared before rounding, so e.g. p = 0.0009 is reported as "p < 0.001" again.reportNPAV(), reportART(), and reportNparLD() with write_to_clipboard = TRUE now write all significant effects to the clipboard at once; previously each effect overwrote the previous one and only the last sentence survived. These functions now also invisibly return the reported sentences.ggbetweenstatsWithPriorNormalityCheckAsterisk(), ggwithinstatsWithPriorNormalityCheckAsterisk()) no longer drop significant comparisons whose adjusted p-value is exactly 0.01 or 0.001.replace_values() no longer round-trips untouched numeric columns through as.character(), which silently truncated values to 15 significant digits.reportggstatsplotPostHoc() handles missing values in the dependent variable (na.rm = TRUE) and falls back to the raw level name when a label_mappings entry is missing instead of producing malformed text.reportNparLD() output now correctly says "nparLD analysis" instead of "NPAV", and the required \df LaTeX command is documented.reportDunnTestTable() and reportArtConTable(): orderByP = TRUE now takes precedence over the default alphabetical ordering instead of being silently undone by it.reportART()/reportNPAV() is now computed from the unrounded F statistic.rFromWilcoxAdjusted() caps the adjusted p-value at 1, avoiding NaN results.generateMoboPlot2() stops with a clear error when the phase column lacks "sampling"/"optimization" rows instead of producing -Inf positions.normalize() rejects a zero-width input range instead of returning Inf/NaN.remove_outliers_REI() now actually uses its range argument (validation plus an out-of-range warning) and ignores NA responses when tallying instead of poisoning row counts.reshape_data() reports a clear error when marker-delimited sections have unequal column counts.checkAssumptionsForAnova() distinguishes "normality could not be assessed" from "normality violated" and uses a consistent p < 0.05 boundary.gg*WithPriorNormalityCheck*() wrappers now pass the palette in the "pals::glasbey" format required by current ggstatsplot; the deprecated package=/palette= pair was being ignored with a warning, so plots silently used the default palette instead of glasbey.add_pareto_emoa_column() uses emoa::is_dominated() directly instead of matching rows against the Pareto front with floating-point equality (was O(front size x rows)).annotate() instead of once per data row.ggwithinstatsWithPriorNormalityCheckAsterisk() no longer runs an unused Levene test.car is no longer used by the package code at all.generateMoboPlot()/generateMoboPlot2() fail with "object 'Stat' not found" whenever ggplot2 is not attached. Attaching colleyRstats now attaches ggplot2 as well.inst/WORDLIST.txt and the redundant tests.yml CI workflow (R-CMD-check already runs the tests).add_pareto_moocore_column(). Should be less buggy than the one from emoagenerateMoboPlot2().colleyRstats_setup() and generateMoboPlot2().generateMoboPlot2() default to fillColourGroup = "ConditionID" so the documented default works out of the box.