提问人:arman 提问时间:11/16/2023 最后编辑:Arpita Shrivastavaarman 更新时间:11/20/2023 访问量:113
Polars 如何装箱日期时间类型列
Polars how to bin a datetime type column
问:
我正在尝试装箱日期列。我能够使用 Pandas 库相当轻松地做到这一点。但是我在使用 Polars 时遇到以下错误:
Traceback (most recent call last):
File "\workspace\polars.py", line 4, in <module>
pl.col("ts").cut(
File "\venvs\polars\Lib\site-packages\polars\utils\deprecation.py", line 192, in wrapper
return function(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "\venvs\polars\Lib\site-packages\polars\expr\expr.py", line 3716, in cut
self._pyexpr.cut(breaks, labels, left_closed, include_breaks)
TypeError: argument 'breaks': must be real number, not datetime.datetime
我的代码是:
>>> df
┌────────────┐
│ ts │
│ --- │
│ date │
╞════════════╡
│ 2021-10-01 │
│ 2021-11-01 │
│ 2022-03-01 │
└────────────┘
>>> start_year = df.select("ts").min().item()
>>> end_year = df.select("ts").max().item()
>>> df = df.with_columns(
pl.col("ts").cut(
pl.datetime_range(start_year, end_year, interval="3mo", eager=True),
).alias("period_bins")
)
答:
1赞
ignoring_gravity
11/16/2023
#1
这看起来像一个错误 - 你想把它报告给 https://github.com/pola-rs/polars/issues 吗?
目前,作为一种解决方法,您可以强制转换为 int:
breaks = pl.date_range(start_year, end_year, interval="3mo", eager=True)
df.with_columns(
pl.col("ts").cast(pl.Int64).cut(
breaks.cast(pl.Int64),
).alias("period_bins")
)
评论
0赞
Dean MacGregor
11/17/2023
我不认为这是一个错误,只是不支持按时态列装箱,这当然是一个很好的功能请求。
0赞
arman
11/19/2023
谢谢!我已经接受了这个答案,因为我扩展了这个答案来获得我的解决方案。
2赞
Hericks
11/17/2023
#2
如果中断是等距的,则另一种方法是使用 pl。Expr.dt.round
,如下所示。
df.with_columns(
pl.col("ts").dt.round(every="3mo").alias("rounded_ts")
)
输出。
┌────────────┬────────────┐
│ ts ┆ rounded_ts │
│ --- ┆ --- │
│ date ┆ date │
╞════════════╪════════════╡
│ 2021-10-01 ┆ 2021-10-01 │
│ 2021-11-01 ┆ 2021-10-01 │
│ 2022-03-01 ┆ 2022-04-01 │
└────────────┴────────────┘
评论
1赞
arman
11/19/2023
这是一个非常好的解决方案。但是,我不能使用它,因为我的存储桶边界的月份应该与值无关。ts
0赞
Hericks
11/19/2023
@arman 感谢 - 也感谢您提供您最终使用的完整解决方案。我会对未来的读者有用。点赞!
1赞
FObersteiner
11/17/2023
#3
如果您还想将值放入箱中(例如平均值或总和),polars 提供group_by_dynamic
。前任:
from datetime import datetime
import polars as pl
df = pl.DataFrame(
{
"datetime": pl.datetime_range(datetime(2022, 1, 1), datetime(2022, 12, 31), "1d", eager=True),
"values": pl.Series([1 for _ in range(365)]),
}
)
period = "3mo"
df_out = df.group_by_dynamic("datetime", every=period).agg(pl.col("values").sum())
# summing up the 1's in 'values' over 3mo effectively gives us the days in a quarter;
# Q1: 90d, Q2: 91d, Q3: 92d, Q4: 92d
print(df_out.head())
┌─────────────────────┬────────┐
│ datetime ┆ values │
│ --- ┆ --- │
│ datetime[μs] ┆ i64 │
╞═════════════════════╪════════╡
│ 2022-01-01 00:00:00 ┆ 90 │
│ 2022-04-01 00:00:00 ┆ 91 │
│ 2022-07-01 00:00:00 ┆ 92 │
│ 2022-10-01 00:00:00 ┆ 92 │
└─────────────────────┴────────┘
2赞
arman
11/19/2023
#4
我已经扩展了ignoring_gravity的答案以获得所需的结果。
>>> df = df.with_columns(
pl.col("ts").dt.epoch(time_unit="us")
.cut(breaks.dt.epoch(time_unit="us"))
.str.replace_all("\(|]|\s+", "")
.str.split(',')
.cast(pl.List(pl.Int64))
.cast(pl.List(pl.Datetime))
.cast(pl.List(pl.Date))
.alias("bins")
)
┌────────────┬──────────────────────────┐
│ ts ┆ bins │
│ --- ┆ --- │
│ date ┆ list[date] │
╞════════════╪══════════════════════════╡
│ 2021-10-01 ┆ [2021-07-01, 2021-10-01] │
│ 2021-11-01 ┆ [2021-10-01, 2022-01-01] │
│ 2022-03-01 ┆ [2022-01-01, 2022-04-01] │
└────────────┴──────────────────────────┘
评论