Polars按session_id分组统计fqid频次并转宽表的实现方法
Polars按session_id分组后实现fqid列的频次透视转换
原始数据与需求
原始DataFrame代码
import polars as pl df = pl.from_repr(""" ┌───────────────────┬──────────────┬────────┐ │ session_id ┆ elapsed_time ┆ fqid │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i32 ┆ cat │ ╞═══════════════════╪══════════════╪════════╡ │ 20090312431273200 ┆ 0 ┆ intro │ │ 20090312431273200 ┆ 1323 ┆ gramps │ │ 20090312431273200 ┆ 831 ┆ gramps │ │ 20090312431273200 ┆ 1147 ┆ gramps │ │ 20090312431273200 ┆ 5197 ┆ teddy │ │ 20090312431273200 ┆ 6180 ┆ teddy │ │ 20090312431273200 ┆ 7014 ┆ teddy │ │ 20090312431273200 ┆ 7946 ┆ teddy │ └───────────────────┴──────────────┴────────┘ """)
目标格式
希望转换为按session_id分组,统计fqid各分类的频次,并将分类转为fqid_分类值格式的列:
┌───────────────────┬─────────────┬────────────┬────────────┐ │ session_id ┆ fqid_gramps ┆ fqid_intro ┆ fqid_teddy │ │ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ i32 ┆ i32 ┆ i32 │ ╞═══════════════════╪═════════════╪════════════╪════════════╡ │ 20090312431273200 ┆ 3 ┆ 1 ┆ 4 │ └───────────────────┴─────────────┴────────────┴────────────┘
需求要点
- 按
session_id分组 - 统计
fqid各分类的出现频次 - 将列名重命名为
fqid_+分类值 - 转换为宽表格式
问题场景
你可以通过非分组代码实现全局统计:
# 全局统计的非分组代码 column_values = df['fqid'].value_counts().with_columns(pl.concat_str(pl.lit('fqid_'), pl.col('fqid').cast(pl.String))).transpose() column_values = column_values.rename(column_values.head(1).to_dicts().pop()).slice(1)
但尝试将逻辑改为分组聚合时,用pl.col('fqid')替代df['fqid']并执行group_by('session_id').aggregate(func('fqid')),会报错:AttributeError: 'Expr' object has no attribute 'with_columns'。
错误原因
pl.col('fqid')是Polars表达式对象,而非DataFrame,因此没有with_columns方法。聚合操作中需要使用符合Polars表达式规则的聚合逻辑,不能直接套用DataFrame的链式操作。
正确实现方式
方法1:使用pivot(推荐,简洁高效)
利用Polars的pivot方法直接实现分组后的宽表转换:
result = df.group_by(['session_id', 'fqid']).agg( pl.count().alias('count') ).pivot( index='session_id', columns='fqid', values='count' ).fill_null(0).rename( lambda col: f'fqid_{col}' if col != 'session_id' else col ) print(result)
步骤说明:
- 先按
session_id和fqid分组,统计每个组合的频次 - 使用
pivot将fqid的分类转为列,值为对应的频次 - 用
fill_null(0)填充未出现分类的缺失值为0 - 重命名列,给非
session_id的列加上fqid_前缀
方法2:提前获取分类,逐个聚合计数
如果需要明确指定要统计的分类,可以先获取所有fqid的唯一值,再在聚合中逐个计数:
# 获取所有fqid的唯一分类 fqid_categories = df['fqid'].unique().to_list() result = df.group_by('session_id').agg( [pl.count().filter(pl.col('fqid') == cat).alias(f'fqid_{cat}') for cat in fqid_categories] ) print(result)
步骤说明:
- 提取
fqid的所有唯一分类 - 按
session_id分组,对每个分类用filter筛选后统计数量,并重命名列
两种方法都能得到你需要的结果,其中pivot方法更简洁,适合大多数场景。
内容的提问来源于stack exchange,提问作者yk4r2
相关产品推荐
相关产品推荐

