在处理数据分析和构建机器学习模型时,我们经常面临一个基础但至关重要的问题:如何准确地总结数据的中心趋势?
平均值 和 中位数 是两个最常用的统计指标,虽然它们看似简单,但在面对真实世界的复杂数据时,选择哪一个往往直接决定了模型的鲁棒性和最终结论的可信度。在这篇文章中,我们将深入探讨这两者的定义、背后的数学原理,以及——更重要的是——如何在不同的数据分布和业务场景中做出正确的选择。
我们将通过代码示例、可视化和实际应用场景,看看这两个指标是如何影响我们的数据分析结果的。让我们开始吧!
什么是平均值?
平均值,或者我们常说的算术平均数,是衡量数据集中趋势最直观的指标。它的计算逻辑非常简单:将所有数值相加,然后除以数值的数量。在数学上,这代表数据的“重心”。
为什么平均值如此重要?
由于平均值利用了数据集中的每一个数据点,它包含的信息量最大。在统计学中,它被称为充分统计量。这也是为什么在许多经典机器学习算法中,平均值占据着核心地位。
平均值的核心应用场景
#### 1. 特征归一化和缩放
这是数据预处理中最重要的步骤之一。许多机器学习算法(如支持向量机、神经网络)对数据的尺度非常敏感。如果特征之间的尺度差异很大,模型可能会被误导。
Z-score 标准化 是最常用的技术之一,它的核心就是平均值。公式如下:
$$ z = \frac{x – \mu}{\sigma} $$
其中 $\mu$ 是平均值,$\sigma$ 是标准差。通过这个变换,我们可以将数据转化为均值为 0,标准差为 1 的分布。
代码实战:Z-score 标准化
让我们看看如何使用 Python 的 scikit-learn 来实现这一过程。在这里,我们不仅计算平均值,还利用它来“校准”我们的数据。
import numpy as np
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
# 假设我们有一个包含两个特征的数据集
# 特征1:年龄 (范围 20-60)
# 特征2:收入 (范围 50000-100000)
data = np.array([
[25, 50000],
[30, 60000],
[35, 80000],
[40, 85000],
[50, 120000] # 收入有一个较高的值
])
print("原始数据平均值:
", np.mean(data, axis=0))
# 初始化 StandardScaler
# 它会自动计算每个列的平均值和标准差
scaler = StandardScaler()
# 拟合并转换数据
scaled_data = scaler.fit_transform(data)
print("
标准化后的数据:
", scaled_data)
print("标准化后的平均值 (应非常接近 0):
", np.mean(scaled_data, axis=0))
在上面的代码中,你可以看到,通过减去平均值,我们将数据的中心移到了原点。这是模型能够高效学习的基础。
#### 2. 正态分布下的数据插补
当数据呈现完美的正态分布(钟形曲线)时,平均值、中位数和众数是重合的。在这种情况下,平均值代表了出现概率最高的值。因此,当我们遇到缺失值时,用平均值来填补是合理的,因为它不会引入偏差。
#### 3. 损失函数的核心
你可能听说过 MSE(均方误差) 或 MAE(平均绝对误差)。注意它们的名字都带有“平均”。
特别是线性回归,其目标函数本质上是最小化预测值与真实值之间的误差平方和。从数学上可以证明,为了最小化平方误差,最优解就是预测数据的平均值。这意味着,如果我们要用一个数字来尽可能准确地代表一组数据(在最小化平方误差的意义下),平均值是数学上的最佳选择。
#### 4. 图像处理中的像素归一化
在计算机视觉任务中,图像的像素值通常在 0 到 255 之间。为了加速神经网络的收敛,我们通常会将像素值减去平均值(例如 ImageNet 数据集的 RGB 平均值),然后除以标准差。
# 伪代码示例:图像预处理
# 假设 img 是一个 (Height, Width, 3) 的图像数组
# ImageNet 数据集的 RGB 平均值 (示例值)
mean_values = [123.68, 116.78, 103.94]
# 我们将图像减去这个平均值
# 这相当于将图像的“亮度中心”对齐到0
normalized_img = img - mean_values
如果不做这一步,不同的输入图片亮度差异可能会干扰模型提取特征。
平均值的软肋:异常值
虽然平均值很强大,但它有一个致命的弱点:对异常值极其敏感。
举个简单的例子:假设你和比尔·盖茨在一个房间里。
- 你的资产:10万美元
- 比尔·盖茨的资产:1000亿美元
- 平均值:约 500亿美元
这个“平均值”能代表你的财富状况吗?显然不能。这就是我们在分析偏态数据时必须小心的原因。在图像处理中,虽然异常值较少,但在金融欺诈检测或用户行为分析中,异常值随处可见。
什么是中位数?
中位数是数据集排序后位于中间位置的值。与平均值不同,计算中位数不需要知道具体的数值大小,只需要知道它们的排序。
计算逻辑
- 奇数个观测值:取中间的那个值。
- 偶数个观测值:取中间两个值的平均值。
这种计算方式赋予了中位数一个独特的性质:它只关心中间位置的值,而不关心极端值到底有多大。 这使得中位数成为了一种“稳健”的统计量。
中位数在实战中的优势
#### 1. 稳健的数据插补
当数据包含噪声、错误录入或极端异常值时(例如收入数据中混入了一位亿万富翁),使用平均值填充缺失值会人为地抬高整体水平。而中位数则能保持数据的“原汁原味”,代表大多数人的真实情况。
#### 2. 抗异常值的特征缩放
除了传统的 Z-score,还有一种缩放方法叫做 Robust Scaling(鲁棒缩放)。它不使用平均值和标准差,而是使用中位数和四分位距(IQR)。这对于包含离群点的数据集非常有效。
代码实战:鲁棒缩放 vs 标准缩放
让我们构造一个带有明显异常值的数据集,看看两者的区别。
from sklearn.preprocessing import RobustScaler, StandardScaler
import numpy as np
# 构造数据:大部分数据在 0-10 之间,但有一个异常值 1000
X = np.array([[1.0], [2.0], [3.0], [4.0], [5.0], [1000.0]])
# 标准化 (使用平均值)
standard_scaler = StandardScaler()
X_standard = standard_scaler.fit_transform(X)
# 鲁棒缩放 (使用中位数)
robust_scaler = RobustScaler()
X_robust = robust_scaler.fit_transform(X)
print(f"原始数据: {X.flatten()}")
print(f"标准化后 (受异常值影响,正常数据被压缩得很小):
{X_standard.flatten()}")
print(f"鲁棒缩放后 (正常数据依然保持良好的区分度):
{X_robust.flatten()}")
# 验证中位数的稳定性
print(f"
原始中位数: {np.median(X)}")
print(f"标准化数据的中位数: {np.median(X_standard)}")
# 注意:标准化改变了中位数,因为标准差被异常值拉得巨大
在这个例子中,你会注意到 INLINECODE9d04cf0d 产生的结果中,原本正常的 1, 2, 3, 4, 5 变得非常接近,因为那个 1000 把标准差撑得非常大,导致模型很难区分这些正常数据。而 INLINECODEaa77271e 则很好地保留了正常数据之间的差异。
#### 3. 树模型中的决策阈值
决策树算法通过寻找最佳分割点来划分数据。虽然决策树通常基于基尼系数或熵来分割,但在某些回归树或特定的分割逻辑中,使用中位数作为分割点可以有效地减少某一分支中的方差,且不会因为异常值导致分割点偏移。
实际案例:房地产定价
在预测房价时,我们经常遇到“豪宅”现象。某个区域的房价大多在 5 万到 10 万之间,但有一两套庄园卖到了 1000 万。
- 如果我们用平均房价来评估该区域的“典型”居住成本,这个数字会被豪宅严重拉高,导致对普通购房者产生误导。
- 如果我们用中位数房价,豪宅的影响将被忽略,我们得到的是“位于中间位置”的那套房子的价格,这更符合大多数人的市场感知。
深入比较:平均值 vs 中位数
为了让你在面试或实际工作中能清晰地做出选择,我们总结了以下关键差异:
平均值
:—
所有数值之和除以数量
假设数据呈对称分布(如高斯分布)。这是许多线性模型的理论基础。
极高。一个极端值可以任意改变平均值。
$O(N)$。只需一次遍历求和,适合流式数据处理。
线性回归、逻辑回归、K-Means、神经网络。
数据分布的视觉化影响
理解数据分布的形状是选择平均值还是中位数的关键。
- 对称分布:
数据像一座完美的钟形曲线。此时平均值 = 中位数。我们可以放心地使用平均值,因为它包含了所有信息且没有偏差。
- 右偏分布:
也称为正偏分布。比如个人收入。大部分人收入一般,但少数亿万富翁将曲线的“尾巴”拉向了右边。
– 在这种情况下,平均值 > 中位数。
– 平均值会被“拉”向长尾方向,高估了中心趋势。此时报告中位数更诚实。
- 左偏分布:
比如退休年龄。大部分人集中在 60-65 岁退休,但少数人在 20 岁就因病退休(极低值)。
– 在这种情况下,平均值 < 中位数。
– 平均值被向左拉低。
!Mean vs Median in Different Distributions
(图片展示了在不同偏态分布中,平均值如何被拖向“尾巴”的方向,而中位数则保持相对稳定)
代码实战:处理偏态数据
让我们通过一个 Python 脚本来模拟偏态数据,并观察两者的差异。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 设置随机种子以保证结果可复现
np.random.seed(42)
# 生成一个右偏的数据集:模拟1000个人的月薪
# 大部分人集中在 3000-8000
normal_salaries = np.random.normal(loc=5000, scale=1500, size=990)
# 添加 10 个CEO的高薪数据 (异常值)
exec_salaries = np.random.normal(loc=100000, scale=10000, size=10)
# 合并数据
salaries = np.concatenate([normal_salaries, exec_salaries])
# 计算统计量
mean_salary = np.mean(salaries)
median_salary = np.median(salaries)
print(f"平均月薪: {mean_salary:.2f}")
print(f"中位数月薪: {median_salary:.2f}")
# 解读差异
print(f"
差异分析: 平均值比中位数高出 {(mean_salary - median_salary):.2f} 元")
print("如果你用平均值来描述‘普通员工‘的工资,你会给出一个错误的过高预期。")
# 简单的可视化 (需要 matplotlib 环境)
# plt.hist(salaries, bins=50, edgecolor=‘black‘)
# plt.axvline(mean_salary, color=‘red‘, linestyle=‘dashed‘, linewidth=2, label=‘Mean‘)
# plt.axvline(median_salary, color=‘green‘, linestyle=‘dashed‘, linewidth=2, label=‘Median‘)
# plt.legend()
# plt.show()
运行这段代码,你会发现平均值可能远高于中位数。如果你正在制定针对普通员工的薪酬福利政策,依赖平均值可能会导致严重的误判。
常见错误与最佳实践
在与很多初级数据分析师交流时,我们发现了一些常见的误区。这里有几个避坑指南:
- 盲目使用平均值填充缺失值
错误:在包含大量异常值的列(如用户消费金额)直接使用 fillna(df.mean())。
正确:先检查数据的偏度。如果数据严重偏斜,使用 fillna(df.median())。
- 忽略模型对分布的假设
错误:在数据严重偏态且未做变换的情况下,直接将其输入到线性回归模型中。线性模型假设残差正态分布,严重的偏态会导致预测在极端值处偏差很大。
正确:对偏态数据进行对数变换,使其接近正态分布,然后再使用基于平均值的模型。
- 混淆 Scale 和 RobustScaler
场景:你需要处理用户的点击率数据,其中包含一些由于爬虫造成的极端高点击。
建议:抛弃 INLINECODE58ae9226,改用 INLINECODEf80dd425。或者,在计算指标时先清洗掉明显的离群点(例如 3倍标准差之外的数据),然后再算平均值。
总结与下一步
在数据科学和机器学习的旅途中,理解平均值和中位数的区别是最基础但也最关键的一步。
回顾一下核心要点:
- 平均值 是数学优化的宠儿,适合正态分布的数据,用于计算损失函数和归一化,但极易受异常值干扰。
- 中位数 是现实世界的守护者,稳健、抗干扰,是处理收入、房价、用户行为等偏态数据的最佳选择。
给你的建议:
下次当你拿到一份新的数据集时,不要只看 df.describe() 里的 Mean。花一分钟时间,比较一下 Mean 和 Median。如果它们差距很大,那这就不仅仅是一个数字游戏,而是数据在向你讲述一个关于“异常值”和“偏态”的故事。你需要根据这个故事来选择合适的模型和预处理策略。
现在,打开你的 Jupyter Notebook,找一份真实的数据集(比如 Kaggle 上的房价或 Titanic 数据集),尝试分别用平均值和中位数去填补缺失值,看看这对最终模型准确率有什么影响?动手实践是最好的老师。