factorH: intro

What this package does (and why)

factorH provides a simple, single-call workflow for multifactor nonparametric, rank-based ANOVA and publication-ready outputs:

  • ANOVA-like tables based on ranks
  • rank-based effect sizes computed from H
  • Dunn-Bonferroni post hoc comparison matrices
  • simple-effects post hocs (pairwise comparisons within levels of conditioning factors)
  • compact descriptive tables
  • one-call diagnostics for factorial plans
  • TSV export for quick formatting
  • a Jamovi-ready normalization helper for backend integration

Why? Popular GUI stats tools do not offer a ready-made, user-friendly multifactor rank-based pipeline that mirrors standard H / SRH analyses in a way that is easy for beginners. factorH aims to fill that gap with clear R-like formula syntax and a one-command report function.

The package is intentionally small: most users will only ever need:

  • srh.kway.full(...) to compute the full pipeline
  • write.srh.kway.full.tsv(...) to export the results into a single tab-separated file

Advanced integrations can additionally use:

  • as_jamovi_srh_full(...) to normalize srh.kway.full() output into a stable Jamovi/backend structure

Formula syntax at a glance

All high-level functions use standard R model formulas:

response ~ factorA + factorB + factorC
  • + lists the main effects.
  • Interactions are handled internally, so you do not need to write A:B or A*B.
  • The response (left of ~) must be numeric (e.g., a Likert score coded as 1 to 5 and stored as numeric).

Examples below use the included dataset mimicry.

library(factorH)
data(mimicry, package = "factorH")
str(mimicry)

Predictors should be factors. If they are not, the functions will coerce them to factors internally.

What is allowed?

# One factor (KW-style):
liking ~ condition

# Two factors (SRH-style):
liking ~ gender + condition

# Three or more factors (k-way):
liking ~ gender + condition + age_cat

You do not need to write gender:condition or gender*condition. The package constructs the required interaction terms internally when needed.

Numeric response (Likert note)

The response must be numeric. For Likert-type responses (e.g., 1 = strongly disagree, …, 5 = strongly agree), keep the variable numeric. Rank-based procedures can be used with such ordinal-like data.

If your Likert variable has been imported as a factor or character, coerce it safely:

# if stored as character "1", "2", ...:
mimicry$liking <- as.numeric(mimicry$liking)

# if stored as factor with labels "1", "2", ...:
mimicry$liking <- as.numeric(as.character(mimicry$liking))

Diagnostics at a glance

Most users can cover assumption checks with a single command:

diag_out <- plan.diagnostics(response ~ factorA + factorB (+ factorC ...), data = your_data)

What it does:

  1. Raw normality: Shapiro-Wilk in each subgroup and interaction cell of the specified factors.
  2. Residual normality per cell: Shapiro-Wilk on residuals from the corresponding full-factorial ANOVA, tested within each cell.
  3. Homogeneity of variances: Levene/Brown-Forsythe across full-plan cells (median by default).
  4. Count balance: chi-square homogeneity / independence / log-linear independence across factors.
  5. It prints a concise overall summary (share of OK and overall status) and returns all detailed tables in diag_out$results, with per-type OK percentages in diag_out$summary.

For most workflows, this single command is enough to document design diagnostics alongside rank-based analyses.

The one-call pipeline

The main function srh.kway.full() runs:

  1. an ANOVA-like table on ranks
  2. a descriptive summary
  3. post hoc matrices (Dunn; adjusted P.adj)
  4. simple-effects post hocs

It also supports: - type = 2 vs type = 3 - scope = "within" vs scope = "global" for simple-effects Bonferroni - design diagnostics and warnings stored in res$meta

For 2 factors:

res2 <- srh.kway.full(liking ~ gender + condition, data = mimicry)
names(res2)
res2$anova
head(res2$summary)
names(res2$posthoc_cells)
names(res2$posthoc_simple)

For 2 factors with Type III SS:

res2_t3 <- srh.kway.full(liking ~ gender + condition, data = mimicry, type = 3)
res2_t3$anova

For 3 factors:

res3 <- srh.kway.full(liking ~ gender + condition + age_cat, data = mimicry)
res3$anova

For global simple-effects Bonferroni:

res3g <- srh.kway.full(
  liking ~ gender + condition + age_cat,
  data = mimicry,
  scope = "global"
)
names(res3g$posthoc_simple)

Export full result to a tab-separated file

f <- file.path(tempdir(), "result.tsv")
write.srh.kway.full.tsv(res3, file = f, dec = ".")
file.exists(f)

If you need a decimal comma:

f2 <- file.path(tempdir(), "result_comma.tsv")
write.srh.kway.full.tsv(res3, file = f2, dec = ",")
file.exists(f2)

The TSV contains clearly separated sections:

  • ## SRH: EFFECTS TABLE
  • ## SUMMARY STATS
  • ## POSTHOC CELLS
  • ## SIMPLE EFFECTS
  • ## META

and can be easily opened in spreadsheet software such as Excel or Google Sheets.

Jamovi/backend helper

If you want to map the full pipeline into a Jamovi module or another structured frontend, use:

jam <- as_jamovi_srh_full(res3)
names(jam)

This helper does not build Jamovi result objects directly. Instead, it normalizes the srh.kway.full() output into a predictable list of sections and items that can be consumed by a backend.

What is in the example dataset?

mimicry is a real study on the chameleon effect (Trzmielewska et al., 2025) about how movement conditions affect liking of an interlocutor. Potential moderators include gender and age (with dichotomized age_cat and a 3-level age_cat2). This makes it a natural playground for multifactor rank-based analyses.

table(mimicry$condition)
table(mimicry$gender)
table(mimicry$age_cat)

What the functions compute (high level)

  • srh.kway(): rank-based k-way ANOVA table using Type II SS by default, with an optional switch to Type III SS; p-values are tie-corrected; H is reported with and without the correction factor; effect sizes are computed from unadjusted H.
  • srh.effsize(): 2-factor SRH table with effect sizes (eta2H, eps2H) computed from H.
  • nonpar.datatable(): compact descriptive tables with global mean ranks, medians, quartiles, IQR, etc., for all main effects and interactions.
  • srh.posthocs(): Dunn-Bonferroni pairwise matrices (P.adj) for all effects (main effects and interactions).
  • srh.simple.posthoc() / srh.simple.posthocs(): simple-effects pairwise comparisons within levels of conditioning factors (scope = "within" by default).
  • srh.kway.full(): orchestrates all of the above.
  • write.srh.kway.full.tsv(): exports everything into one TSV (with a dot or comma decimal mark).
  • as_jamovi_srh_full(): normalizes full-pipeline results for Jamovi/backend integration.
  • plan.diagnostics(): one-call diagnostics: raw normality, residuals cellwise normality, Levene (median), and balance chi-square; prints an overall summary and returns full tables.

That is it. For most users, the intro ends here: use srh.kway.full() and export with write.srh.kway.full.tsv().