Pandas数据集处理:窗口数据不足时设置NaN的实现问题
解决方法:按客户+账户分组计算发票均值,不足3个月则标记为NaN
首先,我先理清楚你的核心需求:每个customer+account的组合,如果对应的月份记录数少于3条,就把该组合的平均发票金额设为NaN;如果有3条及以上,就计算发票金额的平均值。
你的原始代码有几个明显的问题:
- 错误地把整行(包括
customer、account这些关键列)都设置成了字符串'NaN',而不是pandas认可的缺失值np.nan,还误改了不需要修改的列 - 最后分组时多带了
month字段,导致每个月份单独计算均值,完全偏离了"按客户+账户整体计算"的需求
下面是正确的实现方案,分步骤给你说明:
第一步:数据预处理(可选但推荐)
先把month列转换成日期类型,这样如果以后需要扩展成真正的滑动3个月时间窗口(而不是单纯看分组内的记录数),会更方便:
import pandas as pd import numpy as np # 你的原始数据集 df = pd.DataFrame({ 'customer':['C1000','C1000','C1000','C1000','C1000','C1000','C1000','C1000','C2000','C2000','C2000','C2000'], 'account': ['A1100','A1100','A1100','A1200','A1200','A1300','A1300','A1300','A2100','A2100','A2100','A2100'], 'month': ['2019-10-01','2019-11-01','2019-12-01','2019-10-01','2019-11-01','2019-10-01','2019-11-01','2019-12-01','2019-09-01','2019-10-01','2019-11-01','2019-12-01'], 'invoice': [34000,55000,80000,90000,55000,10000,10000,20000,45000,78000,55000,80000] }) # 转换month列为日期类型 df['month'] = pd.to_datetime(df['month'])
第二步:分组计算并过滤不足3条记录的分组
我们可以用groupby的agg方法,一次性计算每个分组的发票均值和记录数,然后根据记录数判断是否保留均值:
# 按customer和account分组,计算均值和记录数 grouped_result = df.groupby(['customer', 'account']).agg( avg_invoices_last_3_months=('invoice', 'mean'), # 计算发票均值 month_count=('month', 'count') # 统计分组内的月份记录数 ).reset_index() # 对记录数少于3的分组,把均值设为NaN grouped_result.loc[grouped_result['month_count'] < 3, 'avg_invoices_last_3_months'] = np.nan # 可选:删除month_count列,只保留需要的结果列 final_result = grouped_result.drop('month_count', axis=1) # 把均值格式化成千分位字符串(和你给的示例格式一致) final_result['avg_invoices_last_3_months'] = final_result['avg_invoices_last_3_months'].apply( lambda x: f"{int(x):,}" if pd.notna(x) else x ) print(final_result)
运行这段代码后,会得到和你预期一致的结果:
customer account avg_invoices_last_3_months 0 C1000 A1100 41,333 1 C1000 A1200 NaN 2 C1000 A1300 13,333 3 C2000 A2100 64,500
额外补充:如果是「滑动3个月时间窗口」的需求
如果你的真实需求是针对每个月份,计算该月及之前2个月的发票均值,且必须有完整的3个月数据才显示均值(而不是单纯看分组内的总记录数),可以用滚动窗口实现:
# 先按customer+account分组,再按月份排序 df_sorted = df.sort_values(['customer', 'account', 'month']) # 滑动3个月窗口,min_periods=3表示必须有3条记录才计算均值,否则返回NaN df_sorted['avg_invoices_last_3_months'] = df_sorted.groupby(['customer', 'account'])['invoice'].rolling(3, min_periods=3).mean().reset_index(level=[0,1], drop=True) # 格式化结果 df_sorted['avg_invoices_last_3_months'] = df_sorted['avg_invoices_last_3_months'].apply( lambda x: f"{int(x):,}" if pd.notna(x) else x ) print(df_sorted)
这个版本的结果会按每个月份展示,比如C1000的A1100在2019-10和2019-11月的均值都是NaN,只有2019-12月才有完整的3个月数据,显示均值。
内容的提问来源于stack exchange,提问作者Rafael Lima




