You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Pandas数据集处理:窗口数据不足时设置NaN的实现问题

解决方法:按客户+账户分组计算发票均值,不足3个月则标记为NaN

首先,我先理清楚你的核心需求:每个customer+account的组合,如果对应的月份记录数少于3条,就把该组合的平均发票金额设为NaN;如果有3条及以上,就计算发票金额的平均值

你的原始代码有几个明显的问题:

  • 错误地把整行(包括customeraccount这些关键列)都设置成了字符串'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条记录的分组

我们可以用groupbyagg方法,一次性计算每个分组的发票均值和记录数,然后根据记录数判断是否保留均值:

# 按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

火山引擎 最新活动