# Pearson vs Spearman correlation: which to use, and how to report it

> Choose Pearson or Spearman from the scatterplot and your design, report the coefficient with a 95% confidence interval, and handle several measurements per animal. Worked examples in R and Python.

Canonical: https://www.licklider.ai/guides/pearson-vs-spearman-correlation/
Section: Guides › Correlation, regression and survival
Author: Licklider · Published 2026-11-11 · Last reviewed 2026-11-11 (UTC)

> **Quick answer.** **Plot the data first, then choose.** Before you compute either coefficient, decide: **Pearson's r** when the points follow a roughly straight line with no point far from the rest; **Spearman's rs**, chosen in advance, when the relationship keeps rising (or falling) but curves, when the values are scores or ranks, or when extreme values are expected or the plot shows such a point. Report the coefficient with its **95% confidence interval**: with 12 animals and r = 0.5 the interval runs from −0.10 to 0.83. With **several measurements per animal**, first decide whether you are asking about differences *between* animals (correlate per-animal means, n = number of animals) or changes *within* animals (repeated-measures correlation, which assumes a similar slope in every animal — plot each animal's points to check). If you have already computed a coefficient without a plan, report it as the main result and the other coefficient as a labelled sensitivity analysis, with the scatterplot; if the two disagree, say so and state that the choice was made after seeing the data. To show that two methods give the same values, use a Bland–Altman analysis, not a correlation.

> **Terms used below.** *Correlation coefficient*: a number from −1 to +1 describing how closely two measurements move together. *Pearson's r*: measures how close the points are to a straight line. *Spearman's rs*: Pearson's r computed on the ranks, so it measures whether one variable keeps rising (or falling) as the other increases, in a straight line or a curve (a *monotonic* relationship). *Confidence interval (CI)*: the range of correlations compatible with the data. *Fisher z*: a transformation of r used to compute its interval. *Permutation test*: a p-value obtained by shuffling one variable many times. *Repeated-measures correlation (r_rm)*: the association within animals, fitted with one common slope and a separate intercept for each animal (an *analysis of covariance*: a regression with a group term and a continuous predictor). *Jointly (bivariate) normal*: both variables normally distributed together, so the cloud of points is an ellipse. *Ordinal*: values with an order but no fixed spacing, such as scores. *Ties*: two animals with exactly the same value; ties rule out R's exact Spearman p-value. *Exact test*: a p-value computed from all possible orderings instead of an approximation. *Degrees of freedom (df)*: the information left after fitting the model; for r_rm with complete data, the number of measurements minus the number of animals minus 1 (here 24 − 6 − 1 = 17). *Residual sum of squares* (`ssr`, `deviance`): how far the points sit from the fitted model. *Sensitivity analysis*: the same question answered a second way to show how much the answer depends on a choice. *α*: the p-value threshold, usually 0.05. *Limits of agreement*: the range expected to hold 95% of the differences between two methods. *Cq*: the qPCR cycle at which a sample crosses the detection threshold. *False positive*: a "significant" result where nothing is related.

## Choose from the scatterplot and the design

Pearson's r is the standard summary for jointly normal data and a straight-line relationship; for non-normal continuous data, ordinal data or data with relevant outliers, Spearman's rs measures a monotonic association (Schober, Boer & Schwarte 2018). Judge this choice from the plot and from what you know about the measurement, not from a normality test. When Pearson's conditions do hold, Spearman gives up a little power, which is why it is not the default for every dataset. Read the rows in order and stop at the first that fits.

| What you see or know | Use | Why |
| --- | --- | --- |
| Several measurements per animal (time points, cells, wells) | First pick the question — between or within animals ([several measurements per animal](#several-measurements-per-animal-pick-the-question)) | Measurements from one animal are not independent, so a correlation over all rows overstates the evidence |
| The two variables measure the same quantity (two assays, two observers) | A Bland–Altman analysis of the differences, not a correlation | A correlation measures association, not agreement ([correlation is not agreement](#correlation-is-not-agreement)) |
| Points rise and then fall (or the reverse) | Neither coefficient: describe the curve, for example with a regression that allows it | Both coefficients summarise one direction and can be near 0 for a strong but curved relationship |
| Values are scores, grades or ranks | Spearman | Only the order of the values carries meaning |
| One or two points sit far from the rest, or the relationship curves but keeps one direction | Spearman, chosen in advance; check the extreme points against your records | Pearson's r can be created or destroyed by a single point ([worked example](#worked-example-one-mouse-can-make-the-correlation)) |
| A roughly straight cloud with no extreme points | Pearson | It uses the actual values and matches a straight-line summary |

**What to do:** draw the scatterplot, label each point with its animal ID, pick the row, and write the choice in your analysis plan before computing anything. A normality test is not the deciding step ([should you test for normality?](/guides/should-you-test-for-normality/)). The [statistical test chooser](/tools/statistical-test-chooser/) applies the same rows: it asks about several measurements per animal, and every correlation result flags the agreement and rise-then-fall rows.

## Plot first: four datasets with the same r

![Four scatterplots from Anscombe's quartet. All four have a Pearson's r of 0.82 and the same fitted line, but the first is a straight cloud, the second a smooth curve, the third a straight line with one outlier, and the fourth a vertical column of points plus one far point. Spearman's rs is 0.82, 0.69, 0.99 and 0.50.](/guides/pearson-vs-spearman-correlation/anscombe.svg)

Anscombe (1973) built four small datasets with the same Pearson correlation and fitted line but very different shapes. Pearson's r is 0.82 in all four; Spearman's rs is 0.82, 0.69, 0.99 and 0.50, and none of the numbers tells you what the plot shows at a glance. In panel IV a single point creates the whole correlation.

**What to do:** show the scatterplot in the paper or the supplement; in your own data, label every point by animal.

## Worked example: one mouse can make the correlation

Twelve mice, with daily food intake (g/day) and weight gain over four weeks (g); constructed data ([intake.csv](/guides/pearson-vs-spearman-correlation/intake.csv)). Mouse M12 ate far more than the others and gained far more.

| Analysis | Coefficient (95% CI) | p |
| --- | --- | --- |
| Spearman, all 12 mice (the planned analysis in this example) | rs = 0.09 (−0.51 to 0.63) | 0.79 (permutation) |
| Pearson, all 12 mice — driven by M12 alone (see below) | r = 0.86 (0.58 to 0.96) | 0.0003 |
| Pearson without M12 (illustration only; removing M12 because of its value would be invalid) | r = −0.13 (−0.67 to 0.51) | 0.71 |

In this constructed example, Pearson's r of 0.86 comes from M12 alone: without it the remaining 11 mice show no clear association (r = −0.13, 95% CI −0.67 to 0.51). Spearman's rs, which uses only the order of the values, reports what most of the mice show. If your plan named Spearman because extreme intakes were expected, rs = 0.09 is the result. Delete M12 only for a recorded reason that does not depend on its value, such as a feeding-hopper fault ([when can you exclude an outlier?](/guides/when-can-you-exclude-an-outlier/)); a value that makes r large or small is not such a reason.

Python (SciPy 1.18.1, statsmodels 0.15.0, Matplotlib 3.11), with [intake.csv](/guides/pearson-vs-spearman-correlation/intake.csv) and [repeated.csv](/guides/pearson-vs-spearman-correlation/repeated.csv) in the working folder. If you have one row per animal and no repeated measurements, delete everything from the `repeated.csv` line onwards in either script.

```python
# Install once: pip install numpy pandas scipy statsmodels matplotlib
import math
import numpy as np
import pandas as pd
from scipy import stats
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt

d = pd.read_csv("intake.csv")            # columns: mouse, intake, gain
n = len(d)
ax = d.plot.scatter("intake", "gain")
for _, row in d.iterrows():
    ax.annotate(row.mouse, (row.intake, row.gain))   # label each point with its animal ID
plt.savefig("intake_gain.png")           # look at this before choosing the coefficient

pr = stats.pearsonr(d.intake, d.gain)
ci = pr.confidence_interval()            # Fisher z interval
print(f"Pearson r = {pr.statistic:.2f}, 95% CI {ci.low:.2f} to {ci.high:.2f}, p = {pr.pvalue:.4f}")

rs = stats.spearmanr(d.intake, d.gain).statistic
se = math.sqrt((1 + rs**2 / 2) / (n - 3))
lo, hi = (math.tanh(math.atanh(rs) + k * 1.959964 * se) for k in (-1, 1))
perm = stats.permutation_test((d.gain,), lambda g: abs(stats.spearmanr(d.intake, g).statistic),
                              permutation_type="pairings", alternative="greater", n_resamples=200_000,
                              rng=np.random.default_rng(20261110))   # two-sided: compares |rs|; takes about a minute
print(f"Spearman rs = {rs:.2f}, 95% CI {lo:.2f} to {hi:.2f}, permutation p = {perm.pvalue:.2f}")

r = pd.read_csv("repeated.csv")          # columns: mouse, time, insulin, glucose
m = r.groupby("mouse")[["insulin", "glucose"]].mean()
bm = stats.pearsonr(m.insulin, m.glucose)
bci = bm.confidence_interval()
print(f"Between mice: r = {bm.statistic:.2f}, 95% CI {bci.low:.2f} to {bci.high:.2f} (n = {len(m)} mice), p = {bm.pvalue:.3f}")
full = smf.ols("glucose ~ C(mouse) + insulin", data=r).fit()
base = smf.ols("glucose ~ C(mouse)", data=r).fit()
ss_x = base.ssr - full.ssr
r_rm = math.copysign(math.sqrt(ss_x / (ss_x + full.ssr)), full.params["insulin"])
p_rm = stats.f.sf(ss_x / (full.ssr / full.df_resid), 1, full.df_resid)
lo_rm, hi_rm = (math.tanh(math.atanh(r_rm) + k * 1.959964 / math.sqrt(full.df_resid - 1)) for k in (-1, 1))
print(f"Within mice: r_rm = {r_rm:.2f}, 95% CI {lo_rm:.2f} to {hi_rm:.2f}, df = {int(full.df_resid)}, p = {p_rm:.1e}")
```

R 4.3.3 (no extra packages; the r_rm interval uses the same formula as the `rmcorr` package):

```r
d <- read.csv("intake.csv")              # columns: mouse, intake, gain
n <- nrow(d)
png("intake_gain.png")                    # look at this before choosing the coefficient
plot(gain ~ intake, data = d)
text(d$intake, d$gain, d$mouse, pos = 3)   # label each point with its animal ID
invisible(dev.off())

p <- cor.test(d$intake, d$gain, method = "pearson")   # CI by Fisher z
cat(sprintf("Pearson r = %.2f, 95%% CI %.2f to %.2f, p = %.4f\n",
            p$estimate, p$conf.int[1], p$conf.int[2], p$p.value))

rs <- cor(d$intake, d$gain, method = "spearman")
se <- sqrt((1 + rs^2 / 2) / (n - 3))
ci <- tanh(atanh(rs) + c(-1, 1) * qnorm(0.975) * se)
set.seed(20261110)
perm <- replicate(200000, cor(d$intake, sample(d$gain), method = "spearman"))
cat(sprintf("Spearman rs = %.2f, 95%% CI %.2f to %.2f, permutation p = %.2f\n",
            rs, ci[1], ci[2], mean(abs(perm) >= abs(rs) - 1e-12)))

r <- read.csv("repeated.csv")            # columns: mouse, time, insulin, glucose
m <- aggregate(cbind(insulin, glucose) ~ mouse, data = r, FUN = mean)
bm <- cor.test(m$insulin, m$glucose)
cat(sprintf("Between mice: r = %.2f, 95%% CI %.2f to %.2f (n = %d mice), p = %.3f\n",
            bm$estimate, bm$conf.int[1], bm$conf.int[2], nrow(m), bm$p.value))
full <- lm(glucose ~ factor(mouse) + insulin, data = r)
base <- lm(glucose ~ factor(mouse), data = r)
ss_x <- deviance(base) - deviance(full)
r_rm <- sign(coef(full)["insulin"]) * sqrt(ss_x / (ss_x + deviance(full)))
p_rm <- pf(ss_x / (deviance(full) / df.residual(full)), 1, df.residual(full), lower.tail = FALSE)
ci_rm <- tanh(atanh(r_rm) + c(-1, 1) * qnorm(0.975) / sqrt(df.residual(full) - 1))   # as rmcorr
cat(sprintf("Within mice: r_rm = %.2f, 95%% CI %.2f to %.2f, df = %d, p = %.1e\n",
            r_rm, ci_rm[1], ci_rm[2], df.residual(full), p_rm))
```

Both scripts print the lines below and save the labelled scatterplot as `intake_gain.png`:

```text
Pearson r = 0.86, 95% CI 0.58 to 0.96, p = 0.0003
Spearman rs = 0.09, 95% CI -0.51 to 0.63, permutation p = 0.79
Between mice: r = 0.91, 95% CI 0.39 to 0.99 (n = 6 mice), p = 0.011
Within mice: r_rm = -0.90, 95% CI -0.96 to -0.75, df = 17, p = 1.8e-07
```

**What to do:** report the coefficient you planned, with its interval, and show the scatterplot so readers can see any point that drives it. If you had no plan, follow the third template under [Report it](#report-it).

## Report r with its confidence interval

With lab-sized samples a correlation is imprecise. The table gives the 95% Fisher-z interval for an observed Pearson r; for Spearman's rs, see the interval described further down.

| n | Pearson r = 0.3 | Pearson r = 0.5 | Pearson r = 0.8 |
| --- | --- | --- | --- |
| 6 | −0.68 to 0.89 | −0.52 to 0.93 | −0.03 to 0.98 |
| 8 | −0.51 to 0.83 | −0.32 to 0.89 | 0.22 to 0.96 |
| 12 | −0.33 to 0.75 | −0.10 to 0.83 | 0.42 to 0.94 |
| 20 | −0.16 to 0.66 | 0.07 to 0.77 | 0.55 to 0.92 |
| 50 | 0.02 to 0.53 | 0.26 to 0.68 | 0.67 to 0.88 |

Sample correlations also scatter widely around the true value. This is a different quantity from the interval above: it is not an interval around one observed r but the spread of r across repeated experiments, a spread that is skewed at small n. When the true correlation is 0.5, the middle 95% of sample r values runs from −0.27 to 0.90 with 8 animals, from −0.07 to 0.84 with 12, from 0.18 to 0.73 with 30 and from 0.34 to 0.63 with 100 (simulation, 20,000 datasets per row; [simulation.py](/guides/pearson-vs-spearman-correlation/simulation.py) and its output, [simulation_output.json](/guides/pearson-vs-spearman-correlation/simulation_output.json)).

**Pearson's interval.** The Fisher-z interval is what R's `cor.test` and SciPy's `pearsonr(...).confidence_interval()` report; in our simulation (bivariate normal data, true correlation 0.5) it covered the true correlation 94.8–95.2% of the time for 8 to 40 animals.

**Spearman's interval.** R's `cor.test` gives no interval for Spearman. In the same simulation (bivariate normal, true correlation 0.5), a Fisher-z interval with variance 1/(n − 3) covered the population Spearman correlation only 94.0–95.0% of the time; with variance (1 + rs²/2)/(n − 3) it covered 95.3–96.4% (slightly conservative: a little wider than its stated level), so the code above uses that version. Bonett & Wright (2000) review interval estimates for the Spearman correlation and propose an improved standard error for it.

**What to do:** report r (or rs) with its 95% CI and n as the number of independent units, and read the interval the way you would any other: which correlations does it rule out ([how to interpret a confidence interval](/guides/how-to-interpret-confidence-intervals/))?

## Software defaults that change the p-value

| Software | What it does | What to do |
| --- | --- | --- |
| SciPy `spearmanr` | Its p-value is a large-sample approximation; the documentation says it "is only accurate for very large samples (>500 observations)". With independent data our simulation gave 8.3% false positives at n = 5, 6.0% at n = 6 and 5.9% at n = 8 | Use a permutation test (as in the code above, which needs SciPy 1.15 or later), or R's exact test when there are no ties |
| R `cor.test(method = "spearman")` | Without ties and with fewer than 1,290 pairs it uses algorithm AS 89 (exact below n = 10); with ties it warns "Cannot compute exact p-value with ties" and uses the t approximation; it gives no confidence interval | With ties at small n, use a permutation test; compute the interval separately |
| R `cor.test` (Pearson), SciPy `pearsonr` | t-test p-value (SciPy uses the exact distribution of r, which gives the same value) and Fisher-z interval | Fine for a straight-line relationship without extreme points |

The example data have one tie in intake (two mice at 3.6 g/day), so R falls back to the approximation there; the permutation p (0.79) and the approximate p (0.787) happen to agree.

**What to do:** name the p-value method in your Methods ("permutation test with 200,000 shuffles", "exact test").

## Several measurements per animal: pick the question

Six mice, each measured at four time points for plasma insulin and glucose ([repeated.csv](/guides/pearson-vs-spearman-correlation/repeated.csv); the data were constructed so that the two questions below give opposite answers). Repeated measurements break the independence that a correlation's p-value relies on: "the assumption of independence is violated in repeated measures, in which each participant provides more than one data point" (Bakdash & Marusich 2017).

| Question | Analysis | Result |
| --- | --- | --- |
| No question — all 24 rows treated as independent | Pearson on all rows | r = 0.65, p = 0.0006 — overstates the evidence and mixes the two questions below |
| Do mice with higher insulin have higher glucose? (between animals) | Pearson on the 6 per-mouse means; n = 6 | r = 0.91 (0.39 to 0.99), p = 0.011 |
| When a mouse's insulin rises, does its glucose fall? (within animals) | Repeated-measures correlation | r_rm = −0.90 (−0.96 to −0.75), df = 17, p = 1.8 × 10⁻⁷ |

Between mice the association is positive; within each mouse it is negative. The pooled r = 0.65 answers neither question. Bland & Altman (1995a, 1995b) described both analyses; the repeated-measures correlation of Bakdash & Marusich (2017) fits one common slope with a separate intercept for each animal (an analysis of covariance, ANCOVA), so it assumes the within-animal slopes are similar — plot each animal's points to check. The code above computes it with an ordinary linear model, following the calculation in their paper; their R package `rmcorr` implements the same method.

**What to do:** decide which question your hypothesis asks before analysing, report n as the number of animals for the between-animal question, and give the degrees of freedom for the within-animal one, with the per-animal plot that shows the slopes are similar. For more on what counts as one observation, see [what counts as n?](/guides/what-counts-as-n/) and [nested data](/guides/nested-data-mixed-model/).

## Correlation is not agreement

Two assays can correlate perfectly and still disagree: if one always reads twice as high as the other, r = 1. Bland & Altman (1986) put it plainly: "r measures the strength of a relation between two variables, not the agreement between them."

**What to do:** to compare two methods, plot the difference between them against their mean and report the mean difference with limits of agreement (a Bland–Altman analysis); every correlation result of the [statistical test chooser](/tools/statistical-test-chooser/) flags this.

## Many correlations at once

Testing many correlations produces false positives at the stated rate, α, for each one: correlating 300 genes with one outcome gives about 15 "significant" results at α = 0.05 when nothing is related. Correct for the number of tests, or label the analysis exploratory ([which correction to use](/guides/which-multiple-comparison-correction/)). Another trap in qPCR data: a lower Cq means higher expression, so a correlation with Cq has the opposite sign to the correlation with expression; convert before interpreting.

**What to do:** say how many correlations you computed and how you corrected for them.

## Check what an AI assistant ran

1. **Did it pool repeated measurements?** Count the rows used against the number of animals.
2. **Did it choose the coefficient from a normality test?** Decide from the plot and the design instead.
3. **Is there a confidence interval?** A bare r and p hide how imprecise a small-sample correlation is.
4. **Which Spearman p-value?** SciPy's `spearmanr` p-value is approximate; its documentation calls it accurate only above about 500 observations.
5. **Did it call a correlation "agreement" or "validation"?** That needs a Bland–Altman analysis.
6. **Did it describe |r| = 0.4 as "strong" from a generic table?** Interpret the size in your field's terms, with the interval.

## Report it

> Food intake and weight gain were related using Spearman's rank correlation (Spearman's rs), chosen in advance because extreme intakes were expected (n = 12 mice). The correlation was rs = 0.09 (95% CI −0.51 to 0.63; Fisher-z interval with variance (1 + rs²/2)/(n − 3)), permutation p = 0.79 (200,000 shuffles). One mouse with high intake (M12) is shown in the scatterplot; Pearson's r with and without this mouse is reported in the supplement for comparison.

> Within mice, glucose fell as insulin rose (repeated-measures correlation, r_rm = −0.90, 95% CI −0.96 to −0.75, df = 17, p < 0.001; 6 mice × 4 time points). Between mice, mean insulin and mean glucose were positively correlated (Pearson's r = 0.91, 95% CI 0.39 to 0.99, n = 6 mice).

> [If no coefficient was chosen in advance:] Pearson's r between food intake and weight gain was 0.86 (95% CI 0.58 to 0.96, p = 0.0003; n = 12 mice), computed before a coefficient had been chosen in a plan. As a sensitivity analysis, Spearman's rs was 0.09 (95% CI −0.51 to 0.63, permutation p = 0.79). The two disagree because one mouse (M12) drives Pearson's r, as the scatterplot shows.

Replace the numbers with your own, keep n as the number of animals, and say which interval and p-value method you used. A correlation describes association; it does not by itself show that one variable causes the other.

## Corrections

**11 November 2026:** this page said that the statistical test chooser applies the same routing. Until this date the chooser did not ask about several measurements per animal, and its correlation results did not flag agreement or rise-then-fall data (only the Pearson row of its decision table mentioned agreement); its Spearman code also printed SciPy's approximate p-value without an interval. The chooser now does all three as this page describes, and the two sentences that refer to it were reworded to say what it covers. On the same date the Python permutation code on this page was changed to compare the absolute value of rs, as the R code already did; SciPy's default two-sided definition (twice the smaller one-sided p) can differ from it when values are tied (in one tied example, 0.019 against 0.0095). On this page's data both give 0.79.

## References

- Anscombe FJ (1973). Graphs in statistical analysis. *The American Statistician* 27:17–21. [doi:10.1080/00031305.1973.10478966](https://doi.org/10.1080/00031305.1973.10478966)
- Bakdash JZ, Marusich LR (2017). Repeated measures correlation. *Frontiers in Psychology* 8:456. [doi:10.3389/fpsyg.2017.00456](https://doi.org/10.3389/fpsyg.2017.00456) (corrigendum 2019, notation in two equations: [doi:10.3389/fpsyg.2019.01201](https://doi.org/10.3389/fpsyg.2019.01201))
- Bland JM, Altman DG (1986). Statistical methods for assessing agreement between two methods of clinical measurement. *Lancet* 327:307–310. [doi:10.1016/S0140-6736(86)90837-8](https://doi.org/10.1016/S0140-6736(86)90837-8)
- Bland JM, Altman DG (1995a). Calculating correlation coefficients with repeated observations: part 1 — correlation within subjects. *BMJ* 310:446. [doi:10.1136/bmj.310.6977.446](https://doi.org/10.1136/bmj.310.6977.446)
- Bland JM, Altman DG (1995b). Calculating correlation coefficients with repeated observations: part 2 — correlation between subjects. *BMJ* 310:633. [doi:10.1136/bmj.310.6980.633](https://doi.org/10.1136/bmj.310.6980.633)
- Bonett DG, Wright TA (2000). Sample size requirements for estimating Pearson, Kendall and Spearman correlations. *Psychometrika* 65:23–28. [doi:10.1007/BF02294183](https://doi.org/10.1007/BF02294183)
- Schober P, Boer C, Schwarte LA (2018). Correlation coefficients: appropriate use and interpretation. *Anesthesia & Analgesia* 126:1763–1768. [doi:10.1213/ANE.0000000000002864](https://doi.org/10.1213/ANE.0000000000002864)
- SciPy 1.18.1 documentation: [`scipy.stats.spearmanr`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.spearmanr.html), [`scipy.stats.pearsonr`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.pearsonr.html).
- R 4.3.3 documentation: `help("cor.test")`.

## Related

- [Agreement between raters](https://www.licklider.ai/guides/inter-rater-agreement/)
- [Statistical test chooser](https://www.licklider.ai/tools/statistical-test-chooser/)
- [How to interpret a confidence interval](https://www.licklider.ai/guides/how-to-interpret-confidence-intervals/)
- [What counts as n?](https://www.licklider.ai/guides/what-counts-as-n/)
- [Correlation vs linear regression](https://www.licklider.ai/guides/correlation-vs-linear-regression/)

## How this page was made

- Written and published by Licklider. Recommendations are cited to the peer-reviewed papers and software documentation under References; quoted text was checked against the source.
- Every number comes from stored code on constructed data (Python 3.13 with SciPy 1.18.1 and statsmodels 0.15.0; R 4.3.3), and the two languages agree; the simulations use a seed fixed before running.
- Separate review passes checked the statistics, the citations and readability before publication.
