资讯详情

资讯详情

建站行业动态 · 设计趋势 · 数字化升级干货

Python机器学习:均值、中位数、众数的核心原理与实战应用

Python机器学习:均值、中位数、众数的核心原理与实战应用 1. 项目概述从“三巨头”开始理解数据如果你刚开始接触数据分析或者机器学习面对一堆数字是不是常常感觉无从下手数据就像一堆散乱的乐高积木直接看过去你很难想象它能拼出什么。而“平均、中位数、模式”这三个概念就是帮你快速给这堆积木分类、找到关键形状、理解整体轮廓的第一把也是最核心的一把钥匙。很多人把它们当成数学课本里的简单公式背完就忘。但在我十多年的数据工作里无数次看到恰恰是对这三个基础概念的深刻理解和灵活运用区分开了“只会调包”的入门者和“能解决问题”的分析师。这个项目我们就来彻底拆解Python机器学习中关于“平均、中位数、模式”的一切。这不仅仅是算几个数而是要弄明白在什么场景下该用谁用Python怎么高效、正确地计算更重要的是当你的数据不那么“完美”时——比如里面混进了几个离谱的异常值或者数据分布长得歪七扭八——这三个“描述性统计量”会如何“说谎”以及我们该如何识破并应对。我会结合大量的实际代码和场景案例让你不仅知道怎么算更知道为什么这么算以及算完之后该怎么用。2. 核心概念深度解析与场景抉择描述性统计的核心任务是“化繁为简”用少数几个有代表性的数字来概括一大波数据的主要特征。平均、中位数、模式就是这个家族里最出名的三个成员它们从不同角度描述了数据的“中心”位置。2.1 均值敏感的全能选手也是最脆弱的“老好人”均值也就是我们常说的平均数计算方法是所有数据加起来除以数据的个数。在Python里用numpy或pandas计算均值轻而易举。import numpy as np import pandas as pd data [23, 29, 35, 42, 48, 52, 58] mean_value np.mean(data) print(f数据均值: {mean_value}) # 输出: 数据均值: 41.0均值最大的优点是它利用了数据中的每一个信息在数据分布比较对称比如经典的钟形正态分布且没有极端值时它是衡量数据中心位置非常有效的指标。在后续很多机器学习算法中比如计算损失函数的均方误差本质上也是在利用均值的思想。但是均值有个致命的弱点对异常值极度敏感。一个“坏”数据就能把它拖下水。# 假设上述数据中混入了一个录入错误或极端值 data_with_outlier [23, 29, 35, 42, 48, 52, 58, 200] # 最后一个数据是异常值 mean_with_outlier np.mean(data_with_outlier) print(f含异常值的数据均值: {mean_with_outlier}) # 输出: 含异常值的数据均值: 60.875看仅仅一个200就把均值从41拉高到了接近61完全扭曲了我们对这组数据“中心”的认知。这就好比计算一个小组的平均收入如果组里突然来了一个亿万富翁这个平均收入对理解其他组员的收入水平就毫无意义了。实操心得在查看任何数据的均值报告时我的第一反应不是相信它而是立刻在脑子里打个问号“这组数据里有没有异常值” 尤其是在分析收入、房价、用户消费金额、网络延迟等场景下异常值几乎必然存在。直接使用均值做决策非常危险。2.2 中位数稳健的“中间派”异常值的克星中位数顾名思义就是数据排序后正好处在最中间位置的那个数。如果数据个数是偶数则取中间两个数的平均值。它的计算不关心具体数值有多大只关心排位。median_value np.median(data) median_with_outlier np.median(data_with_outlier) print(f原始数据中位数: {median_value}) print(f含异常值的数据中位数: {median_with_outlier}) # 输出: # 原始数据中位数: 42.0 # 含异常值的数据中位数: 45.0 (排序后中间两个数42和48的平均值)对比一下当异常值200出现时均值从41暴涨到61而中位数仅仅从42微调到45。中位数对异常值几乎免疫因为它只取决于中间位置的数值无论边缘的数据是1还是10000只要不改变中间位置的排序中位数就稳如泰山。因此在数据可能存在极端值、或者分布严重偏斜非对称时中位数是衡量“典型”水平远比均值更可靠的指标。在描述居民收入、房产价格、客户等待时间时报告中更常看到的是中位数原因就在于此。2.3 众数洞察“主流”与“热点”众数是一组数据中出现次数最多的那个值。它揭示的是数据中最常见的类别或水平。from scipy import stats mode_data [1, 2, 2, 2, 3, 3, 4, 5] mode_result stats.mode(mode_data) print(f众数: {mode_result.mode[0]}, 出现次数: {mode_result.count[0]}) # 输出: 众数: 2, 出现次数: 3众数在分类数据中特别有用。比如分析一个电商平台上销量最高的商品颜色众数是“白色”或者一个应用中用户最常使用的功能模块。对于连续型数值数据直接计算众数可能意义不大因为每个精确值可能只出现一次。这时通常会将数据分箱离散化后再找众数区间例如“大多数用户的年龄集中在25-35岁这个区间”。众数的一个特点是一组数据可能没有众数所有值出现次数相同也可能有多个众数。存在多个众数的数据分布往往暗示数据来源于多个不同的群体或过程。2.4 三者的对比与选用指南光知道定义不够关键是要会在正确的场景调用正确的“武器”。我总结了一个快速决策表特征均值中位数众数定义所有数据之和除以个数排序后位于中间的值出现次数最多的值优点利用全部信息数理性质优良抗异常值干扰稳健性强反映最普遍情况适用于类别数据缺点对异常值极度敏感忽略了数据的具体数值信息可能不存在或多个对连续数据不直接适用适用场景数据分布对称、无异常值后续进行数学建模、优化数据有异常值、分布偏斜报告“典型”水平收入、房价了解最常见类别、热门选项分析分类或离散化数据Python工具np.mean(),pd.Series.mean()np.median(),pd.Series.median()scipy.stats.mode(),pd.Series.mode()如何选择一个简单的流程先做可视化永远不要直接计算。用直方图或箱线图快速看一眼数据分布。看图形是否对称如果图形大致对称均值和中位数会接近可以优先用均值信息量更足。检查有无“尾巴”或离群点如果图形严重偏斜或者箱线图上有明显的异常点那些独立在“箱子”外的点毫不犹豫地使用中位数。思考业务问题如果你关心“大多数情况是什么”比如最畅销的型号那就用众数。3. Python实战高效计算与可视化诊断理解了理论我们就要在Python里动手了。这里不仅仅是调用函数更重要的是建立一套从计算到诊断的完整工作流。3.1 使用Pandas进行一站式描述性统计对于现实中的数据我们通常处理的是表格。Pandas的.describe()方法是我们快速了解数据全貌的“瑞士军刀”。import pandas as pd import numpy as np # 创建一个包含不同类型数据的DataFrame df pd.DataFrame({ 销售额: [120, 150, 130, 900, 140, 135, 128], # 包含一个疑似异常值900 客户评分: [4, 5, 5, 4, 5, 4, 5], # 分类数据 地区: [北京, 上海, 北京, 广州, 上海, 北京, 北京] }) print( 基本描述统计 ) print(df.describe()) # 注意describe()默认只针对数值列它会输出 # count计数, mean均值, std标准差, min最小值, 25%第一四分位数, # 50%中位数, 75%第三四分位数, max最大值。 # 这里‘销售额’的中位数是135而均值是243.29均值远大于中位数是异常值的典型信号。 print(\n 单独计算中位数和众数 ) print(f销售额中位数: {df[销售额].median()}) print(f客户评分众数: {df[客户评分].mode().tolist()}) # 可能有多众数返回一个Series print(f地区众数: {df[地区].mode().tolist()}).describe()方法非常方便但它的默认输出里均值排在前面容易让人先入为主。我的习惯是拿到描述统计表第一眼先看50%中位数并将其与mean进行对比。如果两者差异巨大警报就拉响了。3.2 可视化让数据分布一目了然数字是抽象的图形是直观的。结合可视化是诊断该用均值还是中位数的最有效手段。import matplotlib.pyplot as plt import seaborn as sns # 设置图形 fig, axes plt.subplots(1, 2, figsize(12, 4)) # 子图1直方图与均值、中位数线 axes[0].hist(df[销售额], bins10, edgecolorblack, alpha0.7) axes[0].axvline(df[销售额].mean(), colorred, linestyle--, labelf均值: {df[销售额].mean():.1f}) axes[0].axvline(df[销售额].median(), colorgreen, linestyle-, labelf中位数: {df[销售额].median()}) axes[0].set_xlabel(销售额) axes[0].set_ylabel(频次) axes[0].set_title(销售额分布含异常值) axes[0].legend() axes[0].grid(True, linestyle--, alpha0.5) # 子图2箱线图 axes[1].boxplot(df[销售额], vertTrue, patch_artistTrue) axes[1].set_ylabel(销售额) axes[1].set_title(销售额箱线图) axes[1].grid(True, linestyle--, alpha0.5) plt.tight_layout() plt.show()图形解读直方图可以清晰看到大部分数据集中在120-150之间但有一个柱子900远远甩开其他。红色的均值线被这个异常值狠狠地“拉”向了右边而绿色的中位数线则稳稳地待在数据密集的区域。这张图完美解释了为什么在这里中位数比均值更有代表性。箱线图那个在箱体上方远远独立的点就是异常值的标准图示。箱体本身展示了中位数箱体内的线和四分位距的范围。箱线图是快速检测异常值、判断数据分散程度的利器。注意事项绘制直方图时bins箱子数量的选择会影响图形呈现。bins太少会掩盖细节太多则会使图形琐碎。一个经验法则是可以尝试sqrt(n)数据点数的平方根或Sturges‘公式等。多试几次选择最能清晰展示数据分布的参数。3.3 处理异常值识别与决策发现异常值后怎么办不是简单地删除而是要有一套决策流程。核实首先检查是否为数据录入错误。比如900是不是90.0的笔误这需要回溯数据源或联系业务方。理解如果不是错误那么这个异常值是否有合理的业务解释例如销售额900可能来自一个企业大客户的一次性采购它虽然是异常值但是真实且有价值的业务信息。决策保留如果异常值具有特殊业务意义且你分析的目标包含此类情况则应保留并在报告中明确指出其影响。此时报告中应同时提供均值和中位数。修正如果是已知错误且可修正则用合理值替换如用前后数据的均值、中位数或利用模型预测值。剔除如果异常值是无关的噪声且会严重干扰你对主体模式的分析例如分析普通用户的消费行为时混入了测试账号的极端数据则可以将其剔除。剔除后再使用均值进行分析会更合理。# 示例使用IQR四分位距法则识别异常值 Q1 df[销售额].quantile(0.25) Q3 df[销售额].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers df[(df[销售额] lower_bound) | (df[销售额] upper_bound)] print(f基于IQR法则识别的异常值:\n{outliers}) # 创建一个剔除异常值后的数据副本仅供分析主体模式使用 df_clean df[(df[销售额] lower_bound) (df[销售额] upper_bound)].copy() print(f\n清洗后数据均值: {df_clean[销售额].mean():.2f}) print(f清洗后数据中位数: {df_clean[销售额].median()})4. 在机器学习流程中的核心应用这三个基础统计量绝非仅仅用于出报告它们深深嵌入机器学习的每一个环节。4.1 数据探索与预处理在开始建模之前探索性数据分析是必经之路。均值、中位数、标准差等统计量是pandas-profiling、Sweetviz等自动化EDA工具生成报告的核心组成部分。通过它们你可以快速判断数据尺度不同特征的均值差异巨大如年龄vs收入可能需要进行标准化或归一化。缺失值处理策略对于数值型缺失值是用均值填充还是中位数填充如果数据有偏斜中位数是更稳健的选择。特征工程有时可以直接将“与均值的差值”或“与中位数的比值”作为一个新特征来表征样本的偏离程度。4.2 目标变量理解与模型评估在监督学习中理解目标变量你要预测的那个值的分布至关重要。如果目标变量是连续的且分布相对对称使用基于均方误差的模型如线性回归可能效果不错。如果目标变量有严重偏斜比如预测房价、点击率直接建模可能效果很差。此时对目标变量取对数是一种常见技巧这可以压缩极端值的影响使其分布更接近正态。在这个过程中中位数和均值的关系能给你重要提示。取对数后数据的几何均值会更有意义。在模型评估时MAE平均绝对误差的优化目标等价于中位数而MSE均方误差的优化目标等价于均值。因此如果你的数据异常值多使用MAE作为评估指标会比MSE更稳健。4.3 简单而强大的基线模型在构建复杂模型前建立一个简单的基线模型是衡量模型提升的基准。而基于“平均、中位数、模式”的预测就是最简单的基线。回归问题你可以直接使用目标变量在训练集的均值或中位数作为对所有测试样本的预测。任何复杂的模型其效果都应该显著优于这个“猜平均数”的模型。分类问题你可以直接使用训练集中最众数的类别即最多的那一类作为对所有测试样本的预测。这就是“零规则”分类器。from sklearn.metrics import mean_absolute_error, mean_squared_error # 假设这是你的训练集和测试集目标变量 y_train np.array([10, 20, 30, 40, 50]) y_test np.array([15, 25, 35]) # 基线模型1预测为中位数 baseline_pred_median np.median(y_train) # 中位数是30 mae_median mean_absolute_error(y_test, [baseline_pred_median]*len(y_test)) print(f中位数基线模型的MAE: {mae_median}) # 基线模型2预测为均值 baseline_pred_mean np.mean(y_train) # 均值是30.0 mse_mean mean_squared_error(y_test, [baseline_pred_mean]*len(y_test)) print(f均值基线模型的MSE: {mse_mean})如果你的复杂模型费了九牛二虎之力效果却和这个简单的基线模型差不多那就需要深刻反思特征或模型的选择了。5. 高级话题与常见陷阱掌握了基础应用我们再看一些更深层次的问题和容易踩的坑。5.1 分组聚合与“平均的平均”这是数据分析中一个经典的陷阱。比如你想计算不同部门的人均绩效。错误做法是先算出每个部门的平均绩效然后再对这些部门的平均值求平均。# 错误示范 dept_a [70, 80, 90] # 部门A人均绩效 80 dept_b [50, 60] # 部门B人均绩效 55 dept_means [np.mean(dept_a), np.mean(dept_b)] overall_mean_wrong np.mean(dept_means) # 得到 (8055)/2 67.5 # 正确做法需要加权平均权重是各部门人数 all_scores dept_a dept_b overall_mean_correct np.mean(all_scores) # (7080905060)/5 70.0 print(f错误的‘平均的平均’: {overall_mean_wrong}) print(f正确的整体平均: {overall_mean_correct})结论除非各组数据量完全相等否则对组均值再求平均会完全忽略组间样本量的差异导致结果失真。正确的做法是合并所有原始数据求总平均或者以各组样本量为权重进行加权平均。5.2 偏态分布下的均值与中位数关系在单峰分布中均值、中位数、众数三者的位置关系能揭示分布的偏斜方向。右偏分布数据右侧有长尾巴存在较大异常值。此时均值 中位数 众数。大部分数据集中在左侧但右侧的极端值把均值拉高了。收入分布是典型右偏。左偏分布数据左侧有长尾巴。此时众数 中位数 均值。对称分布三者大致相等。理解这个关系可以帮助你仅通过统计摘要就大致判断数据形状尤其是在无法立即绘图的情况下。5.3 众数的计算陷阱与分类数据对于连续数据直接计算scipy.stats.mode可能返回一个无意义的结果因为每个值都唯一。此时必须先将数据离散化分箱。# 连续数据计算众数区间 age_data [22, 25, 27, 28, 30, 35, 35, 40, 45, 80] # 最后一个可能是异常值 # 直接计算众数 print(stats.mode(age_data)) # 可能输出35但忽略了数据的连续性意义 # 更合理的做法分箱后看哪个区间最多 binned_age pd.cut(age_data, bins[20, 30, 40, 50, 90]) print(pd.Series(binned_age).value_counts().head(1)) # 查看频次最高的区间对于分类数据众数就是最直观的“热点”。在pandas中Series.mode()返回的是一个Series因为可能存在多个众数这本身也是一个重要的洞察。6. 性能考量与大数据处理当数据量非常大时即使是计算均值这样的简单操作也需要考虑性能。NumPy和Pandas的底层实现已经高度优化。但需要注意df.mean()默认会跳过NaN值进行计算这与np.nanmean()行为一致。确保你了解缺失值处理方式。对于超大规模数据如果无法全部读入内存可以考虑使用流式计算或近似算法。例如计算均值可以使用在线算法每次读取一部分数据不断更新均值而无需存储所有数据。中位数和众数的大数据近似计算则更为复杂可能需要借助采样或特殊的数据结构如T-Digest用于近似分位数计算。一个简单的在线更新均值示例def online_mean(previous_mean, previous_count, new_batch): 在线更新均值 batch_mean np.mean(new_batch) batch_size len(new_batch) new_total_count previous_count batch_size new_mean (previous_mean * previous_count batch_mean * batch_size) / new_total_count return new_mean, new_total_count # 模拟流式数据 stream_data_batches [[1,2,3], [4,5], [6,7,8,9]] current_mean 0 current_count 0 for batch in stream_data_batches: current_mean, current_count online_mean(current_mean, current_count, batch) print(f处理新批次后累计均值: {current_mean:.2f}, 累计数量: {current_count})最后我想分享一个最深的体会在数据科学中没有“最好”的统计量只有“最合适”的统计量。均值、中位数、模式就像螺丝刀里的平口、十字和六角。一个老师傅不会只用一把螺丝刀干活。下次当你拿到一份数据报告或者自己生成统计摘要时不要只盯着一个数字看。问问自己数据是怎么分布的有没有异常值我的业务问题到底关心的是什么把这三个简单的工具用活了你就已经具备了超越许多初学者的数据直觉这将是你在机器学习道路上走得更稳、更远的坚实基础。

相关资讯