50 分钟真实数据快照已执行的网页阅读版

对真实测量做 Bootstrap 分析

对真实 Iris 组内测量重采样,比较百分位区间和标准误公式,并说明采样假设。

数学与统计 · 第 05 课🔋 离线核心分析⏱ 50 分钟内嵌真实数据📦 numpy · pandas · matplotlib · IPython

目标与运行方法

本课适合具备 Python 基础的本科生。先阅读每一步的方法与图注,再修改参数。AI 练习为可选部分,不需要 AI 账号即可完成核心课程。数据写在 notebook metadata 中,不需要另下载数据文件。请保持 notebook 已保存到当前工作文件夹。

学习任务是理解本课的研究问题、执行透明的计算、校验结果,并说明结论的边界。

环境与参数

In [1] · Python
from pathlib import Path
import base64, gzip, hashlib, json, platform
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, HTML
from IPython.display import display as _display
def display(value):
    if isinstance(value,pd.DataFrame):
        table=value.to_html(max_rows=12,escape=True)
        style="<style>.vibeit-readable-table{max-width:100%;overflow-x:auto}.vibeit-readable-table table{width:max-content;max-width:none;border-collapse:collapse}.vibeit-readable-table td,.vibeit-readable-table th{white-space:nowrap!important;word-break:normal!important;overflow-wrap:normal!important}</style>"
        return _display(HTML(style+'<div class="vibeit-readable-table">'+table+'</div>'))
    return _display(value)
RECIPE_ID='ma05-bootstrap'
OUTPUT_DIR=Path.cwd()/"results"/RECIPE_ID
plt.rcParams.update({"figure.dpi":120,"font.size":10,"axes.spines.top":False,"axes.spines.right":False,
                      "axes.prop_cycle":plt.cycler(color=["#18785f","#416aa6","#bc6541","#9974a5"])})
print("Python:",platform.python_version(),"; NumPy:",np.__version__,"; Pandas:",pd.__version__)
print("Working folder:",Path.cwd())
print("Core data mode: embedded snapshot; no network requests")
Python: 3.13.13 ; NumPy: 2.5.3 ; Pandas: 3.0.6
Working folder: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads
Core data mode: embedded snapshot; no network requests

读取内嵌快照

In [2] · Python
def load_snapshot(recipe_id, expected_sha256, snapshot_key):
    """Read embedded data from this notebook, including a renamed copy."""
    for path in sorted(Path.cwd().glob("*.ipynb")):
        try:
            notebook = json.loads(path.read_text(encoding="utf-8"))
            metadata = notebook.get("metadata", {}).get("vibeit_cookbook", {})
            blob = metadata.get("snapshots", {}).get(snapshot_key)
            if metadata.get("recipe_id") != recipe_id or not blob:
                continue
            if blob.get("sha256") != expected_sha256:
                continue
            raw = gzip.decompress(base64.b64decode(blob["gzip_base64"], validate=True))
            if hashlib.sha256(raw).hexdigest() != expected_sha256:
                raise ValueError("Embedded snapshot checksum mismatch: " + snapshot_key)
            return json.loads(raw)
        except (OSError, json.JSONDecodeError):
            continue
    raise FileNotFoundError(
        "Save/open the complete .ipynb in the active working folder. "
        "The embedded snapshot could not be located for " + recipe_id)

SNAPSHOT_HASHES={'iris': 'cb2512f34911979eef3a42b7a9e6fbc6ca566fd2c0eaa7bd86daefd7ac1cf5ad'}
def snapshot(key):
    return load_snapshot(RECIPE_ID,SNAPSHOT_HASHES[key],key)
print("Embedded snapshots:",list(SNAPSHOT_HASHES))
Embedded snapshots: ['iris']

方法函数

In [3] · Python
def audit_numeric_table(frame,columns):
    """Audit observed rows; never silently impute or remove them."""
    if frame.empty or frame.index.has_duplicates:
        raise ValueError('Expected a nonempty table with unique row IDs')
    missing=[name for name in columns if name not in frame.columns]
    if missing:raise ValueError('Missing required columns: '+str(missing))
    values=frame[columns].to_numpy(dtype=float)
    if not np.isfinite(values).all():raise ValueError('Non-finite measurements; inspect missing values explicitly')
    return {'rows':len(frame),'features':len(columns),'finite':True,'duplicate_measurement_rows':int(frame[columns].duplicated().sum())}

print("Method ready: audit_numeric_table")
Method ready: audit_numeric_table
In [4] · Python
def bootstrap_mean(values,repetitions=2000,seed=42):
    """Ordinary iid bootstrap; exchangeability is a statistical assumption."""
    values=np.asarray(values,dtype=float)
    if values.ndim!=1 or len(values)<2 or not np.isfinite(values).all() or repetitions<2:
        raise ValueError('Need finite observations and at least two resamples')
    rng=np.random.default_rng(seed)
    resamples=rng.choice(values,size=(repetitions,len(values)),replace=True)
    means=resamples.mean(axis=1)
    return dict(estimate=float(values.mean()),means=means,percentile_95=np.quantile(means,[.025,.975]),standard_error=float(means.std(ddof=1)))

print("Method ready: bootstrap_mean")
Method ready: bootstrap_mean

数据与模型边界

解析函数与 Monte Carlo 点属于数学教学模型,不是实验测量。使用 Iris 观测时,可交换性和采样设计仍是统计假设;方便样本不自动代表总体。数值离散误差与采样不确定性应分别解释。

分步分析

1. 定义组内测量目标

使用来源中 50 条 setosa 花瓣长度,单位为厘米。在 iid 重采样模型下研究均值。Bootstrap 不会自动纠正采样偏差、重复植株或来源缺乏代表性。

In [5] · Python
iris=snapshot("iris")
frame=pd.DataFrame(iris["rows"],columns=iris["columns"])
features=list(frame.columns[:4])
audit=audit_numeric_table(frame,features)
assert len(frame)==150 and frame["species"].nunique()==3
assert (frame[features]>0).all().all()
print("Audit:",audit,"; measurement unit: cm")
print("Source variant:",iris["variant"])
display(frame.head(8))
observed=frame.loc[frame.species.eq("Iris-setosa"),"petal_length_cm"].to_numpy(float)
assert len(observed)==50
bootstrap=bootstrap_mean(observed,repetitions=2000,seed=42)
print("Observed mean (cm):",bootstrap["estimate"],"; percentile interval:",bootstrap["percentile_95"])
fig,ax=plt.subplots(figsize=(7,4));ax.hist(observed,bins=np.arange(1,2.11,.1),edgecolor="white")
ax.set(xlabel="Observed petal length (cm)",ylabel="Source observations",title="Empirical group measurements");plt.tight_layout();plt.show()
Audit: {'rows': 150, 'features': 4, 'finite': True, 'duplicate_measurement_rows': 3} ; measurement unit: cm
Source variant: Original UCI iris.data; no silent correction of the two documented transcription discrepancies
sepal_length_cm sepal_width_cm petal_length_cm petal_width_cm species
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
5 5.4 3.9 1.7 0.4 Iris-setosa
6 4.6 3.4 1.4 0.3 Iris-setosa
7 5.0 3.4 1.5 0.2 Iris-setosa
Observed mean (cm): 1.464 ; percentile interval: [1.416 1.51 ]
Computed figure from this code cell

2. 解读重采样均值分布

每次从相同 50 条测量中有放回抽取 50 个值。直方图是 2000 个均值,不是 2000 株新测量植物。着色范围表示给定重采样模型的百分位区间。

In [6] · Python
fig,ax=plt.subplots(figsize=(7,4));ax.hist(bootstrap["means"],bins=35,edgecolor="white")
ax.axvspan(*bootstrap["percentile_95"],alpha=.2,color="orange",label="Percentile 95% range")
ax.axvline(bootstrap["estimate"],color="black",linestyle="--",label="Observed mean")
ax.set(xlabel="Bootstrap mean (cm)",ylabel="Resamples",title="Conditional bootstrap mean distribution");ax.legend();plt.tight_layout();plt.show()
Computed figure from this code cell

3. 检查重采样次数的影响

增加重采样次数会改变 Monte Carlo 误差,但原始 50 条观测不变。更多重采样改善条件分布的数值稳定性,不会扩大实际观测样本。

In [7] · Python
counts=[100,300,1000,2000];intervals=np.array([np.quantile(bootstrap["means"][:n],[.025,.975]) for n in counts])
fig,ax=plt.subplots(figsize=(7,4));ax.plot(counts,intervals[:,0],"o-",label="2.5th percentile");ax.plot(counts,intervals[:,1],"o-",label="97.5th percentile")
ax.set(xlabel="Resamples used",ylabel="Percentile endpoint (cm)",title="Monte Carlo stability with fixed source data");ax.legend();plt.tight_layout();plt.show()
Computed figure from this code cell

4. 检查重采样变异并导出

经验分布的均值 Bootstrap 标准误为总体标准差/sqrt(n)。与实际重采样标准差比较,并允许有限重采样误差。教科书样本标准差/sqrt(n) 是另一有限样本估计。

In [8] · Python
empirical_se=observed.std(ddof=0)/np.sqrt(len(observed));sample_se=observed.std(ddof=1)/np.sqrt(len(observed))
assert np.isclose(bootstrap["standard_error"],empirical_se,rtol=.08)
assert bootstrap["percentile_95"][0]<=bootstrap["percentile_95"][1]
print("Empirical-distribution SE:",empirical_se,"; sample-SD SE:",sample_se,"; resample SE:",bootstrap["standard_error"])
OUTPUT_DIR.mkdir(parents=True,exist_ok=True)
pd.DataFrame({"bootstrap_mean_cm":bootstrap["means"]}).to_csv(OUTPUT_DIR/"bootstrap-means.csv",index=False)
(OUTPUT_DIR/"bootstrap-summary.json").write_text(json.dumps({"source_group":"Iris-setosa","n":50,"resamples":2000,"seed":42,"mean_cm":bootstrap["estimate"],"percentile_95_cm":bootstrap["percentile_95"].tolist(),"empirical_distribution_SE_cm":empirical_se,"resample_SE_cm":bootstrap["standard_error"],"assumption":"iid exchangeable observations; source sampling bias not corrected"},indent=2))
print("Bootstrap variance check passed; exports:",OUTPUT_DIR)
Empirical-distribution SE: 0.024291562321102363 ; sample-SD SE: 0.02453818348981016 ; resample SE: 0.023742351476355786
Bootstrap variance check passed; exports: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads/results/ma05-bootstrap

自主练习与参考答案

  1. 为何重采样均值不代表新植物?
  2. 经验 Bootstrap 方差恒等式为何使用总体标准差?

参考解释。 重采样重复使用观测经验分布,其方差分母为 n;常规样本估计用 n−1。两者均不能消除采样偏差。

先保留当前 notebook 副本,再修改参数。把新图与原图一起比较,并记录改变了哪些输入、哪些计算和哪些结论。

可选的 AI 编程练习

提示词 1

仅用 NumPy、Pandas、Matplotlib 对另一个已声明物种组重复 Bootstrap,保留厘米单位及采样假设。

提示词 2

使用同样的包比较三个独立种子的区间端点,不将其当作新增观测。

人工验收。 观测 n=50 不变,区分重采样次数与观测数,并核对经验分布标准误。

检查修改后的源代码,再手动运行受影响单元及后续校验。要求助手解释修改的目的、使用的包和数据来源,并用实际输出核对解释。

可选联网扩展

In [9] · Python
RUN_ONLINE_UPDATE=False
if RUN_ONLINE_UPDATE:
    import requests
    try:
        response=requests.get('https://archive.ics.uci.edu/dataset/53/iris',timeout=20)
        response.raise_for_status()
        print("Source:",response.url)
        print(response.text[:700])
    except requests.RequestException as error:
        print("Online extension unavailable:",type(error).__name__,str(error)[:160])
else:
    print("Online extension skipped; embedded core data unchanged")
Online extension skipped; embedded core data unchanged

来源、快照与下一步

  • UCI iris — CC BY 4.0; credit UCI and original dataset authors; retrieved 2026-10-09. SHA-256 d11fe30213d36434a0879aab7cb00ce3c812eb7ba2495874438abff7b7b762e9. Extract iris.data from original download; preserve all rows and declared units; no imputation. Fisher, R. (1936). Iris. UCI Machine Learning Repository. DOI: 10.24432/C56C76.

下一步。 在保留本课的数据检查、参数记录和结果校验之后,将相同方法用于自己的公开或已获授权数据。记录研究设计与方法限制,不把示例结果当成新实验的验证。完整数据许可与生成说明见 cookbook 网页。