本章深入探讨了数据类型与抽样设计,核心概念包括数据分类四象限、测量尺度和抽样误差来源。数据分类四象限将数据分为定量、分类、离散和连续四类,并指出实际应用中常见的混淆,例如将排名、电话号码和年份误认为定量数据。测量尺度则细分为定类、定序、定距和定比四类,0 的绝对意义是区分定距和定比的关键。抽样误差来源于随机误差、抽样偏差和测量误差,需通过增加样本量、改进抽样设计和校准工具来控制。读者将学会区分不同数据类型和测量尺度,理解抽样误差的来源和量化方法,并掌握实验设计的三大原则:随机化、区组化和重复。这些知识将帮助读者在实际应用中选择合适的统计方法,设计有效的实验,并识别和减少数据偏差。
数据类型与抽样设计
本章问题: 为什么 sklearn 对"性别"列用 OneHotEncoder, 对"年龄"列用 StandardScaler? 因为它们是不同类型的数据。本章把"数据类型"讲透, 这是选统计方法的第一关。
1. 数据分类四象限 (回顾 + 深入)
上一章我们把数据分成了 4 类:
- 定量 (Quantitative): 数字, 可加减, 有单位
- 分类 (Qualitative): 类别、标签、不能算平均
- 离散 (Discrete): 整数、可数
- 连续 (Continuous): 实数、不可数
但实务中, 这 4 类经常被混淆, 下面 3 个最常见的坑:
坑 1: "把排名当数字"
问卷结果: "请为这个商品打分 1-5"
得到的 "1, 2, 3, 4, 5" 看起来是数字
但本质是: 1=非常不满意, 5=非常满意 → 这叫"有序型分类"
→ 算 "平均分 3.8" 在数学上对, 但统计上"3.8"没有意义
坑 2: "把电话号码当数字"
手机号 13812345678 — 13 位数字, 但前 7 位是运营商 + 地区
→ 算"平均手机号" = 1.38e10 没有任何意义
→ 应该当"标签型分类"处理
坑 3: "把年份当连续"
2020, 2021, 2022, 2023 看起来是连续整数
但年份是"时间标签", 间距相等, 但加权和减权不总有意义
→ 业务上是时间序列数据, 不是简单连续定量
2. 测量尺度:从粗到精的 4 级
统计学家 Stanley Smith Stevens 提出了更精细的"测量尺度"分类:
| 尺度 | 允许的运算 | 例子 | 中心趋势 |
|---|---|---|---|
| 定类 (Nominal) | 仅判等/不等 | 性别、血型 | 众数 |
| 定序 (Ordinal) | + 大小比较 | 满意度、排名 | 中位数、众数 |
| 定距 (Interval) | + 加减 | 温度(°C)、年份 | 均值、方差 |
| 定比 (Ratio) | + 乘除 | 身高、体重、收入 | 几何均值 |
💡 关键区别: 温度 0°C 不是"没有温度", 所以是定距; 身高 0 cm 是"没有身高", 所以是定比。0 是否有"绝对意义"决定了能否算比率。
ML 应用:
- 树模型 (XGBoost, LightGBM): 不在意尺度, 定序也能用
- 线性模型 / 神经网络: 严格区分定距 vs 定比 (因为要乘系数)
- 距离类模型 (KNN, K-means, SVM): 必须标准化, 否则定比变量压制定距变量
3. 抽样误差:不可消除但可量化
任何抽样都有误差, 这不是统计学的缺陷, 而是物理事实。关键是把误差量化出来。
抽样误差的 3 个主要来源:
| 来源 | 解释 | 控制方法 |
|---|---|---|
| 随机误差 | 抽到不同样本, 结果会波动 | 增加样本量 (误差 ∝ 1/√n) |
| 抽样偏差 | 抽样框本身就不代表总体 | 改进抽样设计 (分层抽样) |
| 测量误差 | 测不准 (仪器/问卷设计) | 校准工具、培训调查员 |
🎯 机器学习里的"抽样": 训练集/测试集划分就是抽样。sklearn 的
train_test_split用随机抽样,StratifiedShuffleSplit用分层抽样 — 跟我们这章讲的是一回事!
4. 实验设计三原则
Triola 总结了实验设计的 3 个核心原则 (出自 R.A. Fisher):
4.1 随机化 (Randomization)
目的: 让"未知混杂因素"在处理组/对照组间均匀分布。
# 错: 便利抽样
treatment = df[df["age"] > 30] # 老年人进实验组
control = df[df["age"] <= 30] # 年轻人进对照组
# 任何年龄相关的差异, 都不是处理效应
# 对: 随机分配
np.random.seed(42)
df["group"] = np.random.choice(["treatment", "control"], size=len(df))
# 平衡了年龄、性别等所有潜在混杂
4.2 区组化 (Blocking)
目的: 对已知的重要变量,先匹配再随机。把"苹果比苹果, 橙子比橙子"。
# 实验: 比较两种降压药
# 已知"年龄"影响血压, 先按年龄段分组
df["age_block"] = pd.cut(df["age"], bins=[0, 40, 60, 100], labels=["young", "mid", "old"])
# 在每个区组内随机分配药物
df["treatment"] = df.groupby("age_block", group_keys=False).apply(
lambda g: np.random.choice(["drug_A", "drug_B"], size=len(g))
)
4.3 重复 (Replication)
目的: 同一处理下多次实验, 得到误差的估计。没有重复就没有统计显著性。
# 每个 (年龄区组 × 药物) 组合做 30 个病人
sample_size_per_cell = 30
df_experiment = df.groupby(["age_block", "treatment"]).apply(
lambda g: g.sample(n=min(len(g), sample_size_per_cell), random_state=42)
).reset_index(drop=True)
5. 抽样框与覆盖率偏差
抽样框 (Sampling Frame) = 你能"接触到"的名单。理想情况 = 总体, 实际总有差异。
| 偏差类型 | 例子 | 后果 |
|---|---|---|
| 覆盖率过低 | 1936 文学摘要用电话簿调查总统大选 | 大萧条时期穷人家没电话, 全被遗漏 |
| 覆盖率偏高 | 微信调查消费者偏好 | 没微信的老年人/小孩被低估 |
| 志愿者偏差 | 网上问卷 | 自愿参与者 ≠ 真实用户 |
⚠️ 机器学习中的偏差: 训练数据 = 抽样框, 如果它不代表真实世界, 模型上线必失败。这就是为什么 NLP 模型对"非主流方言"表现差, 推荐系统对"长尾用户"不友好。
6. Python 实战: 数据类型自动识别 + 抽样偏差检测
import pandas as pd
import numpy as np
# 模拟一个真实的用户数据集
np.random.seed(42)
n = 5000
df = pd.DataFrame({
"user_id": range(n),
"age": np.random.normal(35, 12, n).clip(18, 80).astype(int),
"income": np.random.lognormal(10.5, 0.5, n), # 收入对数正态
"platform": np.random.choice(["iOS", "Android", "Web"], n, p=[0.4, 0.5, 0.1]),
"satisfaction": np.random.choice([1, 2, 3, 4, 5], n, p=[0.05, 0.1, 0.3, 0.4, 0.15]),
})
# === 自动识别每列的数据类型和测量尺度 ===
def classify_column(s: pd.Series) -> str:
if s.dtype in ["int64", "float64"]:
nunique = s.nunique()
if nunique <= 20:
return "quantitative-discrete"
return "quantitative-continuous"
# 分类
if s.dtype.name == "category" and hasattr(s.cat, "ordered") and s.cat.ordered:
return "qualitative-ordinal"
return "qualitative-nominal"
for col in df.columns:
kind = classify_column(df[col])
print(f" {col:15s} → {kind}")
# === 抽样偏差检测: 样本 vs 总体的分布差异 ===
# 假设总体年龄段分布 (来自人口普查)
population_age_dist = pd.Series({
"18-25": 0.15, "26-35": 0.25, "36-45": 0.20,
"46-55": 0.20, "56+": 0.20
})
# 我们数据里的分布
df["age_group"] = pd.cut(df["age"], bins=[18, 25, 35, 45, 55, 100],
labels=["18-25", "26-35", "36-45", "46-55", "56+"])
sample_age_dist = df["age_group"].value_counts(normalize=True).sort_index()
# 卡方检验看是否有显著差异
from scipy.stats import chisquare
stat, p_value = chisquare(f_obs=sample_age_dist.values,
f_exp=population_age_dist.values)
print(f"\n年龄段分布 χ² 检验: stat={stat:.2f}, p={p_value:.3f}")
if p_value < 0.05:
print("⚠️ 样本与总体分布显著不同, 存在抽样偏差!")
7. 小结
| 你学到了 | 关键点 |
|---|---|
| 数据类型 4 象限 | 定量/分类 × 离散/连续, 决定可用方法 |
| 测量尺度 4 级 | 定类 < 定序 < 定距 < 定比, 0 的意义是关键 |
| 抽样误差的 3 来源 | 随机、偏差、测量 — 都需要量化 |
| 实验设计 3 原则 | 随机化、区组化、重复 — Fisher 1935 |
| 抽样框 | 永远是"你接触到的 ≠ 真实世界", 任何统计推断都有前提 |
8. 习题
-
下面哪些是"定比"变量? 哪些是"定距"?
- 温度(°C)
- 温度(K)
- 年龄(岁)
- 考试成绩(0-100)
- 银行账户余额
-
用卡方检验, 判断
df["platform"]列是否与总体分布 (iOS=40%, Android=50%, Web=10%) 一致。如果不一致, 说明什么问题?
👉 查看参考答案
-
答案:
- 温度(°C) — 定距 (0°C 不是"没有温度")
- 温度(K) — 定比 (0K 是"绝对零度", 有意义)
- 年龄(岁) — 定比 (0 岁有意义)
- 考试成绩(0-100) — 定距 (0 分不是"不知道成绩", 是真的零分, 但通常按定距处理, 因为不存在"分数比")
- 银行账户余额 — 定比 (0 元 = 真没余额, 余额可以算比率)
-
如果
chisquarep < 0.05, 说明 Web 平台样本比例与总体差异显著 — 可能是 Web 用户更愿意填写问卷, 出现"志愿者偏差"。解决方案: 主动邀请 Web 平台用户填问卷, 或对 Web 平台加权。
9. 下一章
📚 本章来源: 改编自 Triola《基础统计学》第 14 版 第 1 章 1-2、1-3 节, 加入 Python 抽样实战。
章末小测验
检验你对《数据类型与抽样设计》的掌握程度。
以下关于数据类型的描述,哪一项是正确的?
在机器学习模型中,以下哪种数据处理方法适用于定序数据?
关于抽样误差,以下说法正确的是:
以下关于测量尺度的描述,哪一项是错误的?
在实验设计中,随机化的主要目的是: