Repository navigation
v2.0.0
Published to PyPI: https://pypi.org/project/fewlab/2.0.0/
The sampler did not deliver the inclusion probabilities it reported, so every
estimator weighted by 1 / pi was biased. Anyone who has used Design.sample,
balanced_fixed_size or core_plus_tail for a real estimate should re-draw the
sample and re-estimate.
Fixed
-
balanced_fixed_sizenow delivers its stated inclusion probabilities. It
drewbudgetitems withrng.choice(..., replace=False, p=pi)and then swapped
items greedily to improve balance. Neither step preservespi: sequential
without-replacement draws condition on what came before, and the swap decisions
depend on the data. On the package's own test fixture (make_synth(n=60, m=120),
budget 40) items withpi == 1were included 74-78% of the time before the
swaps and 45-93% after, and the correlation between empirical and stated
inclusion rates was 0.836.calibrated_ht_estimatorwas measurably biased as a
result -- row 0 was off by 4.28 Monte Carlo standard errors over 400 replicates.The sampler is now the cube method (Deville and Tille 2004), which holds the
inclusion probabilities, the fixed sample size and the balancing constraints
simultaneously. On the same fixture: certainty items included exactly 100% of
the time, correlation 0.9998, largest standardised deviation 2.08 sigma across
120 items over 4000 draws, sample size exactly the budget every time. It is also
roughly 3x faster, being O(m) rather than a bounded local search. -
core_plus_tailreports the design it runs. It returned the A-optimalpi
it optimised over, but selects its core deterministically, so core items are
included with probability one. Weighting them by1 / pi_j > 1inflated exactly
the items chosen for having the most influence.probabilitiesandht_weights
now describe the executed design: one for the core, and the tail's own rescaled
probabilities for the tail. -
Design.sample(method="balanced")reports the delivered probabilities.
pi_aopt_for_budgethits its budget to a solver tolerance rather than exactly;
the reported vector is now the one the fixed-size design realises. -
calibrate_weights(nonneg=True)no longer breaks the constraint it solves.
It computed the exact chi-square calibration and then clipped negative weights
to zero, which silently violatesG_S w = t-- the equation the function
exists to satisfy. On a 100-item fixture the raw solution went negative in 41
draws out of 200, and in 10 of those the clipped weights reproduced the
population totals worse than the unadjusted Horvitz-Thompson weights. It now
pins offenders at the floor and re-solves over the rest, restoring the
constraint exactly (largest residual over 200 draws: 2.8e-07) with no negative
weights. When too few weights remain free to carry the constraints it warns
rather than returning a silently wrong answer. -
scale_pi_to_budgetrejects a budget its positive entries cannot carry.
Scaling leaves a zero at zero, soscale_pi_to_budget([0, 0, 0.5], 2)returned
a vector summing to 1 and produced a sample quietly smaller than requested. It
now raises. -
The cube landing phase settles leftovers by systematic pi-ps rather than
independent Bernoulli draws. Both preserve the marginals, but only the former
preserves the count, so accumulated floating-point drift in the constraint rows
can no longer cost or gain a whole item.
Added
scale_pi_to_budget, the rescaling the sampler applies.sum(pi)is the
expected sample size, so a fixed-size design ofbudgetitems is only coherent
when the probabilities sum tobudget. Exported so callers can compute the
delivered probabilities themselves.tests/test_sampling_design.py: Monte Carlo tests, via
simcheck, that the empirical
inclusion rate of every item matches its stated probability, that certainty
items are always selected, and thatcalibrated_ht_estimatoris unbiased row by
row. Each gate derives its tolerance from the replicate count, and the
unbiasedness test has a falsification partner that must be caught as biased.
The suite previously checked only that the sample had the right length, which
a sampler ignoringpientirely also passes.
Removed
- Breaking:
balanced_fixed_sizeno longer takesmax_swapsortol. Both
parameterised the greedy search that has been removed; the cube method has no
corresponding knobs. No caller in the package passed either.