如何用Polars将列表列元素与另一列数值逐一相乘?
问题描述
我有一个Polars DataFrame,包含分组列、Weight列和长度可变的Vals列表列,示例如下:
df = pl.DataFrame( { "Group": ["Group1", "Group2", "Group3"], "Weight": [100.0, 200.0, 300.0], "Vals": [[0.5, 0.5, 0.8],[0.5, 0.5, 0.8], [0.7, 0.9]] } )
对应的DataFrame结构:
┌────────┬────────┬─────────────────┐ │ Group ┆ Weight ┆ Vals │ │ --- ┆ --- ┆ --- │ │ str ┆ f64 ┆ list[f64] │ ╞════════╪════════╪═════════════════╡ │ Group1 ┆ 100.0 ┆ [0.5, 0.5, 0.8] │ │ Group2 ┆ 200.0 ┆ [0.5, 0.5, 0.8] │ │ Group3 ┆ 300.0 ┆ [0.7, 0.9] │ └────────┴────────┴─────────────────┘
目标是生成一个Weighted列,其中每个元素是Vals列表中对应元素与该行Weight值的乘积,预期结果如下:
┌────────┬────────┬─────────────────┬─────────────────┐ │ Group ┆ Weight ┆ Vals ┆ Weighted │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ f64 ┆ list[f64] ┆ list[i64] │ ╞════════╪════════╪═════════════════╪═════════════════╡ │ Group1 ┆ 100.0 ┆ [0.5, 0.5, 0.8] ┆ [50, 50, 80] │ │ Group2 ┆ 200.0 ┆ [0.5, 0.5, 0.8] ┆ [100, 100, 160] │ │ Group3 ┆ 300.0 ┆ [0.7, 0.9] ┆ [210, 270] │ └────────┴────────┴─────────────────┴─────────────────┘
尝试了几种方法,结果不符合预期:
df.with_columns( pl.col("Vals").list.eval(pl.element() * 3).alias("Weight1"), # 与字面量相乘有效 pl.col("Vals").list.eval(pl.element() * pl.col("Weight")).alias("Weight2"), # 无效 pl.col("Vals").list.eval(pl.element() * pl.col("Unknown")).alias("Weight3"), # 未知列得到相同错误结果 pl.col("Vals").list.eval(pl.col("Vals") * pl.col("Weight")).alias("Weight4"), # 同样无效 # pl.col('Vals') * 3 -> 报错 )
得到的错误结果:
┌────────┬────────┬────────────┬────────────┬──────────────┬──────────────┬────────────────────┐ │ Group ┆ Weight ┆ Vals ┆ Weight1 ┆ Weight2 ┆ Weight3 ┆ Weight4 │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ f64 ┆ list[f64] ┆ list[f64] ┆ list[f64] ┆ list[f64] ┆ list[f64] │ ╞════════╪════════╪════════════╪════════════╪══════════════╪══════════════╪════════════════════╡ │ Group1 ┆ 100.0 ┆ [0.5, 0.5, ┆ [1.5, 1.5, ┆ [0.25, 0.25, ┆ [0.25, 0.25, ┆ [0.25, 0.25, 0.64] │ │ ┆ ┆ 0.8] ┆ 2.4] ┆ 0.64] ┆ 0.64] ┆ │ │ Group2 ┆ 200.0 ┆ [0.5, 0.5, ┆ [1.5, 1.5, ┆ [0.25, 0.25, ┆ [0.25, 0.25, ┆ [0.25, 0.25, 0.64] │ │ ┆ ┆ 0.8] ┆ 2.4] ┆ 0.64] ┆ 0.64] ┆ │ │ Group3 ┆ 300.0 ┆ [0.7, 0.9] ┆ [2.1, 2.7] ┆ [0.49, 0.81] ┆ [0.49, 0.81] ┆ [0.49, 0.81] │ └────────┴────────┴────────────┴────────────┴──────────────┴──────────────┴────────────────────┘
问题在于list.eval函数中无法直接访问列表列之外的其他列,请问推荐的实现方法是什么?
解决方案
方法1:list.eval结合first()获取外部列
在list.eval的上下文里,外部列可以通过pl.col("Weight").first()获取当前行的标量值,因为每行的Weight是单一值,first()能正确拿到对应行的Weight:
df.with_columns( pl.col("Vals") .list.eval(pl.element() * pl.col("Weight").first()) .cast(pl.List(pl.Int64)) .alias("Weighted") )
方法2:使用struct+map_elements
将Vals和Weight打包成结构体,再通过lambda遍历列表元素计算乘积:
df.with_columns( pl.struct(["Vals", "Weight"]) .map_elements(lambda x: [v * x["Weight"] for v in x["Vals"]], return_dtype=pl.List(pl.Int64)) .alias("Weighted") )
方法3:展开后计算再聚合
适合复杂场景,先将列表展开为多行,计算乘积后再重新分组聚合回列表:
df.explode("Vals") .with_columns((pl.col("Vals") * pl.col("Weight")).cast(pl.Int64).alias("Weighted")) .group_by("Group", "Weight") .agg(pl.col("Weighted").alias("Weighted"))
方法4:Polars 0.19+ 版本的pl.parent()
如果使用Polars 0.19及以上版本,可以直接用pl.parent()访问外部列,写法更直观:
df.with_columns( pl.col("Vals") .list.eval(pl.element() * pl.parent().col("Weight")) .cast(pl.List(pl.Int64)) .alias("Weighted") )
内容的提问来源于stack exchange,提问作者StijnKas
相关产品推荐
相关产品推荐

