我有一个具有一定数量组的数据框,包含一个权重列和一个值列表,可以是任意长度,例如:
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]]
}
)
┌────────┬────────┬─────────────────┐
│ 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] │
└────────┴────────┴─────────────────┘
我的目标是计算一个“加权”列,它是值列表中每个项目与权重列中的值的倍数:
┌────────┬────────┬─────────────────┬─────────────────┐
│ 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").arr.eval(pl.element() * 3).alias("Weight1"), #Multiplying with literal works
pl.col("Vals").arr.eval(pl.element() * pl.col("Weight")).alias("Weight2"), #Does not work
pl.col("Vals").arr.eval(pl.element() * pl.col("Unknown")).alias("Weight3"), #Unknown columns give same value
pl.col("Vals").arr.eval(pl.col("Vals") * pl.col("Weight")).alias("Weight4"), #Same effect
# pl.col('Vals') * 3 -> gives an error
]
)
┌────────┬────────┬────────────┬────────────┬──────────────┬──────────────┬────────────────────┐
│ 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] │
└────────┴────────┴────────────┴────────────┴──────────────┴──────────────┴────────────────────┘
除非我没有正确理解它,否则您似乎无法从 eval 函数内访问列表之外的列。也许有一种方法可以在语句中使用列表理解,但这看起来并不是一个很好的解决方案。
这里推荐的方法是什么?任何帮助将不胜感激!
从最新版本的 Polars 开始,这是现在正确的语法:
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]]
}
)
(df
.explode('Vals')
.with_columns(Weighted = pl.col('Weight')*pl.col('Vals'))
.groupby('Group')
.agg(
pl.col('Weight').first(),
pl.col('Vals'),
pl.col('Weighted')
)
)
(df
.explode('Vals')
.with_columns(Weighted = pl.col('Weight')*pl.col('Vals'))
.groupby('Group')
.agg([
pl.col('Weight').first(),
pl.col('Vals').list(),
pl.col('Weighted').list()
])
)
所以你可以通过两种方式解决这个问题。说实话我不知道哪一个更好,我还没有测试过性能或 RAM 使用情况。
我们可以将两列放入一个结构体中,然后对它们应用自定义函数。 (文档中对此进行了解释此处)
import polars as pl
import numpy as np
def weighted_list(ls, weight):
return(list(np.array(ls) * weight))
(df.with_columns([pl.struct(["Weight", "Vals"])
.apply(lambda x: weighted_list(x["Vals"], x["Weight"]))])
)
因此在这种情况下,您必须小心您的“Group”、“Weight”列是唯一的。因此,如果您有两个条目,例如 Group3 和 Weight 300。
(df.explode("Vals")
.with_column((pl.col("Vals") * pl.col("Weight")).alias("Weighted"))
.groupby(["Group", "Weight"])
.agg([pl.list("Vals"), pl.list("Weighted")])
)
不幸的是,尚不支持列表上的操作,但您可以使用 Struct 来解决它并避免数据爆炸和内爆。您可以从列
a
创建一个列表,然后通过将结构相乘并将其转换回列表来获得结果。
repeat_by()
使用第 a
列中列表的 list.len()
创建第 b
列中的值列表。list.to_struct()
将列表转换为结构。struct.field()
获取结构体的所有字段(使用 *
)作为列。concat_list()
将列连接到列表中。list.drop_nulls()
删除由于列表长度不同而可能出现的空值。cast()
将浮点结果列表转换为整数列表。(
df
.with_columns(Weighted = pl.col.Weight.repeat_by(pl.col.Vals.list.len()))
.with_columns(pl.col.Weighted.list.to_struct() * pl.col.Vals.list.to_struct())
.with_columns(Weighted = pl.concat_list(pl.col.Weighted.struct.field("*")))
.with_columns(pl.col.Weighted.list.drop_nulls().cast(pl.List(pl.Int64)))
)
┌────────┬────────┬─────────────────┬─────────────────┐
│ 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] │
└────────┴────────┴─────────────────┴─────────────────┘