如何在Pandas中计算含部分数据的月度YoY同比变化?
修正部分月度数据的YoY同比计算逻辑
核心思路
放弃直接用月度重采样总和做同比的方式,转而基于日度数据匹配时间长度完全一致的周期:当前不完整月份已统计N天,就取去年同期同一月份的前N天数据做对比,确保两个统计窗口的时间范围对等,避免失真。
具体实现步骤
假设你的日度数据DataFrame为df,日期列是date,目标统计列是customer_count:
锁定当前不完整月份的时间范围
# 获取最新一条数据的日期 latest_date = df['date'].max() # 确定当前不完整月份的第一天 current_month_start = latest_date.replace(day=1) # 计算当前月份已统计的天数 days_in_current = (latest_date - current_month_start).days + 1匹配去年同期的对应时间范围
# 去年同期月份的第一天 last_year_month_start = current_month_start.replace(year=current_month_start.year - 1) # 去年同期对应结束日期(和今年统计天数一致) last_year_end = last_year_month_start + pd.Timedelta(days=days_in_current - 1)计算两个周期的累计值
# 今年部分月份的累计客户数 current_total = df[(df['date'] >= current_month_start) & (df['date'] <= latest_date)]['customer_count'].sum() # 去年同期对应天数的累计客户数 last_year_total = df[(df['date'] >= last_year_month_start) & (df['date'] <= last_year_end)]['customer_count'].sum()计算同比变化率
# 处理除数为0的边界情况 if last_year_total != 0: yoy_change = (current_total - last_year_total) / last_year_total * 100 else: yoy_change = None # 可根据业务需求替换为0或其他默认值 print(f"当前部分月度客户数同比变化: {yoy_change:.1f}%")
批量处理所有月份(含完整/部分)
如果要对所有月份统一计算,自动区分完整月份和最后一个不完整月份,可以用分组+自定义函数实现:
def calculate_yoy(group): group_max_date = group['date'].max() group_month_start = group_max_date.replace(day=1) # 判断是否为完整月份 is_full_month = (group_month_start + pd.offsets.MonthEnd(0)) == group_max_date if is_full_month: # 完整月份用常规月度同比 last_year_group = df[(df['date'].dt.year == group_month_start.year - 1) & (df['date'].dt.month == group_month_start.month)] last_year_total = last_year_group['customer_count'].sum() else: # 部分月份取同期对应天数 days_in_current = (group_max_date - group_month_start).days + 1 last_year_month_start = group_month_start.replace(year=group_month_start.year - 1) last_year_end = last_year_month_start + pd.Timedelta(days=days_in_current - 1) last_year_group = df[(df['date'] >= last_year_month_start) & (df['date'] <= last_year_end)] last_year_total = last_year_group['customer_count'].sum() current_total = group['customer_count'].sum() return (current_total - last_year_total) / last_year_total * 100 if last_year_total != 0 else None # 按月份分组计算同比 df['month'] = df['date'].dt.to_period('M') yoy_results = df.groupby('month').apply(calculate_yoy).rename('yoy_change')
关键说明
- 核心是确保对比窗口的时间长度完全一致,从根源上解决部分月份和完整月份直接对比的失真问题。
- 若业务需要统计均值而非总和,只需将代码中的
sum()替换为mean()即可。
内容的提问来源于stack exchange,提问作者Giacomo
相关产品推荐
相关产品推荐

