codexhorvitz-thompson-three-unit-design.py
horvitz-thompson-three-unit-design.py
"""Exact enumeration of a three-unit unequal-probability sampling design."""
from fractions import Fraction as F
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
samples = [(0, 1), (0, 2), (1, 2)]
probabilities = [F(1, 2), F(1, 3), F(1, 6)]
inclusion = [F(5, 6), F(2, 3), F(1, 2)]
values = [F(2), F(4), F(6)]
weighted = [sum(values[i]/inclusion[i] for i in s) for s in samples]
naive = [F(3, 2)*sum(values[i] for i in s) for s in samples]
assert sum(p*t for p, t in zip(probabilities, weighted)) == 12
assert sum(p*t for p, t in zip(probabilities, naive)) == 11
assert sum(p*(t-12)**2 for p, t in zip(probabilities, weighted)) == F(72, 5)
estimates = []
for sample, pij in zip(samples, probabilities):
i, j = sample
v = sum((1-inclusion[k])*(values[k]/inclusion[k])**2 for k in sample)
v += 2*(pij-inclusion[i]*inclusion[j])/pij*(values[i]/inclusion[i])*(values[j]/inclusion[j])
estimates.append(v)
assert estimates == [F(244, 25), F(1464, 25), F(-60)]
assert sum(p*v for p, v in zip(probabilities, estimates)) == F(72, 5)
fig, (chance, totals) = plt.subplots(1, 2, figsize=(9.6, 4.2), dpi=100,
layout="constrained", width_ratios=[1, 1.5])
fig.set_facecolor("white")
x = np.arange(3)
labels = ["{1, 2}", "{1, 3}", "{2, 3}"]
bars = chance.bar(x, [float(p) for p in probabilities], color="#577d9d", width=.65)
for bar, label in zip(bars, ["1/2", "1/3", "1/6"]):
chance.text(bar.get_x()+bar.get_width()/2, bar.get_height()+.015,
label, ha="center")
chance.set(xticks=x, xticklabels=labels, xlabel="Selected sample",
ylabel="Selection probability", ylim=(0, .62),
title="Unequal chances of selecting each pair")
totals.bar(x-.18, [float(t) for t in weighted], width=.36,
label="Horvitz–Thompson total", color="#1565a5")
totals.bar(x+.18, [float(t) for t in naive], width=.36,
label="Three × sample mean", color="#c56427")
totals.axhline(12, color="#555555", ls="--", lw=1.2, label="True total = 12")
totals.set(xticks=x, xticklabels=labels, xlabel="Selected sample", ylabel="Estimated total",
ylim=(0, 22), title="Expected totals: 12 versus 11")
totals.legend(fontsize=8, loc="upper left")
for ax in [chance, totals]:
ax.grid(axis="y", alpha=.15)
ax.set_axisbelow(True)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()