On this page

The P-value Is Not a Verdict: An Interactive Consonance Curve

Drag an estimate and its interval and watch the entire P-value function, every compatibility interval, and the S-value move together.
Author

Zad Rafi

Published

September 24, 2026

A single P-value gives us one number about one hypothesis. The P-value function takes the same test statistic and applies it to every candidate parameter value at once, and (as a result) it contains every compatibility interval at every level as a horizontal slice. None of the mathematics below is new. It is the usual normal approximation that most of us already rely on, except that here we draw it in full instead of evaluating it at one point.

1 If we slice the curve at height p=0.05p = 0.05 and read off the x-coordinates, we get the 95% limits. If we slice at p=0.01p = 0.01, we get the 99% limits. Thus, the interval was never a separate calculation.

The display itself is quite old. Poole called for it in 1987, Sullivan and Foster catalogued its uses in 1990, and it has sat in Modern Epidemiology ever since. So what has changed? For one, it now costs nothing to render. We also have what I think is a better vocabulary for the vertical axis, where we speak of compatibility in place of confidence and surprisal in place of significance.

TipDefinitions, once

A P-value function (equivalently, a consonance or compatibility curve) plots, for every candidate parameter value μ\mu, the P-value from testing μ\mu against the data under the full set of analysis assumptions.

An S-value is the Shannon transform s=−log⁡2ps = -\log_2 p: the number of consecutive heads from a fair coin that would be exactly as surprising as the observed test statistic, if μ\mu and every other assumption were correct.

A 95% compatibility interval is the set of μ\mu whose P-value exceeds 0.05. It is a horizontal slice through the curve, so we don’t need to treat it as a separate object.

The explorer

Let’s start with the explorer itself. When we move the sliders, everything recomputes in the browser (there is no server involved).

Plot = Module {Area: class, Arrow: class, BarX: class, BarY: class, Cell: class, Contour: class, Density: class, Dot: class, Frame: class, Geo: class, Hexgrid: class, Image: class, Line: class, Link: class, Mark: class, Raster: class, Rect: class, RuleX: class, RuleY: class, Text: class, …}
  import {consonanceCurve as consonanceCurve, testValue as testValue, seFromRatioCI as seFromRatioCI} from "./ojs/stats.js"
zq = ƒ(L)
est = 1.61
cir = 2.6
mu = 1
level = 0.95
limits = Object {lower: 0.9984796134431281, upper: 2.5960469949521334}
se = 0.24375739874925995
slice = Object {p: 0.050000000000000044, lower: 0.9984796130585426, upper: 2.5960469959520553}
curve = Array(601) [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, …]
atMu = Object {mu: 1, p: 0.050734109671123656, s: 4.300900160352262}
1.61 (95% compatibility interval 1.00 to 2.60)
Testing μ = 1.00: P = 0.051, S = 4.3 bits
The data are no more surprising against μ = 1.00 than 4.3 consecutive heads from a fair coin.
95.0% slice (at P = 0.050): 1.00 to 2.60
0.00.20.40.60.81.0↑ P-value0.50.7511.52345Rate ratio →95% slice
05101520↑ S-value (bits of information against)0.50.7511.52345Rate ratio →S = 4.3 bits (P = 0.05)

Calibrating bits

Why bother with the S-value at all? Mostly because “P = 0.05” doesn’t carry much intuition, and “significant” tends to carry the wrong one. Bits seem to be easier to think about, since most of us have a physical sense of what flipping a coin is like. If we drag μ above, we can watch the coins change.

4.30 bits against μ = 1.00. Less surprising than the conventional 0.05 threshold.

The S-value measures how surprising the data are given the whole model, and μ\mu is only one part of that model. Every bit is also a bit against the assumed absence of uncontrolled confounding, selection effects, measurement error, and misspecified model form. Thus, a large S-value tells us that the data are hard to reconcile with μ\mu together with everything else that we assumed. However, it cannot tell us which of those assumptions to give up. In observational work, the “everything else” is usually the weaker link.

A worked example: one data set, four analyses

Brown and colleagues studied serotonergic antidepressant use in pregnancy and autism spectrum disorder in 35,906 Ontario births, of which 2,837 pregnancies (7.9%) were exposed. They reported four hazard ratios from the same cohort under progressively stronger confounding control. This is the example reanalysed at length in, and the working code for that paper lives .

Before anything else, let’s look at the point estimates.

brown = Array(4) [Object, Object, Object, Object]
brownStats = Array(4) [Object, Object, Object, Object]
AnalysisConfounding controlHR95% CIInterval ratioP (vs HR = 1)S (bits)
CrudeNone2.161.64 – 2.861.745.7e-824.07
Covariate-adjustedRegression adjustment1.591.17 – 2.171.850.00338.26
HdPS IPT-weightedHigh-dimensional propensity score1.610.997 – 2.592.600.05054.31
Sibling-matchedWithin-family (shared confounders)1.600.69 – 3.745.420.27571.86

Three of the four point estimates are 1.59, 1.60 and 1.61. By the conventional reading, one of them is a finding and two of them are nothing. However, the estimate barely changes between them. What changes is the width of the interval, which grows from a ratio of 1.9 to 5.4 across the three adjusted analyses, as each one trades precision for stronger confounding control. So the verdicts flip while the estimate stays where it was.

The published conclusion was that exposure “was not associated with” autism. I don’t think the arithmetic supports that sentence, since the IPT-weighted analysis puts P=0.0505P = 0.0505 against the null (4.31 bits of surprise), and its interval runs to 2.59.

shown = Array(4) [Object, Object, Object, Object]
brownCurves = Array(2004) [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, …]
Covariate-adjustedCrudeHdPS IPT-weightedSibling-matched
0.00.10.20.30.40.50.60.70.80.91.0↑ P-value0.50.7511.52345Hazard ratio →

If we uncheck the crude analysis, the remaining three curves are nearly concentric, with the same peak and different widths. That is what a precision problem looks like, and it gives us little reason to think the analyses disagree about the size of the effect. Now, let’s test a value other than the null:

muBrown = 1.5
AnalysisHRP (vs 1.50)S (bits)Reading
Crude2.160.0106.62Poorly compatible
Covariate-adjusted1.590.7120.49Highly compatible
HdPS IPT-weighted1.610.7710.37Highly compatible
Sibling-matched1.600.8810.18Highly compatible

If we set the slider to 1.5, the four analyses give SS = 6.6, 0.5, 0.4 and 0.2 bits. Three of the four find a 50% increase in hazard less surprising than a single coin flip. Thus, against this hypothesis, the analysis that was “not significant” and the analysis that was “significant” are saying nearly the same thing.

Testing HR = 1 is a legitimate question. However, if we report only that test, and only its dichotomised outcome, we discard the answer to every other question that the same data address. Some of those are questions the reader may care about more, such as whether an effect large enough to change prescribing can be ruled out. On these data, it cannot be ruled out, since the IPT-weighted 95% interval reaches 2.59, and even the 50% interval runs from 1.37 to 1.90.

What more data would do

Precision and the parameter value are two separate things, but a single P-value fuses them into one number. To pull them apart, we can hold the IPT-weighted estimate fixed and vary only the amount of information (the standard error scales as 1/k1/\sqrt{k} when the sample size is multiplied by kk).

kInfo = 1
base = Object {hr: 1.61, se: 0.24354079775495885}
scaled = Object {se: 0.24354079775495885}
scaledCurve = Array(501) [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, …]
baseCurve = Array(501) [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, …]
scaledAtNull = Object {mu: 1, p: 0.050528853158511446, s: 4.306748753957083}
At 1.00× the original sample size, with the estimate held at 1.61: 95% interval 1.00 to 2.59, P = 0.0505 against the null, S = 4.3 bits.
0.00.20.40.60.81.0↑ P-value0.50.7511.52345Hazard ratio →

The dashed ghost curve is the study as it was published. If we push kk to 4, the null falls well outside the 95% interval, even though the estimate hasn’t moved at all. If we pull kk down to 0.3, the same estimate becomes compatible with almost everything between 0.7 and 3.9.

I think this deserves some attention, because it seems to be the mechanism behind a lot of “failure to replicate” reporting. A study that reports P=0.04P = 0.04 and a study that reports P=0.12P = 0.12 can have identical point estimates and differ only in how many people they enrolled. Neither of those numbers is a property of the effect.

ImportantThe one thing this widget cannot show

Scaling the standard error only models random error. Multiplying the sample size by 16 does nothing to confounding, selection, or measurement error, since those biases do not shrink with n\sqrt{n}, and past some sample size they dominate entirely. Thus, a very narrow curve can still be centred in the wrong place, and it will look far more convincing than it should. If we want those biases quantified (instead of assumed away), we need a bias analysis, and adding more subjects won’t help.

Do two estimates conflict?

We often hear something like “Study A was significant, study B was not, so they disagree.” However, that compares two verdicts and says nothing direct about the two estimates. The comparison that actually answers the question has a curve of its own. Under independence, the log ratio-of-ratios has standard error seA2+seB2\sqrt{se_A^2 + se_B^2}, and the null of no difference sits at a ratio of 1.

Study A is the explorer at the top of this page, and we can set study B here.

estB = 1.05
cirB = 1.6
limitsB = Object {lower: 0.8300978857941996, upper: 1.3281566172707193}
seB = 0.11990108822230006
diff = Object {ratio: 1.5333333333333334, se: 0.2716504010707466}
diffDomain = Object {from: 0.25, to: 4.6000000000000005}
diffCurve = Array(501) [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, …]
diffAtNull = Object {mu: 1, p: 0.11560129228171975, s: 3.1127705693843435}
Study A: 1.61 (1.00–2.60), P vs 1 = 0.051
Study B: 1.05 (0.83–1.33), P vs 1 = 0.684
Ratio of ratios A ÷ B = 1.53 (95% CI 0.90–2.61), P for no difference = 0.116, S = 3.1 bits
0.00.20.40.60.81.0↑ P-value for the difference0.30.40.50.61234Ratio of ratios (A ÷ B) →

With the defaults (A at 1.61 with P=0.051P = 0.051, and B at 1.05 with P=0.684P = 0.684), the two studies land on opposite sides of the conventional threshold. However, their ratio of ratios is 1.53 (95% interval 0.90 to 2.61), and the difference carries 3.1 bits of information. That is fewer than the 4.3 bits that the 0.05 convention itself demands, and fewer than four coin flips. So the two verdicts disagree, while the direct comparison gives us little evidence of a difference.

WarningThis does not apply to the four Brown analyses

The overlay in the previous section shows four analyses of the same subjects. Their estimates are strongly correlated, so seA2+seB2\sqrt{se_A^2 + se_B^2} badly overstates the standard error of their difference, and the resulting P-value is not interpretable. Thus, this widget should only be used for studies that are actually independent. For nested analyses of one data set, I’d stick with the overlay itself as the summary (same peak, different width).

A P-value and an S-value are the same quantity on different scales, so a dual axis is technically defensible. However, I still think it is a bad idea. Readers reliably misread which curve belongs to which axis, and the visual slope of the P-value curve near the tails is uninformative in the same region where the S-value is most informative. Two stacked panels that share an x-axis cost one extra inch of page, and they remove the ambiguity.

  • No shading of “significant” regions. The whole point of the curve is that there is no cliff at 0.05.
  • No default null at 1. The μ slider starts at 1 only because that is the conventional test value, and I wouldn’t treat it as privileged.
  • No P-value below about 1e-300. Double precision runs out. The implementation stays accurate to roughly z = 37, which is far past the point where the normal approximation itself becomes the binding constraint.
  • No normality diagnostics. Every curve here assumes that the log estimate is approximately normal with the reported standard error. The published interval already made that same assumption, so the curve adds nothing new here and only makes the assumption visible across the whole range.

Interactive table

Here’s the same idea applied to tabular results. The table is searchable, and it can be sorted by clicking the column headers, which is most of what makes a long results table usable. These are the Brown analyses again, with each row carrying its own P-value against whichever hypothesis we choose.

muTable = 1
tableRows = Array(4) [Object, Object, Object, Object]
4 results
tableSearch = Array(4) [Object, Object, Object, Object]
AnalysisHazard ratio95% CISE (log scale)P▾S (bits)
Crude2.161.64 – 2.860.1425.7e-824.07
Covariate-adjusted1.591.17 – 2.170.1580.0038.26
HdPS IPT-weighted1.610.997 – 2.590.2440.0514.31
Sibling-matched1.60.69 – 3.740.4310.2761.86

How to report this

None of this requires a new statistic, a new threshold, or new software. I’d suggest four sentences that can go straight into a results section:

  1. Report the estimate and interval without the word “significant” (for example, “HR 1.61, 95% compatibility interval 0.997 to 2.59”).
  2. Report the P-value as a number instead of as an inequality against 0.05, and say which hypothesis it tests.
  3. Report at least one P-value for a non-null hypothesis that matters clinically or practically. If the data cannot distinguish the null from a 60% increase, then that is the finding.
  4. Say that the interval and the P-value are conditional on the entire model, and that the parameter is only one part of it.

What if a reviewer asks for the significance verdict back? I’d point them to the curve, since it contains their verdict as one horizontal slice, along with every slice they did not ask for.

Reader infrastructure that is already on

Lastly, three of the things that people associate with Gwern’s pages happen to be Quarto defaults, and they need no code at all:

  • Citation popups. Hover any @citekey reference.
  • Footnote popups. Hover the marker on above.
  • Cross-reference popups. Hover any @fig- or @tbl- reference.

2 It helps to put the 0.05 convention in bits. It amounts to 4.32 bits of information against the tested hypothesis, or roughly the surprise of four consecutive heads. I doubt that many of us would abandon a belief on four coin flips.

All three are controlled by citations-hover, footnotes-hover and crossrefs-hover, which default to true. The margin placement of footnotes here comes from reference-location: margin in this document’s front matter.

References

1. Poole C. (1987). “Beyond the confidence interval.” American Journal of Public Health. 77:195–199. doi: .
Abstract

Beyond the confidence interval. C PooleCopyRight https://doi.org/10.2105/AJPH.77.2.195 Published Online: October 07, 2011

2. Sullivan KM, Foster DA. (1990). “Use of the confidence interval function.” Epidemiology. 1:39–42. doi: .
Abstract

Graphics displaying all confidence intervals around a point estimate have been referred to as P-value functions and consonance intervals. We recommend use of the term confidence interval function (CI function) rather than P-value function. The CI function is useful because it simultaneously depicts point estimation, variability, and the relation of these two factors to the null value. The usefulness of the CI function in demonstrating the concepts of effect modification and confounding, in meta-analysis, and in the comparison of various confidence interval procedures is evaluated. Software packages that produce CI functions are described.

3. Rafi Z, Greenland S. (2020). “Semantic and cognitive tools to aid statistical science: Replace confidence and significance by compatibility and surprise.” BMC Medical Research Methodology. 20:244. doi: .
Abstract

BACKGROUND: Researchers often misinterpret and misrepresent statistical outputs. This abuse has led to a large literature on modification or replacement of testing thresholds and P-values with confidence intervals, Bayes factors, and other devices. Because the core problems appear cognitive rather than statistical, we review some simple methods to aid researchers in interpreting statistical outputs. These methods emphasize logical and information concepts over probability, and thus may be more robust to common misinterpretations than are traditional descriptions. METHODS: (p), to provide a measure of the information supplied by the testing procedure, and to help calibrate intuitions against simple physical experiments like coin tossing. We also use tables or graphs of test statistics for alternative hypotheses, and interval estimates for different percentile levels, to thwart fallacies arising from arbitrary dichotomies. Finally, we reinterpret P-values and interval estimates in unconditional terms, which describe compatibility of data with the entire set of analysis assumptions. We illustrate these methods with a reanalysis of data from an existing record-based cohort study …

4. Brown HK, Ray JG, Wilton AS, Lunsky Y, Gomes T, Vigod SN. (2017). “Association between serotonergic antidepressant use during pregnancy and autism spectrum disorder in children.” JAMA. 317:1544–1552. doi: .
Abstract

IMPORTANCE: Previous observations of a higher risk of child autism spectrum disorder with serotonergic antidepressant exposure during pregnancy may have been confounded. OBJECTIVE: To evaluate the association between serotonergic antidepressant exposure during pregnancy and child autism spectrum disorder. DESIGN, SETTING, AND PARTICIPANTS: Retrospective cohort study. Health administrative data sets were used to study children born to mothers who were receiving public prescription drug coverage during pregnancy in Ontario, Canada, from 2002-2010, reflecting 4.2% of births. Children were followed up until March 31, 2014. EXPOSURES: Serotonergic antidepressant exposure was defined as 2 or more consecutive maternal prescriptions for a selective serotonin or serotonin-norepinephrine reuptake inhibitor between conception and delivery. MAIN OUTCOMES AND MEASURES: Child autism spectrum disorder identified after the age of 2 years …

Back to top

Comments

Webmentions

Replies, likes, and mentions from around the web, tracked via .