分组

按固定窗口分组

可以使用group_by_dynamic将数据行按照天/月/年等分组

下面代码生成一份7行的数据(可以在group_by_dynamic的文档中看到)

1import polars as pl
2from datetime import datetime
3df = pl.DataFrame(
4    {
5        "time": pl.datetime_range(
6            start=datetime(2021, 12, 16),
7            end=datetime(2021, 12, 16, 3),
8            interval="30m",
9            eager=True,
10        ),
11        "n": range(7),
12    }
13)
14print(df)
1shape: (7, 2)
2┌─────────────────────┬─────┐
3│ time                ┆ n   │
4│ ---                 ┆ --- │
5│ datetime[μs]        ┆ i64 │
6╞═════════════════════╪═════╡
7│ 2021-12-16 00:00:00 ┆ 0   │
8│ 2021-12-16 00:30:00 ┆ 1   │
9│ 2021-12-16 01:00:00 ┆ 2   │
10│ 2021-12-16 01:30:00 ┆ 3   │
11│ 2021-12-16 02:00:00 ┆ 4   │
12│ 2021-12-16 02:30:00 ┆ 5   │
13│ 2021-12-16 03:00:00 ┆ 6   │
14└─────────────────────┴─────┘

接下来我们按小时分组, 然后统计n这一列的和

1res = df.group_by_dynamic(pl.col("time"), every="1h").agg(pl.sum("n"))
2
3print(res)
1shape: (4, 2)
2┌─────────────────────┬─────┐
3│ time                ┆ n   │
4│ ---                 ┆ --- │
5│ datetime[μs]        ┆ i64 │
6╞═════════════════════╪═════╡
7│ 2021-12-16 00:00:00 ┆ 1   │
8│ 2021-12-16 01:00:00 ┆ 5   │
9│ 2021-12-16 02:00:00 ┆ 9   │
10│ 2021-12-16 03:00:00 ┆ 6   │
11└─────────────────────┴─────┘
INFO

日期这一列按照升序排列, 如果不按照此顺序排列, 输出结果是错误的

group_by_dynamic参数

  • every: 窗口间隔
    • 1y: 表示每年开一个新窗口
    • 2y: 表示每2年开一个新窗口
    • 1h: 表示每小时开始一个新窗口
  • period: 表示窗口的长度, 设置每个组的时间段
    • every="1d", period="2d", 表示每天开一窗口, 每个窗口持续2天, 窗口如下
    • 第一个窗口:2024-01-01 → 2024-01-03
    • 第二个窗口:2024-01-02 → 2024-01-04
    • 第三个窗口:2024-01-03 → 2024-01-05
  • offset: 每个窗口的起点相对于自然对齐的偏移, 可以理解为窗口起点的偏移量
    • every="1mo", period="1mo": 表示每个月开一个窗口, 每个窗口的持续时间是1个月
      • [2024-01-01 → 2024-02-01)
      • [2024-02-01 → 2024-03-01)
    • every="1mo", period="1mo", offset="15d": 窗口起点向后偏移15天, 从每个月的16号开始
      • [2024-01-16 → 2024-02-16)
      • [2024-02-16 → 2024-03-16)

配合表达式使用

下面示例计算: 一个月的天数

1from datetime import date
2df = (
3    pl.date_range(
4        start=date(2021, 1, 1),
5        end=date(2021, 12, 31),
6        interval="1d",
7        eager=True,
8    )
9    .alias("time")
10    .to_frame()
11)
12out = (
13	df.group_by_dynamic("time", every="1mo", period="1mo", closed="left")
14    .agg(
15            ((pl.col("time") - pl.col("time").first()).last().dt.total_days() + 1).alias("days_in_month")
16    )
17)
18print(out)

等效的更好阅读的写法(上面写法是官方文档的写法):

1out = (
2	df.group_by_dynamic("time", every="1mo", period="1mo", closed="left")
3    .agg(
4        # 分组后令每组的最大值-每组的最小值, 最后+1
5        ((pl.col("time").max()-pl.col("time").min()).dt.total_days()+1)
6	    .alias("days_in_month")
7    )
8)
1shape: (12, 2)
2┌────────────┬───────────────┐
3│ time       ┆ days_in_month │
4│ ---        ┆ ---           │
5│ date       ┆ i64           │
6╞════════════╪═══════════════╡
7│ 2021-01-01 ┆ 31            │
8│ 2021-02-01 ┆ 28            │
9│ 2021-03-01 ┆ 31            │
10│ 2021-04-01 ┆ 30            │
11│ 2021-05-01 ┆ 31            │
12│ …          ┆ …             │
13│ 2021-08-01 ┆ 31            │
14│ 2021-09-01 ┆ 30            │
15│ 2021-10-01 ┆ 31            │
16│ 2021-11-01 ┆ 30            │
17│ 2021-12-01 ┆ 31            │
18└────────────┴───────────────┘

group_by参数

我们还可以使用group_by参数再次分组

1df = pl.DataFrame(
2    {
3        "time": pl.datetime_range(
4            start=datetime(2021, 12, 16),
5            end=datetime(2021, 12, 16, 3),
6            interval="30m",
7            eager=True,
8        ),
9        "groups": ["a", "a", "a", "b", "b", "a", "a"],
10    }
11)
12print(df)
1shape: (7, 2)
2┌─────────────────────┬────────┐
3│ time                ┆ groups │
4│ ---                 ┆ ---    │
5│ datetime[μs]        ┆ str    │
6╞═════════════════════╪════════╡
7│ 2021-12-16 00:00:00 ┆ a      │
8│ 2021-12-16 00:30:00 ┆ a      │
9│ 2021-12-16 01:00:00 ┆ a      │
10│ 2021-12-16 01:30:00 ┆ b      │
11│ 2021-12-16 02:00:00 ┆ b      │
12│ 2021-12-16 02:30:00 ┆ a      │
13│ 2021-12-16 03:00:00 ┆ a      │
14└─────────────────────┴────────┘

认真比对结果和数据集就可以看明白

1out = df.group_by_dynamic(
2    "time",
3    every="1h",
4	period="1h",
5    group_by="groups",
6	closed="both"   # 包含左右边界
7).agg(pl.len())
8print(out)
1shape: (6, 3)
2┌────────┬─────────────────────┬─────┐
3│ groups ┆ time                ┆ len │
4│ ---    ┆ ---                 ┆ --- │
5│ str    ┆ datetime[μs]        ┆ u32 │
6╞════════╪═════════════════════╪═════╡
7│ a      ┆ 2021-12-16 00:00:00 ┆ 3   │
8│ a      ┆ 2021-12-16 01:00:00 ┆ 1   │
9│ a      ┆ 2021-12-16 02:00:00 ┆ 2   │
10│ a      ┆ 2021-12-16 03:00:00 ┆ 1   │
11│ b      ┆ 2021-12-16 01:00:00 ┆ 2   │
12│ b      ┆ 2021-12-16 02:00:00 ┆ 1   │
13└────────┴─────────────────────┴─────┘