在业务数据分析中,经常需要对比当前周期与历史同期的数据差异,比如计算月度销售额的同比变化、周度活跃用户的环比变化等,这类需求可以通过Pandas的相关功能高效实现。Pandas内置了时间序列处理工具,配合分组、偏移操作,能够快速完成历史同期数据匹配和变化率计算。

基础数据准备
首先构造一份带时间维度的示例数据集,包含日期、类别和数值三个字段,后续的计算都基于这份数据展开。
import pandas as pd
import numpy as np
# 构造示例数据,时间范围为2023年1月到2024年6月,每月一条记录,两个类别
date_rng = pd.date_range(start='2023-01-01', end='2024-06-30', freq='MS')
data = {
'date': np.tile(date_rng, 2),
'category': ['A'] * len(date_rng) + ['B'] * len(date_rng),
'value': np.random.randint(100, 500, size=len(date_rng)*2)
}
df = pd.DataFrame(data)
# 将date列设置为 datetime 类型
df['date'] = pd.to_datetime(df['date'])
print(df.head(10))
计算历史同期数据的核心方法
同比历史同期计算
同比通常指与上年同一时期的数据对比,比如2024年3月的同比是2023年3月的数据。可以通过pd.DateOffset实现时间偏移,匹配对应的历史同期记录。
# 计算同比历史同期数据,偏移一年
df['last_year_same_period'] = df['date'] - pd.DateOffset(years=1)
# 通过merge匹配历史同期的数值,先构造历史数据的映射表
history_df = df[['date', 'category', 'value']].copy()
history_df = history_df.rename(columns={'date': 'last_year_same_period', 'value': 'last_year_value'})
# 关联获取历史同期数值
df = pd.merge(df, history_df, on=['last_year_same_period', 'category'], how='left')
print(df[df['date'] >= '2024-01-01'].head(5))
环比历史同期计算
环比通常指与上一个相邻周期的数据对比,比如月度环比是上月数据,周度环比是上周数据。这里以月度环比为例,偏移一个月即可。
# 计算环比历史同期数据,偏移一个月
df['last_month_same_period'] = df['date'] - pd.DateOffset(months=1)
history_month_df = df[['date', 'category', 'value']].copy()
history_month_df = history_month_df.rename(columns={'date': 'last_month_same_period', 'value': 'last_month_value'})
df = pd.merge(df, history_month_df, on=['last_month_same_period', 'category'], how='left')
print(df[df['date'] >= '2023-02-01'].head(5))
变化率计算逻辑
变化率的计算公式为:(当前值 - 历史同期值) / 历史同期值 * 100%,如果历史同期值为0,变化率可以设置为空值或者自定义标识。
# 计算同比变化率
df['yoy_change_rate'] = np.where(
df['last_year_value'].notna() & (df['last_year_value'] != 0),
(df['value'] - df['last_year_value']) / df['last_year_value'] * 100,
np.nan
)
# 计算环比变化率
df['mom_change_rate'] = np.where(
df['last_month_value'].notna() & (df['last_month_value'] != 0),
(df['value'] - df['last_month_value']) / df['last_month_value'] * 100,
np.nan
)
# 查看最终结果
print(df[['date', 'category', 'value', 'last_year_value', 'yoy_change_rate', 'last_month_value', 'mom_change_rate']].tail(10))
分组场景下的高效计算
如果数据存在多个分组维度,比如上面的category字段,也可以直接通过分组后使用shift方法实现偏移,避免多次merge,提升计算效率。
# 按类别分组,计算同比偏移(每年12个月,偏移12位就是上年同期)
df_sorted = df.sort_values(['category', 'date']).reset_index(drop=True)
df_sorted['group_last_year_value'] = df_sorted.groupby('category')['value'].shift(12)
df_sorted['group_yoy_change_rate'] = np.where(
df_sorted['group_last_year_value'].notna() & (df_sorted['group_last_year_value'] != 0),
(df_sorted['value'] - df_sorted['group_last_year_value']) / df_sorted['group_last_year_value'] * 100,
np.nan
)
print(df_sorted[['date', 'category', 'value', 'group_last_year_value', 'group_yoy_change_rate']].tail(10))
注意事项
- 时间偏移前需要确保日期列是
datetime类型,否则会报错。 - 如果数据存在缺失周期,比如某个月没有记录,shift方法会匹配到空值,需要根据业务场景补充缺失数据或者过滤空值。
- 变化率计算时要处理历史同期值为0或者空值的情况,避免除零错误。
- 大批量数据计算时,优先使用分组shift的方式,比多次merge的效率更高。