You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas按分组补全DataFrame缺失年份行及asfreq丢行问题解决

问题原因

你之前编写的代码丢失近一半行的核心原因是:将字符串格式的年份转为datetime类型时,默认生成的是对应年份的1月1日,而调用asfreq(freq='Y')时,Y年频的时间锚点是每年的12月31日,索引日期和频率锚点完全不匹配,导致原有数据行无法被识别匹配,最终大量数据被丢弃。

推荐解决方案

优先用MultiIndex重索引的方法实现,不需要做日期类型转换,没有时间锚点匹配的坑,在数千条数据的场景下性能表现也更好,代码逻辑更易维护。

import pandas as pd

# 读取数据并去重
df = pd.read_csv(r'filepath')
df = df.drop_duplicates(subset=['donor_id','year'])
# 年份转为整数类型,避免字符串排序、匹配异常
df['year'] = df['year'].astype(int)

# --------------------------
# 二选一即可:全局年份范围/单捐赠者自有年份范围
# 选项1:按全局所有捐赠者的最小-最大年份统一补全
full_index = pd.MultiIndex.from_product(
    [df['donor_id'].unique(), range(df['year'].min(), df['year'].max()+1)],
    names=['donor_id', 'year']
)

# 选项2:按每个捐赠者自己的首捐-末捐年份范围补全
# full_index_pairs = []
# for donor_id, sub_df in df.groupby('donor_id'):
#     min_year, max_year = sub_df['year'].min(), sub_df['year'].max()
#     full_index_pairs.extend([(donor_id, year) for year in range(min_year, max_year+1)])
# full_index = pd.MultiIndex.from_tuples(full_index_pairs, names=['donor_id', 'year'])
# --------------------------

# 重索引补全缺失行,缺失捐赠额填充为0
df = df.set_index(['donor_id', 'year']).reindex(full_index, fill_value=0).reset_index()

# 如果需要年份转回字符串格式,取消注释下一行即可
# df['year'] = df['year'].astype(str)

执行上述代码后,输出结果和你预期完全一致:

donor_id  year  donation
0        A  2011        10
1        A  2012        10
2        A  2013        10
3        B  2011        20
4        B  2012         0
5        B  2013        20
修正asfreq方案的方法

如果你一定要沿用之前的时间序列补全思路,只需要修正频率参数和日期锚点的匹配关系即可,注意要把年频从Y(年末)改成*YS(年初)*,和转datetime后生成的1月1日索引匹配:

import pandas as pd

df = pd.read_csv(r'filepath')
df = df.drop_duplicates(subset=['donor_id','year'])
# 转年初日期
df['year_DT'] = pd.to_datetime(df['year'], format='%Y')

df = (df.set_index('year_DT')
      .groupby('donor_id')
      # 用YS年初频率匹配索引
      .apply(lambda x: x.asfreq(freq='YS'))
      .drop('donor_id', axis=1)
      # 缺失捐赠额填充0
      .fillna({'donation': 0})
      .reset_index(drop=True))
# 从日期字段提取年份,替换原year字段
df['year'] = df['year_DT'].dt.year.astype(str)
df = df.drop('year_DT', axis=1)

内容的提问来源于stack exchange,提问作者Jacob Evers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 03:54:20