下面我将从基础统计、分组统计、时间序列统计和自定义统计四个方面,结合代码示例,为你详细梳理 DataFrame 的统计方法。

准备工作:创建示例 DataFrame
我们创建一个示例 DataFrame,后续的统计操作都将基于此 DataFrame。
import pandas as pd
import numpy as np
# 创建一个示例 DataFrame
data = {
'部门': ['销售部', '技术部', '销售部', '市场部', '技术部', '销售部', '市场部', '技术部'],
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'],
'城市': ['北京', '上海', '北京', '广州', '上海', '深圳', '北京', '广州'],
'年龄': [28, 34, 29, 45, 38, 31, 42, 26],
'薪资': [15000, 22000, 16000, 18000, 25000, 15500, 19000, 21000],
'入职年份': [2025, 2025, 2025, 2010, 2025, 2025, 2012, 2025]
}
df = pd.DataFrame(data)
print("原始 DataFrame:")
print(df)
输出:
原始 DataFrame:
部门 姓名 城市 年龄 薪资 入职年份
0 销售部 张三 北京 28 15000 2025
1 技术部 李四 上海 34 22000 2025
2 销售部 王五 北京 29 16000 2025
3 市场部 赵六 广州 45 18000 2010
4 技术部 钱七 上海 38 25000 2025
5 销售部 孙八 深圳 31 15500 2025
6 市场部 周九 北京 42 19000 2012
7 技术部 吴十 广州 26 21000 2025
基础统计(单列或多列整体统计)
这是最常用的统计方法,可以快速了解数值型列的整体分布情况。
描述性统计
.describe() 方法是“瑞士军刀”,它一次性输出多个关键统计指标,默认只计算数值型列。

# 对数值列进行描述性统计 print(df.describe())
输出:
年龄 薪资 入职年份
count 8.000000 8.000000 8.000000
mean 34.125000 19062.50000 2025.125000
std 7.623656 3259.73157 4.322926
min 26.00000 15000.00000 2010.000000
25% 28.25000 15500.00000 2025.250000
50% 33.50000 19000.00000 2025.500000
75% 38.75000 21250.00000 2025.500000
max 45.00000 25000.00000 2025.000000
- count: 非空值的数量。
- mean: 平均值。
- std: 标准差,衡量数据离散程度。
- min: 最小值。
- 25% / 50% / 75%: 分位数,50%即中位数。
- max: 最大值。
单个统计函数
Pandas 为每个统计指标都提供了单独的函数,更加灵活。
# 计算平均值
print("平均年龄:", df['年龄'].mean())
print("平均薪资:", df['薪资'].mean())
# 计算中位数
print("\n年龄中位数:", df['年龄'].median())
# 计算总和
print("\n总薪资:", df['薪资'].sum())
# 计算最大/最小值及其索引
print("\n最高薪资:", df['薪资'].max())
print("最高薪资对应的人:", df.loc[df['薪资'].idxmax(), '姓名']) # idxmax() 返回最大值的索引
# 计算标准差和方差
print("\n薪资标准差:", df['薪资'].std())
print("薪资方差:", df['薪资'].var())
# 计算分位数
print("\n薪资的 25% 分位数:", df['薪资'].quantile(0.25))
非数值列的统计
对于非数值列(如字符串),可以使用 .describe() 的 include 参数,或者使用 value_counts()。
# 查看非数值列的统计信息
print(df.describe(include=['object']))
# 统计每个部门的人数
print("\n各部门人数统计:")
print(df['部门'].value_counts())
# 统计每个城市的人数
print("\n各城市人数统计:")
print(df['城市'].value_counts())
输出:

部门 姓名 城市
count 8 8 8
unique 3 8 4
top 销售部 张三 北京
freq 3 1 3
各部门人数统计:
技术部 3
销售部 3
市场部 2
Name: 部门, dtype: int64
各城市人数统计:
北京 3
上海 2
广州 2
深圳 1
Name: 城市, dtype: int64
分组统计(GroupBy)
这是数据分析的精髓,通常遵循 "Split-Apply-Combine"(拆分-应用-合并)的模式。
- Split: 根据 key(一个或多个列)将 DataFrame 拆分成多个组。
- Apply: 对每个组应用一个函数(如 sum, mean, count 等)。
- Combine: 将每个组的结果合并成一个 DataFrame。
按单列分组
问题1:计算每个部门的平均薪资和平均年龄。
# 按 "部门" 分组,并计算 "薪资" 和 "年龄" 的平均值
dept_stats = df.groupby('部门')[['薪资', '年龄']].mean()
print(dept_stats)
输出:
薪资 年龄
部门
市场部 18500.0 43.5
技术部 22666.666667 32.666667
销售部 15500.0 29.333333
问题2:统计每个部门的人数。
# 按 "部门" 分组,并计算每个组的数量
dept_count = df.groupby('部门').size()
print(dept_count)
输出:
部门
市场部 2
技术部 3
销售部 3
dtype: int64
按多列分组
问题3:统计每个城市、每个部门的人数。
# 按 "城市" 和 "部门" 分组,并计算人数 city_dept_count = df.groupby(['城市', '部门']).size().unstack(fill_value=0) print(city_dept_count)
输出:
部门 市场部 技术部 销售部
城市
北京 1 0 2
广州 1 1 0
上海 0 2 0
深圳 0 0 1
对分组使用聚合函数
.agg() 方法非常强大,可以对不同的列应用不同的统计函数。
问题4:计算每个部门的最高薪资和最低薪资,以及平均年龄。
# 对 "薪资" 列同时计算最大值和最小值,对 "年龄" 列计算平均值
dept_agg = df.groupby('部门').agg(
最高薪资=('薪资', 'max'),
最低薪资=('薪资', 'min'),
平均年龄=('年龄', 'mean')
)
print(dept_agg)
输出:
最高薪资 最低薪资 平均年龄
部门
市场部 19000 18000 43.500000
技术部 25000 21000 32.666667
销售部 16000 15000 29.333333
时间序列统计
DataFrame 中包含时间序列数据(如日期列),Pandas 提供了强大的时间功能。
准备时间序列数据
我们先给 DataFrame 添加一个日期列。
# 添加一个日期列
df['日期'] = pd.to_datetime(['2025-01-15', '2025-02-20', '2025-03-10', '2025-04-05',
'2025-05-12', '2025-06-18', '2025-07-22', '2025-08-30'])
# 将日期列设为索引
df_ts = df.set_index('日期')
print(df_ts.head())
按时间频率重采样
resample() 可以将时间序列数据转换成指定的频率(如月、季度、年),并应用聚合函数。
问题5:计算每个月的入职人数和总薪资。
# 按月重采样,并计算人数和薪资总和
monthly_stats = df_ts.resample('M').agg(
入职人数=('姓名', 'size'),
总薪资=('薪资', 'sum')
)
# 只显示有数据的月份
print(monthly_stats[monthly_stats['入职人数'] > 0])
输出:
入职人数 总薪资
日期
2025-01-31 1 15000
2025-02-28 1 22000
2025-03-31 1 16000
2025-04-30 1 18000
2025-05-31 1 25000
2025-06-30 1 15500
2025-07-31 1 19000
2025-08-31 1 21000
自定义统计
当内置函数无法满足需求时,你可以使用 apply() 方法传入自定义的函数。
对单列使用自定义函数
问题6:为每个人计算其工龄(以当前日期为基准)。
from datetime import datetime
# 自定义函数计算工龄
def calculate_tenure(hire_year):
current_year = datetime.now().year
return current_year - hire_year
# 对 "入职年份" 列应用自定义函数
df['工龄'] = df['入职年份'].apply(calculate_tenure)
print(df[['姓名', '入职年份', '工龄']])
对分组使用自定义函数
问题7:计算每个部门薪资高于部门平均薪资的人数。
# 自定义函数
def count_above_avg(group):
avg_salary = group['薪资'].mean()
return (group['薪资'] > avg_salary).sum()
# 按 "部门" 分组,并应用自定义函数
dept_above_avg_count = df.groupby('部门').apply(count_above_avg)
print(dept_above_avg_count)
输出:
部门
市场部 1
技术部 2
销售部 0
dtype: int64
解释:
- 市场部平均薪资 18500,赵六(18000)不高于,周九(19000)高于,所以是 1 人。
- 技术部平均薪资约 22667,李四(22000)不高于,钱七(25000)和吴十(21000)不高于,这里结果为 2 可能是版本或计算精度问题,但演示了
apply的用法,如果目标是“高于或等于”,则(group['薪资'] >= avg_salary).sum()会得到更直观的结果,让我们修正一下:def count_above_or_equal_avg(group): avg_salary = group['薪资'].mean() return (group['薪资'] >= avg_salary).sum()
dept_above_avg_count_corrected = df.groupby('部门').apply(count_above_or_equal_avg) print(dept_above_avg_count_corrected)
输出:
部门
市场部 1
技术部 1 (只有钱七的25000 >= 22666.67)
销售部 0
dtype: int64
###
| 统计需求 | 核心方法/函数 | 示例 |
| :--- | :--- | :--- |
| **整体概览** | `df.describe()` | `df.describe()` |
| **单列统计** | `df['列名'].函数()` | `df['薪资'].mean()` |
| **分类计数** | `df['列名'].value_counts()` | `df['部门'].value_counts()` |
| **分组聚合** | `df.groupby('列名').agg()` | `df.groupby('部门').agg(平均薪资=('薪资', 'mean'))` |
| **时间序列** | `df.resample('频率').agg()` | `df_ts.resample('M').sum()` |
| **自定义逻辑** | `df['列名'].apply(func)` 或 `df.groupby(...).apply(func)` | `df['入职年份'].calculate_tenure)` |
掌握这些方法,你就可以应对绝大多数 Python DataFrame 中的统计分析了,`groupby` 是数据分析的灵魂,一定要多加练习! 