描述性统计与汇总
本教程共 54 篇 · 第 18 篇 · 更新于 2026-08-11 · 约 8 分钟阅读
本节目标:学会用 describe 和各类统计方法快速了解数据分布,能算平均值、中位数、分位数,还能看两列之间的相关性。
describe():一份现成的统计报告
对数值列调用 describe(),一次性给出 count、mean、std、min、四分位数、max:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"年龄": [25, 30, 28, 35, 22, 40, 38, 45, 29, 31],
"薪资": [12000, 15000, 11000, 18000, 9000, 20000, 17000, 22000, 12500, 14000],
"城市": ["北京", "上海", "广州", "北京", "深圳", "杭州", "北京", "上海", "广州", "深圳"],
})
print(df.describe())
输出的每一行都值得看懂:
| 行 | 含义 |
|---|---|
| count | 非空值数量,比行数少说明有缺失 |
| mean | 平均值 |
| std | 标准差,衡量数据分散程度,越大越散 |
| min | 最小值 |
| 25% | 第一四分位数,25% 的数据比它小 |
| 50% | 中位数 |
| 75% | 第三四分位数 |
| max | 最大值 |
想多看几个分位点,用 percentiles 参数:
import pandas as pd
import numpy as np
df = pd.DataFrame({"年龄": [25, 30, 28, 35, 22, 40, 38, 45, 29, 31]})
print(df.describe(percentiles=[0.05, 0.25, 0.75, 0.95]))
默认只统计数值列。想连文本列一起看,用 include="all";只想看某类列,用 include=["number"] 或 include=["str"]。文本列的统计长这样:
import pandas as pd
import numpy as np
s = pd.Series(["a", "a", "b", "b", "a", "a", "c"])
print(s.describe())
输出是 count(非空个数)、unique(几种取值)、top(出现最多的值)、freq(它出现了几次),对分类列同样有参考价值。
单个统计量:sum、mean、median、std
describe 是一整套报告,单个统计方法用起来更灵活:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"年龄": [25, 30, 28, 35, 22, 40],
"薪资": [12000, 15000, 11000, 18000, 9000, 20000],
})
print(df["年龄"].mean()) # 平均年龄
print(df["薪资"].median()) # 薪资中位数
print(df["年龄"].max()) # 最大年龄
print(df["薪资"].min()) # 最低薪资
print(df["年龄"].std()) # 标准差
print(df["年龄"].var()) # 方差
print(df["年龄"].count()) # 非空个数
print(df["薪资"].sum()) # 总和
选中多列(返回 DataFrame)再算,每个数值列各出一个结果:
import pandas as pd
import numpy as np
df = pd.DataFrame({"年龄": [25, 30, 28, 35], "薪资": [12000, 15000, 11000, 18000]})
print(df[["年龄", "薪资"]].median())
统计方法默认跳过缺失值,可以用 skipna=False 关闭。另外这些方法默认按列计算(axis=0),想按行算就传 axis=1:
import pandas as pd
import numpy as np
df = pd.DataFrame({"语文": [80, 90], "数学": [70, 85]}, index=["小明", "小红"])
print(df.sum()) # 每列求和,各科总分
print(df.sum(axis=1)) # 每行求和,每个学生的总分
常用统计方法速查:
| 方法 | 含义 | 方法 | 含义 |
|---|---|---|---|
| count | 非空个数 | var | 方差 |
| sum | 求和 | std | 标准差 |
| mean | 平均值 | min / max | 最小 / 最大值 |
| median | 中位数 | quantile | 分位数 |
Note中位数和平均值的区别:平均值容易被极端值拉偏,中位数不受影响。薪资这类分布偏斜的数据,看中位数比看平均值更接近真实水平。
value_counts():数一数各类有多少
分类列最常用的统计是计数。value_counts() 数出每个取值出现几次,默认按次数从高到低排:
import pandas as pd
import numpy as np
df = pd.DataFrame({"城市": ["北京", "上海", "广州", "北京", "深圳", "杭州"]})
print(df["城市"].value_counts())
常用参数:
normalize=True:返回占比而不是次数dropna=False:把缺失值也统计进去sort:默认 True,按次数排序
import pandas as pd
import numpy as np
df = pd.DataFrame({"城市": ["北京", "上海", "广州", "北京", "深圳", "杭州"]})
print(df["城市"].value_counts(normalize=True)) # 各城市占比
结果本身是个 Series,可以继续操作,比如只看前三名:
import pandas as pd
import numpy as np
df = pd.DataFrame({"城市": ["北京", "上海", "广州", "北京", "深圳", "杭州", "北京", "上海"]})
print(df["城市"].value_counts().head(3))
数值列也能 value_counts,但取值很多时意义不大。想知道某列一共有几种取值,用 nunique()(不重复值的个数);想知道具体是哪些值,用 unique()。
value_counts() 的结果也可以按取值顺序排列而不是按次数,加 sort_index=True 就行:
import pandas as pd
import numpy as np
df = pd.DataFrame({"城市": ["北京", "上海", "广州", "北京", "深圳"]})
print(df["城市"].value_counts().sort_index()) # 按城市名排序
quantile():任意分位数
想算 25% 分位、90% 分位,用 quantile:
import pandas as pd
import numpy as np
df = pd.DataFrame({"薪资": [12000, 15000, 11000, 18000, 9000, 20000, 17000, 22000]})
print(df["薪资"].quantile(0.25)) # 单个分位数
print(df["薪资"].quantile([0.1, 0.5, 0.9])) # 多个分位数
分位数就是”排在某个位置的值”:0.5 分位就是中位数,0.9 分位表示 90% 的数据比它小。常用它来找异常值边界,比如”超过 0.95 分位的值要检查”。传一个列表进去,一次算出多个分位数,结果是个 Series。
相关性:两列一起动
corr() 计算列与列之间的相关系数,默认皮尔逊(pearson),也可以换成 spearman、kendall:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"年龄": [25, 30, 28, 35, 22],
"薪资": [12000, 15000, 11000, 18000, 9000],
"绩效": [85, 92, 78, 88, 65],
})
print(df[["年龄", "薪资", "绩效"]].corr())
结果是个矩阵,每对列一个值,对角线全是 1(自己和自己当然完全相关)。结果范围 -1 到 1:
- 接近 1:正相关,一个涨另一个也涨
- 接近 -1:负相关,一个涨另一个跌
- 接近 0:基本没关系
只看某一列和其他列的关系,取出那一行;或者用两个 Series 直接算:
import pandas as pd
import numpy as np
df = pd.DataFrame({"年龄": [25, 30, 28, 35, 22], "薪资": [12000, 15000, 11000, 18000, 9000]})
print(df.corr()["薪资"])
print(df["年龄"].corr(df["薪资"])) # 两个 Series 单独算
协方差 cov() 也能算,但数值受单位影响,不好横向比较,实际分析里 corr 更常用。
Warning相关不等于因果。“冰淇淋销量和溺水人数相关”不代表冰淇淋导致溺水。看到高相关,先想清楚业务上是否说得通。
小补充:极值位置和累计值
idxmin()/idxmax():返回最大、最小值所在的行标签cumsum()/cummax():累计和、累计最大值,按顺序一路累加mode():出现次数最多的值(众数)
import pandas as pd
import numpy as np
s = pd.Series([1, 2, 2, 3, 3, 3])
print(s.mode()) # 众数是 3
import pandas as pd
import numpy as np
df = pd.DataFrame({"薪资": [12000, 15000, 11000, 18000]})
print(df["薪资"].idxmax()) # 谁薪资最高
print(df["薪资"].cumsum()) # 累计和序列
累计值在时间序列里很常用,比如算”到某天为止的总销售额”,§43 会细讲。想按类别分别统计(比如每个城市的平均薪资),那是分组操作 groupby 的领域,后面有专门章节(如 §37 所述)。
数值列如果位数太多看着累,可以 round 一下再输出:
import pandas as pd
import numpy as np
df = pd.DataFrame({"得分": [88.555, 92.333, 79.999]})
print(df.round(2)) # 保留两位小数
统计方法怎么挑
看数据类型:数值列用 mean、median、quantile 看分布;分类列用 value_counts、mode 看分布。混着用容易得出没意义的数字,比如对”城市”列求平均值,结果是一堆 NaN——这也是 describe 默认只统计数值列的原因。
一个完整的”摸底”流程
import pandas as pd
import numpy as np
df = pd.DataFrame({
"年龄": [25, 30, 28, 35, 22, 40],
"薪资": [12000, 15000, 11000, 18000, 9000, 20000],
"城市": ["北京", "上海", "广州", "北京", "深圳", "杭州"],
})
print(df.describe()) # 数值总览
print(df["城市"].value_counts()) # 分类分布
print(df[["年龄", "薪资"]].corr()) # 相关性
这三行基本覆盖了新手阶段 80% 的探索需求。
最后记一个原则:统计数字是工具,不是答案。看到异常值、看到强相关,先回到原始数据核实一下,再下结论。