UK-based online statistics and data analysis support for USA, UK, and international clients. No exams, no impersonation, no fabricated data.



Python rank, exact and permutation inference

Nonparametric Tests in Python: 12 Essential Methods, Code and Interpretation

Nonparametric tests in Python provide practical inference when the research question concerns ranks, medians, ordered outcomes, distributional differences, monotonic association, exact probabilities or resampling rather than a narrowly defined normal-theory mean model. This complete Python-only guide shows how to select the correct method, clean data, run SciPy procedures, calculate effect sizes, validate p-values and report a reproducible school-absence example.

Python onlySciPy workflowsRanks and mediansPermutation testsEffect sizes
Complete records649
Median: GP / MS2 / 2
Observed difference0 days
Permutation p1.000
Quick answer

The correct Python test depends on the design and the exact null hypothesis.

Nonparametric tests in Python are not one interchangeable family of “tests for non-normal data.” Mann–Whitney U compares two independent distributions; Wilcoxon signed-rank analyzes paired differences under a symmetry condition; Kruskal–Wallis handles several independent groups; Friedman handles repeated conditions; Spearman and Kendall address monotonic association; exact and permutation procedures let the statistic define the question directly.

In the verified Python example, school absences were compared between Gabriel Pereira (GP, n = 423) and Mousinho da Silveira (MS, n = 226) with a two-sided independent-label permutation test of the median difference. Both medians were 2 absences, so the observed MS-minus-GP median difference was 0. All 3,000 resampled statistics were at least as extreme as zero, producing p = 1.000. The analysis therefore found no evidence of a difference in population medians.

Important boundary: equal sample medians do not establish equal distributions. The GP group had IQR = 6 and the MS group IQR = 4, so a test targeting the entire distribution can answer a different question and reach a different conclusion.
1

What are nonparametric tests in Python?

Methods whose inferential target is expressed through ranks, signs, order, exact counts or resampled statistics rather than a single normal-theory template.

Nonparametric tests in Python are best understood by the information they use and the hypothesis they test. Some replace raw measurements with ranks, some count positive and negative differences, some compare empirical distribution functions, and some repeatedly reassign labels to construct a null distribution for a custom statistic.

What “nonparametric” does mean

A nonparametric procedure usually avoids specifying a complete finite-dimensional probability model such as “both populations are normal with unknown means and a common variance.” It may still assume independence, exchangeability, symmetry, continuity, comparable shape, random sampling or a particular ordering structure. The method is therefore less restrictive in one dimension, not assumption-free.

Nonparametric tests in Python often remain useful for ordinal responses, severe skew, heavy tails, outliers, ceiling effects, zero inflation and small samples. Their strength is alignment with an estimand: stochastic ordering, median difference, monotonic association, equality of distribution, equal scale or a user-defined resampling statistic.

What “nonparametric” does not mean

It does not mean every rank test tests medians. The Mann–Whitney U null is naturally expressed through distributions and pairwise ordering; the Wilcoxon signed-rank procedure concerns the distribution of paired differences and relies on symmetry for a location interpretation; the Kolmogorov–Smirnov statistic measures the largest gap between empirical cumulative distributions.

It also does not mean nonparametric methods are automatically more robust, more powerful or more honest. A poorly matched rank test can be less informative than a well-specified model. Selection must begin with design, dependence, outcome scale and the scientific question.

Core rule: never choose nonparametric tests in Python solely because a normality test has p < .05. Large samples make tiny deviations easy to detect, while many mean-based procedures remain reliable. Choose the inferential target first and use diagnostics to assess whether the chosen method represents the data.

For foundational context, review parametric vs nonparametric tests, null and alternative hypotheses and p-value interpretation.

2

How to choose nonparametric tests in Python

A design-first decision table prevents the common error of treating all rank procedures as substitutes.

Choosing nonparametric tests in Python begins with four questions: Are observations independent or paired? How many groups or conditions are present? Is the target location, distribution, scale, association or trend? Does the outcome support ordering?

Research design and targetPrimary Python methodSciPy functionWhat the result addressesCritical caution
Two independent groups; ordering/distributionMann–Whitney Ustats.mannwhitneyuWhether one distribution tends to produce larger observations than the other.A median-shift interpretation needs comparable shapes.
Two paired measurementsWilcoxon signed-rankstats.wilcoxonWhether paired differences are centered symmetrically around zero.Zeros, ties and asymmetry affect the null distribution.
Two paired measurements; signs onlySign/binomial teststats.binomtestWhether positive and negative nonzero changes are equally likely.Uses less magnitude information than signed ranks.
Three or more independent groupsKruskal–Wallisstats.kruskalWhether pooled rank distributions differ across groups.A significant omnibus test does not identify the groups that differ.
Three or more related conditionsFriedmanstats.friedmanchisquareWhether repeated-condition ranks differ.Rows must represent matched blocks or participants.
Ordered repeated alternativesPage trend teststats.page_trend_testWhether responses follow a prespecified ordered trend.The order must be specified before inspecting outcomes.
Two independent continuous distributionsTwo-sample KSstats.ks_2sampThe largest empirical-CDF difference.Classical interpretation assumes continuous distributions.
Independent group scale/dispersionFligner–Killeenstats.flignerWhether group dispersions differ robustly.Not a location test.
Monotonic associationSpearman / Kendallstats.spearmanr, stats.kendalltauStrength and direction of monotonic ordering.Nonlinearity is allowed, but the relation should be monotonic.
Custom statistic under label exchangeabilityPermutation teststats.permutation_testA null distribution for the exact statistic chosen.The permutation scheme must match the design.

Define the unit

Identify the participant, school, device, household or experimental unit that supplies independent information.

Map dependence

Decide whether rows are independent, paired, clustered or repeated. Dependence determines valid permutations and ranks.

Name the estimand

State whether the target is median, stochastic ordering, distribution, spread, correlation, trend or a custom contrast.

Inspect support

Check ties, zeros, discrete outcomes, missingness, sample sizes and whether exact inference is feasible.

Predefine inference

Choose one- or two-sided alternatives, alpha, resample count, random seed and multiplicity correction before results.

3

Assumptions for nonparametric tests in Python

Fewer distributional assumptions do not remove design, exchangeability and measurement requirements.

The assumptions of nonparametric tests in Python are method-specific. Independence is often the most important requirement, while ties, zeros, shape differences and the validity of a permutation scheme determine whether a familiar location interpretation is defensible.

Independent information

For an independent-samples procedure, one observation must not determine another. Repeated records from the same student, patient or machine cannot be treated as separate independent cases. Clustering by classroom, site or family also changes uncertainty.

Ordered measurement

Rank procedures need a meaningful order. Nominal categories without ordering require contingency-table or exact-count methods. Ordinal data can be ranked, but extensive ties reduce resolution and should be reported.

Exchangeability

A permutation test assumes the reassignment performed by the algorithm would be valid under the null. Independent-group labels may be permuted across pooled observations only when the units are exchangeable under that null.

Symmetry when needed

The Wilcoxon signed-rank test uses ranks of absolute paired differences. A clean median-shift interpretation requires a reasonably symmetric difference distribution. The sign test is safer when that assumption is doubtful.

Comparable shape

Mann–Whitney and Kruskal–Wallis can detect distributional differences caused by location, spread or shape. Calling them median tests requires additional shape assumptions. Always compare ECDFs, quantiles and group spreads.

Transparent ties and zeros

Discrete outcomes produce repeated values. Exact algorithms may be unavailable or may ignore tie adjustments. State the method selected by Python, and consider a permutation implementation when small samples contain ties.

Do not test assumptions with p-values alone. Histograms, ECDFs, paired-difference plots, group counts, missingness checks and knowledge of the sampling design are more informative than a mechanical “normality failed, therefore use ranks” rule.
4

Python setup, data cleaning and reproducibility

A reliable analysis records versions, validates variables and makes random procedures repeatable.

A production workflow for nonparametric tests in Python should fail loudly when data types, group labels or sample sizes are wrong. It should also store the random seed and software versions so that permutation and bootstrap results can be regenerated.

Python environment and validationimport numpy as np
import pandas as pd
import scipy
from scipy import stats

SEED = 20_250_711
ALPHA = 0.05

print({
"numpy": np.__version__,
"pandas": pd.__version__,
"scipy": scipy.__version__,
"seed": SEED,
"alpha": ALPHA,
})

df = pd.read_csv("student-por.csv", sep=";")
required = {"school", "absences"}
missing = required.difference(df.columns)
if missing:
raise ValueError(f"Missing required columns: {sorted(missing)}")

analysis = df.loc[:, ["school", "absences"]].dropna().copy()
analysis["school"] = analysis["school"].astype("category")
analysis["absences"] = pd.to_numeric(analysis["absences"], errors="raise")

if set(analysis["school"].astype(str)) != {"GP", "MS"}:
raise ValueError("Expected exactly the school codes GP and MS.")
if len(analysis) != 649:
raise ValueError(f"Expected 649 complete records, found {len(analysis)}.")

Why explicit validation matters

Silent coercion is a common source of incorrect nonparametric tests in Python. A numeric column imported as text can sort lexicographically, a misspelled group can create a third level, and accidental duplicate rows can make a p-value look more precise than the design permits. Assertions convert those failures into visible errors.

Keep the analysis dataset narrow. Select only the outcome, grouping variables and identifiers required for the planned procedure. Record every exclusion rule. For paired data, verify identifiers before pivoting and confirm that each unit supplies one observation per condition.

Random number control

A seed makes a Monte Carlo approximation repeatable; it does not make the result exact. The worked analysis uses seed 20,250,711 and 3,000 independent-label permutations. A different seed can change the last digits when the p-value is estimated from a finite set of random partitions.

For borderline findings, increase the resample count and report Monte Carlo uncertainty. When all distinct rearrangements are computationally feasible, an exact permutation distribution removes simulation error.

Recommended audit trail: save the dataset checksum, selected columns, exclusions, package versions, alternative hypothesis, method parameter, resample count, random seed, statistic definition and all effect-size formulas used by nonparametric tests in Python.
5

Worked example: a Python permutation test of median school absences

The example deliberately targets the median difference rather than treating a rank test as a generic substitute.

The worked nonparametric tests in Python example compares the median number of absences at two Portuguese secondary schools. The statistic is defined before resampling as median(MS) minus median(GP).

Research question and hypotheses

Question: Is the population median number of absences different between MS and GP students?

T = median(MS) − median(GP)

The statistic is directional by definition. A negative value would mean the MS sample median is lower; a positive value would mean it is higher.

H0: Δmedian = 0    versus    H1: Δmedian ≠ 0

The test uses a two-sided independent-label permutation distribution under exchangeability.

Variables and coding

RoleVariableDefinition
OutcomeabsencesNumber of school absences; integer count from 0 to 32 in the analyzed records.
GroupschoolGP = Gabriel Pereira; MS = Mousinho da Silveira.
StatisticMedian differenceMedian(MS) − Median(GP).
Permutation unitStudent recordSchool labels are reassigned across independent records while group sizes remain 226 and 423.
GP groupn = 423Median 2; IQR 6
MS groupn = 226Median 2; IQR 4
Observed statistic0.000MS median minus GP median
Permutation resultp = 1.0003,000 of 3,000 extreme
Python descriptives and observed statisticgp = analysis.loc[analysis["school"].eq("GP"), "absences"].to_numpy()
ms = analysis.loc[analysis["school"].eq("MS"), "absences"].to_numpy()

def summarize(x):
q1, q3 = np.quantile(x, [0.25, 0.75])
return {
"n": x.size,
"mean": float(np.mean(x)),
"sd": float(np.std(x, ddof=1)),
"median": float(np.median(x)),
"q1": float(q1),
"q3": float(q3),
"iqr": float(q3 - q1),
"minimum": float(np.min(x)),
"maximum": float(np.max(x)),
}

summary = {"GP": summarize(gp), "MS": summarize(ms)}
observed = float(np.median(ms) - np.median(gp))
print(summary)
print("observed MS - GP median difference:", observed)

SchoolnMeanSDMedianQ1Q3IQRRange
GP4234.21515.191920660–32
MS2262.61953.130720440–12
Why this example is instructive: the medians are identical, but the means, upper quartiles, IQRs and maxima differ. Nonparametric tests in Python can legitimately disagree when they target different features. A median-difference permutation test answers a narrower question than Mann–Whitney U or a two-sample distribution test.
6

Permutation formula, exact algorithm and verified result

Each shuffled partition keeps the observed sample sizes and recomputes the same median contrast.

Permutation-based nonparametric tests in Python are transparent when the statistic and the exchangeability operation are written explicitly. The p-value counts shuffled statistics at least as extreme as the observed statistic.

p̂ = [1 + Σ I(|Tb| ≥ |Tobs|)] / (B + 1)

The plus-one correction prevents a Monte Carlo p-value of zero and treats the observed arrangement consistently with the random rearrangements. Here B = 3,000 and Tobs = 0.

Python manual permutation implementationdef median_difference(ms_values, gp_values):
return float(np.median(ms_values) - np.median(gp_values))

B = 3_000
rng = np.random.default_rng(SEED)
pooled = np.concatenate([gp, ms])
n_gp = gp.size

null = np.empty(B, dtype=float)
for b in range(B):
shuffled = rng.permutation(pooled)
gp_b = shuffled[:n_gp]
ms_b = shuffled[n_gp:]
null[b] = median_difference(ms_b, gp_b)

extreme = int(np.count_nonzero(np.abs(null) >= abs(observed)))
p_value = (extreme + 1) / (B + 1)

print("observed:", observed)
print("extreme draws:", extreme)
print("two-sided p:", p_value)
print("unique null values:", dict(zip(*np.unique(null, return_counts=True))))

Primary inference

p = 1.000

Fail to reject H0

The observed median difference is exactly zero. Every resampled absolute difference is at least zero, so all 3,000 draws meet the two-sided extremeness rule.

Null-distribution audit

Difference = 0.02,975 draws
Difference = 0.55 draws
Difference = 1.08 draws
Difference = 1.55 draws
Difference = 2.07 draws
Interpretation limit: p = 1.000 does not prove the school distributions are identical and does not prove practical equivalence. It follows mechanically from an observed statistic of zero under this two-sided absolute-extremeness definition. Equivalence requires a prespecified smallest important median difference and an inference procedure designed for that goal.

A supplementary result that answers a different question

Using the same observations, Python’s asymptotic Mann–Whitney U test for MS versus GP gives U = 40,535 and p = 0.000983. The corresponding probability-of-superiority quantity is approximately 0.4240, or rank-biserial correlation −0.1520 with MS coded first. This is not a contradiction: the permutation analysis asks whether medians differ, whereas Mann–Whitney is sensitive to broader ordering differences caused here by the upper tail and spread.

This contrast is one of the most important lessons in nonparametric tests in Python: select a statistic because it represents the research question, not because two methods share the label “nonparametric.”

7

Python charts for the verified permutation workflow

The first chart spans the full width; the remaining four charts are presented as two balanced pairs.

Charts for nonparametric tests in Python should explain the statistic, group structure, null distribution and tail rule. Decorative plots that omit the estimand or resampling logic do not make the analysis auditable.

Python primary metrics for the permutation median-difference analysis

Python primary metrics

The chart stores the complete reproducibility record: observed MS-minus-GP median difference = 0, two-sided permutation p = 1.000, 3,000 random partitions and seed 20,250,711. The seed is numerically much larger than the statistical metrics, so the chart is best read as a verification panel rather than a magnitude comparison.

Python school absence sample size median and IQR summary

School absence summary

GP contributes 423 records and MS contributes 226. Both medians equal 2, while GP’s IQR is 6 compared with 4 for MS. The equal centers explain the observed median contrast; the unequal spreads warn against generalizing that result to complete distributional equality.

Python permutation null distribution of the school median difference

Permutation null distribution

Of the 3,000 shuffled partitions, 2,975 produce a median difference of zero. Only 25 partitions produce positive half-step or whole-step differences from 0.5 through 2.0. The discreteness reflects integer absences and repeated values, not a smooth normal reference curve.

Python tail diagnostics for the two-sided permutation test

Two-sided tail diagnostics

The observed statistic and its absolute value are both zero. Because every absolute permuted statistic is greater than or equal to zero, the extreme-draw count is exactly 3,000. The plot connects the p-value to the programmed extremeness rule.

Verified Python result summary for nonparametric permutation analysis

Verified Python result summary

The final audit panel repeats the median difference, p-value, resample count and seed. Agreement between the calculation table, saved report and chart confirms that the published result comes from one reproducible Python workflow.

8

Independent-sample nonparametric tests in Python

Mann–Whitney, Brunner–Munzel and custom permutation tests address related but nonidentical questions.

For two independent groups, nonparametric tests in Python should be selected according to whether the target is stochastic ordering, a probability-of-superiority effect, a median contrast or a broader distributional difference.

Python Mann–Whitney U with effect sizesfrom scipy import stats

result = stats.mannwhitneyu(
ms,
gp,
alternative="two-sided",
method="asymptotic",
use_continuity=True,
)

u_ms = float(result.statistic)
u_gp = ms.size * gp.size - u_ms
probability_superiority = u_ms / (ms.size * gp.size)
rank_biserial = 2 * probability_superiority - 1

print({
"U_MS": u_ms,
"U_GP": u_gp,
"p": float(result.pvalue),
"P_MS_greater_plus_half_ties": probability_superiority,
"rank_biserial_MS_vs_GP": rank_biserial,
})

Mann–Whitney U

Use mannwhitneyu for two independent samples when the substantive question concerns their ordering or distributions. Python returns the U statistic associated with the first sample. Compute the complementary U to reconcile software conventions.

When distributions have similar shape, the result is often described as a location comparison. When shapes differ, interpretation should remain distributional. Ties favor the asymptotic or permutation method over an unadjusted exact calculation.

Brunner–Munzel

stats.brunnermunzel estimates whether a randomly selected observation from one population tends to be larger than one from the other without requiring equal distributional shapes. It is useful when heteroscedasticity makes a simple shift interpretation doubtful.

Report the statistic, degrees-of-freedom reference choice, p-value and a probability-based effect estimate. As with all nonparametric tests in Python, inspect small-sample behavior and ties.

Custom permutation contrast

Use stats.permutation_test when the scientific estimand is a trimmed mean, quantile difference, robust slope or another statistic not represented by a standard rank procedure. The validity comes from the permutation scheme and exchangeability, not from the function name.

Always make the statistic’s sign, alternative and grouping order explicit in the report.

Do not call Mann–Whitney a nonparametric t test without qualification. A t test targets a difference in population means. Mann–Whitney U is based on cross-sample ordering and can react to location, scale or shape differences.

See the full guides to the Mann–Whitney U test, Brunner–Munzel test and Wilcoxon rank-sum test.

9

Paired nonparametric tests in Python

Pairing must be represented in the data and preserved by the inference procedure.

Paired nonparametric tests in Python analyze within-unit changes. The correct input is a vector of aligned differences, not two unrelated columns that happen to have the same length.

Python Wilcoxon signed-rank and sign test# x and y must be aligned measurements from the same units.
d = np.round(x - y, decimals=10)

wilcoxon_result = stats.wilcoxon(
d,
zero_method="wilcox",
correction=False,
alternative="two-sided",
method="auto",
)

nonzero = d[d != 0]
positive = int(np.count_nonzero(nonzero > 0))
sign_result = stats.binomtest(
positive,
n=nonzero.size,
p=0.5,
alternative="two-sided",
)

print(wilcoxon_result)
print(sign_result)

Wilcoxon signed-rank

The signed-rank procedure orders absolute nonzero differences and then restores signs. It uses both direction and magnitude ranks. The null is commonly described as symmetry of the paired-difference distribution around zero. A median interpretation is strongest when that distribution is reasonably symmetric.

Python offers three zero conventions: discard zero differences, include their ranks but remove their contribution, or split zero ranks between positive and negative sums. State the chosen convention. Compute and round differences before calling the function when floating-point subtraction would create false non-ties.

Sign test through binomial inference

The sign test ignores magnitudes and asks whether positive and negative nonzero differences are equally probable. It needs fewer shape assumptions than signed ranks but often has lower power. It is a good sensitivity analysis when difference symmetry is implausible.

Exclude exact zeros from the ordinary sign count unless a different convention was prespecified. Report positive, negative and zero counts so readers can understand how much information the test used.

Pairing error: sorting each condition independently destroys the original matches. Nonparametric tests in Python for paired data require the same participant or unit to remain aligned across conditions.

Continue with the Wilcoxon signed-rank test and sign test guides.

10

Several independent groups: Kruskal–Wallis and follow-ups

An omnibus rank test must be followed by multiplicity-controlled comparisons when the global null is rejected.

For three or more independent groups, nonparametric tests in Python often begin with Kruskal–Wallis. It pools observations, assigns average ranks to ties and compares group rank sums.

Python Kruskal–Wallis and pairwise Mann–Whitneygroups = {
name: values.dropna().to_numpy()
for name, values in df.groupby("Mjob", observed=True)["absences"]
}

kw = stats.kruskal(*groups.values(), nan_policy="omit")
print("Kruskal-Wallis:", kw)

from itertools import combinations
raw = []
for a, b in combinations(groups, 2):
res = stats.mannwhitneyu(
groups[a], groups[b],
alternative="two-sided",
method="asymptotic",
)
raw.append({"comparison": f"{a} vs {b}", "p_raw": res.pvalue})

# Holm adjustment without another dependency.
order = np.argsort([row["p_raw"] for row in raw])
adjusted = np.empty(len(raw))
running = 0.0
for rank, idx in enumerate(order):
candidate = (len(raw) - rank) * raw[idx]["p_raw"]
running = max(running, candidate)
adjusted[idx] = min(running, 1.0)

for row, p_adj in zip(raw, adjusted):
row["p_holm"] = float(p_adj)
print(raw)

Interpreting the omnibus test

A significant H statistic indicates that at least one group’s rank distribution differs. It does not prove that every pair differs, and it does not identify which group is responsible. Report group sample sizes, medians, IQRs and mean ranks alongside H, degrees of freedom and p.

If group shapes are comparable, a location interpretation may be reasonable. If spreads or shapes differ, use the broader language of rank-distribution differences.

Post-hoc control

Pairwise tests inflate the chance of at least one false positive. Holm adjustment is a strong default because it controls familywise error and is uniformly no worse than the simple Bonferroni rule. Benjamini–Hochberg controls the false discovery rate and may be suitable for exploratory families.

Define the comparison family before looking at results. Report raw and adjusted p-values rather than hiding the correction.

Effect size: an omnibus rank effect can be summarized with epsilon-squared, but finite-sample formulas vary. State the exact formula. Pairwise probability superiority or rank-biserial correlations are easier to interpret when reported with confidence intervals.

See Kruskal–Wallis, Dunn’s test, Conover test and the Holm–Bonferroni method.

11

Repeated-condition nonparametric tests in Python

Friedman tests unordered condition differences; Page’s test uses a prespecified directional order.

Repeated-measures nonparametric tests in Python rank conditions within each participant or block, thereby preserving the dependence that independent-group methods would ignore.

Python Friedman and Page trend tests# Each array contains the same participants in the same order.
friedman = stats.friedmanchisquare(condition_1, condition_2, condition_3)
print(friedman)

# Rows are blocks/participants; columns are ordered treatments.
matrix = np.column_stack([condition_1, condition_2, condition_3])
page = stats.page_trend_test(
matrix,
ranked=False,
predicted_ranks=[1, 2, 3],
method="auto",
)
print(page)

Friedman test

Use Friedman when every block is observed under three or more conditions and the alternative is any systematic condition difference. The test ranks conditions within blocks, so between-person level differences do not dominate the statistic.

Page trend test

Use Page’s L when the alternative specifies an ordered trend, such as dose 1 ≤ dose 2 ≤ dose 3. It can be more focused than Friedman when the order is scientifically justified before analysis.

Post-hoc paired tests

After a significant omnibus result, use prespecified paired contrasts with Wilcoxon or sign tests and adjust the resulting p-values. Preserve row alignment and report missing-block rules.

Incomplete blocks: listwise deletion can waste data and alter the target population. When missing repeated measurements are substantial or informative, a model that represents the dependence and missingness may be preferable to rank-based complete-case analysis.

Read the Friedman test and Page’s trend test guides.

12

Rank correlation and monotonic association in Python

Spearman and Kendall quantify ordering, not arbitrary nonlinear dependence.

Correlation-focused nonparametric tests in Python are appropriate when the research question concerns monotonic association between ordered variables. They do not require a straight-line relationship, but a strongly curved nonmonotonic pattern can yield a small coefficient despite clear dependence.

Python Spearman and Kendall associationspearman = stats.spearmanr(
df["studytime"],
df["G3"],
alternative="two-sided",
nan_policy="omit",
)

kendall = stats.kendalltau(
df["studytime"],
df["G3"],
alternative="two-sided",
method="auto",
variant="b",
nan_policy="omit",
)

print("Spearman:", spearman)
print("Kendall tau-b:", kendall)

Spearman rank correlation

Spearman’s rho is Pearson correlation applied to ranks. It measures the strength and direction of a monotonic relation and is sensitive to how consistently higher values of one variable accompany higher values of the other. Ties receive average ranks.

Report rho, sample size, p-value and a scatterplot or jittered ordinal plot. A small p-value does not make a weak association practically important.

Kendall tau-b

Kendall’s tau compares concordant and discordant pairs. Tau-b adjusts for ties and has a direct pair-order interpretation. It can be preferable for small samples, ordinal scales and heavy ties.

Exact inference may be available without ties; asymptotic or permutation inference is needed when ties complicate the null distribution. State the method selected.

Correlation is not agreement. Two methods can be strongly monotonically associated while differing systematically in level. Use an agreement design when interchangeability is the question.

Continue with Spearman rank correlation, Kendall’s tau-b and correlation vs regression.

13

Distribution, median and scale tests in Python

Choose among KS, Mood’s median, Fligner–Killeen, Ansari–Bradley and Mood scale tests according to the target.

Distribution and scale nonparametric tests in Python are frequently confused. A complete-distribution test, a median test and a dispersion test do not share the same null hypothesis.

Python procedureTargetUseful whenInterpretation warning
stats.ks_2sampDifference between two continuous distributionsLocation, spread or shape may differDiscrete ties affect classical calibration and meaning.
stats.median_testEquality of group medians through a contingency tableA direct but coarse median-focused omnibus questionHandling observations equal to the grand median matters.
stats.flignerEquality of dispersionRobust multi-group scale comparisonDoes not test location.
stats.ansariTwo-sample scale under common locationSymmetric center-outward rank scoresLocation differences contaminate a pure scale interpretation.
stats.moodTwo-sample scale differenceIndependent samples and scale-focused inferenceNot the same procedure as Mood’s median test.
Python distribution and scale functionsks = stats.ks_2samp(x, y, alternative="two-sided", method="auto")
median = stats.median_test(x, y, ties="below", correction=True)
fligner = stats.fligner(x, y, center="median")
ansari = stats.ansari(x, y, alternative="two-sided")
mood_scale = stats.mood(x, y, axis=None)

print(ks)
print(median)
print(fligner)
print(ansari)
print(mood_scale)

Plot before labeling: if ECDFs cross repeatedly, a one-number location story is inadequate. If medians agree while tails differ, report that pattern directly. Nonparametric tests in Python are most useful when the output language matches the feature the statistic actually measures.

Related guides include the two-sample Kolmogorov–Smirnov test, median test, Fligner–Killeen test and Ansari–Bradley test.

14

Exact and categorical nonparametric tests in Python

Counts and binary outcomes require probability models for frequencies, not rank ordering of category labels.

Categorical nonparametric tests in Python include exact binomial inference, Fisher’s exact test and contingency-table procedures. These methods should not be replaced with numeric ranks assigned to unordered categories.

Python binomial, Fisher and chi-square tests# One binary proportion.
binomial = stats.binomtest(k=successes, n=trials, p=0.5, alternative="two-sided")
print(binomial.pvalue, binomial.proportion_ci(confidence_level=0.95))

# A 2 x 2 table.
table_2x2 = np.array([[a, b], [c, d]])
fisher = stats.fisher_exact(table_2x2, alternative="two-sided")
print(fisher)

# A general contingency table.
observed = np.array([[12, 8, 5], [7, 14, 9]])
chi = stats.chi2_contingency(observed, correction=False)
print(chi.statistic, chi.pvalue, chi.dof, chi.expected_freq)

Exact binomial test

Use when each trial is success/failure and the null specifies a success probability. Report successes, trials, null probability, alternative, exact p-value and a confidence interval for the observed proportion.

Fisher exact test

Use a 2×2 table when expected counts are small or exact conditional inference is desired. Report the table, odds ratio convention, alternative and p-value. Direction depends on row and column ordering.

Chi-square contingency test

Use for larger tables when expected-frequency conditions are acceptable. Inspect expected counts and report an association effect such as Cramér’s V. Exact or resampling methods may be preferable for sparse tables.

Do not rank nominal codes. Coding “red = 1, blue = 2, green = 3” does not create a meaningful order. Use count-based nonparametric tests in Python for nominal categories.

See binomial test, Fisher’s exact test and chi-square test of independence.

15

Permutation tests, Monte Carlo error and bootstrap intervals

Resampling is a framework for matching inference to a statistic, not a license to shuffle data arbitrarily.

Resampling-based nonparametric tests in Python can test medians, trimmed means, robust correlations and model diagnostics, but the resampling operation must reproduce the null hypothesis and dependence structure.

Python SciPy permutation_test and bootstrapdef statistic(x, y, axis=0):
return np.median(x, axis=axis) - np.median(y, axis=axis)

perm = stats.permutation_test(
(ms, gp),
statistic,
permutation_type="independent",
vectorized=True,
n_resamples=3_000,
alternative="two-sided",
rng=np.random.default_rng(SEED),
)

boot = stats.bootstrap(
(ms, gp),
statistic,
paired=False,
vectorized=True,
n_resamples=20_000,
confidence_level=0.95,
method="BCa",
rng=np.random.default_rng(SEED),
)

print(perm.statistic, perm.pvalue)
print(boot.confidence_interval)

Three permutation structures

Independent labels: pool and repartition independent observations. Samples within pairs: swap condition labels while keeping pairs intact. Pairings: permute association pairings to test independence. Choosing the wrong structure breaks the null model.

With multiple groups, preserve every observed group size. With stratification or blocks, permute only within valid exchangeability sets.

Monte Carlo precision

A finite randomization sample estimates the tail probability. Near a decision threshold, rerun with many more resamples and report the resample count. The minimum attainable plus-one p-value is 1/(B+1); with B = 3,000 it is approximately 0.000333.

A bootstrap confidence interval describes sampling uncertainty for an effect estimate; it is not automatically equivalent to a permutation test of a sharp null.

Reproducibility practice: create separate random generators for distinct analyses rather than relying on hidden global state. Save the seed and the exact statistic function with every set of nonparametric tests in Python.
16

Effect sizes, multiple testing and reporting nonparametric results

A p-value is incomplete without the estimand, direction, magnitude, uncertainty and method details.

Reporting nonparametric tests in Python requires more than copying a result object. Readers need the exact null hypothesis, group order, statistic convention, p-value method, ties, effect size and descriptive context.

MethodUseful effect sizeInterpretationReport with
Mann–Whitney UProbability superiority; rank-biserial correlationCross-sample ordering advantageU for named first sample, complementary U, ties and method
Wilcoxon signed-rankMatched-pairs rank-biserial; standardized z/√nDirection and magnitude-rank imbalancePositive, negative and zero differences; zero convention
Kruskal–WallisEpsilon-squaredOmnibus rank separationFormula, H, df, p and adjusted follow-ups
FriedmanKendall’s WAgreement/separation of within-block ranksBlocks, conditions, χ², df and post-hoc family
Spearman / Kendallρ or τ with confidence intervalMonotonic association strengthn, ties, alternative and visual pattern
Permutation contrastObserved statistic in original unitsDirect scientific magnitudePermutation type, B, seed, tail rule and interval

APA-style worked example

A two-sided independent-label permutation test with 3,000 resamples was used to compare the median number of absences between MS (n = 226, Mdn = 2, IQR = 4) and GP (n = 423, Mdn = 2, IQR = 6). The observed median difference, MS minus GP, was 0 days. Using seed 20,250,711 and the plus-one two-sided tail rule, all 3,000 resampled statistics were at least as extreme as the observed value, p = 1.000. The analysis did not detect a difference in population medians.

What not to write

Do not write “there is no difference between the schools.” The test addressed only the median contrast. Do not write “the null hypothesis is true.” Failure to reject is not proof. Do not label p = 1 as evidence of equivalence. Do not omit the fact that IQRs and upper tails differ.

Name the exact Python function and package version.
State independent, paired, blocked or pairing permutations.
Identify the first sample and statistic direction.
Report ties, zeros, missing-data handling and continuity correction.
Give sample sizes and robust descriptive summaries.
Report an effect size in interpretable units or probabilities.
State raw and adjusted p-values for multiple comparisons.
Archive the seed, resample count and analysis code.

For interpretation support, see effect size, interquartile range, box plot interpretation and multiple-comparison correction.

Python verification checklist before publishing a result

Nonparametric tests in Python should be treated as complete analytical workflows rather than isolated function calls. A polished report begins by confirming that the data structure matches the design and ends by reproducing every displayed number from saved code. The checks below prevent many of the errors that survive ordinary syntax testing.

1. Confirm the observational unit

Before running nonparametric tests in Python, identify the unit that contributes independent information. A dataset may contain several rows per student, patient, customer or machine. Treating repeated rows as independent increases the apparent sample size and produces standard errors and p-values that are too optimistic. When repeated observations are intentional, select a paired, blocked or clustered design rather than an independent-samples rank test.

2. Check group labels and order

The direction of many nonparametric tests in Python depends on which array is passed first. Mann–Whitney U, median differences, probability superiority and rank-biserial correlation all change interpretation when sample order changes. Print the group names, sample sizes and first few values immediately before the test. In a public report, name the first sample instead of writing only “U = 40,535.”

3. Separate estimand from procedure

Good nonparametric tests in Python start with the quantity of interest. “Difference in medians,” “probability that an MS observation exceeds a GP observation,” “largest ECDF gap” and “difference in dispersion” are different estimands. A function should be chosen after that statement is written. This order prevents a convenient function from silently redefining the research question.

4. Audit missing data

Missing values can change the sample and the population represented by nonparametric tests in Python. Record how many rows are excluded from each variable and group. For paired data, report complete pairs rather than separate nonmissing counts. If missingness depends on the outcome or condition, complete-case rank analysis may be biased even though the test itself is distribution-free under its stated null.

5. Count ties and zeros

Discrete outcomes often contain many repeated values. Tie frequency changes rank variance, exact-test availability and the resolution of nonparametric tests in Python. In paired analysis, zero differences can be discarded, ranked conservatively or split, depending on the selected convention. Report the convention and the number of observations affected instead of relying on an undocumented default.

6. Verify exact versus asymptotic inference

The word “exact” has a precise computational meaning in nonparametric tests in Python. An exact distribution may assume no ties, or it may condition on margins or enumerate distinct partitions. An asymptotic result uses a limiting reference distribution and may apply tie or continuity corrections. Read the returned method when available and state it in the final report.

7. Validate a permutation scheme

Permutation-based nonparametric tests in Python are valid only when the shuffle represents the null hypothesis. Independent observations can be repartitioned across groups; paired conditions can be swapped within pairs; association tests can permute pairings. Shuffling every cell independently destroys structure and generates an irrelevant null distribution. Describe the exact reassignment in plain language.

8. Inspect the null distribution

Saving the resampled statistics makes nonparametric tests in Python auditable. Plot the null distribution, mark the observed statistic and count extreme values directly. In the school example, the null is highly discrete: 2,975 of 3,000 draws equal zero. A smooth bell curve would hide that feature and could encourage unjustified precision.

9. Recalculate the tail count

For Monte Carlo nonparametric tests in Python, independently recompute the p-value from the saved null array. Confirm whether the rule uses greater-than, greater-than-or-equal, absolute values or a doubled one-sided tail. The choice matters when the null distribution is discrete and many statistics equal the observed value. State the plus-one correction when it is used.

10. Report original-unit effects

Readers understand nonparametric tests in Python better when the effect remains connected to the measurement scale. A median difference of zero days, a Hodges–Lehmann shift of two grade points or a probability superiority of 0.68 communicates more than a standardized z value alone. Pair effect estimates with confidence intervals or resampling intervals whenever the method supports them.

11. Control the comparison family

Running many nonparametric tests in Python without adjustment creates a high chance of at least one small p-value. Define which comparisons form one family, then apply Holm, Bonferroni or false-discovery-rate control according to the inferential goal. The omnibus p-value does not protect an unlimited collection of exploratory pairwise tests performed afterward.

12. Reproduce the published table

The final quality check for nonparametric tests in Python is a clean rerun from raw data to report. Delete temporary objects, restart the environment and execute the saved script. Compare sample sizes, statistics, p-values, effect sizes, chart labels and download content. A publication should not depend on values manually copied from an earlier exploratory session.

Interpretation discipline: nonparametric tests in Python do not create a universal conclusion called “a significant difference.” The conclusion must name the tested feature. The school example supports “no detected median difference,” while a distribution-sensitive procedure can still detect ordering or tail differences. Both statements can be correct because the null hypotheses differ.
Minimum reproducibility record: every set of nonparametric tests in Python should preserve the data snapshot, variable definitions, exclusions, array order, function call, package version, alternative, p-value method, tie and zero rules, continuity correction, effect-size formula, multiplicity adjustment, resample count and seed.
Final Python review: Nonparametric tests in Python should name the estimand before the function. Nonparametric tests in Python should preserve pairing and blocking. Nonparametric tests in Python should disclose ties and zeros. Nonparametric tests in Python should identify the first sample. Nonparametric tests in Python should distinguish exact, asymptotic and permutation p-values. Nonparametric tests in Python should include an effect size. Nonparametric tests in Python should control multiple comparisons. Nonparametric tests in Python should be reproducible from raw data with saved versions, code and seed.
17

Python report download

Open the Python-only analysis report containing the verified statistics and five charts.

The downloadable report reproduces the worked nonparametric tests in Python workflow, including group summaries, permutation statistics, null-distribution counts, tail diagnostics, seed and final interpretation.

Python-only scope: this article and download intentionally contain Python analysis only. Every chart, code block, result explanation and reproducibility step belongs to the same Python workflow.
18

Nonparametric tests in Python FAQs

Answers to practical questions about SciPy methods, assumptions, p-values and interpretation.

These FAQs clarify the most frequent errors in nonparametric tests in Python, including “nonparametric t test” terminology, ties, exact methods, permutation counts, effect sizes and the meaning of a p-value equal to one.

What are nonparametric tests in Python?
Nonparametric tests in Python are procedures implemented through ranks, signs, empirical distributions, exact counts or resampling rather than a single fully specified normal-theory model. Examples include Mann–Whitney U, Wilcoxon signed-rank, Kruskal–Wallis, Friedman, Spearman, Kendall and permutation tests.
Which Python library contains the main nonparametric tests?
SciPy’s scipy.stats module contains the principal hypothesis-test functions used in this guide. NumPy supports array operations, and pandas supports structured data preparation. Record package versions because function defaults and available methods can change.
Is Mann–Whitney U the nonparametric independent t test?
It is commonly presented as an alternative for two independent groups, but it does not directly test equality of means. It tests distributional ordering; a pure location or median interpretation requires additional assumptions about distributional shape.
What is the nonparametric paired t test in Python?
The usual rank-based paired alternative is the Wilcoxon signed-rank test. When paired differences are strongly asymmetric or only direction is reliable, the sign test implemented with binomtest may be more defensible.
How do I run a nonparametric permutation test in Python?
Define the statistic, select the correct permutation type, pass the samples to scipy.stats.permutation_test, choose the alternative and resample count, and supply a reproducible random generator. The exchangeability operation must match the sampling design.
Why is the worked permutation p-value exactly 1?
The observed median difference is zero. Under the two-sided rule based on absolute values, every shuffled statistic has an absolute value greater than or equal to zero. Therefore all 3,000 random partitions are counted as at least as extreme.
Does p = 1 prove that the two school distributions are equal?
No. The result concerns only the median difference statistic. The groups have equal sample medians but different IQRs, upper quartiles, means and maxima. A distribution-focused test addresses a broader null hypothesis.
How many permutations should I use?
Use enough resamples to make Monte Carlo uncertainty small relative to the decision and reporting precision. Thousands may be sufficient for a clearly nonborderline result; tens or hundreds of thousands may be needed for stable tail estimates. Use all distinct arrangements when exact enumeration is feasible.
How should ties be handled in nonparametric tests in Python?
Use functions and methods that account for ties, report the prevalence of repeated values, and avoid an unadjusted exact method when its assumptions exclude ties. For small tied samples, a design-correct permutation method can be preferable.
Should I perform a normality test before choosing a nonparametric method?
Not as an automatic gatekeeper. Test choice should follow the estimand and design. Use plots and diagnostics to assess model fit, outliers, skew, ties and dependence, then choose a procedure whose assumptions and target are scientifically appropriate.
What effect size should accompany Mann–Whitney U?
Probability superiority and rank-biserial correlation are natural choices. State the sample ordering because the sign depends on which group is first. A confidence interval is preferable to a point estimate alone.
What effect size should accompany Kruskal–Wallis?
Epsilon-squared is common for the omnibus result, but formulas differ. State the exact formula and complement it with pairwise probability-based effects after a significant omnibus test.
Can nonparametric tests use ordinal data?
Yes, rank and sign procedures can be suitable when the category order is meaningful. Extensive ties limit resolution, and distances between categories are not assumed equal. Nominal categories without order require count-based procedures.
When should I use Spearman instead of Kendall?
Spearman emphasizes correlation between ranks and is familiar for monotonic association. Kendall tau has a concordant-pair interpretation and often behaves well with small samples and ordinal ties. Report the method chosen and why.
How do I correct pairwise nonparametric p-values?
Define the comparison family, then apply a method such as Holm for familywise error control or Benjamini–Hochberg for false discovery rate control. Report raw and adjusted p-values.
Can I use a bootstrap instead of a permutation test?
They answer different inferential tasks. A permutation test creates a null distribution under a specified exchangeability hypothesis. A bootstrap estimates the sampling distribution of an effect and is often used for confidence intervals.
What should a reproducible Python report contain?
Include data definitions, inclusion rules, package versions, test function, statistic direction, alternative, method, tie and zero handling, sample sizes, effect size, resample count, random seed, charts and interpretation in the original measurement units.
Are nonparametric tests always less powerful?
No. Power depends on the actual distribution, alternative and test statistic. Rank procedures can outperform normal-theory methods under heavy tails or outliers, but they can lose power when a mean-based model is correct and the mean is the target.
+

Related Python and nonparametric guides

Continue with test-specific formulas, assumptions and interpretation.

Statistical note: The worked values were verified from aligned 649-row source records. The Python permutation result is specific to the median-difference estimand and should not be generalized to complete distributional equality.
↑ Back to the top