首页 / Pandas 入门教程 / 描述性统计与汇总

Pandas 入门教程

描述性统计与汇总

本教程共 54 篇 · 第 18 篇 · 更新于 2026-08-11 · 约 8 分钟阅读

PandasPandas 入门教程描述性统计describe分位数相关性

本节目标:学会用 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% 的探索需求。

最后记一个原则:统计数字是工具,不是答案。看到异常值、看到强相关,先回到原始数据核实一下,再下结论。