Skip to content

rstatix 1.1.0

Latest

Choose a tag to compare

@kassambara kassambara released this 23 Jul 21:43
· 33 commits to master since this release
7402db8

New features

  • cohens_d() gains a ci.method argument choosing how the confidence interval is computed when ci = TRUE. The new ci.method = "analytic" returns a deterministic interval obtained by inverting the noncentral t distribution (Steiger, 2004; Cumming & Finch, 2001) — the same approach anova_test(ci = ) uses for partial eta-squared — for the one-sample, paired and two-sample (equal-variance and Welch) cases, and scales it by the Hedges factor when hedges.correction = TRUE. Unlike the bootstrap it needs no seed and is reproducible across runs, and it matches effectsize::cohens_d(ci = ); with hedges.correction = TRUE the estimate and both bounds are scaled by the documented (N - 3)/(N - 2.25) approximation, so they are not identical to effectsize::hedges_g(ci = ), which uses the exact gamma-function correction; the gap is proportional to the effect size and grows as the samples shrink. The default ci.method = "boot" (percentile bootstrap) is unchanged, so existing cohens_d(ci = TRUE) results are the same as before.

  • wilcox_effsize() and kruskal_effsize() gain a method argument offering an alternative rank-based effect size. wilcox_effsize(method = "rank_biserial") reports the rank-biserial correlation — Cliff's delta for an independent test (equal to cliff_delta()) or the matched-pairs rank-biserial for a paired test — instead of the default r = Z / sqrt(N). kruskal_effsize(method = "epsilon2") reports the rank epsilon-squared H / (N - 1) (matching effectsize::rank_epsilon_squared()) instead of the default bias-corrected eta-squared. The defaults are unchanged.

  • check_test_assumptions() checks the assumptions of a one-way, independent-groups design (normality per group with Shapiro-Wilk, homogeneity of variance with Levene) and returns a tidy one-row recommendation: the two p-values, the verdicts, and the omnibus + post-hoc test they imply (anova_test() + tukey_hsd(), welch_anova_test() + games_howell_test(), or kruskal_test() + dunn_test()). The same single check can then drive both the omnibus and the post-hoc coherently; its result can be passed to posthoc_test() through the new .assumptions argument to avoid re-running the checks, and posthoc_test() also gains an omnibus argument that warns if the chosen post-hoc belongs to a different family than an omnibus test already run. The documentation notes the known cost of choosing a test by first testing its assumptions on the same data.

  • posthoc_test() chooses and runs the post-hoc test appropriate to a one-way, independent-groups design (outcome ~ group), following the standard decision tree: it checks normality (Shapiro-Wilk per group) and homogeneity of variance (Levene), then runs Tukey HSD when both hold, Games-Howell when the groups are normal but variances differ, and Dunn's test when the data are not normal. It returns the chosen test's usual pairwise table, with the selected method and the assumption verdicts attached and shown when the result is printed, so the routing is transparent.

  • get_test_label() and create_test_label() gain a style argument. With style = "apa" the label follows the APA-7 in-text reporting format: the leading test-name label is dropped, the p-value is written APA-style (p = .023, p < .001), and the effect size is appended with its confidence interval when the object carries one — for example anova_test(ci = ) — otherwise as a point estimate; bounded effect sizes (eta-squared, r, Cliff's delta, rank-biserial) drop the leading zero while Cohen's d keeps it. For example, get_test_label(anova_test(df, len ~ dose, effect.size = "pes", ci = 0.95), type = "text", style = "apa") gives F(2, 57) = 67.42, p < .001, eta2[p] = .70, 95% CI [.57, .79]; the estimate and interval in the label are re-derived from the row's F and degrees of freedom at full precision, and the interval is labelled with the confidence level it was computed at. The default style = "classic" is unchanged.

  • effect.size = TRUE now covers paired tests. cohens_d() gains an id argument that matches the two groups by subject (as t_test() already does), so t_test(paired = TRUE, id = , effect.size = TRUE) reports a Cohen's d computed on the same subject-matched differences as the test. A paired wilcox_test(effect.size = TRUE) now adds a rank.biserial column — the matched-pairs rank-biserial correlation (R+ - R-) / (R+ + R-) — computed on the paired differences the test used (matched by id when supplied); it carries no magnitude, since no threshold set is calibrated for it. When every paired difference is zero the matched-pairs rank-biserial is undefined (0/0): it is returned as NA with a warning, and the other comparisons of a pairwise call are still computed. Both previously errored.

  • t_test(), wilcox_test(), dunn_test() and games_howell_test() gain an effect.size argument (default FALSE). With effect.size = TRUE each pairwise comparison is annotated with the effect size natural to that test, named by the metric: cohens.d for t_test() (per-pair Cohen's d), cliff.delta for wilcox_test(), r for dunn_test() (r = Z / sqrt(N), the total sample size), and a Welch cohens.d for games_howell_test(); Cohen's d and Cliff's delta also get a magnitude. Each metric is computed consistently with the test that reports it — pairwise_t_test(pool.sd = TRUE) reports the common-SD (pooled-model) d that matches its pooled-SD p-value, and the Games-Howell d is oriented like its own mean difference. The default is off, so existing output is unchanged. Paired designs (including a paired t-test matched by id and a paired Wilcoxon) are covered too — see the entry above; effect.size = TRUE remains undefined only for a one-sample Wilcoxon.

  • cliff_delta() computes Cliff's delta, a non-parametric effect size for the difference between two groups: delta = (#{x > y} - #{x < y}) / (n1 * n2), ranging from -1 to 1 and unaffected by the distribution's shape (Cliff, 1993). It follows the same interface as cohens_d() and wilcox_effsize() — a formula, ref.group, comparisons, grouped data via dplyr::group_by(), and an optional percentile-bootstrap confidence interval with ci = TRUE — and returns a tibble with the columns .y., group1, group2, effsize, n1, n2 and magnitude. The magnitude is labelled by the Romano et al. (2006) thresholds. Cliff's delta is algebraically identical to the rank-biserial correlation, so the estimate equals effectsize::rank_biserial().

  • omega_squared() and partial_omega_squared() compute the classic (full) and partial omega-squared effect sizes for a between-subjects ANOVA model, alongside the existing eta_squared() / partial_eta_squared(). Omega squared is a less-biased estimate of the population effect size. Like eta_squared(), they take a fitted aov or anova model and return a named numeric vector, one value per model term; repeated-measures and mixed models are not supported. A negative point estimate (which arises for a term with F < 1) is floored at 0, since omega squared estimates a non-negative proportion of variance. The values match effectsize::omega_squared(partial = FALSE) and partial = TRUE, computed in base R from the Olejnik and Algina (2003) formulas.

  • tidy() and glance() methods for the rstatix_test objects returned by the test functions (t_test(), wilcox_test(), anova_test(), kruskal_test(), and the rest). tidy() drops the internal rstatix classes and the stashed test arguments and returns the result as a plain tibble, so the object passes cleanly to tools that dispatch on generics::tidy() / generics::glance(), such as broom, gtsummary and gt. For every shape of anova_test() result — between-subjects, repeated-measures or mixed, grouped or not — tidy() returns the corrected ANOVA table, one row per term (per group), via get_anova_table(); the repeated-measures and mixed forms are lists holding the ANOVA table with Mauchly's test and the sphericity corrections, and their grouped forms carry a packed column, none of which tidy() would otherwise flatten. glance() returns a one-row summary with the test method and n, the number of comparisons or model terms. Previously the generics had no method for these objects and gave either a broken result or an error.

  • eta_squared() and partial_eta_squared() gain a ci argument. With ci = NULL (the default) each still returns the bare named vector of effect sizes, unchanged. With a confidence level, e.g. ci = 0.95, each returns a tibble with one row per model term and the columns Effect, effsize, conf.low and conf.high. The interval is computed in base R by inverting the noncentral F distribution (Steiger, 2004) — the machinery anova_test(ci = ) already uses — and matches effectsize::eta_squared(ci = , alternative = "two.sided"): partial = FALSE for eta_squared(), partial = TRUE for partial_eta_squared() (to about four decimals for the non-partial bounds of a small pseudo-F, where that function's inversion uses a looser tolerance). For a non-partial eta squared the interval is the noncentral-F interval of an equivalent pseudo-F built from the estimate, which is how the non-partial case reduces to the same inversion; for a partial eta squared that pseudo-F is the term's own F, so partial_eta_squared(ci = ) reports the same bounds as anova_test(effect.size = "pes", ci = ) whenever the two work from the same F per term — a one-way or balanced design, or anova_test(type = 1); in an unbalanced multi-factor design anova_test()'s default type II F differs from the sequential F of the aov model, and so do the intervals. In a one-way design the two coincide (#18).

  • cramer_v() gains a ci argument to return a confidence interval for Cramer's V, computed in base R by inverting the noncentral chi-square distribution (Smithson, 2003; Steiger, 2004) — the same approach anova_test(ci = ) already uses for partial eta squared, with the chi-square in place of the F distribution. With ci = TRUE the function returns a one-row tibble with the columns effsize, conf.low and conf.high — the same confidence-interval columns anova_test(ci = ) returns — and takes a conf.level argument (default 0.95); the default ci = FALSE still returns the bare numeric value, unchanged. The interval is computed from the same chi-square statistic as the point estimate, so correct = TRUE shifts both. The bounds are clipped to the [0, 1] range of Cramer's V, and are NA with a warning when the statistic or its degrees of freedom are undefined (an empty row or column, or simulate.p.value = TRUE). Near independence — when the observed chi-square falls below the alpha/2 quantile of its central distribution — both bounds collapse to [0, 0] and the point estimate lies above them; this is a property of the noncentral inversion. At the default correct = FALSE the interval matches DescTools::CramerV(conf.level = ), and effectsize::cramers_v(adjust = FALSE, ci = , alternative = "two.sided") away from the near-independence corner, where numerical inversions differ.

Main changes

  • cramer_v() no longer applies Yates' continuity correction by default: correct now defaults to FALSE. Cramer's V is sqrt(chi2 / (N * (k - 1))) computed from the Pearson chi-square statistic (Cramer, 1946). Yates' correction improves the chi-square approximation to the null distribution of the test statistic, so it belongs to the test — chisq_test(), mcnemar_test() and prop_test() keep it on by default — and not to an effect size, where it merely shrinks the statistic and biases V downward. This changes the value returned for 2x2 tables (the only shape Yates affects): cramer_v(as.table(rbind(c(20, 30), c(35, 15)))) now returns 0.3015113 instead of 0.2814106, matching the definition and the values returned by DescTools::CramerV() and effectsize::cramers_v(adjust = FALSE). Larger tables are unaffected. Pass correct = TRUE to recover the previous value (#293).

Minor changes

  • Function help pages and the README now link to the corresponding Datanovia tutorials.

  • New documentation topic ?rstatix-references collecting the sources of the methods rstatix implements — Cramer (1946), Smithson (2003), Steiger (2004), Conover (1999), Nemenyi (1963), Piepho (2004), Dunnett (1955) — and naming the packages that implement some of the same methods and offer functionality rstatix does not: effectsize, DescTools, multcomp, multcompView and PMCMRplus. Every function that documents such an agreement now names the function it agrees with, and the arguments needed to reproduce it: conover_test(), friedman_conover_test() and friedman_nemenyi_test() record the corresponding PMCMRplus function, and anova_test(ci = ) records effectsize::eta_squared(partial = TRUE, ci = , alternative = "two.sided"). The agreements of dunnett_test() with DescTools::DunnettTest() and multcomp::glht(), and of sign_test() with DescTools::SignTest(), which the documentation already claimed, are now checked by the test suite.

  • The note on sign_test() now records that its test code and its confidence interval for the median are adapted from DescTools::SignTest() and DescTools::MedianCI(), written by Andri Signorell, rather than only naming the package. ?rstatix-references records the same, and separates the functions whose code is adapted from another package from the several that call one.

  • The internal helpers get_manova_table(), Pillai(), Wilks(), HL() and Roy() are removed. They were never exported and nothing has called them since they were added in 2019. No user-visible behaviour changes.

  • cohens_d(), wilcox_effsize(), kruskal_effsize() and friedman_effsize() gain boot.parallel and boot.ncpus arguments to compute the bootstrap confidence interval (ci = TRUE) in parallel, for example cohens_d(df, len ~ supp, ci = TRUE, boot.parallel = "multicore", boot.ncpus = 4). They default to getOption("boot.parallel", "no") and getOption("boot.ncpus", 1L), so the bootstrap stays serial by default, the corresponding global options keep working, and all existing results are unchanged. The parallel settings are now passed to boot::boot(), which performs the resampling, rather than to boot::boot.ci(), which has no such argument and silently ignored them. Based on the contribution by @HannesOberreiter (#289, #87).

Bug fixes

  • eta_squared() and partial_eta_squared() now exclude the (Intercept) row that a car::Anova() type 3 table carries. That row's sum of squares belongs to neither the effect decomposition nor the residual, so counting it inflated the eta-squared denominator — with cyl and am as factors, for car::Anova(lm(mpg ~ cyl * am, data = mtcars, contrasts = list(cyl = "contr.sum", am = "contr.sum")), type = 3), eta_squared() returned 0.042 for cyl where the correct value is 0.582 — and partial_eta_squared() reported a meaningless (Intercept) effect row. Both now match effectsize::eta_squared(partial = FALSE) / (partial = TRUE) on such tables; aov and stats::anova() inputs, which carry no intercept row, are unchanged. The new omega_squared() and partial_omega_squared() apply the same exclusion.

  • R CMD check no longer fails when the packages in Suggests are absent, the configuration CRAN checks with _R_CHECK_DEPENDS_ONLY_=true. dunnett_test() and emmeans_test() stop without emmeans, and wilcox_effsize() stops without coin; their examples now run only when the package is installed, and the two test blocks that reached them without a guard now skip. The examples of dunnett_test() and emmeans_test() are wrapped in requireNamespace() rather than \donttest{}, which does not help: R CMD check --as-cran runs those blocks as well. A noSuggests job is added to the check matrix so the configuration is exercised (#296).

  • The bootstrap confidence interval (ci = TRUE) of cohens_d(), wilcox_effsize(), kruskal_effsize() and friedman_effsize() no longer breaks when the bootstrap replicates are degenerate — all identical (a perfect effect size, e.g. Kendall's W = 1) or all missing (constant data). Such an interval is undefined: conf.low and conf.high are now returned as NA with a warning, and the remaining groups or comparisons are still computed. Previously an ungrouped call silently returned empty list-columns in place of the bounds, a grouped call failed with Can't combine ... <list> and ... <double>, and constant data errored with missing value where TRUE/FALSE needed. An interval type that cannot be built (for instance ci.type = "stud", which needs bootstrap variances) is likewise returned as NA with a warning instead of a malformed column. Well-behaved bootstrap intervals are unchanged (#290).