True Population Parameter
A population parameter is a fixed numerical value describing a characteristic of an entire population. The word true is there to insist that the value exists whether or not anyone knows it. The familiar ones are \(\mu\) for the population mean, \(\sigma\) for the population standard deviation, \(p\) for the population proportion, and \(\rho\) for the population correlation.
Each has a sample counterpart, and the pairing is the point:
- Mean: \(\mu\) in the population, \(\bar{x}\) in the sample
- Standard deviation: \(\sigma\) and \(s\)
- Proportion: \(p\) and \(\hat{p}\)
- Correlation: \(\rho\) and \(r\)
The parameter is fixed but unknown. The statistic is known but random — it depends on which sample you happened to draw. Every sentence in inference is built on that asymmetry, and the sentence people actually say about confidence intervals quietly reverses it.
Which of the two is random
“We are 95% confident the true parameter lies between 0.53 and 0.67” sounds like a statement about the parameter. It cannot be. The parameter is a constant: it is either in that interval or it is not, and no repetition of the experiment will change which. What varies from sample to sample is the interval, so the 95% has to be a property of the rule that produced it.
import numpy as np
from scipy.stats import binom, norm
z = norm.ppf(0.975)
n, p = 200, 0.6
k = np.arange(n + 1)
ph = k / n
d = 1 + z**2 / n
center = (ph + z**2 / (2 * n)) / d
half = z * np.sqrt(ph * (1 - ph) / n + z**2 / (4 * n**2)) / d
lo, hi = center - half, center + half
covers = (lo <= p) & (p <= hi)
w = binom.pmf(k, n, p)
"""p is fixed at 0.6 in every line below. What changes from sample to sample
is the interval, so coverage is a property of the procedure, not of p."""
print(f" n = {n}, true p = {p}. Overall coverage {float(w @ covers):.4f}")
print(f"{'observed successes':>20}{'p-hat':>8}{'95% interval':>22}{'contains 0.6':>14}")
for kk in (104, 112, 120, 128, 136):
print(f"{kk:>20}{kk / n:>8.3f}"
f"{'[' + format(lo[kk], '.4f') + ', ' + format(hi[kk], '.4f') + ']':>22}"
f"{('yes' if covers[kk] else 'no'):>14}")
print(" each of those intervals either contains 0.6 or does not. None of them")
print(" is right 95% of the time - the 95% describes the rule that made them")
print(f"\n and the rule is not 95% on every kind of sample. Splitting the same")
print(f" outcomes by what was observed:")
print(f"{'observed p-hat':>26}{'share of samples':>18}{'coverage there':>16}")
bands = ((0.00, 0.55, "below 0.55"), (0.55, 0.58, "0.55 to 0.58"), (0.58, 0.62, "0.58 to 0.62"),
(0.62, 0.65, "0.62 to 0.65"), (0.65, 1.01, "0.65 and above"))
for a, b, name in bands:
m = (ph >= a) & (ph < b)
share = float(w @ m)
cov = float((w * m) @ covers) / share
print(f"{name:>26}{share:>18.4f}{cov:>16.4f}")
print(" the three middle bands cover every time. In the outer bands coverage")
print(" falls to 0.5979 and 0.7071 - and those two bands are 15% of all samples.")
print(" You know your observed band; its true coverage depends on unknown p. That")
print(" is why the guarantee is written about the long run and not about the")
print(" interval in front of you")
rng = np.random.default_rng(0)
runs = 200_000
draws = binom.ppf(rng.random(runs), n, p) / n
"""Counts come from uniform draws through the binomial quantile function,
so the figures do not depend on a particular NumPy version."""
print(f"\n the parameter does not move. Across {runs:,} samples:")
print(f" true p {p:.6f} (the same number every time)")
print(f" mean of p-hat {draws.mean():.6f}")
print(f" sd of p-hat {draws.std(ddof=1):.6f} (this is what varies)")
print(f" range of p-hat {draws.min():.4f} to {draws.max():.4f}")
print(" 'the true population parameter' is not a claim that some number is")
print(" knowable. It is the statement that the target of the estimate is a")
print(" constant, so the randomness in a result belongs to the sampling process.")
# n = 200, true p = 0.6. Overall coverage 0.9489
# observed successes p-hat 95% interval contains 0.6
# 104 0.520 [0.4510, 0.5882] no
# 112 0.560 [0.4907, 0.6270] yes
# 120 0.600 [0.5308, 0.6654] yes
# 128 0.640 [0.5714, 0.7033] yes
# 136 0.680 [0.6125, 0.7407] no
# each of those intervals either contains 0.6 or does not. None of them
# is right 95% of the time - the 95% describes the rule that made them
#
# and the rule is not 95% on every kind of sample. Splitting the same
# outcomes by what was observed:
# observed p-hat share of samples coverage there
# below 0.55 0.0655 0.5979
# 0.55 to 0.58 0.1917 1.0000
# 0.58 to 0.62 0.4348 1.0000
# 0.62 to 0.65 0.2236 1.0000
# 0.65 and above 0.0844 0.7071
# the three middle bands cover every time. In the outer bands coverage
# falls to 0.5979 and 0.7071 - and those two bands are 15% of all samples.
# You know your observed band; its true coverage depends on unknown p. That
# is why the guarantee is written about the long run and not about the
# interval in front of you
#
# the parameter does not move. Across 200,000 samples:
# true p 0.600000 (the same number every time)
# mean of p-hat 0.599913
# sd of p-hat 0.034592 (this is what varies)
# range of p-hat 0.4300 to 0.7500
# 'the true population parameter' is not a claim that some number is
# knowable. It is the statement that the target of the estimate is a
# constant, so the randomness in a result belongs to the sampling process.
Why the distinction earns its keep
The conditional table is the part worth carrying around. The procedure covers 0.6 about 95% of the time overall, but split the same outcomes by what was observed and coverage is 1.0000 in the middle bands and 0.5979 and 0.7071 in the two outer ones. Those outer bands are 15% of all samples. Nothing is broken — the numbers average to the guarantee — but they show that “95%” cannot be transferred to the interval in your hand, because the observed band is known but its conditional coverage depends on \(p\), which is the thing you do not know.
The same asymmetry explains the shape of hypothesis testing. A null hypothesis such as \(H_0: \mu = 100\) is a claim about a fixed number, so it is not assigned a probability either. A p-value is computed the only way it can be — as a probability over samples, assuming the parameter takes the value stated. Reading it as the probability that the hypothesis is true makes the parameter random and the data fixed, which is exactly backwards.
Bayesian inference does put a distribution on the parameter, and that changes what may be said, not what the parameter is. A posterior describes a state of knowledge about a value that is still fixed, which is why a credible interval can be read as “95% probability that \(p\) is in here” and a confidence interval cannot. The cost is a prior. Neither framework recovers the parameter itself.
What the phrase is really asking you to do
Calling a parameter “true” commits you to naming a population before collecting anything. “The true conversion rate” is not defined until someone says which users, over what period, under what product. Change any of those and the constant being estimated changes, without any sample having been wrong.
It also commits you to the claim that the constant exists during the study. A true conversion rate that drifts while the experiment runs is not a parameter being estimated with noise; it is a target that moved, and an interval computed from a single constant-rate model does not report that. Analyses that define the target explicitly, such as the average over a stated period or the rate at a specific time, remain possible. The fixed-but-unknown model is an assumption about the world, and it is the first thing to check when an estimate keeps landing outside its own intervals.
References
- SciPy documentation,
scipy.stats.binomandscipy.stats.norm. docs.scipy.org
Discover more from Insightful Data Lab
Subscribe to get the latest posts sent to your email.
