计算客户近6个月营业额出现负值的原因排查求助
首先,你看到的负值尤其是 -2.147484e+09 是Int32类型的最小值(也就是 -2^31),这说明你的求和结果超过了Int32的最大值(2^31-1 = 2147483647),导致了整数溢出,数值被“绕回”成了负数。这是代码里最核心的问题,咱们一步步拆解修复:
1. 修复类型溢出问题
在你的代码中,滚动求和后调用了 .astype(int),而Pandas在很多环境下默认的int是Int32类型。当单个客户的滚动销售额总和超过2147483647时,就会触发溢出,得到错误的负值。
修改方式:把 astype(int) 替换为 astype(np.int64) 或者 astype('int64'),Int64的范围足够大(从 -9223372036854775808 到 9223372036854775807),完全能覆盖你的业务数据量。
修改后的代码片段:
# 2nd pass: use a sliding window of 180 days to sum df1 = df1.rolling(180, min_periods=0).sum().astype(np.int64) # 这里改了两个地方:窗口从90→180,类型从int→int64
2. 修正滚动窗口的天数(匹配需求)
你的需求是计算近6个月营业额,但代码里用的是 rolling(90, ...)(90天),这和需求不符,所以顺便把窗口大小改成180天(近似6个月的天数)。
3. 修复DataFrame排序的问题
你调用了 df.sort_values(['customerId', 'fromDate'], ignore_index=True) 但没有把结果赋值回df,这意味着原DataFrame并没有被排序!滚动窗口的计算依赖于时间序列的有序性,所以必须把排序后的结果保存下来:
修改成:
# Ensure your dataframe is sorted by fromDate for rolling window df = df.sort_values(['customerId', 'fromDate'], ignore_index=True) # 加上赋值
完整修复后的代码
import pandas as pd import numpy as np import multiprocessing as mp import time def sum_sales(customer, df): # 1st pass: sum sales of same days, reduce the row numbers df1 = df.groupby('fromDate')['sales'].sum() # Generate all missing dates df1 = df1.reindex(pd.date_range(df1.index.min(), df1.index.max(), freq='D'), fill_value=0) # 2nd pass: use a sliding window of 180 days to sum df1 = df1.rolling(180, min_periods=0).sum().astype(np.int64) # Restore original index for the group df1 = df1.reindex(df['fromDate']).reset_index(drop=True).to_frame().set_index(df.index) return df1 if __name__ == '__main__': # Setup a minimal reproducible example N = 3_000_000 D = pd.to_datetime('2021-1-1') rng = np.random.default_rng(2022) dti = D + pd.to_timedelta(rng.integers(0, 365*2, N), unit='D') cust = rng.integers(0, 75000, N) sales = rng.integers(1, 200, N) df = pd.DataFrame({'customerId': cust, 'fromDate': dti, 'sales': sales}) # Ensure your dataframe is sorted by fromDate for rolling window df = df.sort_values(['customerId', 'fromDate'], ignore_index=True) start = time.time() with mp.Pool(mp.cpu_count() - 1) as p: results = p.starmap(sum_sales, df.groupby('customerId')) df['total_sales'] = pd.concat(results) end = time.time() print(f"Elapsed time: {end - start:.2f} seconds") # 验证结果是否正常 print(df['total_sales'].describe())
验证修复效果
运行修复后的代码后,df['total_sales']的统计结果应该全部为正值,不会再出现负数,而且数值符合近6个月销售额的求和逻辑。
内容的提问来源于stack exchange,提问作者Test

