Wilcoxon Signed-Rank Test in Python
Technique overview
Compare paired measurements with a nonparametric test when the differences are not normally distributed.
The Wilcoxon signed-rank test compares paired measurements when the distribution of within-pair differences is not safely normal. It is appropriate for pre-post experiments, matched samples, left-right measurements, and repeated observations where each subject acts as their own control. A clear figure should show the pairing, not just two independent distributions, because the statistical evidence comes from the direction and size of each paired change.
Key points
- Compare paired measurements with a nonparametric test when the differences are not normally distributed.
- The Wilcoxon signed-rank test compares paired measurements when the distribution of within-pair differences is not safely normal.
- It is appropriate for pre-post experiments, matched samples, left-right measurements, and repeated observations where each subject acts as their own control.
- A clear figure should show the pairing, not just two independent distributions, because the statistical evidence comes from the direction and size of each paired change.
Example Visualization
Review the example first, then use the live editor below to run and customize the full workflow.
Mathematical Foundation
The Wilcoxon signed-rank test compares paired measurements when the distribution of within-pair differences is not safely normal.
Equation
W = sum of signed ranks for non-zero paired differencesParameter breakdown
When to use this technique
Use Wilcoxon signed-rank for paired, ordinal, or skewed continuous data. Use a paired t-test instead when paired differences are approximately normal.
Apply This Technique Now
Run this analysis workflow with AI in seconds. Use the prepared technique prompt or bring your own dataset.
View example prompt
"Run a Wilcoxon signed-rank test on my paired data, plot the before-after distributions, and annotate the test statistic and p-value on the figure"
How to apply this technique in 30 seconds
Generate
Run the example prompt and let AI generate this technique automatically.
Refine and Export
Adjust code or prompt, then export publication-ready figures.
Implementation Code
The core data processing logic. Copy this block and replace the sample data with your measurements.
import numpy as np
from scipy import stats
np.random.seed(7)
before = np.random.lognormal(mean=1.4, sigma=0.35, size=24)
after = before * np.random.normal(loc=0.82, scale=0.16, size=24)
stat, p_value = stats.wilcoxon(before, after, zero_method="wilcox", alternative="two-sided")
diff = after - before
effect_r = stats.norm.isf(p_value / 2) / np.sqrt(np.count_nonzero(diff))
print(f"Wilcoxon W: {stat:.3f}")
print(f"p-value : {p_value:.4f}")
print(f"median difference: {np.median(diff):.3f}")
print(f"effect size r: {effect_r:.3f}")Visualization Code
Complete matplotlib code for a publication-ready figure. Copy, paste into your notebook, and adjust labels to match your data.
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
np.random.seed(7)
before = np.random.lognormal(mean=1.4, sigma=0.35, size=24)
after = before * np.random.normal(loc=0.82, scale=0.16, size=24)
stat, p_value = stats.wilcoxon(before, after)
diff = after - before
fig, ax = plt.subplots(figsize=(6, 5))
for i in range(len(before)):
color = "#9240ff" if after[i] < before[i] else "#888888"
ax.plot([0, 1], [before[i], after[i]], color=color, alpha=0.45, lw=1.2)
ax.scatter(np.zeros_like(before), before, color="#111111", s=28, label="Before")
ax.scatter(np.ones_like(after), after, color="#9240ff", s=28, label="After")
ax.set_xticks([0, 1])
ax.set_xticklabels(["Before", "After"])
ax.set_ylabel("Measurement")
ax.set_title("Paired Change with Wilcoxon Signed-Rank Test")
ax.text(0.5, max(before.max(), after.max()) * 1.04,
f"W = {stat:.1f}, p = {p_value:.4f}\nmedian change = {np.median(diff):.2f}",
ha="center", va="bottom", fontsize=9)
ax.spines[["top", "right"]].set_visible(False)
ax.legend(frameon=False)
plt.tight_layout()
plt.savefig("wilcoxon_signed_rank.png", dpi=300, bbox_inches="tight")
plt.show()Visualize Differences Directly
A paired line plot shows subject-level movement, but a one-sample plot of the paired differences makes the signed-rank hypothesis easier to inspect.
fig, ax = plt.subplots(figsize=(5, 4))
ax.axvline(0, color="#888888", lw=1)
ax.boxplot(diff, vert=False, widths=0.35, showfliers=False)
ax.scatter(diff, np.random.normal(1, 0.03, size=len(diff)), color="#9240ff", s=24, alpha=0.75)
ax.set_xlabel("After - Before")
ax.set_yticks([])
ax.set_title("Distribution of Paired Differences")
plt.tight_layout()
plt.show()Common Errors and How to Fix Them
Using Wilcoxon for independent groups
Why: The signed-rank test assumes matched pairs. Independent groups violate that pairing structure.
Fix: Use Mann-Whitney U for independent nonparametric two-group comparisons.
Many zero differences are present
Why: Ties at zero reduce the effective sample size and can make p-values less stable.
Fix: Report the number of zero differences and use scipy zero_method settings consistently.
Plot hides the matching
Why: Separate boxplots make paired data look independent.
Fix: Use paired lines or a paired difference plot so readers can see subject-level changes.
Frequently Asked Questions
Apply Wilcoxon Signed-Rank Test in Python to Your Data
Upload your dataset and Plotivy generates the Python code, runs the analysis, and produces a publication-ready figure.
Generate Code for This TechniquePython Libraries
Quick Info
- Domain
- Statistics
- Typical Audience
- Researchers analyzing matched samples, pre-post interventions, or repeated measurements with non-normal difference distributions
