Polars使用with_columns()创建包含结构体列的新DataFrame时触发ValueError错误
Polars使用with_columns()创建包含结构体列的新DataFrame时触发ValueError错误
嘿,我明白你遇到的问题了!这个错误其实是因为你对with_columns()的用法理解有点偏差——它核心是接收Polars表达式,而不是直接传入完整的DataFrame对象。
问题原因拆解
你写的df.explode(...).group_by(...).agg(...)会返回一个全新的DataFrame(形状是(4,2))。当你把这个DataFrame放进with_columns()的参数列表时,Polars会尝试把它当作单个标量值插入到原DataFrame的列中,但Polars要求这种标量输入必须是形状为(1,1)的DataFrame(也就是单个值),所以就抛出了你看到的ValueError。
你之前单独写这个操作时(不用列表包裹)能“正常工作”,其实是一种非预期的边缘情况——Polars会尝试把这个DataFrame的列直接追加到原df里,但这并不是with_columns()的设计用法,一旦放进列表里,处理逻辑就会严格检查输入类型,所以报错了。
两种解决方案
方案1:用Join合并结果(适配你原来的思路)
因为你的聚合操作都是基于d列分组的,我们可以先分别生成包含test_1和test_2的DataFrame,再通过join把它们合并到原DataFrame中:
import polars as pl df = pl.DataFrame( { "a": [[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4]], "b": [[1, 2, 3, 5],[1, 2, 3, 5],[1, 2, 3, 5],[1, 2, 3, 5]], "c": [[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4]], "d": ['a', 'b', 'c', 'd'] } ) # 生成test_1的分组结果 test_1_df = df.explode('a', 'b')\ .select("a", "b", "d", pl.struct('a', 'b').alias("test_1"))\ .group_by("d")\ .agg("test_1") # 生成test_2的分组结果 test_2_df = df.explode('b', 'c')\ .select("c", "b", "d", pl.struct('b', 'c').alias("test_2"))\ .group_by("d")\ .agg("test_2") # 合并到原DataFrame result = df.join(test_1_df, on="d").join(test_2_df, on="d") print(result)
方案2:用数组函数直接生成(更高效简洁)
其实完全不需要explode和group_by,Polars提供了数组zip函数,可以直接把对应列的数组元素打包成结构体数组,一步到位:
import polars as pl df = pl.DataFrame( { "a": [[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4]], "b": [[1, 2, 3, 5],[1, 2, 3, 5],[1, 2, 3, 5],[1, 2, 3, 5]], "c": [[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4]], "d": ['a', 'b', 'c', 'd'] } ) result = df.with_columns( # 将a和b数组的对应元素打包成结构体数组 pl.struct(pl.col("a"), pl.col("b")).arr.zip().alias("test_1"), # 将b和c数组的对应元素打包成结构体数组 pl.struct(pl.col("b"), pl.col("c")).arr.zip().alias("test_2") ) print(result)
这个方法的输出和你预期的完全一致,而且性能更好,因为避免了拆分再聚合的额外开销。
备注:内容来源于stack exchange,提问作者Ben Lambert
相关产品推荐
相关产品推荐

