polars equivalent to groupby.last



假设我有一个极坐标数据帧:

import polars as pl 
df = pl.DataFrame({'index': [1,2,3,2,1],
'object': [1, 1, 1, 2, 2], 
'period': [1, 2, 4, 4, 23],
'value': [24, 67, 89, 5, 23],
})

我如何获得索引 -> 到最后一个值的字典df.col('value').last().over(['index']).alias("last")是最后一个值,但这需要大量额外的计算和更多的工作才能获得键值对。

over函数将保留所有行,这可能不是您想要的。 获取index最后value的一种简单方法是使用unique.

(
df
.select(['index', 'value'])
.unique(subset='index', keep="last")
)
shape: (3, 2)
┌───────┬───────┐
│ index ┆ value │
│ ---   ┆ ---   │
│ i64   ┆ i64   │
╞═══════╪═══════╡
│ 1     ┆ 23    │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 5     │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 89    │
└───────┴───────┘

此时,可以使用to_dicts方法将数据帧转换为字典列表。

last_values = (
df
.select(["index", "value"])
.unique(subset="index", keep="last")
.to_dicts()
)
last_values
[{'index': 1, 'value': 23}, {'index': 2, 'value': 5}, {'index': 3, 'value': 89}]

如果希望稍后将其导入数据帧,则需要在此时停止。 例如:

pl.DataFrame(last_values)
>>> pl.DataFrame(last_values)
shape: (3, 2)
┌───────┬───────┐
│ index ┆ value │
│ ---   ┆ ---   │
│ i64   ┆ i64   │
╞═══════╪═══════╡
│ 1     ┆ 23    │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 5     │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 89    │
└───────┴───────┘

但是,如果要将其折叠为indexvalue对的单个字典,则可以使用字典理解。


{
next_dict["index"]: next_dict["value"]
for next_dict in last_values
}
{1: 23, 2: 5, 3: 89}

编辑:根据日期更新

假设我们有这些数据:

import polars as pl
import datetime
df = pl.DataFrame({
"index": [1, 2, 3],
"value": [10, 20, 30],
}).join(
pl.DataFrame({
'date': pl.date_range(datetime.date(2021, 1, 1), datetime.date(2023, 1, 1), "1y")
}),
how="cross"
)
df
shape: (9, 3)
┌───────┬───────┬────────────┐
│ index ┆ value ┆ date       │
│ ---   ┆ ---   ┆ ---        │
│ i64   ┆ i64   ┆ date       │
╞═══════╪═══════╪════════════╡
│ 1     ┆ 10    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1     ┆ 10    ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1     ┆ 10    ┆ 2023-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 20    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 20    ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 20    ┆ 2023-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 30    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 30    ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 30    ┆ 2023-01-01 │
└───────┴───────┴────────────┘

我们有这些想要更新的值。

update_df = pl.DataFrame({
"index": [2, 3],
"value": [200, 300],
})
update_df
shape: (2, 2)
┌───────┬───────┐
│ index ┆ value │
│ ---   ┆ ---   │
│ i64   ┆ i64   │
╞═══════╪═══════╡
│ 2     ┆ 200   │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 300   │
└───────┴───────┘

注意:我故意省略了index"1"(以显示将会发生什么)。

如果我们想 更新与每个index关联的value,但仅超过某个日期,我们可以使用join_asof.

由于这是一种高级方法,我们将逐步采用。

我们将current_date作为文本添加到update_df中。 (所有行的值相同。

我们还需要确保两个数据帧都按"as_of"列排序(date,而不是index)。 (update_df已经排序,因为它是每行上的相同日期。

我还将对join_asof进行排序,以便我们可以更清楚地看到正在发生的事情。 (您不需要执行此步骤。

current_date = datetime.date(2022, 1, 1)
(
df
.sort(['date'])
.rename({'value': 'prev_value'})
.join_asof(
update_df.with_column(pl.lit(current_date).alias('date')),
on='date',
by=['index'],
strategy='backward'
)
.sort(['index', 'date'])
)
shape: (9, 4)
┌───────┬────────────┬────────────┬───────┐
│ index ┆ prev_value ┆ date       ┆ value │
│ ---   ┆ ---        ┆ ---        ┆ ---   │
│ i64   ┆ i64        ┆ date       ┆ i64   │
╞═══════╪════════════╪════════════╪═══════╡
│ 1     ┆ 10         ┆ 2021-01-01 ┆ null  │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 1     ┆ 10         ┆ 2022-01-01 ┆ null  │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 1     ┆ 10         ┆ 2023-01-01 ┆ null  │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 20         ┆ 2021-01-01 ┆ null  │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 20         ┆ 2022-01-01 ┆ 200   │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 20         ┆ 2023-01-01 ┆ 200   │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 30         ┆ 2021-01-01 ┆ null  │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 30         ┆ 2022-01-01 ┆ 300   │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 30         ┆ 2023-01-01 ┆ 300   │
└───────┴────────────┴────────────┴───────┘

请注意,只有日期为>= 2022-01-01 的行才具有value的非空值。 (最后我将在 2022-01-01 展示如何做一个>。

接下来,我们将使用fill_nullvalueprev_value列填充 null 值。

current_date = datetime.date(2022, 1, 1)
(
df
.sort(['date'])
.rename({'value': 'prev_value'})
.join_asof(
update_df.with_column(pl.lit(current_date).alias('date')),
on='date',
by=['index'],
strategy='backward'
)
.sort(['index', 'date'])
.with_column(pl.col('value').fill_null(pl.col('prev_value')))
)
shape: (9, 4)
┌───────┬────────────┬────────────┬───────┐
│ index ┆ prev_value ┆ date       ┆ value │
│ ---   ┆ ---        ┆ ---        ┆ ---   │
│ i64   ┆ i64        ┆ date       ┆ i64   │
╞═══════╪════════════╪════════════╪═══════╡
│ 1     ┆ 10         ┆ 2021-01-01 ┆ 10    │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 1     ┆ 10         ┆ 2022-01-01 ┆ 10    │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 1     ┆ 10         ┆ 2023-01-01 ┆ 10    │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 20         ┆ 2021-01-01 ┆ 20    │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 20         ┆ 2022-01-01 ┆ 200   │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 2     ┆ 20         ┆ 2023-01-01 ┆ 200   │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 30         ┆ 2021-01-01 ┆ 30    │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 30         ┆ 2022-01-01 ┆ 300   │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┤
│ 3     ┆ 30         ┆ 2023-01-01 ┆ 300   │
└───────┴────────────┴────────────┴───────┘

现在,为了清理,我们可以删除prev_value列,然后重新排列列。

current_date = datetime.date(2022, 1, 1)
(
df
.sort(['date'])
.rename({'value': 'prev_value'})
.join_asof(
update_df.with_column(pl.lit(current_date).alias('date')),
on='date',
by=['index'],
strategy='backward'
)
.sort(['index', 'date'])
.with_column(pl.col('value').fill_null(pl.col('prev_value')))
.drop(['prev_value'])
.select([
pl.exclude('date'),
pl.col('date')
])
)
shape: (9, 3)
┌───────┬───────┬────────────┐
│ index ┆ value ┆ date       │
│ ---   ┆ ---   ┆ ---        │
│ i64   ┆ i64   ┆ date       │
╞═══════╪═══════╪════════════╡
│ 1     ┆ 10    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1     ┆ 10    ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1     ┆ 10    ┆ 2023-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 20    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 200   ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 200   ┆ 2023-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 30    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 300   ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 300   ┆ 2023-01-01 │
└───────┴───────┴────────────┘

如果您只需要更新那些严格大于current_date的行,您只需将一天添加到您的current_date中即可。 Polars 通过offset_by表达式使这变得容易。

(
df
.sort(['date'])
.rename({'value': 'prev_value'})
.join_asof(
update_df.with_column(pl.lit(current_date).dt.offset_by('1d').alias('date')),
on='date',
by=['index'],
strategy='backward'
)
.sort(['index', 'date'])
.with_column(pl.col('value').fill_null(pl.col('prev_value')))
.drop(['prev_value'])
.select([
pl.exclude('date'),
pl.col('date')
])
)
shape: (9, 3)
┌───────┬───────┬────────────┐
│ index ┆ value ┆ date       │
│ ---   ┆ ---   ┆ ---        │
│ i64   ┆ i64   ┆ date       │
╞═══════╪═══════╪════════════╡
│ 1     ┆ 10    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1     ┆ 10    ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1     ┆ 10    ┆ 2023-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 20    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 20    ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2     ┆ 200   ┆ 2023-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 30    ┆ 2021-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 30    ┆ 2022-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3     ┆ 300   ┆ 2023-01-01 │
└───────┴───────┴────────────┘

最新更新