如何在Polars中合并具有相同ID的所有行?
解决Polars DataFrame按ID合并重复行到多列的问题
问题分析
你尝试用自连接合并同ID的重复数据,但自连接会在相同ID下产生笛卡尔积(每行与同ID的所有行配对),导致结果行数爆炸,完全不符合需求。你实际需要的是将同ID下的多行data值转换为多列,也就是行转宽表的操作。
输入数据
import polars as pl df = pl.DataFrame({'id': [1, 1, 1], 'data': ['a', 'b', 'c']})
原始DataFrame结构:
shape: (3, 2) ┌─────┬──────┐ │ id ┆ data │ │ --- ┆ --- │ │ i64 ┆ str │ ╞═════╪══════╡ │ 1 ┆ a │ │ 1 ┆ b │ │ 1 ┆ c │ └─────┴──────┘
正确解法
方法1:动态生成多列(适配任意数量的重复行)
先按id分组将data聚合为列表,再根据列表长度动态生成对应列:
# 1. 按id分组,将data聚合为列表 grouped_df = df.group_by('id').agg(pl.col('data').alias('data_list')) # 2. 获取每个组的最大data数量(适配不同id有不同重复次数的场景) max_data_count = grouped_df.select(pl.col('data_list').list.len().max()).item() # 3. 动态生成data_1、data_2...列 result = grouped_df.with_columns( [pl.col('data_list').list.get(i).alias(f'data_{i+1}') for i in range(max_data_count)] ).drop('data_list') print(result)
方法2:透视表方式(更直观的行转列)
先给每个组内的行添加序号,再通过透视转换为宽表:
# 1. 给每个id组内的行分配序号(从0开始) df_with_index = df.group_by('id').agg( pl.col('data').enumerate().alias('data_with_idx') ).explode('data_with_idx') # 2. 拆分序号和data值 df_with_index = df_with_index.with_columns( idx=pl.col('data_with_idx').list.first(), data=pl.col('data_with_idx').list.last() ).drop('data_with_idx') # 3. 透视生成多列,并重命名列名 result = df_with_index.pivot( index='id', columns='idx', values='data', aggregate_function=pl.first ).rename({str(i): f'data_{i+1}' for i in range(3)}) print(result)
输出结果
两种方法都能得到你期望的结构:
shape: (1, 4) ┌─────┬────────┬────────┬────────┐ │ id ┆ data_1 ┆ data_2 ┆ data_3 │ │ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ str ┆ str ┆ str │ ╞═════╪════════╪════════╪════════╡ │ 1 ┆ a ┆ b ┆ c │ └─────┴────────┴────────┴────────┘
关键说明
- 自连接的核心是行与行的配对,适合关联不同数据集或同一数据集的不同行属性,不适合行转列的场景。
- 行转列的本质是将分组后的多行数据转换为单行多列,Polars中通过
group_by+agg生成列表,或结合enumerate+pivot都能实现这个需求。
内容的提问来源于stack exchange,提问作者crazydragon777
相关产品推荐
相关产品推荐

