为什么要第二个“over”?

编程语言 2026-07-09

取自这个问题的数据
在不分组的前提下基于分组添加新列

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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章