为什么要第二个“over”?
取自这个问题的数据
在不分组的前提下基于分组添加新列
df = pl.DataFrame({
'year': [ 5, 5, 5,
10, 10,
15, 15,
30, 30, 30 ],
'usage': ['farm', 'best', '',
'manual', 'best',
'best', 'city',
'random', 'best', 'farm' ],
'value': [0.825, 0.83, 0.85,
0.935, 0.96,
1.12, 1.305,
1.34, 1.34, 1.455],
'source': ['wood', 'metal', 'water',
'metal', 'water',
'wood', 'water',
'wood', 'metal', 'water' ]})
Output:
┌──────┬────────┬───────┬────────┐
│ year ┆ usage ┆ value ┆ source │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str │
╞══════╪════════╪═══════╪════════╡
│ 5 ┆ farm ┆ 0.825 ┆ wood │
│ 5 ┆ best ┆ 0.83 ┆ metal │
│ 5 ┆ ┆ 0.85 ┆ water │
│ 10 ┆ manual ┆ 0.935 ┆ metal │
│ 10 ┆ best ┆ 0.96 ┆ water │
│ 15 ┆ best ┆ 1.12 ┆ wood │
│ 15 ┆ city ┆ 1.305 ┆ water │
│ 30 ┆ random ┆ 1.34 ┆ wood │
│ 30 ┆ best ┆ 1.34 ┆ metal │
│ 30 ┆ farm ┆ 1.455 ┆ water │
└──────┴────────┴───────┴────────┘
目标是创建一个新列,其值来自列 source,当 usage 等于 "best"。
我知道有其他可行的方法,但我想理解为什么下面的解法需要多个 over 窗口。没有第二个,它似乎无法正确进行分组。我本以为 over 会应用到整个表达式上。
df.with_columns(
pl.col('source').gather(pl.col('usage').eq('best').cast(pl.Int8).arg_max().over('year')).over('year').alias('best')
)
似乎 gather 把表达式从窗口中拆出。还有哪些函数也会这样做?
解决方案
将表达式拆解后,可以更清楚地看出这一点
# the source column
pl.col("source")
# take value(s) from the source column by index
.gather(pl.col("usage").eq("best").cast(pl.Int8).arg_max().over("year"))
# take that value within each year
.over("year")
.alias("best")
如果只看 gather 里面的内容,我们就能得到每一年中 "best" 的索引。没有额外的 over(),它会在整列的索引处进行聚集。额外的 over 需要用来指示它在每一年内聚到该索引。
并不是 gather(或任何其他函数)把表达式从窗口中拆出。更确切地说,位于 gather 内部的表达式恰好是一个窗口函数,但这并不意味着整个表达式也必须是窗口函数。第一个 over 位于 gather 之内,因此它处于聚合层级,而不是表达式层级。
gather_expression = pl.col("usage").eq("best").cast(pl.Int8).arg_max().over("year")
df.with_columns(
# show what the gather expression returns (the index of best within each year)
index=gather_expression,
# bad_output = get the source at index (whole column)
bad_output=pl.col("source").gather(gather_expression),
# best = get the source at index within the year
best=pl.col("source").gather(gather_expression).over("year")
)
# shape: (10, 6)
# ┌──────┬────────┬────────┬───────┬────────────┬───────┐
# │ year ┆ usage ┆ source ┆ index ┆ bad_output ┆ best │
# │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# │ i64 ┆ str ┆ str ┆ u32 ┆ str ┆ str │
# ╞══════╪════════╪════════╪═══════╪════════════╪═══════╡
# │ 5 ┆ farm ┆ wood ┆ 1 ┆ metal ┆ metal │
# │ 5 ┆ best ┆ metal ┆ 1 ┆ metal ┆ metal │
# │ 5 ┆ ┆ water ┆ 1 ┆ metal ┆ metal │
# │ 10 ┆ manual ┆ metal ┆ 1 ┆ metal ┆ water │
# │ 10 ┆ best ┆ water ┆ 1 ┆ metal ┆ water │
# │ 15 ┆ best ┆ wood ┆ 0 ┆ wood ┆ wood │
# │ 15 ┆ city ┆ water ┆ 0 ┆ wood ┆ wood │
# │ 30 ┆ random ┆ wood ┆ 1 ┆ metal ┆ metal │
# │ 30 ┆ best ┆ metal ┆ 1 ┆ metal ┆ metal │
# │ 30 ┆ farm ┆ water ┆ 1 ┆ metal ┆ metal │
# └──────┴────────┴────────┴───────┴────────────┴───────┘
另外,值得一提的是,下面给出的一些解法更简洁一些,而且没有那种双重OVER的情况
df.with_columns(
best=pl.col("source").filter(pl.col("usage") == "best").first().over("year"),
other_best=pl.col("source").max_by(pl.col("usage") == "best").over("year"),
)
# shape: (10, 5)
# ┌──────┬────────┬────────┬───────┬───────┐
# │ year ┆ usage ┆ source ┆ best2 ┆ best3 │
# │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# │ i64 ┆ str ┆ str ┆ str ┆ str │
# ╞══════╪════════╪════════╪═══════╪═══════╡
# │ 5 ┆ farm ┆ wood ┆ metal ┆ metal │
# │ 5 ┆ best ┆ metal ┆ metal ┆ metal │
# │ 5 ┆ ┆ water ┆ metal ┆ metal │
# │ 10 ┆ manual ┆ metal ┆ water ┆ water │
# │ 10 ┆ best ┆ water ┆ water ┆ water │
# │ 15 ┆ best ┆ wood ┆ wood ┆ wood │
# │ 15 ┆ city ┆ water ┆ wood ┆ wood │
# │ 30 ┆ random ┆ wood ┆ metal ┆ metal │
# │ 30 ┆ best ┆ metal ┆ metal ┆ metal │
# │ 30 ┆ farm ┆ water ┆ metal ┆ metal │
# └──────┴────────┴────────┴───────┴───────┘
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。