Descriptives, tests and the classical toolbox — the 0.23.0 conventions
Part of the numerical-validation suite. The definitions the convention inventory found implicit — chosen in the engine, matched by a reference, but never written down as a choice — written down once. Each section names the definition ChakataStat prints, the reference packages that agree with it, and the alternative a reader may meet elsewhere.
Every section here was written on 2026-08-28 for 0.23.0 item 9 from the engine source, not from memory: the file and function each cites is where the number comes from. Where a pin in the validation suite exercises the choice, it is named.
Descriptives
Skewness and kurtosis
ChakataStat prints the bias-adjusted sample skewness and excess kurtosis — the G₁ and G₂ of Joanes & Gill (1998):
G₁ = √(n(n−1)) / (n−2) · g₁ g₁ = m₃ / m₂^{3/2}
G₂ = (n−1) / ((n−2)(n−3)) · ((n+1)·g₂ + 6) g₂ = m₄ / m₂² − 3
with m_k the central moments about the mean with divisor n
(explore_row in descriptive.rs).
These are the definitions SPSS, SAS, Excel and R's psych::skew(type = 2)
print; SciPy's skew(bias = False) / kurtosis(bias = False) are the same
(pins explore/bp/skewness, explore/bp/kurtosis, both oracles). The
alternatives — the moment estimators g₁, g₂ (R's e1071 type 1, SciPy's
default) and the b₁, b₂ forms (type 3, Minitab) — differ by factors of
order 1/n.
Standard deviation, variance and the standard error of the mean
The sample versions with divisor n − 1; the standard error of the mean is s/√n. Under a frequency weight, n is the sum of the weights (the case-weighting convention). The population (divisor n) forms are not printed anywhere.
Quantiles
Recorded in tolerances.md: the Hazen definition,
plotting position (k − ½)/n — R type 5, NumPy hazen. It is used for every
percentile the application prints, the median included, the box plot's
quartiles and fences, and the quantile method of Bin variable. Weighted,
the same rule runs on cumulative weight, averaging the two adjacent values
on an exact boundary. Readers of SPSS should note that its default
(HAVERAGE, R type 6) is a different rule.
Missing-data policy
Every procedure is listwise on the variables it uses — a case with a
missing value in any analysis variable is dropped from that analysis — with
one exception: bivariate correlations default to pairwise (each pair uses
the cases valid on both), with missing: listwise as the option
(correlation.rs). The
dataset fingerprint's footer names the filter and weight; it does not name
the listwise sample, which is the procedure's, and each table's N is that
sample's.
Tables
Pearson chi-square: no continuity correction
The crosstabs χ² is the plain Pearson statistic on every table, 2 × 2
included — no Yates correction. The likelihood-ratio χ² is
2 Σ O ln(O/E). Pins crosstabs/pearson_chisq and lr_chisq (SciPy
correction = False, R chisq.test(correct = FALSE)). SPSS prints a
"Continuity Correction" row beside the Pearson row for 2 × 2 tables;
ChakataStat does not, and the exact alternative for a small 2 × 2 is
Fisher's test, below.
Fisher's exact test: the two-sided rule
Two-sided p is the sum of the probabilities of every table with the same
margins whose probability is no more likely than the observed table —
the rule R's fisher.test and SciPy use
(fisher_exact in descriptive.rs).
The alternative, doubling the smaller one-sided tail, can exceed 1 and is
not used. The test is not defined for fractional weighted counts and is
omitted then.
McNemar's test
For a matched 2 × 2 table with discordant counts b, c: the exact
binomial two-tailed p when b + c ≤ 25 (integer counts), the continuity-corrected
χ² (|b − c| − 1)²/(b + c) on one df otherwise — SPSS's rule
(mcnemar, same file). The uncorrected form is what Cochran's Q reduces
to for two treatments (recorded in
validation.md).
Cramér's V and φ
φ = √(χ²/N) and V = √(χ²/(N·(k − 1))) with k the smaller dimension — the classical, uncorrected forms. Bergsma's bias-corrected V is not printed.
Mantel–Haenszel and Cochran's tests
The stratified 2 × 2 tests print both: the Mantel–Haenszel statistic with its continuity correction and Cochran's without, as the table note says.
Column-proportion z tests (custom tables)
The pairwise column comparisons use the pooled two-proportion z with no continuity correction and no multiple-comparison adjustment; a cell with an empty column or zero pooled variance prints no test.
Comparing means
The independent-samples t test: both variances rows
Both rows are always printed — equal variances assumed (pooled s²,
df n₁ + n₂ − 2) and not assumed (Welch, with the Welch–Satterthwaite
df (a + b)² / (a²/(n₁−1) + b²/(n₂−1)), a = s₁²/n₁, b = s₂²/n₂) — with
Levene's test beside them to guide the choice. No automatic switch is made.
Pins ttest_ind/* and ttest_ind/welch_*.
Levene's test: mean-centred
The original Levene statistic — a one-way ANOVA on |x − group mean| —
not the Brown–Forsythe median-centred variant (levene_table in
compare_means.rs; pins
levene/W, levene/p against SciPy center = "mean" and
car::leveneTest(center = mean)). R's leveneTest and SciPy default to
the median form, so a reader reproducing the number there must set the
centre.
Cohen's d and Hedges' g
- Independent samples: pooled SD with n − 1 variances, s_p = √(((n₁−1)s₁² + (n₂−1)s₂²)/(n₁+n₂−2)); d = (x̄₁ − x̄₂)/s_p.
- Paired samples: d_z, the mean difference over the SD of the differences (not Cohen's d_av over the average SD).
- One sample: (x̄ − μ₀)/s.
- Hedges' g applies the small-sample factor J = 1 − 3/(4·df − 1) (the usual approximation to the exact gamma ratio).
- The confidence interval on d is from the noncentral t (not the large-sample normal approximation).
Pins ttest_ind/cohen_d, ttest_one/cohen_d (formulas on both oracles).
One-way ANOVA effect sizes
η² = SS_between / SS_total; ε² = (SS_between − df_between·MS_within) /
SS_total; ω² = (SS_between − df_between·MS_within) / (SS_total +
MS_within) — the Olejnik–Algina forms. Pin anova/eta_squared.
Post hoc tests
After a one-way ANOVA the pairwise table carries three adjusted
significances on the pooled within-groups MS: Bonferroni,
Scheffé and Tukey HSD (studentized range on df_within), with the
Bonferroni-adjusted confidence interval of each mean difference. After a
Kruskal–Wallis test, Dunn's pairwise z is adjusted by Bonferroni
(default), Holm or none (adjust). Games–Howell, Dunnett and LSD are
not offered.
Planned contrasts
Reported both ways — pooled (the ANOVA's within MS) and Welch–Satterthwaite — like the t test; the user's coefficients are not normalized.
Correlation
Spearman's ρ
The Pearson correlation of the mid-ranks (tied values share the mean of
the ranks they span); significance by the t approximation on n − 2 df,
t = ρ√((n−2)/(1−ρ²)) — SciPy's rule and SPSS's. R's cor.test uses the
exact / AS 89 distribution for n < 1290, which differs in the third digit at
n = 60 (the second oracle recomputes the t form on R's ρ; pin
correlate/spearman_age_bp_p).
Kendall's τ
τ-b, the tie-corrected form; significance by the normal approximation
with the no-tie variance 2(2n + 5)/(9n(n − 1)). Under a weight the
concordances are weighted by the product of the two cases' weights and the
effective N is the sum of the weights. τ-a and τ-c are not printed. Pin
correlate/kendall_age_bp.
Partial correlation
The first-order partial from the inverse of the correlation matrix over the
listwise sample, tested on n − 2 − k df, k the number of controls
(pin partial/age_bp_given_chol, pingouin and psych::partial.r).
No confidence interval on r
The classical correlation tables print r and its p only; no Fisher-z interval. The Bayesian correlation prints a credible interval (recorded in validation.md).
Regression and modelling
Confidence intervals on GLM coefficients: Wald, z
Logistic, probit, Poisson, negative-binomial and gamma coefficients carry
Wald intervals B ± z·SE, and Exp(B) the interval exp(B ± z·SE) —
statsmodels' and SPSS's convention. R's confint.glm defaults to the
profile-likelihood interval, which differs for small samples and for
coefficients far from zero; confint.default gives the Wald one. Linear
regression uses t on the residual df. Under a survey design every
model's inference is t on (design df) − p + 1, the summary.svyglm
convention (recorded in
clinical-and-survey.md).
Stepwise selection
Forward, backward and stepwise linear regression enter a predictor at
p ≤ .05 and remove at p ≥ .10 (p_enter, p_remove) — the SPSS
defaults, chosen as significance levels rather than F-to-enter values
(regression.rs). Stepwise discriminant analysis uses Wilks' Λ with
F = 3.84 to enter and remove.
Multinomial and ordinal regression: the reference and the sign
- Multinomial logistic fits one logit per non-reference category against
the lowest category (by value) as the baseline — R
nnet::multinomand statsmodelsMNLogit. SPSS defaults to the last category. - Ordinal regression is the proportional-odds cumulative logit
logit P(Y ≤ c) = θ_c − β·x: the thresholds θ_c carry the intercepts and
the slopes β enter with a minus sign, so a positive β means higher
categories — the parameterization of SPSS PLUM,
MASS::polrand statsmodelsOrderedModel(pinsordinal/*, both oracles). Some references (Stataologitreports the same sign; SASproc logisticmodels P(Y ≥ c) by default) flip the sign of β.
Robust regression: the M-estimator constants
Huber's ψ with c = 1.345 and Tukey's bisquare with c = 4.685 (each
~95 % efficient under normality); the scale is the MAD / 0.6745; the
covariance is the M-estimator sandwich — the statsmodels RLM conventions
(robust.rs). R's MASS::rlm
uses the same constants.
Mixed models: REML by default, ML on request
The linear mixed model fits by restricted maximum likelihood unless
reml: false; the fit block names which. REML corrects the downward bias
ML puts on the variance components (the degrees of freedom the fixed
effects absorb — the n − 1 idea), and it is what lme4::lmer, nlme, SAS
proc mixed, Stata mixed, SPSS MIXED and statsmodels MixedLM all
report by default; ML remains the option because a likelihood-ratio test
between models with different fixed effects is only valid under ML. Until
0.23.0 the default was ML — switched on 2026-08-28 when this inventory
surfaced it. The GLMM (random intercept) integrates the
random effect by fixed Gauss–Hermite quadrature
(glmm.rs); lme4::glmer defaults
to the Laplace approximation (one quadrature point), which differs for
binary outcomes with small clusters.
Nonlinear regression
Levenberg–Marquardt from the user's starting values; standard errors
from the Gauss–Newton covariance σ̂²(JᵀJ)⁻¹ at the solution
(nonlinear.rs). The pinned
SSE and R² are optimizer-invariant; the parameters on a flat objective are
not (the second oracle's nls/* agree to ~3 digits; recorded in
validation.md).
Regularized regression
Predictors are standardized (and the response centred) before the
penalty is applied, on each training fold separately; the λ path is 100
log-spaced values from λ_max down; 10-fold cross-validation reports
both the CV-minimum λ and the 1-SE λ, the glmnet conventions
(regularized.rs). The
coefficients are reported on the original scale.
Loglinear models
Fitted as a Poisson GLM on the cell counts (Newton/IRLS), not by
iterative proportional fitting; the two agree at convergence for
hierarchical models, and the Poisson route gives standard errors directly
(regression_ext.rs).
Nonparametric tests
Wilcoxon signed-rank
Zero differences are dropped before ranking (n counts the non-zero
pairs); the asymptotic z carries the tie correction and no
continuity correction — the SPSS form (pin wilcoxon/z; R
wilcox.test(correct = FALSE)). R's default applies the ½ continuity
correction.
The sign test and the runs test
Both use the normal approximation with the continuity correction
(SPSS); the runs test cuts at the median (values below vs at-or-above)
and offers an exact significance from the enumerated runs distribution.
Pins sign/z, runs/*.
The binomial test: which tail
Exact by default; two-tailed when the test proportion is ½, one-tailed
otherwise — SPSS's rule, kept because it is the one users of the
classical toolbox expect (binomial in
nonparametric.rs; pin
binomial/exact_p at ½). R's binom.test is two-sided at any proportion
(summing outcomes no more likely than the observed), so for p₀ ≠ ½ the two
differ by construction; method = "asymptotic" gives the
continuity-corrected normal.
Normality tests
Shapiro–Wilk by Royston's (1992) AS R94 algorithm, and the one-sample
Kolmogorov–Smirnov against the fitted normal with Lilliefors
significance — i.e. the SPSS "Tests of Normality" table. SciPy's
kstest(z, "norm") gives the same D (pin normality/ks_D) but its p
is the plain KS p, which is too conservative when μ and σ are estimated;
ChakataStat's p is the Lilliefors one.
Survival
Kaplan–Meier
The survival table prints S(t) with its Greenwood standard error and
no confidence band — a reader who wants one should know that R's survfit
draws a log-transformed band by default and SPSS none, so bands from
different tools are not comparable without naming the transform. The
median survival is the first time at which S(t) ≤ 0.5 (SPSS's rule;
R's survfit reports the same first crossing, interpolating only when the
curve sits exactly at 0.5). The log-rank test is
Mantel–Cox with the hypergeometric variance; Peto and Gehan–Wilcoxon
weightings are not offered.
Cox regression: tie handling and intervals
Tied event times use Efron's approximation by default, Breslow's on
ties: "breslow"; the fit block names the method. Efron is the more
accurate of the two whenever ties exist (and real event times nearly always
tie) and is what R's coxph, Stata and lifelines use; SPSS and SAS default
to Breslow, which is why it stays an option. Until 0.23.0 the default was
Breslow — the maintainer switched it on 2026-08-28 when this inventory
surfaced the mismatch with the validation pins, which fit Efron on both
sides (survival_cox/*). Hazard-ratio intervals are Wald, exp(B ± z·SE);
the baseline hazard is Breslow's. The proportional-hazards test's form is
recorded in
clinical-and-survey.md.
Time series
Autocorrelation
The ACF uses the divisor n at every lag (the biased, positive-definite
estimator; statsmodels adjusted = False, R acf), Bartlett's
cumulative standard error √((1 + 2Σ_{j<k} r_j²)/n), and the Box–Ljung
Q on k df at lag k (no model-order adjustment — the residual ACF after
an ARIMA fit subtracts p + q). The PACF is computed by Durbin–Levinson
from the ACF, not by successive OLS regressions; the two agree to ~5 digits
at n = 120 (pin pacf/lag1).
ARIMA
Conditional sum of squares by default (method: "css"), exact Gaussian
maximum likelihood by Kalman filter on "ml" (warm-started from CSS); a
constant is fitted by default for an undifferenced series. Recorded for the
AR(1) pin in tolerances.md.
Stationarity tests
ADF and KPSS both default their lag / bandwidth to Schwert's rule
⌊12·(n/100)^¼⌋; ADF's p is MacKinnon's (1994) polynomial
approximation as embedded in statsmodels — his 1996 response-surface
tables, which R's urca::punitroot uses, agree to 2–3 digits (pin adf/p,
inside 1e-3). KPSS uses the Bartlett kernel. R's tseries::adf.test
defaults to ⌊(n−1)^⅓⌋ lags, so a default-to-default comparison differs.
Exponential smoothing and decomposition
Simple exponential smoothing initializes the level at the first
observation and chooses α by minimizing the one-step SSE (statsmodels'
legacy-heuristic, R HoltWinters); the trended and seasonal forms are
unit-tested at fixed parameters, not pinned (their optima are flat —
validation.md). Seasonal decomposition is
the classical centred moving-average decomposition, additive or
multiplicative — not STL. Cross-correlations carry the ±2/√N bands.
Scale reduction and classification
Factor analysis: extraction, retention, rotation
Principal components by default, iterated principal-axis factoring
(started from the squared multiple correlations) and maximum likelihood
(Jöreskog's concentrated criterion) on request; components retained by
Kaiser's rule (eigenvalue > 1) unless a count is given; Varimax with
Kaiser normalization, or Promax (κ = 4 by convention) on request, no
rotation by default. SPSS defaults agree; R's psych::fa defaults to
minres extraction and oblimin rotation, factanal to ML — none of which
match a default-to-default comparison.
Clustering
k-means (Lloyd's) on squared Euclidean distance over the variables as
given (no automatic standardization — standardize first if the scales
differ); the initial centres are the deterministic maximin (farthest-
point) rule unless a seed asks for seeded random starts, so an unseeded
run is reproducible. Hierarchical clustering defaults to average linkage
(UPGMA); Ward's method works on squared Euclidean distance (the ward.D2
convention), single and complete on Euclidean; a case weight counts as that
many coincident objects. Two-step selects the cluster count by a
BIC-style criterion. Cluster labels and centres are not pinned across
packages (recorded in validation.md).
Discriminant analysis: prior probabilities
Classification functions use priors proportional to the group sizes
(ln(n_g/n) in the constant), not equal priors. SPSS defaults to equal
priors; R's lda to proportional.
Reliability
Both Cronbach's α on the raw covariances and the standardized α on
the correlation matrix are printed (pin reliability/alpha is the raw one).
The six ICC forms follow Shrout & Fleiss (recorded in
validation.md).
Charts
Box plot
Tukey's rule: fences at Q₁ − 1.5·IQR and Q₃ + 1.5·IQR, whiskers to the last observation inside the fences, points past them listed as outliers; the quartiles are the Hazen ones above.
Histogram
Equal-width bins over [min, max], 10 bins by default (bins); no
automatic Sturges / Freedman–Diaconis rule.
Q–Q plot
Blom plotting positions (r − ⅜)/(n + ¼) against the theoretical
quantiles, the reference line through the first and third quartile pairs
(R's qqline). SPSS uses Blom by default as well; R's qqnorm uses
ppoints, which is Blom for n > 10.
Power analysis
Two-tailed by default (tails: 1 for one-sided); every power is a tail
of the relevant noncentral distribution at the central critical value
(recorded in validation.md).
Degenerate inputs — the 0.23.0 conventions
Decided by the adversarial fixture battery (0.23.0 item 6): 84 rows of
degenerate data, degenerate designs, numeric extremes and case-semantics
edges, each driven through the tool layer and held to one of three
outcome classes — a number with a reference, a named error, or an
undefined cell whose reason is recorded. The battery's list lives beside
its fixtures in the repository (test/fixtures/battery/README.md); these
are the choices it forced, stated once.
Three outcome classes, and what is never one
A statistic that exists is printed and pinned; one that cannot be computed for the input is either refused with a named reason (when the whole procedure has no answer — a t test on one case, a regression with no residual degrees of freedom) or left blank (when one cell is undefined while its neighbours are not — skewness on two cases, a correlation with a constant). What is never acceptable: a crash, a hang, a silent drop of cases, or a plausible-looking number that is not one (a negative F, a fitted coefficient of 12,911 with no standard error).
Fewer than two cases, or a constant variable
A t test (one-sample, independent, paired) on fewer than two valid cases
or pairs, or on a variable with zero variance, is refused — R's
not enough 'x' observations and data are essentially constant. With
one case in one of two groups the pooled t still exists on
n₁ + n₂ − 2 df and is printed (R's var.equal = TRUE); the Welch row is
blank. A one-way ANOVA on a constant prints its sums of squares and a
blank F; on a one-level factor it is refused, as the t test and the
Kruskal–Wallis test already were. A correlation involving a constant is
blank; on two points it is ±1 with a blank significance. Normality tests
need n ≥ 3 and variance, and are blank otherwise. Cronbach's α with a
constant item is refused (its standardized form is undefined; the raw
form printed 0.12 beside a standardized 0.95 before this was decided).
Zero cases is an answer
A filter that selects no case, a split group with no valid case, or a variable that is entirely user-missing all report N = 0 rows, not an error: an empty selection is a legitimate description of the data. A weighted count that comes to nothing prints 0, never −0.
Weights
Case weights are frequency weights: a weight of 10¹² is 10¹²
replicates, and the inference is at that N. A case whose weight is zero
or negative is excluded — it contributes nothing, and a group left
empty by exclusion is reported as absent (the t test then refuses for
want of a second group). R refuses negative weights outright; this
engine follows the exclusion rule its Weight Cases documentation
already states. A survey-design weight is different: every case needs a
positive one, and a non-positive value is refused naming the case.
Regression: collinearity, degrees of freedom, separation
- Exact collinearity is refused with the verdict text the
predictors are collinear (singular matrix) — a constant predictor
falls under it (it is collinear with the intercept). R drops the
aliased column and prints
NA; Stata omits it with a note; this engine names the condition and leaves the choice of column to the analyst. - No residual degrees of freedom — weighted N at or below the parameter count — is refused, stating both numbers. R fits such a model and prints NaN standard errors; a weighted N below p printed a negative df and a negative F here before the battery.
- Complete or quasi-complete separation in logistic regression is
refused: the maximum-likelihood estimate does not exist (Albert &
Anderson 1984). The test is R's own — a fitted probability within ten
machine epsilons of 0 or 1 — but where
glm.fitwarns and prints the diverged fit, this engine stops and names Firth's penalised likelihood (Heinze & Schemper 2002) as the remedy. The propensity-score tool already refused separation; the two now agree. - A binary outcome with one observed level, or a count outcome that is all zero, is refused: the intercept's MLE is infinite.
- A negative survival time is a data error and is refused naming the case, in Kaplan–Meier as in Cox; it was dropped silently before.
Extremes
Statistics are scale-invariant: a variable at 10¹⁵ or 10⁻¹⁵ gives the same t, p and R² as at unit scale (the 0.22.0 precision review's precedent), and a slope of 10³⁰ is exact. At the limits of a double the order statistics (min, max, median) are exact and the moments that overflow (mean, SD, sum) are blank. A power analysis with an effect of 0 reports that no sample size reaches the target; an n below 2 is refused in every power tool.