功能说明
Bootstrap 不假设数据服从正态分布,而是把已有样本当作总体,反复有放回抽样并计算统计量,用经验分布的分位数作为置信区间。它适合延迟分位数、转化率、评分均值这类分布偏斜的指标,也适合样本量只有几十条时快速给出不确定性范围。
函数接受任意统计量回调,默认均值,也可以传 statistics.median 或自定义分位数函数。返回值包含下界、上界和原始样本上的点估计,方便直接打印或写入监控报告。
关键代码
每次迭代按样本长度抽取同等数量的元素,允许重复,然后调用统计量函数。所有估计值排序后,按 1 减置信度再除以二得到尾部比例,取对应下标即为区间端点;上界下标做了越界保护。随机源使用独立的 random.Random(seed),不会影响全局随机状态,因此测试可以完全复现。
使用注意
迭代次数太少会让区间抖动,示例要求至少一百次,生产环境常用两千到一万次。Bootstrap 假设样本独立同分布,存在时间相关性或分组结构时应使用块采样或分层采样。样本量极小时区间会非常宽,这是真实不确定性而非缺陷。对最大值、最小值这类极值统计量,普通 Bootstrap 表现较差。传入的统计量必须能处理长度为样本量的序列,且不能修改输入。
验证方式
示例延迟数据均值为 20.35,打印结果形如 mean=20.35,区间应覆盖该值并明显向上偏斜,因为 40 和 55 两个长尾点拉高了上界。固定 seed 为 42 时两次运行输出完全一致;把 seed 改为其他值,区间端点会小幅变化。将统计量换成中位数后,区间明显收窄,说明长尾对中位数影响更小。
代码示例
import random
import statistics
from collections.abc import Callable, Sequence
def bootstrap_confidence_interval(
sample: Sequence[float],
*,
statistic: Callable[[Sequence[float]], float] = statistics.mean,
iterations: int = 2000,
confidence: float = 0.95,
seed: int | None = None,
) -> tuple[float, float, float]:
if not sample:
raise ValueError("sample must not be empty")
if not 0 < confidence < 1:
raise ValueError("confidence must be between 0 and 1")
if iterations < 100:
raise ValueError("use at least 100 iterations")
rng = random.Random(seed)
size = len(sample)
estimates: list[float] = []
for _ in range(iterations):
resample = [sample[rng.randrange(size)] for _ in range(size)]
estimates.append(statistic(resample))
estimates.sort()
alpha = (1 - confidence) / 2
lower = estimates[int(alpha * iterations)]
upper = estimates[min(iterations - 1, int((1 - alpha) * iterations))]
return lower, upper, statistic(sample)
def main() -> None:
latencies = [12.0, 15.5, 11.2, 40.0, 13.8, 14.1, 16.0, 12.9, 55.0, 13.0]
lower, upper, observed = bootstrap_confidence_interval(
latencies, iterations=4000, seed=42
)
print(f"mean={observed:.2f} 95% CI=({lower:.2f}, {upper:.2f})")
median_lower, median_upper, _ = bootstrap_confidence_interval(
latencies,
statistic=statistics.median,
iterations=4000,
seed=7,
)
print(f"median CI=({median_lower:.2f}, {median_upper:.2f})")
if __name__ == "__main__":
main()