未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 本文适用范围与待维护项
本文的内置列表和随机生成数据足以支持基础练习,无需额外数据集;代码按正文顺序执行,后续演示复用首段导入。定序类别不能直接对中间两项取算术平均;等频众数按 Pandas 的全部并列最大频次值口径解释,SciPy 示例只返回其中一个。均值与中位数的大小关系不构成偏态判定,连续数据的分组众数也只是依赖分箱的近似峰值位置。修订完成前,本文适用于基础指标计算和比较,不宜直接作为分布形态自动判定规则。
🎯 集中趋势指标(Measures of Central Tendency)
abstract 📋 本节概览
- 🎯 学习目标:掌握均值、中位数、众数三大集中趋势指标的计算方法和适用场景
- 📚 前置知识:描述性统计|描述性统计 | 数据类型|数据类型
- ⏱️ 预计用时:12分钟
🎯 核心概念
note 📖 **什么是集中趋势指标?**
集中趋势指标用于描述一组数据的"中心"位置,反映数据的典型水平,是描述性统计分析的核心工具。
为什么需要集中趋势指标?
- 数据摘要:将大量数据浓缩为一个代表性数值
- 比较分析:便于不同组群或时期的数据对比
- 趋势判断:识别数据的典型特征和发展方向
- 决策支持:为业务决策提供量化依据
📊 三大集中趋势指标详解
1. 📈 均值(Mean/Average)
定义:所有数值的算术平均数
计算公式:
x̄ = (x₁ + x₂ + ... + xₙ) / n = Σxᵢ / n
适用条件:
- 数值型数据(定距或定比)
- 数据分布相对对称
- 无极端异常值
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
def mean_analysis_demo():
"""均值分析详细演示"""
# 示例1:学生成绩数据
scores = [85, 92, 78, 88, 95, 82, 90, 87, 91, 83, 89, 86]
print("=== 均值分析演示 ===")
print(f"学生成绩: {scores}")
# 计算均值的多种方法
manual_mean = sum(scores) / len(scores)
numpy_mean = np.mean(scores)
pandas_mean = pd.Series(scores).mean()
print(f"\n均值计算结果:")
print(f"手动计算: {manual_mean:.2f}")
print(f"NumPy计算: {numpy_mean:.2f}")
print(f"Pandas计算: {pandas_mean:.2f}")
# 均值的特性演示
print(f"\n均值的数学特性:")
deviations = [x - numpy_mean for x in scores]
print(f"偏差和: {sum(deviations):.10f}") # 应该接近0
print(f"验证: Σ(xᵢ - x̄) = 0 (数值精度内)")
# 异常值对均值的影响
scores_with_outlier = scores + [150] # 添加异常值
normal_mean = np.mean(scores)
outlier_mean = np.mean(scores_with_outlier)
print(f"\n异常值影响分析:")
print(f"正常数据均值: {normal_mean:.2f}")
print(f"含异常值均值: {outlier_mean:.2f}")
print(f"差异: {outlier_mean - normal_mean:.2f}")
# 可视化
plt.figure(figsize=(15, 5))
# 原始数据分布
plt.subplot(1, 3, 1)
plt.hist(scores, bins=8, alpha=0.7, color='skyblue', edgecolor='black')
plt.axvline(numpy_mean, color='red', linestyle='--', linewidth=2, label=f'均值: {numpy_mean:.1f}')
plt.xlabel('成绩')
plt.ylabel('频数')
plt.title('成绩分布与均值')
plt.legend()
# 异常值影响对比
plt.subplot(1, 3, 2)
plt.hist(scores, alpha=0.6, label='正常数据', color='skyblue', bins=8)
plt.hist(scores_with_outlier, alpha=0.6, label='含异常值', color='lightcoral', bins=10)
plt.axvline(normal_mean, color='blue', linestyle='--', label=f'正常均值: {normal_mean:.1f}')
plt.axvline(outlier_mean, color='red', linestyle='--', label=f'异常值均值: {outlier_mean:.1f}')
plt.xlabel('成绩')
plt.ylabel('频数')
plt.title('异常值对均值的影响')
plt.legend()
# 偏差分析
plt.subplot(1, 3, 3)
x_pos = range(len(scores))
plt.bar(x_pos, deviations, color=['red' if d < 0 else 'green' for d in deviations], alpha=0.7)
plt.axhline(y=0, color='black', linestyle='-', alpha=0.5)
plt.xlabel('学生编号')
plt.ylabel('偏差 (xᵢ - x̄)')
plt.title('各数据点相对均值的偏差')
plt.tight_layout()
plt.show()
return scores, numpy_mean
# 执行均值分析
scores_data, mean_value = mean_analysis_demo()
2. 📊 中位数(Median)
定义:将数据按大小排序后,位于中间位置的数值
计算方法:
- 奇数个数据:中间数值
- 偶数个数值型数据(定距或定比):中间两数的平均;定序类别不能直接取算术平均,应报告中间两项或说明采用的中位类别约定
适用条件:
- 有序数据(定序、定距、定比)
- 数据分布偏态
- 存在异常值
def median_analysis_demo():
"""中位数分析详细演示"""
# 创建不同分布的数据
np.random.seed(42)
normal_data = np.random.normal(50, 10, 100) # 正态分布
skewed_data = np.random.exponential(2, 100) # 右偏分布
datasets = {
'正态分布数据': normal_data,
'右偏分布数据': skewed_data,
'收入数据示例': [3000, 3500, 4000, 4200, 4500, 5000, 5500, 6000, 8000, 50000] # 含极值
}
print("=== 中位数分析演示 ===")
for name, data in datasets.items():
# 计算中位数和均值
median_val = np.median(data)
mean_val = np.mean(data)
# 手动计算中位数验证
sorted_data = np.sort(data)
n = len(sorted_data)
if n % 2 == 1:
manual_median = sorted_data[n // 2]
else:
manual_median = (sorted_data[n // 2 - 1] + sorted_data[n // 2]) / 2
print(f"\n{name}:")
print(f" 数据量: {len(data)}")
print(f" 均值: {mean_val:.2f}")
print(f" 中位数: {median_val:.2f}")
print(f" 手动计算中位数: {manual_median:.2f}")
print(f" 均值-中位数差异: {mean_val - median_val:.2f}")
# 判断分布偏态
if abs(mean_val - median_val) < 0.1:
skewness = "均值与中位数接近(不据此判定对称)"
elif mean_val > median_val:
skewness = "均值>中位数(不据此判定右偏)"
else:
skewness = "均值<中位数(不据此判定左偏)"
print(f" 分布特征: {skewness}")
# 中位数的稳健性演示
income_normal = [3000, 3500, 4000, 4200, 4500, 5000, 5500, 6000, 6500, 7000]
income_outlier = income_normal[:-1] + [100000] # 将最后一个数据改为极值
print(f"\n中位数的稳健性演示:")
print(f"正常收入数据 - 均值: {np.mean(income_normal):.0f}, 中位数: {np.median(income_normal):.0f}")
print(f"含极值数据 - 均值: {np.mean(income_outlier):.0f}, 中位数: {np.median(income_outlier):.0f}")
print(f"中位数变化: {np.median(income_outlier) - np.median(income_normal):.0f}")
print(f"均值变化: {np.mean(income_outlier) - np.mean(income_normal):.0f}")
# 可视化对比
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# 正态分布数据
axes[0, 0].hist(normal_data, bins=20, alpha=0.7, color='skyblue', density=True)
axes[0, 0].axvline(np.mean(normal_data), color='red', linestyle='--', label=f'均值: {np.mean(normal_data):.1f}')
axes[0, 0].axvline(np.median(normal_data), color='green', linestyle='--', label=f'中位数: {np.median(normal_data):.1f}')
axes[0, 0].set_title('正态分布:均值≈中位数')
axes[0, 0].legend()
# 右偏分布数据
axes[0, 1].hist(skewed_data, bins=20, alpha=0.7, color='lightcoral', density=True)
axes[0, 1].axvline(np.mean(skewed_data), color='red', linestyle='--', label=f'均值: {np.mean(skewed_data):.1f}')
axes[0, 1].axvline(np.median(skewed_data), color='green', linestyle='--', label=f'中位数: {np.median(skewed_data):.1f}')
axes[0, 1].set_title('右偏分布:均值>中位数')
axes[0, 1].legend()
# 收入数据对比
income_data = datasets['收入数据示例']
axes[1, 0].boxplot([income_data], labels=['收入分布'])
axes[1, 0].set_title('收入数据箱线图')
axes[1, 0].set_ylabel('收入')
# 稳健性对比
methods = ['均值', '中位数']
normal_values = [np.mean(income_normal), np.median(income_normal)]
outlier_values = [np.mean(income_outlier), np.median(income_outlier)]
x = np.arange(len(methods))
width = 0.35
axes[1, 1].bar(x - width/2, normal_values, width, label='正常数据', alpha=0.8)
axes[1, 1].bar(x + width/2, outlier_values, width, label='含极值数据', alpha=0.8)
axes[1, 1].set_ylabel('数值')
axes[1, 1].set_title('均值vs中位数:异常值影响对比')
axes[1, 1].set_xticks(x)
axes[1, 1].set_xticklabels(methods)
axes[1, 1].legend()
plt.tight_layout()
plt.show()
# 执行中位数分析
median_analysis_demo()
3. 🎪 众数(Mode)
定义:数据中出现频率最高的数值
特点:
- 可能有多个众数(多个值并列最高频,不等同于连续分布的多峰)
- 本文按 Pandas 口径将所有并列最高频值列为众数;所有值等频时,没有唯一的最常见值
- 适用于所有数据类型
适用条件:
- 所有类型数据(定类、定序、定距、定比)
- 离散型数据
- 分类变量
def mode_analysis_demo():
"""众数分析详细演示"""
print("=== 众数分析演示 ===")
# 示例数据集
datasets = {
'服装尺码': ['S', 'M', 'L', 'M', 'L', 'M', 'S', 'M', 'L', 'M'],
'家庭子女数': [0, 1, 2, 1, 1, 2, 3, 1, 2, 1, 0, 1],
'考试成绩': [85, 90, 85, 88, 90, 85, 92, 88, 85, 90, 88],
'等频数据': [1, 2, 3, 4, 5], # 每个值出现一次,Pandas返回全部并列众数
'多众数数据': [1, 1, 2, 2, 3, 3] # 多个众数
}
for name, data in datasets.items():
print(f"\n{name}: {data}")
# 使用pandas计算众数
series = pd.Series(data)
mode_values = series.mode()
value_counts = series.value_counts()
print(f" 频次统计: {dict(value_counts)}")
if len(mode_values) == 0:
print(f" 众数: 无众数")
elif len(mode_values) == 1:
print(f" 众数: {mode_values.iloc[0]} (出现{value_counts.iloc[0]}次)")
else:
print(f" 众数: {list(mode_values)} (多众数)")
# 使用scipy.stats计算众数(仅适用于数值数据;并列时仅返回一个众数)
if all(isinstance(x, (int, float)) for x in data):
scipy_mode = stats.mode(data, keepdims=True)
print(f" SciPy众数: {scipy_mode.mode[0]} (出现{scipy_mode.count[0]}次)")
# 实际应用场景演示
print(f"\n=== 实际应用场景演示 ===")
# 1. 市场调研:最受欢迎的产品颜色
colors = ['红色', '蓝色', '绿色', '蓝色', '红色', '蓝色', '黄色', '蓝色', '绿色', '蓝色']
color_mode = pd.Series(colors).mode().iloc[0]
print(f"产品颜色偏好调查: {colors}")
print(f"最受欢迎颜色: {color_mode}")
# 2. 质量控制:产品缺陷类型
defect_types = ['划痕', '变形', '划痕', '污渍', '变形', '划痕', '破损', '划痕']
defect_mode = pd.Series(defect_types).mode().iloc[0]
print(f"产品缺陷类型: {defect_types}")
print(f"最常见缺陷: {defect_mode}")
# 3. 连续数据的众数处理
np.random.seed(42)
continuous_data = np.random.normal(100, 15, 1000)
# 方法1:分组后找众数
bins = np.arange(60, 141, 5) # 5分一组
digitized = np.digitize(continuous_data, bins)
bin_mode = stats.mode(digitized, keepdims=True).mode[0]
mode_range = f"{bins[bin_mode-1]:.0f}-{bins[bin_mode]:.0f}"
print(f"\n连续数据众数分析:")
print(f"数据范围: {continuous_data.min():.1f} - {continuous_data.max():.1f}")
print(f"分组后众数区间: {mode_range}")
# 可视化
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
# 分类数据众数
color_counts = pd.Series(colors).value_counts()
axes[0, 0].bar(color_counts.index, color_counts.values, color=['red', 'blue', 'green', 'yellow'])
axes[0, 0].set_title('产品颜色偏好\n众数:蓝色')
axes[0, 0].set_ylabel('频次')
# 离散数值数据众数
children_counts = pd.Series(datasets['家庭子女数']).value_counts().sort_index()
axes[0, 1].bar(children_counts.index, children_counts.values, color='lightgreen')
axes[0, 1].set_title('家庭子女数分布\n众数:1')
axes[0, 1].set_xlabel('子女数')
axes[0, 1].set_ylabel('频次')
# 多众数情况
multi_mode_counts = pd.Series(datasets['多众数数据']).value_counts().sort_index()
axes[0, 2].bar(multi_mode_counts.index, multi_mode_counts.values, color='orange')
axes[0, 2].set_title('多众数分布\n众数:1,2,3')
axes[0, 2].set_xlabel('数值')
axes[0, 2].set_ylabel('频次')
# 连续数据分组众数
axes[1, 0].hist(continuous_data, bins=bins, alpha=0.7, color='skyblue', edgecolor='black')
axes[1, 0].axvspan(bins[bin_mode-1], bins[bin_mode], alpha=0.3, color='red', label=f'众数区间: {mode_range}')
axes[1, 0].set_title('连续数据分组众数')
axes[1, 0].set_xlabel('数值')
axes[1, 0].set_ylabel('频次')
axes[1, 0].legend()
# 对比不同集中趋势指标
sample_data = [1, 2, 2, 3, 3, 3, 4, 4, 5, 100] # 含异常值
mean_val = np.mean(sample_data)
median_val = np.median(sample_data)
mode_val = pd.Series(sample_data).mode().iloc[0]
measures = ['均值', '中位数', '众数']
values = [mean_val, median_val, mode_val]
colors_bar = ['red', 'green', 'blue']
axes[1, 1].bar(measures, values, color=colors_bar, alpha=0.7)
axes[1, 1].set_title('含异常值数据的集中趋势对比')
axes[1, 1].set_ylabel('数值')
# 数据分布与指标位置
axes[1, 2].hist(sample_data[:-1], bins=5, alpha=0.6, color='lightgray', label='正常数据')
axes[1, 2].axvline(mean_val, color='red', linestyle='--', label=f'均值: {mean_val:.1f}')
axes[1, 2].axvline(median_val, color='green', linestyle='--', label=f'中位数: {median_val:.1f}')
axes[1, 2].axvline(mode_val, color='blue', linestyle='--', label=f'众数: {mode_val}')
axes[1, 2].scatter([100], [0.1], color='red', s=100, label='异常值', zorder=5)
axes[1, 2].set_title('数据分布与集中趋势指标')
axes[1, 2].legend()
plt.tight_layout()
plt.show()
# 执行众数分析
mode_analysis_demo()
🎯 选择指南:何时使用哪个指标?
📋 决策流程图
def choose_central_tendency():
"""集中趋势指标选择指南"""
decision_tree = {
"数据类型": {
"定类数据": "只能使用众数",
"定序数据": "中位数或众数",
"定距/定比数据": "均值、中位数或众数"
},
"数据分布": {
"对称分布": "均值最佳,中位数也可",
"偏态分布": "中位数更合适",
"多峰分布": "众数能体现峰值特征"
},
"异常值": {
"无异常值": "均值、中位数都合适",
"有少量异常值": "中位数更稳健",
"异常值很多": "考虑众数或数据清洗"
},
"应用目的": {
"总体水平": "均值",
"典型水平": "中位数",
"最常见值": "众数"
}
}
print("=== 集中趋势指标选择指南 ===")
for category, options in decision_tree.items():
print(f"\n{category}考虑因素:")
for condition, recommendation in options.items():
print(f" {condition}: {recommendation}")
choose_central_tendency()
🏢 实际应用对照表
| 场景 | 数据类型 | 推荐指标 | 理由 |
|---|---|---|---|
| 销售业绩分析 | 定比数据 | 均值+中位数 | 均值看整体,中位数看典型 |
| 用户满意度 | 定序数据 | 中位数+众数 | 中位数稳健,众数显示主流 |
| 产品颜色选择 | 定类数据 | 众数 | 只有众数适用 |
| 员工薪资分析 | 定比数据 | 中位数 | 避免高薪异常值影响 |
| 考试成绩报告 | 定比数据 | 均值 | 反映整体水平 |
| 故障类型统计 | 定类数据 | 众数 | 找出最常见问题 |
🧮 综合对比分析
def comprehensive_comparison():
"""三种集中趋势指标的综合对比"""
# 创建不同特征的数据集
np.random.seed(42)
datasets = {
'正态分布': np.random.normal(100, 15, 1000),
'右偏分布': np.random.exponential(2, 1000),
'双峰分布': np.concatenate([np.random.normal(80, 10, 500), np.random.normal(120, 10, 500)]),
'含异常值': np.concatenate([np.random.normal(50, 5, 95), [100, 105, 110, 115, 120]])
}
print("=== 三种指标综合对比分析 ===")
comparison_results = []
for name, data in datasets.items():
mean_val = np.mean(data)
median_val = np.median(data)
# 处理众数(连续数据需要分组)
if name in ['正态分布', '右偏分布', '双峰分布', '含异常值']:
# 对连续数据分组求众数
hist, bin_edges = np.histogram(data, bins=20)
mode_bin = np.argmax(hist)
mode_val = (bin_edges[mode_bin] + bin_edges[mode_bin + 1]) / 2
else:
mode_val = pd.Series(data).mode().iloc[0]
comparison_results.append({
'dataset': name,
'mean': mean_val,
'median': median_val,
'mode': mode_val,
'skewness': stats.skew(data)
})
print(f"\n{name}:")
print(f" 均值: {mean_val:.2f}")
print(f" 中位数: {median_val:.2f}")
print(f" 众数: {mode_val:.2f}")
print(f" 偏度: {stats.skew(data):.2f}")
# 指标关系分析
if abs(mean_val - median_val) < 0.5:
print(f" 指标关系: 均值与中位数接近;分布形态需结合直方图与偏度分析")
elif mean_val > median_val:
print(f" 指标关系: 均值>中位数;分布形态需结合直方图与偏度分析")
else:
print(f" 指标关系: 均值<中位数;分布形态需结合直方图与偏度分析")
# 可视化对比
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
axes = axes.ravel()
for i, (name, data) in enumerate(datasets.items()):
result = comparison_results[i]
axes[i].hist(data, bins=30, alpha=0.7, density=True, color='lightblue', edgecolor='black')
axes[i].axvline(result['mean'], color='red', linestyle='--', linewidth=2, label=f"均值: {result['mean']:.1f}")
axes[i].axvline(result['median'], color='green', linestyle='--', linewidth=2, label=f"中位数: {result['median']:.1f}")
axes[i].axvline(result['mode'], color='blue', linestyle='--', linewidth=2, label=f"众数: {result['mode']:.1f}")
axes[i].set_title(f'{name}\n偏度: {result["skewness"]:.2f}')
axes[i].set_xlabel('数值')
axes[i].set_ylabel('密度')
axes[i].legend()
plt.tight_layout()
plt.show()
return comparison_results
# 执行综合对比
comparison_data = comprehensive_comparison()
⚠️ 使用注意事项
🚫 常见错误
- 盲目使用均值:忽略数据分布特征和异常值
- 混淆适用条件:对分类数据计算均值
- 单一指标依赖:只看一个指标,忽略数据全貌
- 连续数据众数:直接对连续数据求众数
💡 最佳实践建议
-
选择策略:
- 报告多个指标进行对比
- 结合数据类型选择合适指标
- 考虑业务场景的实际需求
-
数据预处理:
- 检查异常值影响
- 了解数据分布特征
- 进行必要的数据清洗
-
结果解释:
- 说明选择指标的理由
- 解释指标间的差异
- 结合可视化增强理解
🔗 相关知识
#统计学 #集中趋势 #描述性统计 #Python