CRAN resubmission (theme: make the print/cat console contract
lexically visible — the 2026-09 second-round remark on
R/kendall_tau.R).
cat() in
R/kendall_tau.R sat in
.kendall_tau_spec$pair_extras, a display callback that only
ever ran inside the summary() print layer — functionally
exempt, but lexically indistinguishable from computation code. All three
correlation pair_extras callbacks (kendall, pearson,
spearman) now return formatted lines; the single
cat() lives in the engine’s
.print_cor_verbose(). Output is byte-identical.format_stat_table() was renamed to
print_stat_table() (it prints, it never returned a
formatted string), and for_each_group()’s group header line
moved into a new print_group_label() helper. After this,
every cat()/print() call in R/
lives in a function whose name starts with
print/.print.test-console-discipline.R locks
the rule in: it parses every file in R/ and fails if a
cat(), print(), or writeLines()
call appears in any top-level object not named
print*/.print* (calls inside
capture.output() are exempt as silent). Together with the
runtime test-silent-computation.R (zero stdout from every
analysis entry point), this makes the CRAN console contract
regression-proof.CRAN resubmission (theme: address all four points of the 2026-09 manual review).
\dontrun{} blocks are gone. The 15
import/export examples are now genuinely executable
\donttest{} roundtrips through tempfile()
(guarded by requireNamespace() for the Suggests packages);
the two formats R cannot produce (.por, .sas7bdat) run behind a
file.exists() guard. The unlabel() example
runs unconditionally on the bundled data.on.exit() now registers the restoration before
the option is changed in ancova(),
factorial_anova(), and the correlation-matrix print helper,
so an interrupt between the two lines cannot leak a changed
options() setting.test-silent-computation.R proves the console
contract the reviewer asked about: every analysis entry point produces
zero stdout (32 assertions); all cat() calls in the package
live exclusively in the print()/summary()
display layer, and remaining runtime messages go through suppressable
message()-based conditions.Making the former \dontrun{} examples actually run
uncovered two real crashes on integer columns (tagged NAs are NaN
payloads in doubles; haven::na_tag() errors on integer
input):
unlabel() crashed on any data frame with
integer columns (“x must be a double vector”) —
including the bundled survey_data’s Likert variables.
Integer vectors can never carry tagged NAs and now pass through
directly.write_xpt() crashed on integer columns
for the same reason in its tag-uppercasing step. Both fixes carry
regression tests.CRAN resubmission (theme: address the incoming-pretest NOTE) plus two small robustness patches.
kendall_tau() examples now run on a 300-case
subset: Kendall’s tau is O(n^2) and the full-sample examples exceeded
CRAN’s 5-second limit on the Debian pretest machine (7.5s). A comment in
the example explains the subsetting.cli >= 3.0.0, dplyr >= 1.0.0,
rlang >= 1.0.0, tidyselect >= 1.1.0,
tibble >= 3.0.0, htmltools >= 0.5.0), so
installations with stale libraries get an automatic update instead of
runtime errors.set_na() on a whole data frame without haven installed
now aborts with the friendly “Package haven is required for tagged NAs”
hint instead of the raw namespace error (the unnamed-values path reached
haven::tagged_na() without its guard; the vector and
named-pairs paths were already guarded).Assumption checks and model interpretation (feature set: three new functions closing the most common SPSS/Stata gaps for survey researchers).
normality_test() — the
SPSS EXAMINE “Tests of Normality” table: Kolmogorov-Smirnov with
Lilliefors significance correction (Dallal-Wilkinson 1986 approximation,
as in SPSS) and Shapiro-Wilk (computed for 3 <= n <= 5000,
matching the SPSS convention). Supports tidyselect and
group_by() (SPSS EXAMINE ... BY factor).
Deliberately takes no weights argument: neither test has a
well-defined fractional-frequency-weight form (documented in the help
page).partial_cor() — SPSS
PARTIAL CORR (Stata pcorr): partial correlations of two or
more variables controlling for one or more others, with the zero-order
correlation alongside for comparison, df = n - 2 - k, two-tailed t-test,
listwise deletion, weights (frequency semantics, unrounded
sum(w) in df), grouping, and a partial-correlation matrix
for three or more variables.marginal_effects() —
average marginal effects for logistic_regression() models
(Stata margins, dydx(*)): average probability-scale
derivatives for continuous predictors, average discrete changes vs. the
reference level for factors, delta-method standard errors with analytic
gradients, weights and group_by() support. Calling it on a
linear_regression() explains that B already is the marginal
effect there.multiple_response() —
SPSS MULT RESPONSE for “check all that apply” questions (dichotomy
sets): the frequencies table with both percentage bases (percent of
responses, summing to 100%, and percent of cases, summing
above it), and via by = the set-by-demographic crosstab
with case-based column percentages. SPSS case rule (valid = at least one
non-missing indicator), variable labels as option labels, frequency
weights with unrounded sums, group_by() support.All four ship with the full three-layer print/summary output.
w_* help pages embed LaTeX via
\eqn{}, but the site configuration loaded no math engine,
so pages showed raw LaTeX source (\frac{...}).
_pkgdown.yml now sets
template: math-rendering: katex.normality_test() against stats::ks.test() and
the independent nortest::lillie.test() implementation (new
Suggests dependency, test-only); partial_cor() against the
residual-of-regressions characterization via lm();
multiple_response() against direct hand-computation from
the indicator matrix; marginal_effects() against the
analytic logit-AME formula and an independent finite-difference
delta-method recomputation (SPSS has no AME procedure — permanently Tier
4). Weighted paths have w == 1 invariance blocks; grouped
paths are pinned to per-subset recomputation. SPSS v29 reference syntax
for all pending runs lives in
.claude/spss-syntax-0.7.0-references.sps.Crosstab cell diagnostics (theme: after the chi-square test, show which cells drive the association).
crosstab() now computes expected cell counts
and adjusted standardized residuals (SPSS
CROSSTABS /CELLS=EXPECTED ASRESID, Haberman 1973). Display
them with summary(result, residuals = TRUE) — a sub-row per
cell, 1 decimal as in SPSS, with a footnote explaining the |adj.res.|
> 2 rule of thumb. The raw matrices are available as
$expected and $adj_residuals. Weighted tables
use the unrounded weighted cell counts (Charter §5.1) and reduce exactly
to the unweighted result at weights == 1 (invariance suite
extended).chisq.test()$stdres (unweighted) and a
hand-derived weighted recomputation; the SPSS v29
/CELLS=ASRESID reference run is pending and the
compatibility vignette discloses them as Tier 4 until it lands.CRAN readiness (theme: the package passes CRAN’s submission
conventions, not just R CMD check). No statistical behavior
changes.
>=, <=,
-> replace their typographic variants) — local and CI
checks had always run --no-manual, so this was never
exercised.\value{} sections added to the five documented S3
method pages that lacked them (predict/anova
for both regressions, print.w_quantile).set_na() example is now runnable (haven-guarded)
instead of \dontrun; the efa() oblimin example
guards its GPArotation dependency so --run-donttest passes
without Suggests installed.?mariposa now resolves: the package help topic is
generated from DESCRIPTION (previously suppressed by
@noRd).assert_spss() and
everything built on it) skip on CRAN. They are golden-number comparisons
against SPSS v29 references and remain the release gate in CI; skipping
them on CRAN removes the false-positive risk from BLAS/platform numeric
variation. Unit, property, invariance, and print/summary tests still run
on CRAN.Regression correctness (theme: the two regression functions compute what they claim under weights and degenerate inputs, and say what they show).
logistic_regression(): -2LL, omnibus
chi-square, and Cox & Snell / Nagelkerke R-squared were wrong under
fractional weights. The -2 log-likelihood came from
stats::logLik(), whose binomial method rounds
prior weights internally
(dbinom(round(m*y), round(m), mu)). All likelihood-based
statistics now derive from the residual/null deviance, which equals -2LL
exactly for a 0/1 response and honors fractional frequency weights
unrounded (Charter §5.1). Unweighted and integer-weighted results are
unchanged. A weighted validation test with a hand-summed log-likelihood
oracle pins the fix.linear_regression(use = "pairwise") no longer
silently drops interactions and transformed terms. The pairwise
path rebuilds the model from the raw correlation matrix, so
y ~ a * b was silently fitted as y ~ a + b.
Formulas with non-additive terms now abort with a pointer to
use = "listwise".linear_regression() path (“Tibble columns must
have compatible sizes”): perfectly collinear terms are excluded from the
coefficient table (matching SPSS’s excluded-variables handling) with a
one-line message, and the ANOVA df now count estimated terms
(model$rank) instead of raw coefficients — the same rule
the weighted path has used since 0.6.4.linear_regression() coefficients table now
prints the confidence intervals for B (SPSS
/STATISTICS CI) that it always computed; the level is the
conf.level argument. A new
summary(..., conf_int = FALSE) toggle hides the two
columns.factors = "dummy" docs (both regressions) now state
that ordered factors get R’s default polynomial contrasts
(.L/.Q/.C terms), not treatment
dummies, and how to get dummy coding.logistic_regression() as Tier 4 (textbook-formula oracle;
no SPSS v29 reference run yet) instead of implying SPSS-validated
status.Codebook robustness (theme: codebook() survives
real-world data and says what it shows). A stress test of the codebook
stack (metadata extraction, console print/summary, HTML builder, xlsx
export) surfaced a batch of crashes, silent data errors, and display
leaks; this release fixes all of them and adds a view
argument for side-effect control.
attr(x, "label") partially matched the labels
attribute; all label reads now use exact = TRUE. The
“Variables with labels” count excludes such variables accordingly.codebook() no longer errors on inline data
expressions (e.g. codebook(data.frame(...))
spanning multiple deparse lines); long expressions collapse to the
generic dataset name “data”.print(summary(cb)) gains a “Missing values:” section
(codes, labels, counts) — show_na was a no-op on the
console layer before.empirical_values vs. new
empirical_keys).prc,
valid_prc, cum_prc, n_eff) on
write_xlsx(cb, frequencies = TRUE) sheets are rounded to 2
decimals.max_values (single integer >= 1) and
max_len (single integer >= 4) are validated up front
with a clear error.max_values and truncate with
the same “… (N more)” note used for character values.file = into a nonexistent directory aborts early,
naming the missing directory.max_len with “…” (raw values still drive frequency
matching); zero-row data frames say “(no observations)” instead of “(all
missing)”.view argument for codebook() (default:
interactive()): controls whether the HTML codebook opens in
the RStudio Viewer. view = FALSE suppresses the Viewer side
effect entirely; writing via file = is unaffected. The
compact print() only advertises the Viewer when it was
actually opened (result gains a viewed flag).McDonald’s omega (theme: reliability() learns a second reliability
coefficient). reliability() now reports McDonald’s omega
alongside Cronbach’s alpha — a new statistic within an existing
function, hence a PATCH per the clarified versioning policy.
reliability() computes McDonald’s
omega from a one-factor maximum-likelihood model
(stats::factanal on the same (weighted) correlation matrix
already used for standardized alpha):
omega — raw/total omega in the covariance metric
(analogous to raw alpha), reported as “McDonald’s Omega”;omega_std — standardized omega in the correlation
metric (analogous to standardized alpha);omega_if_deleted — a new column in
item_total, refitting the one-factor model per deleted item
(NA when the reduced scale has fewer than 3 items, where the model is
unidentified). Scales with fewer than 3 items get NA omega fields plus a
warning (alpha is unaffected); non-convergent factor fits degrade to NA
with the factanal message. The compact print() shows omega
next to alpha, and summary() adds omega rows to the
Reliability Statistics block and an omega column to the Item-Total
table.RELIABILITY, but IBM’s
algorithm documentation is not publicly retrievable and no SPSS v29
reference run exists yet. The pending reference run is prepared in
.claude/spss-syntax-omega-references.sps (expected values
included); until it lands, omega is guarded by a parameter-recovery test
on simulated congeneric data, exact cross-checks against a manual
factanal computation, cross-checks against psych::omega()
and a lavaan/semTools one-factor CFA
(tests/testthat/test-reliability-omega.R), and a
w == 1 block in the weights-invariance suite. The help page
carries the Tier-4 disclosure; the compatibility vignette flags omega as
Internal (Tier 4).psych, lavaan, and semTools
added to Suggests (cross-check tests only; all gated by
skip_if_not_installed()).Weighted-rank correctness and accurate claims (theme: the weighted rank family says exactly what it is). Two formula errors in weighted rank statistics are fixed and a package-wide invariance suite now guards every weighted entry point; alongside, the user-facing claim surface (README, DESCRIPTION, help pages, compatibility vignette) is realigned with what the validation suite actually covers.
kendall_tau(): the tau-b denominator omitted
double-tied pairs (ties_both) from the two tie-correction
factors, deflating |tau| on tied data. The weighted denominator now
mirrors the unweighted (n0 - Tx - Txy)(n0 - Ty - Txy)
structure.kruskal_wallis(): the grand mean rank was
still the hard-coded N/2 of the pre-0.6.4 rank convention
instead of (N+1)/2, inflating H. It is now derived from the
weighted mid-ranks themselves.tests/testthat/test-weights-invariance.R) enforces this w
== 1 reduction for every weighted entry point; intentionally approximate
reductions (design-based mann_whitney, weighted Kendall
z/p) are documented exceptions with bounded assertions.vignette("spss-compatibility")
for per-function status.mann_whitney(), kruskal_wallis(),
wilcoxon_test(), friedman_test(),
binomial_test(), dunn_test(),
pairwise_wilcoxon(), and kendall_tau() — are
now disclosed as R-only (Tier 4) in a “Weighted variants” note on each
help page: SPSS NPAR TESTS / NONPAR CORR
ignore WEIGHT BY, so no SPSS reference exists for these
weighted paths. mann_whitney()’s note also states that its
design-based U/W may differ from SPSS’s expanded-data U (Z and p are the
validated quantities); oneway_anova() now documents that
omega-/epsilon-squared are truncated at 0 (negative raw estimates occur
when F < 1).w_* family and
scheffe_test are correctly matched to their shared test
files (previously shown as “not validated” despite existing tests),
zero-match tier counts no longer report as 1, and
assert_spss_count() call sites are tallied as Spec.test-t-test-spss-validation.R: the header tier table
claimed the t-statistic at Spec (±1e-5) while the assertions use
Display(3); the header now matches the assertions.expect_no_error() in a validation file
(test-linear-regression-spss-validation.R) is replaced by
real assertions on the per-group predictions; the validation-discipline
meta-test now passes with
MARIPOSA_VALIDATION_STRICT=TRUE.Deprecation cleanup (theme: the due bridges come out). Two batches of deprecations reached their removal release together: the 0.6.9 argument bridges (originally slated for 0.6.10) and the 0.6.10 duplicate result columns. Removing both here keeps the run-up to the 1.0 API freeze tidy.
codebook(): show.id,
show.type, show.labels,
show.values, show.freq, show.na,
show.unused, max.values, max.len,
sort.by.name (use show_id,
show_type, show_labels,
show_values, show_freq, show_na,
show_unused, max_values, max_len,
sort_by_name)val_labels(): drop.na (use
drop_na)drop_labels(): drop.na (use
drop_na) These bridges were originally slated for removal
in 0.6.10 and are batched into this release. The
frequency()/rec()/to_label()
family of removed-argument errors introduced in 0.6.9 remain in place as
permanent guidance (their ... consumes tidyselect, so a
clear error beats a silent misinterpretation).chisq_gof(), friedman_test():
chi_sq removed (use chi_squared)mcnemar_test(): statistic removed (use
chi_squared)mann_whitney(): effect_size_r removed (use
r_effect)oneway_anova(): F_stat removed (use
F_statistic) The statistical values are unchanged; only the
redundant column names go away.Result-column harmonization (theme: one statistic, one column name). A style audit found the same statistic carrying different result-column names across sibling functions; the drifted names now converge on the canonical spelling, with the old columns kept as duplicates for one release.
$results columns for shared statistics are
harmonized on the canonical names already used elsewhere in the package:
chi_squared (as in
chi_square()) - now also in chisq_gof(),
friedman_test(), and mcnemar_test()r_effect (as in
wilcoxon_test()) - now also in
mann_whitney()F_statistic (as in
levene_test()) - now also in oneway_anova()
Print and summary methods read the canonical columns; the statistical
values are unchanged.chisq_gof(), friedman_test():
chi_sq (use chi_squared)mcnemar_test(): statistic (use
chi_squared)mann_whitney(): effect_size_r (use
r_effect)oneway_anova(): F_stat (use
F_statistic)API-cleanup completion (theme: the 0.6.8 bridges come out, the last dot-case stragglers get theirs). One step closer to the 1.0 API freeze.
frequency()/fre(): sort.frq,
show.na, show.prc, show.valid,
show.sum, show.labels,
show.unusedrec(): as.factor, var.label,
val.labelsto_label()/to_character()/to_numeric():
drop.na, drop.unused,
add.non.labelled, use.labels,
start.at, keep.labelsread_spss()/read_por()/read_stata()/read_sas()/read_xpt():
tag.na Before:
frequency(data, x, sort.frq = "desc") warned and worked.
After: it errors with a pointer to sort_frq. In the
functions whose ... selects variables, the old names raise
a clear “removed in 0.6.9” error instead of being silently swallowed by
tidyselect; in the readers they fail as unused arguments.codebook(): show_id,
show_type, show_labels,
show_values, show_freq, show_na,
show_unused, max_values, max_len,
sort_by_nameval_labels(): drop_nadrop_labels(): drop_na The display options
stored on codebook results (result$options) use the
snake_case keys as well.API-unification release (theme: snake_case arguments). One release-long deprecation bridge per the versioning policy - old names keep working and warn once per session; they will be removed in 0.6.9.
frequency()/fre(): sort_frq,
show_na, show_prc, show_valid,
show_sum, show_labels,
show_unusedrec(): as_factor, var_label,
val_labelsto_label()/to_character()/to_numeric():
drop_na, drop_unused,
add_non_labelled, use_labels,
start_at, keep_labelsread_spss()/read_por()/read_stata()/read_sas()/read_xpt():
tag_na Base-R-universal names (na.rm,
conf.level, var.equal) are kept.t_test() results no longer carry the duplicated
CI_lower/CI_upper alias columns;
conf_int_lower/conf_int_upper are the
contract.sort_frq is validated
("none"/"asc"/"desc") - typos used to silently produce an
unsorted table; show_labels validates its
TRUE/FALSE/"auto" values with a
clear error.Output-layer release (theme: uniform three-layer output). Statistical results are unchanged; what changed is how results present themselves.
Every analysis class now follows the documented pattern that t_test
and chi_square pioneered: result prints a compact overview
(headline statistic, p-value, significance stars, one line per test),
and summary(result) carries the full detailed output behind
boolean section toggles. Newly migrated: kruskal_wallis, wilcoxon_test,
friedman_test, binomial_test, fisher_test, chisq_gof, mcnemar_test,
levene_test, tukey_test, scheffe_test, dunn_test, pairwise_wilcoxon,
frequency, crosstab (describe was already compact and gained the summary
layer for uniformity). Nothing was removed - everything the old print()
showed is in summary(), verified line-by-line.
Internal-architecture release (theme: shared cores and formatting utilities). No statistical results change; table rendering in the Tukey/Scheffe output is now aligned and uses SPSS-style p display.
Housekeeping release (theme: package hygiene). No statistical results change.
importFrom entries.cli_abort(parent = ...) so the original condition is
preserved; the haven requirement is enforced by one central guard that
reports the calling function instead of an internal helper.read_stata(), read_sas() and
read_xpt() now lives in one helper instead of three
copies.sapply() calls in the
oldest files converted to type-stable vapply(); pkgdown
reference now lists phi(), cramers_v(),
goodman_gamma().A quality release. Following an in-depth internal review of the
entire statistical codebase, this version sharpens the accuracy of
several statistics, makes the package behave more consistently across
functions, and adds a dedicated regression-test suite
(tests/testthat/test-audit-regressions.R) so these
guarantees hold in future releases. Some outputs change slightly as a
result - in every case toward the standard reference
implementations.
stats::cor.test() (and the SPSS formula) to machine
precision, which is most noticeable for heavily tied data such as binary
variables.pairwise_wilcoxon()) now uses frequency-expansion
mid-ranks: with integer weights the statistic equals the expanded-data
Wilcoxon exactly, and weights = 1 reproduces the unweighted
test. Displayed rank means in the weighted Kruskal-Wallis and Dunn tests
follow the same convention.epsilon_squared (previously named
eta_squared).linear_regression() gains SPSS-style collinearity
diagnostics (Tolerance and VIF per model term), including a
collinearity toggle in summary().phi(), cramers_v(), and
goodman_gamma() now return the requested effect size
directly as a numeric value - the convenient behavior their names
suggest. For the full test output, use chi_square().t_test() now honors
var.equal for its primary result.
oneway_anova() always reports both the classical and Welch
results (like SPSS ONEWAY), so its var.equal argument is
deprecated; ss_type in
factorial_anova()/ancova() is likewise
deprecated in favor of the SPSS-standard Type III.frequency() header statistics use the same formulas as
describe() and the w_* functions.frequency(show.unused = TRUE) works
on variables tagged via set_na()/read_spss(),
and frequency(sort.frq =) now sorts by frequency with a
monotone cumulative-percent column.write_spss() protects valid values when many
missing-value codes must be consolidated into a range, and explains what
it is doing.logistic_regression() surfaces separation and
convergence warnings again; post-hoc tests report when a computation
could not be carried out instead of skipping it silently.w_* results print their
statistics again.rec()
reliably matches decimal single valuesSingle-value recode rules now match decimal codes
(e.g. "3.6=2") even when the stored value carries
floating-point representation error. The single-value comparison was
changed from exact numeric equality (x == value) to a
string comparison (as.character(x) == as.character(value)),
which rounds to 15 significant digits and thereby absorbs the error.
Reason: a value such as 0.1 + 0.2 is stored as
0.30000000000000004, so the previous exact ==
test silently failed to match a rule "0.3=...". This
mirrors the behaviour of sjmisc::rec(), on which
rec()’s string syntax is modelled. Range rules were already
robust (they use >=/<=) and are
unchanged.
Adds explicit tidy(), glance(), and
augment() methods for both linear_regression
and logistic_regression results, registered via the
standard s3_register() pattern (broom in Suggests, no hard
dep).
Reason: with class(r) = c("linear_regression", "lm"),
broom::tidy.lm() and broom::glance.lm()
dispatched as expected, but internally called summary(x) —
which (because of our specialised
summary.linear_regression() overriding
summary.lm) returned the mariposa SPSS-style summary
instead of the lm summary broom needs. The visible failures:
broom::glance(r) raised
object 'r.squared' not found because mariposa’s summary
stores it as R_squared.broom::tidy(r, conf.int = TRUE) returned only 4 columns
(term, estimate, conf.low,
conf.high) instead of the expected 6+ (term,
estimate, std.error, statistic,
p.value, conf.low,
conf.high).The new methods strip our linear_regression /
logistic_regression class before delegating to
broom::tidy.lm / tidy.glm etc., so the inner
summary() call dispatches to summary.lm /
summary.glm and broom receives its expected shape. The
user-facing summary(r) still returns mariposa’s SPSS-style
output (more specific method wins).
Edge cases stay consistent with the rest of the lm-generic surface:
broom::tidy() / glance() /
augment() on a grouped or pairwise result raise an
actionable error pointing at lapply(r$groups, ...) or
use = "listwise".
New tests in test-broom-methods.R cover all three
tidiers for both regression types, plus the grouped/pairwise error
paths.
lm /
glmlinear_regression() and
logistic_regression() results now ARE the fitted
lm / glm object (with mariposa-specific tables
attached as additional slots), instead of wrapping it in
$model. All base-R and broom generics dispatch
natively:
r <- linear_regression(survey_data, life_satisfaction ~ age + income)
coef(r) # named numeric vector
predict(r, newdata = head(survey_data)) # works directly
anova(r) # sequential SS table
vcov(r); confint(r); residuals(r); fitted(r)
broom::tidy(r); broom::glance(r); broom::augment(r)Class hierarchy is c("linear_regression", "lm") for
linear and c("logistic_regression", "glm", "lm") for
logistic. summary(r) still returns the SPSS-style mariposa
summary (more specific method wins); for the raw
lm/glm summary call
stats::summary.lm(r) /
stats::summary.glm(r).
Two slots collided with lm/glm conventions
and were renamed:
| Before | After |
|---|---|
$coefficients (tibble) |
$coef_table (tibble) |
$anova (tibble) |
$anova_table (tibble) |
$model (lm/glm) |
the object IS the model — use
r directly |
Migration:
r$coefficients → r$coef_table (SPSS-style
tibble) or coef(r) (named numeric vector).r$anova → r$anova_table (SPSS-style
overall-model ANOVA tibble) or anova(r) (R’s per-term
sequential SS table).r$model |> predict(...) →
predict(r, ...) directly.r$model |> broom::tidy() →
broom::tidy(r) directly.use = "pairwise": no single fitted lm is available, so
the result is a custom list with class "linear_regression"
only. predict()/anova() etc. raise an
informative error pointing at use = "listwise".predict()/anova() raise an informative error
pointing at lapply(r$groups, predict, ...). Each
r$groups[[i]] is itself an lm-inheriting object, so
per-group generics work directly.test-linear-regression-spss-validation.R verifies that
coef(), predict(), anova(),
vcov(), confint(), residuals(),
fitted(), formula(), nobs(),
model.matrix() all dispatch natively, plus the
grouped/pairwise error paths.linear_regression()
and logistic_regression(): factor predictor handlingBoth regression functions now expose a factors argument
controlling how factor predictors enter the model. The new default
factors = "dummy" matches base R lm() /
glm(): a factor with L levels expands into
L - 1 dummy contrasts via
stats::model.matrix(). Previous versions silently coerced
factor levels to integer codes (SPSS ordinal-as-scale default) with no
warning, which surprised users who relied on standard R semantics.
To restore the previous SPSS-style behavior, pass
factors = "numeric" explicitly. That mode emits a one-line
cli::cli_inform() listing the coerced variables for
transparency. The “numeric” mode is required to reproduce SPSS
REGRESSION / LOGISTIC REGRESSION output when
factor predictors carry ordered meaning (e.g., a 4-level education
variable treated as 1–4 ordinal scale).
Behavioral consequences:
use = "pairwise"),
factor predictors are not supported with factors = "dummy";
the function now errors with an actionable message pointing to either
factors = "numeric" or use = "listwise".Migration: scripts that depend on the old SPSS-style coercion should
set factors = "numeric" at the call site. The
cli_inform() message can be silenced with
suppressMessages() if desired.
Two more functions joined the Charter §5.1 audit list (the “unrounded
sum(w)” weighted-statistics convention previously applied
to t_test, oneway_anova, and
levene_test):
linear_regression(): weighted variance, SE, df, F, R²,
and adjusted-R² now use the unrounded sum(weights)
throughout. Earlier versions used
n_effective <- round(sum(w)) in df and MS calculations,
producing systematic drift from SPSS REGRESSION (off by ~0.001 on F,
~0.01 on adj-R² for typical weights). The displayed N is still
round(sum(w)).logistic_regression(): pseudo-R² formulas (Cox &
Snell, Nagelkerke, McFadden) now use the unrounded
sum(weights) in the exponential denominator. The displayed
N and rounded classification counts remain integers.These are bug fixes; weighted results may shift slightly toward closer agreement with SPSS v29.
summary.linear_regression(descriptives = TRUE) now
actually prints the Descriptive Statistics table (Variable, Mean, SD,
N). Previously the parameter was accepted but documented as “Reserved
for future use” and produced no output.print.linear_regression() no longer crashes
on weighted models with non-integer df: the F-statistic line now rounds
df for display before formatting with %d.summary.linear_regression
(collinearity = FALSE) and
summary.logistic_regression
(classification_table = FALSE) referenced parameters that
do not exist; corrected to descriptives = FALSE and
classification = FALSE respectively.The linear_regression SPSS validation test suite
expanded from 1 scenario (unweighted bivariate) to 6 scenarios covering
all four Charter §8 quadrants — Tests 1a, 1c, 2a, 2c, 3a, and 4a from
tests/spss_reference/outputs/linear_regression_output.txt.
The weighted scenarios (2a, 2c, 4a) verify the Charter §5.1 fix above.
New behavioral tests cover the factors argument (dummy
expansion, numeric coercion, pairwise + dummy + factor error path).
222/222 assertions pass.
Substantial hardening of the SPSS-compatibility test suite. All 29
SPSS- validation test files were rewritten under a new Validation
Charter (see vignette("spss-compatibility")) that defines
tolerance tiers (Spec / Display / Exception / Internal), forbids inline
tolerance literals, NA placeholders, and expect_true(TRUE)
reporting blocks, and requires citation comments linking every reference
value to its source line in
tests/spss_reference/outputs/.
tests/testthat/helper-validation-tolerances.R
provides assert_spss() and tol() helpers with
explicit tier semantics.tests/testthat/test-validation-discipline.R
meta-test lints validation files for Charter-forbidden patterns.vignettes/spss-compatibility.Rmd reports
per-function validation status, auto-generated from the test suite..github/workflows/strict-validation.yaml runs the full
suite in strict-discipline mode on release tags and weekly.Three weighted statistical functions were corrected to use unrounded
sum(w) per SPSS frequency-weights convention. Earlier
versions rounded too early and produced systematic drift from SPSS in
weighted scenarios.
t_test(): weighted variance, SE, and df calculations
now use unrounded sum(w) (one-sample and two-sample paths).
Welch- Satterthwaite df now derived from unrounded per-group weighted
N.oneway_anova(): weighted variance divisor is now
(sum(w) - 1) (sample formula, not population). Weighted SE
uses sqrt(sum(w)), not sqrt(physical n).
Weighted CI t-critical-value uses df = sum(w) - 1, not Kish
design-effective N. df_within now uses
floor(sum(w)) - k (SPSS ONEWAY-specific convention).levene_test(): weighted Levene df now uses unrounded
sum(w) - k (SPSS T-TEST family convention).These changes are bug fixes and may slightly shift weighted-scenario results in user code. Differences are small (typically < 0.01 on F or t) and bring mariposa into closer agreement with SPSS v29.
vignette("spss-compatibility") documents the
per-function validation status, the four tolerance tiers, and the
SPSS-procedure-specific WEIGHT BY conventions discovered during the
migration:
sum(w)floor(sum(w))Title shortened to “SPSS-Compatible Statistical Tools
for Survey Data” (CRAN soft-limit compliance).PMCMRplus and
survey (no longer needed).A second audit pass identified additional math defects and test fudges, all corrected in this release:
dunn_test(): SE now includes the Dunn (1964) / Conover
(1999) tie correction. Previous versions systematically under-estimated
|Z| on tied data (e.g., Likert scales). Baselines
regenerated from PMCMRplus::kwAllPairsDunnTest (exact match
to 4 decimals).friedman_test(): weighted branch now applies the tie
correction consistently with stats::friedman.test
(unweighted branch). The inconsistency caused weighted chi-squared
values to be too low for tied data.describe(): weighted skewness and kurtosis now delegate
to .calc_skewness() / .calc_kurtosis() in
helpers.R (Joanes-Gill Type-2 with Σw
substitution), matching w_skew() /
w_kurtosis() and SPSS FREQUENCIES exactly. The previous
duplicate implementation used a simple weighted moment without bias
correction..w_quantile(): weighted quantiles now use Type-6
(HAVERAGE) linear interpolation between cumulative-weight crossings —
matches SPSS FREQUENCIES /PERCENTILES. Unweighted quantiles also
switched from R default type = 7 to SPSS-compatible
type = 6.Several SPSS-compatibility claims were narrowed to reflect what the code actually does:
kruskal_wallis(), wilcoxon_test(), and
friedman_test() corrected from “design-based” /
“Lumley-Scott” to “frequency-weighted approximation”. Only
mann_whitney() is a genuine Lumley & Scott (2013)
implementation; the others substitute sum(w) for
n in the standard variance formula.mann_whitney() test now includes a permanent
cross-check against survey::svyranktest() (skipped when
survey is not installed).spearman_rho(): weights parameter
docstring rewritten to disclose that weights are used only for case
filtering (per SPSS NONPAR CORR convention), not in the rank correlation
itself.pearson_cor(): docstring now warns that the weighted-df
convention (n = sum(w)) gives spuriously narrow CIs for raw
expansion weights; users with such weights should normalize first.logistic_regression(): test file replaced with
property-based assertions (Wald formula, Sig from chi-sq, exp(B) vs
independent 2x2 odds ratio, Cox & Snell/Nagelkerke/McFadden from
textbook formulas, Omnibus from likelihood ratio). No longer a
tautological glm-vs-glm self-comparison.oneway_anova(): removed dead-code overwrite of
grand_mean_welch in the weighted Welch path.levene_test(): stale comment claiming
df2 = floor(sum(w)) - k corrected — the code uses unrounded
sum(w) - k (T-TEST family convention).This release adds 10 label management functions for working with
labelled survey data (inspired by sjlabelled, consolidated
into a clean, consistent API), plus data transformation, row operations,
and data exploration functions.
New var_label(): dual-mode function for getting and
setting variable labels. var_label(data) returns all
variable labels as a named character vector;
var_label(data, x = "Age", y = "Gender") sets labels for
specific columns. Supports tidyselect for column selection when getting
labels.
New val_labels(): dual-mode function for getting and
setting value labels. val_labels(data) returns all value
labels as a named list;
val_labels(data, x = c("Low" = 1, "High" = 2)) sets labels.
Use .add = TRUE to extend existing labels without replacing
them.
New copy_labels(): copies all label attributes
(variable labels, value labels, class, tagged NA metadata) from a source
data frame to matching columns in the target. Essential for preserving
labels after dplyr operations that strip
attributes.
New drop_labels(): removes value labels for values
that do not actually occur in the data. Use drop.na = TRUE
to also remove labels for tagged NA values.
New to_label(): converts haven_labelled
vectors to factors, using value labels as factor levels. Supports
ordered, drop.na, drop.unused,
and add.non.labelled options. Factor levels are ordered by
their original numeric codes (not alphabetically).
New to_character(): converts
haven_labelled vectors to character, replacing numeric
codes with their label text.
New to_numeric(): converts factors or labelled
vectors to numeric. When use.labels = TRUE, uses value
labels if they are numeric; otherwise assigns sequential integers
(controlled by start.at).
New to_labelled(): converts factors, character, or
numeric vectors to haven_labelled with proper value labels.
Factor levels become value labels automatically.
New set_na(): declares specific numeric values as
missing (NA or tagged NA). Supports unnamed values (applied to all
numeric columns) and named pairs for per-variable control (e.g.,
set_na(data, income = c(-9, -8))). With
tag = TRUE (default), creates tagged NAs that integrate
with na_frequencies(), frequency(), and
codebook(). Can be called incrementally to add new missing
value codes.
New unlabel(): strips all label metadata from
variables, converting haven_labelled vectors to plain base
R types. Removes variable labels, value labels, tagged NA metadata, and
format attributes. Tagged NAs become regular NA. Supports tidyselect for
selective column unlabelling.
New rec(): flexible recoding with string syntax
(e.g., rec(data, x, rec = "1:2=1 [Low]; 3:5=2 [High]")).
Supports value ranges, min/max keywords,
copy for unchanged values, and automatic value label
generation from bracket syntax. Works with numeric, character, and
labelled vectors.
New to_dummy(): creates dummy (indicator) variables
from categorical or labelled vectors. Generates one 0/1 column per
unique value with informative column names. Supports tidyselect for
multi-variable dummy coding and suffix = "label" to use
value labels in column names.
New std(): z-standardization with four methods
("sd", "2sd", "mad",
"gmd"). Supports survey weights, grouped standardization
via dplyr::group_by(), and robust = TRUE for
median/MAD-based standardization.
New center(): mean-centering (grand-mean or
group-mean). Supports survey weights and dplyr::group_by()
for group-mean centering. Returns centered values with the centering
value stored as an attribute.
New row_means(): computes row-wise means across
selected columns, with min_valid parameter matching SPSS
MEAN.x() syntax. Designed for use inside
dplyr::mutate(). Replaces the deprecated
scale_index().
New row_sums(): computes row-wise sums across
selected columns, with min_valid parameter for minimum
valid (non-NA) values.
New row_count(): counts occurrences of specific
values per row. Useful for counting endorsements in multi-item scales
(e.g., how many items a respondent agreed with).
find_var(): searches variables by name or label
using regular expressions. Returns matching variable names with their
labels. Useful for exploring large survey datasets with many
variables.scale_index() has been removed and replaced by
row_means(), which provides the same functionality with a
clearer name. Update existing code:
scale_index(data, x, y, z) →
row_means(data, x, y, z).New write_spss() function: exports data frames to
SPSS .sav format with full tagged NA roundtripping. Tagged
NAs are converted back to SPSS user-defined missing values, enabling
lossless roundtrips via read_spss() -> processing ->
write_spss(). Supports byte, none, and zsav
compression.
New write_stata() function: exports data frames to
Stata .dta format. Tagged NAs from any source format are
written as Stata extended missing values (.a through
.z). Supports Stata versions 8-15.
New write_xpt() function: exports data frames to SAS
transport .xpt format. Tagged NAs are written as SAS
special missing values (.A through .Z,
._). Supports transport versions 5 and 8.
read_spss(),
read_por(), read_stata(),
read_sas(), read_xpt(),
read_xlsx()write_spss(),
write_stata(), write_xpt(),
write_xlsx()read_xlsx() function: reads Excel
(.xlsx) files with automatic label reconstruction. When
reading back files created by write_xlsx(), variable
labels, value labels, and tagged NA metadata are fully restored –
enabling lossless roundtripping of labelled survey data through Excel.
haven_labelled columns, factor levels, and
variable labelsna_tag_map from missing codes
in the datawrite_xlsx() generic: exports data frames,
codebooks, and named lists to Excel (.xlsx) with full
support for variable labels, value labels, and tagged NA metadata. Uses
openxlsx2 as an optional dependency.
write_xlsx(data, "file.xlsx") – data + “Labels”
reference sheet with variable labels, value labels, and missing value
codescodebook(data) |> write_xlsx("codebook.xlsx") –
structured codebook workbook with Overview, Codebook, and optional
per-variable frequency sheets (frequencies = TRUE)write_xlsx(list(a = df1, b = df2), "multi.xlsx") –
multi-sheet export where each named list element becomes a sheetwrite_xlsx() now preserves tagged NA codes (-9, -11,
etc.) as visible values in the data sheet instead of empty cells,
enabling perfect roundtripping with read_xlsx(). System NAs
remain as empty cells.Column_Type column
(haven_labelled or factor) so
read_xlsx() can deterministically reconstruct column
types.openxlsx2 as a suggested dependency for Excel
import/export.New read_stata() function: reads Stata
.dta files and annotates native extended missing values
(.a through .z) for use with mariposa’s tagged
NA system. Stata tagged NAs are preserved automatically by haven;
read_stata() adds the na_tag_map attribute for
seamless integration with na_frequencies(),
frequency(), and codebook().
New read_sas() function: reads SAS
.sas7bdat files with optional catalog file
(.sas7bcat) for value labels. Annotates SAS special missing
values (.A through .Z and ._) for
tagged NA integration.
New read_xpt() function: reads SAS transport files
(.xpt) with tagged missing value support. Transport files
are the FDA-approved, platform-independent SAS data format.
New read_por() function: reads SPSS portable
.por files with the same tagged NA support as
read_spss(). Shares the SPSS missing value conversion logic
internally.
na_frequencies() column spss_code has been
renamed to code to reflect multi-format support. The column
now contains character values: numeric SPSS codes (e.g.,
"-9") or native format codes (e.g., ".a" for
Stata, ".A" for SAS).na_frequencies(), untag_na(), and
strip_tags() now work universally with data from all
supported formats (SPSS, Stata, SAS).
untag_na() is now format-aware: for Stata and SAS
data (where tagged NAs are the native representation with no numeric
codes to recover), it warns and falls back to strip_tags()
behavior.
frequency() and codebook()
automatically display format-appropriate missing value codes (e.g.,
-9 for SPSS, .a for Stata, .A for
SAS).
New read_spss() function: reads SPSS
.sav files and preserves user-defined missing values as
tagged NAs instead of converting them to regular NA. This
allows distinguishing between different types of missing data (e.g., “no
answer”, “not applicable”, “refused”) while still treating them as
NA in standard R operations. Fixes the
sjlabelled::read_spss(tag.na=TRUE) crash on large datasets
(e.g., ALLBUS) caused by out-of-bounds letters[]
indexing.
New na_frequencies() function: shows a breakdown of
the different types of missing values in a tagged NA variable, with
counts, original SPSS codes, and value labels.
New untag_na() function: converts tagged NAs back to
their original SPSS missing value codes (e.g., -9, -8, -42).
New strip_tags() function: converts all tagged NAs
to regular (untagged) NA values, producing the same result
as reading with haven::read_sav() directly.
frequency() now displays tagged NAs individually
when data was imported with read_spss(). Each missing value
type is shown as a separate row with its original SPSS code and label,
followed by a “Total Valid” and “Total Missing” summary row.
frequency() with show.unused = TRUE
correctly handles tagged NA labels (no longer shows them as unused with
freq=0).
fre() shorthand alias for frequency().
Both functions are identical; fre() simply provides a
quicker way to call frequency analysis. ?fre shows the same
help page as ?frequency.New codebook() function: generates an interactive
HTML data dictionary displayed in the RStudio Viewer pane. Shows
variable ID, name, type, label, empirical values, value labels, and
frequencies in a clean, scrollable table. Inspired by sjPlot’s
view_df() but built natively with
htmltools.
HTML codebook features a subtle-accent design: dark header, alternating row stripes, monospace type badges, and per-value frequency counts displayed as vertical lists aligned across columns.
Console print() shows a minimal metadata overview
(variable count, observations, types). Full details are reserved for the
HTML viewer.
summary() method provides a detailed text-based
fallback with toggleable sections (overview,
variable_details, value_labels).
Supports tidyselect variable selection, optional survey weights
for weighted frequencies, and sort.by.name
ordering.
htmltools as an imported dependency for HTML
codebook generation.All 13 analysis functions now support summary() for
detailed SPSS-style output with toggleable sections. The three-layer
pattern works as follows:
print() — compact one-line overview (default when
typing the object name)summary() — builds a detailed summary object with
boolean section togglesprint.summary() — renders the full verbose output with
all requested sectionsSupported functions: t_test(),
oneway_anova(), factorial_anova(),
ancova(), chi_square(),
mann_whitney(), pearson_cor(),
spearman_rho(), kendall_tau(),
reliability(), efa(),
linear_regression(),
logistic_regression().
Each summary() method accepts boolean parameters to
control which output sections are displayed (e.g.,
summary(result, effect_sizes = FALSE) or
summary(result, descriptives = FALSE)).
build_summary_object() and
format_p_compact() in R/summary_helpers.R as
shared infrastructure for all summary methods.Complete Roxygen2 documentation for all 39 S3 methods (13 print + 13 summary
@description,
@param, @return, @examples, and
@seealso.All 13 main function @examples now demonstrate the
three-layer output pattern (result,
summary(result),
summary(result, toggle = FALSE)).
Added print.reliability() and
print.efa() documentation (previously
undocumented).
Fixed print.chi_square() Roxygen2 tag
(@keywords internal replaced with correct
@method print chi_square).
Fixed example syntax errors in ancova() and
factorial_anova() (formula syntax replaced with correct
dv/between interface).
Fixed incorrect variable name education_level in
examples (corrected to education).
Added test-summary-methods.R with tests for all 13
summary methods.
Updated test-print-methods.R to reflect the new
three-layer structure.
Added factorial_anova() for multi-factor
between-subjects ANOVA (up to 3 factors) with Type III Sum of Squares
matching SPSS UNIANOVA. Includes main effects, all interaction terms,
partial eta squared, R-squared, and Levene’s test for homogeneity of
variance. Full survey weight support via WLS (matching SPSS /REGWGT).
Integrates with existing tukey_test(),
scheffe_test(), and levene_test() S3
generics.
Added ancova() for Analysis of Covariance — tests
group differences after controlling for continuous covariates. Matches
SPSS UNIANOVA with the WITH keyword. Provides ANOVA table, parameter
estimates (B, SE, t, p, partial eta squared), estimated marginal means
(adjusted for covariates), and Levene’s test. Supports up to 3 factors
and multiple covariates with full survey weight support.
Added 612 SPSS validation tests for
factorial_anova() across 9 scenarios: unweighted (2-factor,
3-factor, 2-factor with missing data), weighted (2-factor, 3-factor),
grouped (2-factor, 3-factor), and weighted+grouped (2-factor,
3-factor).
Added 579 SPSS validation tests for ancova() across
11 scenarios: one-way ANCOVA, two-way ANCOVA, weighted, grouped,
weighted+grouped, multiple covariates, and single factor with single
covariate.
Total test suite: 4,986 tests passing (0 failures).
Type III Sum of Squares computed via contr.sum
contrasts and stats::drop1() — no dependency on the
car package.
Weighted analyses use WLS (stats::lm() with
weights), matching SPSS’s /REGWGT subcommand behavior exactly.
Weighted Levene’s test uses the SPSS /REGWGT algorithm:
z_i = sqrt(w_i) * |y_i - weighted_cell_mean_i| followed by
unweighted ANOVA.
Corrected Model SS computed as
Corrected Total - Error (not sum of Type III SS) to
correctly handle unbalanced designs.
Added fisher_test() for Fisher’s exact test of
independence in contingency tables. Recommended when sample sizes are
small or expected cell frequencies fall below 5 (where chi-square
approximation becomes unreliable). Supports survey weights,
multi-variable analysis, and group_by().
Added chisq_gof() for chi-square goodness-of-fit
testing. Tests whether the observed frequency distribution of a
categorical variable matches an expected distribution (default: equal
proportions). Supports custom expected proportions, residual analysis,
survey weights, and multi-variable analysis.
Added mcnemar_test() for testing changes in paired
proportions between two dichotomous measurements (e.g., before/after
designs). Provides both asymptotic and exact binomial p-values, 2×2
contingency tables, and continuity correction. Supports survey
weights.
Added dunn_test() as an S3 generic for Dunn’s
post-hoc pairwise comparisons following a significant Kruskal-Wallis
test. Identifies which specific group pairs differ using rank-based
Z-statistics with adjustable p-value correction (Bonferroni, Holm, BH,
etc.). Dispatches on kruskal_wallis result
objects.
Added pairwise_wilcoxon() as an S3 generic for
pairwise Wilcoxon signed-rank post-hoc comparisons following a
significant Friedman test. Identifies which measurement pairs differ
with adjustable p-value correction. Dispatches on
friedman_test result objects.
dunn_test() and
pairwise_wilcoxon() join tukey_test(),
scheffe_test(), and levene_test() as S3
generics that dispatch on their parent test result objects.efa() now supports Maximum Likelihood (ML)
extraction via extraction = "ml". ML extraction provides a
goodness-of-fit chi-square test, initial communalities as SMC (squared
multiple correlations), and uniquenesses. Uses
stats::factanal() with correlation matrix input for
seamless survey weight support.
efa() now supports Promax rotation via
rotation = "promax". Like Oblimin, Promax is an oblique
rotation that produces Pattern Matrix, Structure Matrix, and Factor
Correlation Matrix. Uses stats::promax() (base R, no new
dependency).
Internal refactoring of efa(): extraction logic
separated into .efa_extract_pca() and
.efa_extract_ml() for cleaner architecture and easier
extension with future extraction methods (PAF planned).
Added kruskal_wallis() for comparing 3+ independent
groups on ordinal data (non-parametric alternative to one-way ANOVA).
Supports survey weights, group_by(), and multi-variable
analysis. Effect size: Eta-squared.
Added wilcoxon_test() for comparing two paired
measurements without assuming normality (Wilcoxon signed-rank test).
Includes rank categories (negative, positive, ties) and effect size
r.
Added friedman_test() for comparing 3+ related
measurements on ordinal data (non-parametric alternative to
repeated-measures ANOVA). Effect size: Kendall’s W.
Added binomial_test() for testing whether an
observed proportion matches an expected value (exact binomial test).
Supports multiple binary variables and custom test proportions.
Added 294 new SPSS validation tests across all 4 non-parametric functions, covering weighted/unweighted and grouped/ungrouped scenarios.
Total test suite: 2,227 tests passing (0 failures, 0 skips).
Added reliability() for Cronbach’s Alpha with item
statistics, including corrected item-total correlations,
alpha-if-item-deleted, and inter-item correlation matrix. Genuine
implementation with full survey weight support.
Added efa() for Exploratory Factor Analysis with PCA
extraction. Supports Varimax rotation (Base R) and Oblimin rotation (via
optional GPArotation package). Includes KMO measure,
Bartlett’s test, communalities, and sorted factor loading matrix with
configurable blank threshold.
Added scale_index() for creating mean indices across
survey items, with min_valid parameter matching SPSS
MEAN.x() syntax. Designed for use inside
dplyr::mutate().
Added pomps() for Percent of Maximum Possible Scores
transformation, rescaling values to a 0-100 range for cross-scale
comparability.
Added linear_regression() as a wrapper around
stats::lm() with SPSS-compatible output: coefficients table
(B, SE, Beta, t, p), ANOVA table, model summary (R, R-squared, adjusted
R-squared), and standardized coefficients. Supports both formula and
SPSS-style (dependent/predictors) interfaces.
Added logistic_regression() as a wrapper around
stats::glm() with odds ratios, Wald statistics,
pseudo-R-squared measures (Nagelkerke, Cox-Snell, McFadden), and
classification table.
Added GPArotation as suggested dependency for
Oblimin rotation in efa().
Added MASS as suggested dependency for enhanced
regression diagnostics.
All 6 new functions support survey weights and grouped analysis
via dplyr::group_by().
All functions include comprehensive roxygen2 documentation with practical examples, “When to Use” guidance, and “Understanding the Output” sections.
gamma() has been renamed to
goodman_gamma() to avoid shadowing
base::gamma(). The function remains an alias for
chi_square() and works identically.
S3 class names unified: removed _results suffix from
all result classes (e.g., chi_square_results ->
chi_square, t_test_results ->
t_test). Class names now match the function name that
created them.
Fixed namespace collisions from triple-defined internal helper
functions (.process_variables(),
.process_weights(), .effective_n()). These are
now defined once in helpers.R and shared across all
functions.
Fixed weighted variance/SD formula inconsistency. All weighted
calculations now use the SPSS frequency weights formula:
sum(w * (x - w_mean)^2) / (V1 - 1).
Fixed Gamma ASE (asymptotic standard error) calculation. Replaced empirical magic-number formula with the correct ASE0 formula from Agresti (2002).
Fixed weighted Cohen’s d calculation in t_test().
Previously multiplied values by weights (x * w); now uses
proper weighted means and pooled weighted standard deviation.
Fixed weighted kurtosis formula. Changed from population excess
kurtosis (m4/m2^2 - 3) to SPSS Type 2 sample-corrected
formula (G2 = ((n+1)*g2 + 6) * (n-1) / ((n-2)*(n-3))),
matching SPSS output.
Refactored 9 of 11 w_* functions to use a shared
factory pattern (R/w_factory.R). Eliminated ~2,460 lines of
duplicated boilerplate (4,204 → 1,740 lines, -58.6%).
w_modus and w_quantile remain standalone due
to their fundamentally different interfaces.
Added 83 SPSS validation tests for all w_* functions
across 4 scenarios (weighted/unweighted × grouped/ungrouped) in
test-weighted-statistics-spss-validation.R.
Reduced memory usage: result objects now store only the columns needed for post-hoc tests instead of the full input data frame.
Unified print helper system: all output formatting now uses
print_helpers.R. Removed deprecated
.print_header(), .print_border(),
.get_border(), and .print_group_header() from
helpers.R.
Deduplicated t_test.R print methods:
print.t_test_results and print.t_test_result
now share a common implementation (~360 fewer lines).
Added input validation:
t_test(): validates conf.level is between
0 and 1t_test(), oneway_anova(): validate that
selected variables are numericchi_square(): warns when expected cell counts <
5Migrated error handling from
stop()/warning() to
cli_abort()/cli_warn() with structured
messages, {.arg} and {.var} markup, and
pluralization support.
Added cli as dependency for professional user-facing
messages and print output.
Added @family tags to all 24 exported functions for
cross-referencing in documentation (families: descriptive,
hypothesis_tests, correlation, posthoc, weighted_statistics).
Added tests/testthat/helper-mariposa.R with shared
test utilities and centralized SPSS validation tolerances.
Migrated print_helpers.R infrastructure to
cli (cli_rule(), cli_bullets(),
cli_h2()).
Extended globals.R with missing NSE variable
declarations.
Fixed “SURVEYSTAT” reference in imports.R.