Pandas 高级操作:分组聚合、透视表与时间序列
Pandas 高级操作:分组聚合、透视表与时间序列
欢迎来到本教程!如果你已经掌握了 Pandas 的基础知识(数据读取、筛选、排序和简单统计),那么是时候深入探索那些真正让数据分析工作流变得高效的“高级操作”了。本教程将聚焦于三大核心模块:分组聚合、透视表 以及 时间序列处理。我们将用清晰的代码示例与实际场景,帮助你彻底理解这些概念。
1. 分组聚合:超越 groupby() 的基础用法
分组聚合是数据分析中最强大的手段之一,它遵循“分割-应用-合并”的模式。我们先从增强版的聚合开始。
1.1 多函数聚合与自定义函数
groupby().agg() 允许对不同的列应用不同的聚合函数。你可以传入字典来指定列与函数的映射,甚至传入自定义函数。
import pandas as pd
import numpy as np
# 示例数据:电商销售记录
df = pd.DataFrame({
'类别': ['电子产品', '服装', '电子产品', '服装', '电子产品'],
'子类别': ['手机', '上衣', '笔记本电脑', '裤子', '手机'],
'销售额': [200, 150, 800, 120, 300],
'利润': [50, 40, 150, 30, 80]
})
# 对不同列使用不同的聚合方式
result = df.groupby('类别').agg(
总销售额=('销售额', 'sum'),
平均利润=('利润', 'mean'),
最高销售额=('销售额', 'max'),
销售笔数=('销售额', 'count')
).reset_index()
print(result)
还可以使用 agg 配合 NumPy 函数或自定义 lambda 函数:
# 计算销售额的极差(最大值-最小值)
grouped = df.groupby('类别')['销售额'].agg(
['sum', 'mean', lambda x: x.max() - x.min()]
)
grouped.columns = ['总和', '均值', '极差']
1.2 变换操作:transform 的妙用
transform 与 agg 的关键区别在于,它返回与原 DataFrame 索引一致、长度相同的数据。这对“组内标准化”或“填充缺失值”非常有用。
场景:计算每个类别内部的销售额相对于该类别平均销售额的比例。
df['销售额组内均值'] = df.groupby('类别')['销售额'].transform('mean')
df['销售额偏离均值'] = df['销售额'] - df['销售额组内均值']
print(df[['类别', '销售额', '销售额组内均值', '销售额偏离均值']])
场景:用组内平均值填充该组内的缺失值。(先模拟缺失值)
df.loc[1, '利润'] = np.nan # 人为制造一个缺失值
df['利润'] = df.groupby('类别')['利润'].transform(lambda x: x.fillna(x.mean()))
1.3 过滤操作:filter
filter 可以根据组的整体属性来筛选整个组,而不是组内的行。例如,保留销售额总和大于 500 的类别。
filtered_df = df.groupby('类别').filter(lambda g: g['销售额'].sum() > 500)
print(filtered_df)
2. 透视表与交叉表:灵活重塑数据
透视表是 Excel 用户非常熟悉的功能,Pandas 中的 pivot_table 更强大且可编程。
2.1 创建透视表
语法:pd.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', ...)
我们用一个新的销售数据集来说明:
sales_data = pd.DataFrame({
'月份': ['1月', '1月', '1月', '2月', '2月', '2月'],
'地区': ['华北', '华东', '华南', '华北', '华东', '华南'],
'产品': ['A', 'A', 'B', 'A', 'B', 'B'],
'销售额': [100, 150, 200, 110, 160, 190]
})
默认聚合函数是平均值,我们可以改成 sum 来得到汇总:
pivot = pd.pivot_table(sales_data,
values='销售额',
index='月份',
columns='地区',
aggfunc='sum',
fill_value=0) # 用0填充NaN
print(pivot)
2.2 多值透视与边际汇总
你可以计算多个值字段,并且添加“合计”行列 (margins=True)。
# 增加一列利润数据
sales_data['利润'] = sales_data['销售额'] * 0.2
pivot_multi = pd.pivot_table(sales_data,
values=['销售额', '利润'],
index=['月份'],
columns=['地区'],
aggfunc='sum',
margins=True,
margins_name='总计')
print(pivot_multi)
2.3 交叉表:crosstab
交叉表是专门计算分组频率的特殊透视表,常用于统计列联表。
# 用户行为数据
users = pd.DataFrame({
'性别': ['男', '女', '男', '男', '女'],
'偏好': ['科技', '时尚', '科技', '科技', '时尚'],
'年龄段': ['青年', '青年', '中年', '青年', '中年']
})
# 统计性别与偏好的频次
cross = pd.crosstab(users['性别'], users['偏好'])
print(cross)
# 更高级:多列交叉,并显示总计
cross_norm = pd.crosstab(users['性别'], [users['年龄段'], users['偏好']],
margins=True, normalize='index') # 按行归一化
print(cross_norm)
3. 时间序列高级处理
Pandas 拥有卓越的时间序列功能,掌握它能让金融、物联网、日志等数据的处理事半功倍。
3.1 时间索引与日期功能
首先,确保数据带有正确的时间索引,并使用 DatetimeIndex。
date_rng = pd.date_range(start='2024-01-01', end='2024-01-10', freq='D')
ts_df = pd.DataFrame(date_rng, columns=['日期'])
ts_df['销售额'] = np.random.randint(100, 500, size=len(date_rng))
ts_df.set_index('日期', inplace=True)
利用日期属性提取特征:
ts_df['星期'] = ts_df.index.dayofweek # 0=周一
ts_df['是否周末'] = ts_df['星期'].isin([5,6]).astype(int)
ts_df['月份'] = ts_df.index.month
3.2 重采样:改变时间频率
resample() 是时间序列分组的利器。降采样(从高频到低频)需要聚合,升采样(从低频到高频)需要插值。
降采样:日数据聚合为周数据
weekly_sales = ts_df.resample('W-MON')['销售额'].agg(['sum', 'mean', 'count'])
print(weekly_sales)
其中 'W-MON' 表示以周一作为每周的起始日。常用的频率有 'D' 天,'H' 小时,'M' 月末,'Q' 季末,'A' 年末等。
升采样与插值
# 创建一个小数据集,从2天数据升采样到每小时数据
daily = pd.DataFrame({
'值': [10, 20]
}, index=pd.to_datetime(['2024-01-01', '2024-01-02']))
hourly = daily.resample('H').asfreq() # 产生NaN
hourly_interpolated = hourly.resample('H').interpolate(method='linear')
print(hourly_interpolated.head())
3.3 滑动窗口计算:rolling
移动窗口常用于计算移动平均、移动标准差,消除噪声。
# 7天移动平均销售额
ts_df['7天移动平均'] = ts_df['销售额'].rolling(window=7, min_periods=1).mean()
# 计算滑动窗口内的标准差
ts_df['7天移动标准差'] = ts_df['销售额'].rolling(window=7).std()
rolling 也支持时间偏置窗口(rolling 结合时间索引):
# 基于时间的窗口,例如滚动2天窗口(需要考虑观测的时间密度)
# 如果索引是日期时间,可以这样:
# ts_df.rolling('2D').mean()
3.4 时间位移与滞后
使用 shift() 可以轻松创建滞后特征,常用于时间序列预测。
ts_df['前一日销售额'] = ts_df['销售额'].shift(1)
ts_df['前两日销售额'] = ts_df['销售额'].shift(2)
# 向前移动(未来值)
ts_df['后一日销售额'] = ts_df['销售额'].shift(-1)
3.5 日期范围生成与业务日
pd.bdate_range 生成工作日序列,适合金融分析。
bus_days = pd.bdate_range(start='2024-01-01', periods=10)
# 合并自定义假日
from pandas.tseries.holiday import USFederalHolidayCalendar
cal = USFederalHolidayCalendar()
holidays = cal.holidays(start='2024-01-01', end='2024-12-31')
bus_days_us = pd.bdate_range(start='2024-01-01', periods=10, holidays=holidays)
综合实例:销售仪表板数据准备
让我们将上述技术组合起来,完成一个接近实际工作的任务:有原始订单数据,需要生成按月、地区的销售汇总,并计算月环比增长率。
# 构造原始订单数据
np.random.seed(42)
n = 200
orders = pd.DataFrame({
'order_date': pd.date_range('2024-01-01', periods=n, freq='D'),
'region': np.random.choice(['北区', '南区', '东区', '西区'], n),
'sales': np.random.randint(50, 500, n)
})
# 按月汇总
orders['月份'] = orders['order_date'].dt.to_period('M') # 周期类型,方便排序
monthly_region = orders.groupby(['月份', 'region']).agg(
月销售额=('sales', 'sum')
).reset_index()
# 创建透视表
pivot_region = pd.pivot_table(monthly_region,
values='月销售额',
index='月份',
columns='region',
aggfunc='sum',
fill_value=0)
print("各地区月度销售额透视表:")
print(pivot_region)
# 计算月环比增长率(对所有地区列应用pct_change)
growth_pivot = pivot_region.pct_change() * 100
print("\n月环比增长率(%):")
print(growth_pivot.round(2))
# 利用时间序列重采样验证总销售额趋势
ts = orders.set_index('order_date')['sales'].resample('M').sum()
print("\n总月度销售额:")
print(ts)
结语
通过本教程,你已经掌握了 Pandas 在分组聚合、透视表重构和时间序列分析方面的核心高级技巧。这些操作是构建高效数据管道和进行深入探索性分析的基石。建议你下载本页代码示例,用你自己的数据集练习,并查阅 Pandas 官方文档中关于 groupby 增强功能、resample 偏移别名以及窗口函数的更多参数。不断实践,你将成为数据处理的高手!
本教程由「免费在线教程」提供,欢迎访问我们的其他课程。