polars 滚动选项不允许?

5 投票
1 回答
92 浏览
提问于 2025-04-13 02:29

我有一个数据框,内容大概是这样的:

df = pl.LazyFrame({"day": [1,2,4,5,2,3,5,6], 'type': ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'], "value": [1, 0, 3, 4, 2, 2, 0, 1]})


day type    value
i64 str i64
1   "a" 1
2   "a" 0
4   "a" 3
5   "a" 4
2   "b" 2
3   "b" 2
5   "b" 0
6   "b" 1

我想创建一个滚动求和的变量,也就是在每种不同的“类型”中,计算一个两天的窗口内的值的总和。理想情况下,最终的数据集应该是这样的:

天数 类型 数值 滚动总和
1 a 1 1
2 a 0 1
4 a 3 3
5 a 4 7
2 b 2 2
3 b 2 4
5 b 0 0
6 b 1 1

我尝试使用以下代码:

df = df.with_columns(pl.col("value")
                     .rolling(index_column="day", by="type", period="2i")
                     .sum().alias("rolling_sum"))

但是我遇到了一个错误:“TypeError: rolling() got an unexpected keyword argument 'by'”。

你能帮我解决这个问题吗?

1 个回答

7

这是因为在你的代码中,你试图使用 Expr.rolling(),但这个方法没有 by 参数(奇怪的是,它在文档中提到了 check_sorted 参数——难道这个功能还没实现吗?),而是应该使用 DataFrame.rolling()

如果你把代码改成用后者,那么就能正常工作了:

(
    df.rolling(
        index_column="day", by="type", period="2i"
    )
    .agg(
        pl.col('value').sum().alias("rolling_sum")
    )
)

┌──────┬─────┬─────────────┐
│ type ┆ day ┆ rolling_sum │
│ ---  ┆ --- ┆ ---         │
│ str  ┆ i64 ┆ i64         │
╞══════╪═════╪═════════════╡
│ a    ┆ 1   ┆ 1           │
│ a    ┆ 2   ┆ 1           │
│ a    ┆ 4   ┆ 3           │
│ a    ┆ 5   ┆ 7           │
│ b    ┆ 2   ┆ 2           │
│ b    ┆ 3   ┆ 4           │
│ b    ┆ 5   ┆ 0           │
│ b    ┆ 6   ┆ 1           │
└──────┴─────┴─────────────┘

如果你想在结果中包含 value 列,可以使用 Expr.rolling_sum(),并结合 Expr.over()(假设你的 DataFrame 已经按 day 排序):

df.with_columns(
    pl.col("value")
    .rolling_sum(window_size=2,min_periods=0)
    .over("type")
    .alias('rolling_sum')
)

┌─────┬──────┬───────┬─────────────┐
│ day ┆ type ┆ value ┆ rolling_sum │
│ --- ┆ ---  ┆ ---   ┆ ---         │
│ i64 ┆ str  ┆ i64   ┆ i64         │
╞═════╪══════╪═══════╪═════════════╡
│ 1   ┆ a    ┆ 1     ┆ 1           │
│ 2   ┆ a    ┆ 0     ┆ 1           │
│ 4   ┆ a    ┆ 3     ┆ 3           │
│ 5   ┆ a    ┆ 4     ┆ 7           │
│ 2   ┆ b    ┆ 2     ┆ 2           │
│ 3   ┆ b    ┆ 2     ┆ 4           │
│ 5   ┆ b    ┆ 0     ┆ 2           │
│ 6   ┆ b    ┆ 1     ┆ 1           │
└─────┴──────┴───────┴─────────────┘

理想情况下,我可能会期待 Expr.rollingExpr.over 一起工作:

# something like this
df.with_columns(
    pl.col("value")
    .rolling(index_column="day", period="2i")
    .sum()
    .over("type")
    .alias('rolling_sum')
)

# or this
df.set_sorted(['type','day']).with_columns(
    pl.col("value")
    .sum()
    .over('type')
    .rolling(index_column="day", period="2i")
    .alias('rolling_sum')
)

但不幸的是,它并没有这样做:

InvalidOperationError: rolling expression not allowed in aggregation

更新

如果你打算根据天/周等来设置窗口,使用 rolling_sum() 可能不是你想要的。在这种情况下,你仍然可以使用 DataFrame.rolling(),并结合 Expr.last(),放在 GroupBy.agg() 中,以获取窗口中的最后一个值:

(
    df.rolling(
        index_column="day", by="type", period="2i"
    )
    .agg(
        pl.col('value').last(),
        pl.col('value').sum().alias("rolling_sum")
    )
)

┌──────┬─────┬───────┬─────────────┐
│ type ┆ day ┆ value ┆ rolling_sum │
│ ---  ┆ --- ┆ ---   ┆ ---         │
│ str  ┆ i64 ┆ i64   ┆ i64         │
╞══════╪═════╪═══════╪═════════════╡
│ a    ┆ 1   ┆ 1     ┆ 1           │
│ a    ┆ 2   ┆ 0     ┆ 1           │
│ a    ┆ 4   ┆ 3     ┆ 3           │
│ a    ┆ 5   ┆ 4     ┆ 7           │
│ b    ┆ 2   ┆ 2     ┆ 2           │
│ b    ┆ 3   ┆ 2     ┆ 4           │
│ b    ┆ 5   ┆ 0     ┆ 0           │
│ b    ┆ 6   ┆ 1     ┆ 1           │
└──────┴─────┴───────┴─────────────┘

撰写回答