未记录事实核验 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

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()

⚠️ 使用注意事项

🚫 常见错误

  1. 盲目使用均值:忽略数据分布特征和异常值
  2. 混淆适用条件:对分类数据计算均值
  3. 单一指标依赖:只看一个指标,忽略数据全貌
  4. 连续数据众数:直接对连续数据求众数

💡 最佳实践建议

  1. 选择策略:

    • 报告多个指标进行对比
    • 结合数据类型选择合适指标
    • 考虑业务场景的实际需求
  2. 数据预处理:

    • 检查异常值影响
    • 了解数据分布特征
    • 进行必要的数据清洗
  3. 结果解释:

    • 说明选择指标的理由
    • 解释指标间的差异
    • 结合可视化增强理解

🔗 相关知识

#统计学 #集中趋势 #描述性统计 #Python