由于版本的不同,1.5+支持嵌套列表,旧版本只识别字符串或单函数;应优先考虑元组形式,并注意dropna=False保留Nan组,高基数组应使用pd.Grouper或category优化。用
pandas.DataFrame.groupby 当进行分组聚合时,为什么? agg 里传字典会报错吗?
常见的错误是直接写 {'sales': 'sum', 'profit': ['mean', 'std']} 却没注意 pandas 版本。1.5+ 支持嵌套列表和元组写法,但旧版本只识别字符串或单个函数。更安全的方法是统一元组形式:agg([('total_sales', 'sum'), ('avg_profit', 'mean'), ('profit_std', 'std')]),列名清晰,兼容性好。
另一个坑是分组键中的缺失值:默认 dropna=True,若想保留 NaN 组,得显式参数 dropna=False。特别是处理用户 ID 或者当区域字段有空值时,错过这个字段会导致结果行数减少,不易察觉。
在性能方面,如果分组维度高(如千万级唯一用户),优先考虑 pd.Grouper(key='timestamp', freq='D') 避免哈希费用,而不是原始字段分组;或者先用 astype('category') 压缩组列内存。
rolling 链式调用?
不一定要链式,但要注意顺序:df.sort_values('ts').rolling(7, on='ts').agg({'x': 'mean', 'y': lambda s: s.quantile(0.9)}) 这样写会失败——rolling 的 on 参数要求的时间列必须是 datetime 类型已经排序,否则会触发 ValueError: index must be monotonic。
立即学习“Python免费学习笔记(深入);
实操建议:
Python数据分析助手
为业务和科研数据的快速处理提供Python数据清理、统计分析和可视化建议。
下载- 首先确保时间列是
pd.to_datetime(df['ts']),再sort_values+reset_index(drop=True) - 不同窗口长度的多指标?不要硬塞进一个?
rolling:分开算再pd.concat,比如df[ma7] = df['val'].rolling(7).mean()和df[ma30] = df['val'].rolling(30).mean() - 非内置函数,如分位数,使用
rolling(...).apply(lambda x: np.nanpercentile(x, 90), raw=True),加raw=True能提速 2–3 倍
pandas 窗户计算缓慢,可以更换 dask 吗?
可以,但是窗口函数支持有限:dask.dataframe 的 rolling 只支持基础聚合(sum/mean/count),不支持 quantile、apply 或者自定义函数。还有 rolling 必须基于索引(不能使用) on= 因此,必须先参数 set_index('ts') 再算,对无序时间数据非常不友好。
真正可行的替代路径:
- 用
polars:语法接近 pandas,pl.col('x').rolling_mean(window_size=7)支持多列并行,百万行基本秒出 - 若必须用 spark:把数据转成
spark.sql.DataFrame,用F.window+over(Window.partitionBy(...).orderBy(...)),但要注意rangeBetween比较时间窗口更稳定rowsBetween少踩时区,重复时间戳坑 - 纯 Python 大数据流处理?使用
itertools.islice手写滑动窗口 +heapq滚动分位数的维护适用于单指标高频更新场景
GROUP BY + OVER)?
关键不是拼接,而是对齐索引。pandas 直接是最常见的错误 df.merge(grouped_df, on='group_id'),结果爆炸——因为分组后的行数远低于原表。正确的方法是使用它 transform 或 map:
df['group_avg_sales'] = df.groupby('region')['sales'].transform('mean') —— 这将广播回到原表的每一行,长度不变;df['rolling_max_7d'] = df.sort_values('date').groupby('region')['sales'].rolling(7).max().reset_index(level=0, drop=True) —— 注意这里 reset_index 是为了把 multiindex 拉平,否则不能赋值原来 df。
如果分组字段本身具有重复值(如同一点),则很容易被忽略 region 多次出现),transform 没问题,但 map 会因 Series 索引重而丢失数据,一定要检查 grouped_result.index.is_unique。