ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

平均数与中位数:工资分布偏斜下的数据分析与分位数应用

平均数与中位数:工资分布偏斜下的数据分析与分位数应用

做数据分析这些年,被问得最多的一类问题是:“这个数看着挺高的,为什么大家体感完全不一样?”十次里有八次,问题出在同一个地方——拿平均数当成了全部人的代表值。尤其在看工资分布这类明显偏斜的数据时,平均数几乎必然高于大多数人的真实位置,它天然比中位数更容易“骗人”。这篇内容我想把这件事拆到底:不是简单地告诉你“中位数更好”,而是从数据结构、极端值作用机制、分位数体系、清洗口径、代码实现、汇报表述这几个层面,讲清楚什么时候该看哪个数、为什么。适合刚接手数据分析工作的同学,也适合需要看报表做判断的业务负责人——哪怕你暂时不写代码,第1节和第5节的判断逻辑也能直接用。

1. 为什么“平均工资”总让人觉得不对劲

1.1 一张虚构的十人工资表,把问题摊开

我习惯用最小样本讲清楚最核心的矛盾。假设某个十人小团队,月薪(税前、不含年终奖)分别是 4500、5000、5200、5500、5800、6000、6200、6500、7000、45000。最后那位是团队负责人,工资高出一大截,这在小团队里非常常见,并不是什么异常数据。

把十个数加起来是 96700 元,除以 10,平均月薪9670 元。可如果你把这十个人从低到高排成一排,站在第 5 位和第 6 位中间的“正中间位置”,对应的数是 (5800+6000)/2 =5900 元。同一批数据,两个“代表值”差了 3770 元,接近 64%。

更有意思的是,如果把最高的那位拿掉,剩下九个人的平均月薪立刻掉到 5744 元。也就是说,仅仅一个人的存在,就把整体的平均值抬高了将近 4000 元。而中位数呢?十个人变成九个人后,中位数是第 5 位的 5800 元,只挪动了 100 元。这个对比几乎就是全部答案:平均数对“数值大小”极度敏感,中位数只对“排队顺序”敏感。

1.2 平均数是怎么被“拉走”的

很多人以为极端值“影响不大”,因为平均嘛,总会被摊薄。这个直觉只在样本量足够大、且极端值不极端的时候成立。数学上讲,样本里任何一个观测值变动 Δ,平均数就会移动 Δ/n。n 越小,单个样本的杠杆越大;极端值越极端,Δ 越大。两者一乘,一个人的工资就能决定整个团队的平均面貌。

而中位数走的是另一条路:它根本不关心具体数值是多少,只关心“谁排在中间”。你把最高那位从 45000 改成 450000,中位数一点不动;你把前四位全部改成 1 元,中位数也不动。它的崩溃点是 50%——只有当超过一半的数据被替换,中位数才会被真正带偏。平均数的崩溃点是 0,一个数就够。

注意:这不是说平均数没用。平均数在需要“总量除以人数”的场景里不可替代,比如算人力总成本、算人均产出、算预算盘子。问题只出在——把它当成“大多数人的典型水平”来解读。

1.3 中位数究竟在回答哪个问题

我习惯这样区分两者的语义:平均数回答“如果把这笔钱完全平均分,每个人能拿多少”,这是一个分配问题;中位数回答“站在队伍正中间的那个人拿多少”,这是一个位置问题。你要判断“普通人过得怎么样”,问的是位置;你要做预算和成本核算,问的是总量。

对工资这种典型的右偏分布——绝大多数人集中在偏低区间,少数人拉出长尾——位置问题显然更贴近直觉。当有人告诉你“我们这里平均月薪接近一万”,你脑子里该冒出来的第一个追问是:中位数是多少?如果对方答不上来,或者两个数差得很远,那这份数据大概率只能说明“这里有少数高薪岗位”,说明不了“大多数人拿多少”。

2. 三个指标的分工,别混着用

2.1 算法、敏感对象与失效场景

很多人把均值、中位数、分位数当成“同一件事的三种算法”,其实它们回答的是三个不同的问题。我整理了一张对照表,这张表基本可以贴在工位上:

指标计算方式对什么敏感适合回答的问题容易误导的地方
平均数总和 ÷ 个数数值大小、极端值总量摊薄、成本核算、预算基数被少数极端值拉高/拉低,不代表典型水平
中位数排序后取中间位置只有顺序,不受数值影响典型水平、多数人所在区间忽略两端差异,两个完全不同的分布可能中位数相同
分位数排序后按百分比切点顺序 + 切点位置分布形状、区间宽度、尾部厚度小样本下切点抖动大,P90、P99 尤其不稳
众数出现频次最高的值频次最典型的离散取值连续数据几乎无意义,多峰时可能有多个

这张表里最关键的一列是“容易误导的地方”。中位数不是万能的:假设两组数据,一组是 [5000, 6000, 7000],另一组是 [1000, 6000, 60000],中位数都是 6000,但分布形态天差地别。所以我的做法是三个数一起看:均值和中位数的差距告诉你偏斜方向与程度,P25 到 P75 的宽度告诉你集中度,P90 告诉你尾部有多厚。

2.2 用代码把十个人的例子跑一遍

手算容易记混,我一般直接跑一遍确认。下面这段不需要装任何第三方库,标准库就够:

import statistics as st salaries = [4500, 5000, 5200, 5500, 5800, 6000, 6200, 6500, 7000, 45000] print("样本量:", len(salaries)) print("均值:", round(st.mean(salaries), 2)) # 9670.0 print("中位数:", round(st.median(salaries), 2)) # 5900.0 print("去掉最高值后的均值:", round(st.mean(salaries[:-1]), 2)) # 5744.44 print("去掉最高值后的中位数:", round(st.median(salaries[:-1]), 2)) # 5800

跑完你会看到那几个数字安静地躺在终端里,和前面的计算完全对得上。我建议每个刚开始做数据分析的人都亲手跑一次这种“小样本对照”,因为后面处理几十万行数据时,你会反复回到这个直觉:均值和Median拉开距离,就是分布偏斜的信号灯。

顺便说一个容易被忽略的细节:Python 的statistics.median在偶数个样本时取中间两个数的平均,某些业务口径下(比如“最典型的那个人的工资”)更愿意取靠下的那个,这是业务定义问题,不是代码问题,需要提前和需求方对齐。

2.3 分位数:把“中位数思维”推广到整个分布

中位数是 P50,也就是第 50 百分位。把这个思路推广开,你就能拿到一张完整的分布地图。我最常用的五个切点是 P10、P25、P50、P75、P90,对应到工资场景里是这样读的:

  • P10:偏低的一端,通常对应入门岗或实习转正初期;
  • P25:下四分位,四分之一的人低于这个数;
  • P50:中位数,典型水平;
  • P75:上四分位,四分之一的人高于这个数;
  • P90:高薪尾部,十分之一的人高于这个数。

有了这五个数,你甚至可以在不画图的情况下大致还原分布形状。判断方法很朴素:如果 P50 到 P25 的距离明显小于 P50 到 P75 的距离,说明右侧尾巴更长,是右偏分布;如果 P90 与 P50 的比值超过 2,基本可以确定存在少数高值在主导整体观感。这比单看一个平均数信息量大得多,也是我在做薪酬分析时最先输出的那一组数。

3. 从原始数据到完整分布画像的实操流程

3.1 数据清洗:口径不统一比算法错误更致命

我先说一个踩过的坑。早期做薪酬分析时,我直接把几份来源不同的表拼在一起算平均,结果出来一个非常“漂亮”的数字,汇报时被业务方一句话问住:“这里面是不是混了年终奖和外包报价?”回去一查,果然:有的表是月薪税前,有的是年薪包折算,有的包含了十三薪,还有一部分是外包供应商报价而非内部人力成本。口径一乱,后面所有算法都是白算。

所以清洗阶段我固定做四件事,顺序不能颠倒:

  1. 统一时间维度:全部折算成月薪,或者全部折算成年包,选一个,别混。
  2. 统一口径:是否税前、是否含奖金、是否含补贴、是否含股权,逐项确认并记录在字段说明里。
  3. 统一人群范围:正式员工、实习生、外包、兼职是否纳入。纳入和不纳入会得到两个完全不同的中位数,这必须写进结论。
  4. 处理缺失与明显错误:工资为 0、为空、为负、或者出现 999999 这种占位符的,先隔离出来单独看,别急着删。

提示:异常值不要一上来就删。先判断它是“录入错误”还是“真实的高值”。真实高值恰恰是你需要保留的信息——它正是让平均数失真的那个原因。删掉它,你就把要解释的现象本身删掉了。

3.2 一次跑出均值、中位数和分位点

口径统一之后,用 pandas 几行就能出全套指标。我通常会把这段写成一个小函数,重复使用:

import pandas as pd df = pd.read_csv("salary.csv") # 只保留月薪口径,并做基础清洗 df = df[df["salary_type"] == "monthly"].copy() df["salary"] = pd.to_numeric(df["salary"], errors="coerce") df = df.dropna(subset=["salary"]) df = df[(df["salary"] > 1000) & (df["salary"] < 500000)] # 五数概括 + 偏度 q = df["salary"].quantile([0.1, 0.25, 0.5, 0.75, 0.9]).round(0) print(q) print("均值:", round(df["salary"].mean(), 0)) print("中位数:", round(df["salary"].median(), 0)) print("偏度:", round(df["salary"].skew(), 2))

偏度(skew)这个值值得单独说一句。它接近 0 说明分布大致对称,这时候均值和中位数会很接近,你用哪个都行;它明显大于 0(工资数据常见 1.5 到 4 之间),说明右偏严重,均值会被高估,报数时必须两个都给。我个人的经验阈值是:偏度超过 1,就把中位数放在报告的第一位,均值退到括号里作为补充说明。

3.3 画一张图,比十行数字更有说服力

数字看多了会麻木,一张直方图能让业务方三秒钟理解你在说什么。关键技巧是把均值和中位数两条竖线同时画上去,视觉冲击非常直接:

import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(8, 4.5)) ax.hist(df["salary"], bins=40, color="#4a6fa5", edgecolor="white") ax.axvline(df["salary"].mean(), color="#c0392b", linestyle="--", label="均值") ax.axvline(df["salary"].median(), color="#27795b", linestyle="-", label="中位数") ax.legend() ax.set_xlabel("月薪") ax.set_ylabel("人数") plt.tight_layout() plt.savefig("salary_dist.png", dpi=150)

图出来之后,我一般引导对方看三个地方。第一,峰在哪:大多数人的主体集中在哪个区间,这个峰的位置通常就贴着中位数。第二,两条线隔多远:红色虚线(均值)明显在绿色实线(中位数)右边,说明右侧有长尾在往上拽。第三,尾巴有多长:从 P75 到最大值那段拖得多远,拖得越远,均值越不可信。

如果尾部特别长,我还会补一张对数刻度的图,把长尾压一压,让人看清主体区间的形状。这不是为了好看,而是因为线性坐标下主体全挤在左边一小块,看不清细节。

3.4 分组切片:整体中位数也会骗人

这一点很多人没意识到:中位数能防极端值,但防不了结构变化。我做过一个对比,两组数据,两个部门各自的平均工资都在下降,但全公司整体平均工资反而上升了。听起来不可能,其实就是一个权重问题。

设第一年技术部 10 人、人均 15000,运营部 30 人、人均 8000,整体平均是 (10×15000 + 30×8000) ÷ 40 = 9750 元。第二年两个部门的人均都降了——技术部 14500,运营部 7800——但技术部扩到 30 人,运营部缩到 10 人,整体平均变成 (30×14500 + 10×7800) ÷ 40 = 12825 元。两个部门都在降,整体却在涨。这就是典型的结构效应,换成中位数同样会发生,只是形式更隐蔽。

所以只要数据带分组维度,我的默认动作一定是先分组算一遍,再和整体对一遍:

summary = df.groupby("dept")["salary"].agg( n="count", mean="mean", median="median", p25=lambda s: s.quantile(0.25), p75=lambda s: s.quantile(0.75), ).round(0) summary["均值中位数比"] = (summary["mean"] / summary["median"]).round(2) print(summary.sort_values("median", ascending=False))

均值中位数比这一列是我自己加的,超过 1.3 就说明这个组内部偏斜明显,汇报时要特别提示。另外别忘了看n,样本量太小的组(比如少于 15 人)分位数意义不大,P90 尤其容易飘,这种情况下我宁愿只报中位数和区间,不报分位点。

4. 这套思路能落到哪些真实场景

4.1 谈薪与薪酬带宽:别用均值定区间

薪酬带宽的设计里,中位数是锚点,P25 到 P75 是带宽,P90 是上限参考。用均值当锚点会出问题:如果团队里有几个资深高薪,均值被抬高,按均值定出的带宽会把整个岗位序列往上抬,成本直接失控。我的做法是以市场中位数为基准定中点,用 P25 和 P75 划定区间宽度,再单独看 P90 判断是否需要为极少数专家岗开特例通道。

对个人来说也一样。听到“我们这里平均多少”,正确的追问是“这个岗位的中位数是多少,P25 到 P75 是什么范围”。同样一个月薪数字,处在 P25 和处在 P75,含义完全不同——前者说明你在这个群体里偏低,后者说明你已经在多数人之上。

4.2 业务指标:客单价、响应时长、留存都能用

工资只是一个载体,这套思维可以平移到几乎所有右偏的业务指标上。客单价是典型:少数大客户把平均客单价拉得很高,你按平均客单价去预估复购收入,会被高估一大截,用中位数估“普通用户花多少”才靠谱。客服响应时长同理,少数几个超长工单能把平均时长推到天上,看中位数才能反映“用户通常等多久”。

我做过一个更直接的对比:某功能的页面停留时长,平均 47 秒,中位数 12 秒。两者差了近四倍,原因是有一小部分用户挂着页面没关。如果按平均值去判断“用户很投入”,产品决策会完全跑偏。这种情况正确的做法是砍掉尾部(比如只统计 0 到 300 秒之间的会话)再看均值,同时把中位数作为主指标报出来。

4.3 目标设定与考核:避免被平均数带偏

给团队定目标时,如果基准是上一期的平均值,而上一期恰好有一个超常发挥的样本,那这一期的目标就会定得无法完成。我见过更隐蔽的情况:某小组因为一位成员连续两个月业绩特别突出,把小组平均值拉高,次年目标按这个平均值上浮,结果全组都被压得喘不过气。

我的建议是目标基数取近几期的中位数,再结合 P75 做挑战档。中位数代表“正常水平下的产出”,稳定性好得多;P75 代表“状态不错时的产出”,适合作为有挑战性的档位。这样定出来的目标,即使某一期出现极端案例,也不会把整个基准带歪。

5. 高频误区与排查清单

5.1 六个最常踩的坑,做成速查表

下面这张表是我这几年被问过的问题里出现频率最高的,基本覆盖了八成以上的误判场景:

现象常见误判正确做法
均值远高于中位数认为“大家工资都不错”先看 P50 和 P25,判断多数人真实位置
中位数很低但均值高认为数据有问题检查是否存在少量极高值,这通常是真实结构
分组后每组都在降,整体在涨认为统计出错检查各组样本量占比是否发生变化(结构效应)
换了一批数据中位数大变认为中位数不稳定检查口径是否变化、人群范围是否扩大
小样本算 P90直接用于决策样本量低于 30 时不要报 P90,改用最大值并标注样本量
只报一个数字认为已经说清楚必须同时给样本量、均值、中位数、区间范围

这张表里我最想强调最后一行。单报一个数字的风险不在于数字错,而在于它没有上下文。同样一个“中位数 6000”,样本量 12 人和样本量 12000 人,可信度完全是两回事。

5.2 口径不一致的排查路径

如果你发现数据看起来“不对劲”,但说不清哪里不对,我有一套固定的排查顺序,按这个走通常五分钟内能定位:

  1. 先查样本量:前后两期的 n 是否一致?n 突变往往意味着筛选条件变了。
  2. 再查字段定义:同一列名在不同表里是不是同一个意思?税前税后、含不含奖金,这两项最容易出错。
  3. 再查人群范围:是否新纳入了某个群体,比如实习转正、新并购团队。
  4. 最后查数值分布:跑一次describe(),看 min 和 max 有没有离谱值。
  5. 仍然对不上就分桶:按部门、按职级、按入职时间各切一刀,看差异出在哪个切片。

注意:排查时不要一上来就怀疑算法。绝大多数“数据不对”最终都归因于口径,只有极少数是计算方法本身的问题。我自己的经验比例大概是九比一。

5.3 汇报时的三条表达规矩

数字算对了,说错了照样误导人。我给自己定了三条硬规矩。第一,先说样本量和范围,再给数字,“在 320 名正式员工的月薪口径下,中位数是……”比“我们中位数是……”可信得多。第二,均值和Median必须同时出现,并且明确说明两者的差距意味着什么,不要让对方自己去猜。第三,不给单一结论,改成区间表述,“多数人集中在 5000 到 8000 这段”比“平均 9000”有用一百倍。

还有一个小技巧:如果对方坚持只要一个数,我一般会给中位数,然后把均值放在脚注里,附一句“均值受少数高值影响偏高”。这一句话能省掉后面半小时的解释。

6. 我在实际使用中的几点体会

最开始我也迷信平均数,因为它是所有统计工具默认输出的第一个值,Excel 里一拉就有,方便。真正让我改掉这个习惯的,是一次复盘:某次活动数据看着不错,平均客单价挺高,结果运营按这个数做了第二期预算,实际收入差了一大截。后来把分布摊开一看,前 5% 的用户贡献了将近一半的金额,剩下 95% 的人均消费其实很低。那次之后,我看任何偏斜数据都会先把中位数和分位数拉出来,成了肌肉记忆。

还有个细节想分享:不要迷信任何一个单独指标。中位数能防极端值,但它会把两端的信息全部抹平。真正稳妥的做法是养成“五数概括”的习惯——P10、P25、P50、P75、P90 加样本量,六个数字就能画出一张分布草图。我现在的报表模板里,这几个位置是固定字段,填不出来就说明数据准备还没做完。

最后留一个小练习:随便找一组你手头的数据,算出均值和Median,看看差多少。如果差距超过 20%,那这组数据里一定藏着结构性的东西——要么是极端值,要么是分层差异。把它找出来的过程,往往比算出那个数本身更有价值。

返回列表