Polars的replace_time_zone和convert_time_zone对于相同的时区转换显示不同的字符串表示形式

2023-12-28

测试数据:

import polars as pl
import pandas as pd
from datetime import date, time, datetime

# polars df for Jan to 12th, March 2022 before DST transition.
tdf = pl.DataFrame(
    pl.date_range(
        low=date(2022, 1, 3),
        high=date(2022, 3, 12),
        interval="5m",
        time_unit="ns",
        time_zone="UTC",
    ).alias("UTC")
)

我的用例是将 UTC 数据(或不了解时区)转换为 EST/EDT 数据(或了解时区),然后在时间范围(9:30 到 16:00)上过滤数据。

tz_replaced_filtered = tdf.with_columns( 
    pl.col("UTC").dt.replace_time_zone(time_zone="US/Eastern").alias("NY")
).filter(
  pl.col("NY").cast(pl.Time).is_between(time(9), time(16), closed="both")
)

# output
shape: (5780, 2)
┌─────────────────────────┬──────────────────────────┐
│ UTC                     ┆ NY                       │
│ ---                     ┆ ---                      │
│ datetime[ns, UTC]       ┆ datetime[ns, US/Eastern] │
╞═════════════════════════╪══════════════════════════╡
│ 2022-01-03 04:00:00 UTC ┆ 2022-01-03 04:00:00 EST  │
│ 2022-01-03 04:05:00 UTC ┆ 2022-01-03 04:05:00 EST  │
│ 2022-01-03 04:10:00 UTC ┆ 2022-01-03 04:10:00 EST  │
│ 2022-01-03 04:15:00 UTC ┆ 2022-01-03 04:15:00 EST  │
│ …                       ┆ …                        │
│ 2022-03-11 10:45:00 UTC ┆ 2022-03-11 10:45:00 EST  │
│ 2022-03-11 10:50:00 UTC ┆ 2022-03-11 10:50:00 EST  │
│ 2022-03-11 10:55:00 UTC ┆ 2022-03-11 10:55:00 EST  │
│ 2022-03-11 11:00:00 UTC ┆ 2022-03-11 11:00:00 EST  │
└─────────────────────────┴──────────────────────────┘

显然这个结果中没有明显的 9 到 16 时间范围df.

我进一步探究:

tz_replaced_filtered.with_columns(
   pl.col("NY").cast(pl.Time).alias("NY_TIME"),
   pl.col("UTC").cast(pl.Time).alias("UTC_TIME")
)

# output
shape: (5780, 4)
┌─────────────────────────┬──────────────────────────┬──────────┬──────────┐
│ UTC                     ┆ NY                       ┆ NY_TIME  ┆ UTC_TIME │
│ ---                     ┆ ---                      ┆ ---      ┆ ---      │
│ datetime[ns, UTC]       ┆ datetime[ns, US/Eastern] ┆ time     ┆ time     │
╞═════════════════════════╪══════════════════════════╪══════════╪══════════╡
│ 2022-01-03 04:00:00 UTC ┆ 2022-01-03 04:00:00 EST  ┆ 09:00:00 ┆ 04:00:00 │
│ 2022-01-03 04:05:00 UTC ┆ 2022-01-03 04:05:00 EST  ┆ 09:05:00 ┆ 04:05:00 │
│ 2022-01-03 04:10:00 UTC ┆ 2022-01-03 04:10:00 EST  ┆ 09:10:00 ┆ 04:10:00 │
│ 2022-01-03 04:15:00 UTC ┆ 2022-01-03 04:15:00 EST  ┆ 09:15:00 ┆ 04:15:00 │
│ …                       ┆ …                        ┆ …        ┆ …        │
│ 2022-03-11 10:45:00 UTC ┆ 2022-03-11 10:45:00 EST  ┆ 15:45:00 ┆ 10:45:00 │
│ 2022-03-11 10:50:00 UTC ┆ 2022-03-11 10:50:00 EST  ┆ 15:50:00 ┆ 10:50:00 │
│ 2022-03-11 10:55:00 UTC ┆ 2022-03-11 10:55:00 EST  ┆ 15:55:00 ┆ 10:55:00 │
│ 2022-03-11 11:00:00 UTC ┆ 2022-03-11 11:00:00 EST  ┆ 16:00:00 ┆ 11:00:00 │
└─────────────────────────┴──────────────────────────┴──────────┴──────────┘

底层时间戳似乎确实表明我成功地在时间范围 9 到 16 上进行了过滤;这只是不同步的字符串表示形式。

 tdf.with_columns(
     pl.col("UTC").dt.replace_time_zone(time_zone="US/Eastern").alias("NY_REPLACED"),
     pl.col("UTC").dt.convert_time_zone(time_zone="US/Eastern").alias("NY_CONVERTED")
 )

# output
shape: (19585, 3)
┌─────────────────────────┬──────────────────────────┬──────────────────────────┐
│ UTC                     ┆ NY_REPLACED              ┆ NY_CONVERTED             │
│ ---                     ┆ ---                      ┆ ---                      │
│ datetime[ns, UTC]       ┆ datetime[ns, US/Eastern] ┆ datetime[ns, US/Eastern] │
╞═════════════════════════╪══════════════════════════╪══════════════════════════╡
│ 2022-01-03 00:00:00 UTC ┆ 2022-01-03 00:00:00 EST  ┆ 2022-01-02 19:00:00 EST  │
│ 2022-01-03 00:05:00 UTC ┆ 2022-01-03 00:05:00 EST  ┆ 2022-01-02 19:05:00 EST  │
│ 2022-01-03 00:10:00 UTC ┆ 2022-01-03 00:10:00 EST  ┆ 2022-01-02 19:10:00 EST  │
│ 2022-01-03 00:15:00 UTC ┆ 2022-01-03 00:15:00 EST  ┆ 2022-01-02 19:15:00 EST  │
│ …                       ┆ …                        ┆ …                        │
│ 2022-03-11 23:45:00 UTC ┆ 2022-03-11 23:45:00 EST  ┆ 2022-03-11 18:45:00 EST  │
│ 2022-03-11 23:50:00 UTC ┆ 2022-03-11 23:50:00 EST  ┆ 2022-03-11 18:50:00 EST  │
│ 2022-03-11 23:55:00 UTC ┆ 2022-03-11 23:55:00 EST  ┆ 2022-03-11 18:55:00 EST  │
│ 2022-03-12 00:00:00 UTC ┆ 2022-03-12 00:00:00 EST  ┆ 2022-03-11 19:00:00 EST  │
└─────────────────────────┴──────────────────────────┴──────────────────────────┘

最后一步的结论是replaced_time_zone and convert_time_zone不会为相同的时区转换操作生成相同的字符串表示形式。

进一步来说,replaced_time_zone确实替换了底层时间戳,但它不会“替换”字符串表示形式;什么replaced_time_zone表面上对于 UTC(或者只是不知道时区的时间戳)来说就像with_time_zone,即简单地将时区信息附加到现有时间戳上。

如果有人能澄清上述问题,我将不胜感激。

问题背景

这是针对 Polars 用户和开发人员的,以便我提出的所有与时区转换相关的问题polars可以方便地参考。

我首先提出this https://stackoverflow.com/questions/74165901/polars-add-substract-utc-offset-from-datetime-object看看我们如何根据转换后的时间戳(考虑 utc 偏移量)进行过滤,然后polars假如replace_time_zone a question https://stackoverflow.com/questions/75793219/polars-replace-time-zone-function-throws-error-of-no-such-local-time因为它也被提高了。

@FObersteiner 提到的一个相关问题是here https://stackoverflow.com/questions/75268283/how-to-truncate-a-datetime-to-just-the-day.

暂时我的理解是replace_time_zone应该提供相同的字符串表示形式convert_time_zone,因为本质上他们所做的事情是相同的。这将使过滤日期/时间范围polarsWYSIWYG(所见即所得)。

背景资料

Polars提供replace_time_zone and convert_time_zone来处理这样的场景。

convert_time_zone仅更改呈现给用户的字符串表示形式,而不更改底层时间戳。所以cast在我的用例中,它不适用于“转换后的”时间戳(过滤仍然在原始的底层时间戳上)。

关于replace_time_zone, DST 转换导致problem https://stackoverflow.com/questions/75793219/polars-replace-time-zone-function-throws-error-of-no-such-local-time当用户尝试replace_time_zone鉴于由于 DST 转换而导致特定时间戳不存在,这个问题似乎是普遍存在的:

# demo polars df
tpl = pl.DataFrame(
   pl.date_range(
        low=date(2022, 3, 13),
        high=date(2022, 3, 15),
        interval="5m",
        time_unit="ns",
        time_zone="UTC",
   ).alias("UTC")
)

tpl.select(
   pl.col("UTC").dt.replace_time_zone(time_zone="America/New_York").alias("NY")
)

# Panic Exception: no such local time

# demo pandas pf
tpn = pd.DataFrame(
    pd.date_range(start="2022-03-13", 
                  end="2022-03-15",
                  freq="5T", # 5 minute interval
                  tz=None, # default to UTC
                  inclusive="both"),
    columns=["UTC"]
)

tpn.UTC.dt.tz_localize("America/New_York")

# NonExistentTimeError: 2022-03-13 02:00:00

我认为你遇到的问题是pl.cast(pl.Time)不转换为当地时间。

您可能想使用过滤.dt.hour():

In [23]: tz_replaced_filtered.filter(pl.col('NY').dt.hour().is_between(9, 16))
Out[23]:
shape: (6528, 2)
┌─────────────────────────┬──────────────────────────┐
│ UTC                     ┆ NY                       │
│ ---                     ┆ ---                      │
│ datetime[ns, UTC]       ┆ datetime[ns, US/Eastern] │
╞═════════════════════════╪══════════════════════════╡
│ 2022-01-03 09:00:00 UTC ┆ 2022-01-03 09:00:00 EST  │
│ 2022-01-03 09:05:00 UTC ┆ 2022-01-03 09:05:00 EST  │
│ 2022-01-03 09:10:00 UTC ┆ 2022-01-03 09:10:00 EST  │
│ 2022-01-03 09:15:00 UTC ┆ 2022-01-03 09:15:00 EST  │
│ …                       ┆ …                        │
│ 2022-03-11 16:40:00 UTC ┆ 2022-03-11 16:40:00 EST  │
│ 2022-03-11 16:45:00 UTC ┆ 2022-03-11 16:45:00 EST  │
│ 2022-03-11 16:50:00 UTC ┆ 2022-03-11 16:50:00 EST  │
│ 2022-03-11 16:55:00 UTC ┆ 2022-03-11 16:55:00 EST  │
└─────────────────────────┴──────────────────────────┘

EDIT

从 Polars 0.16.15 开始,您可以使用.dt.time:

In [12]: tz_replaced_filtered = tdf.with_columns(
    ...:     pl.col("UTC").dt.replace_time_zone(time_zone="US/Eastern").alias("NY")
    ...: ).filter(
    ...:   pl.col("NY").dt.time().is_between(time(9), time(16), closed="both")
    ...: )

In [13]: tz_replaced_filtered
Out[13]:
shape: (5780, 2)
┌─────────────────────────┬──────────────────────────┐
│ UTC                     ┆ NY                       │
│ ---                     ┆ ---                      │
│ datetime[ns, UTC]       ┆ datetime[ns, US/Eastern] │
╞═════════════════════════╪══════════════════════════╡
│ 2022-01-03 09:00:00 UTC ┆ 2022-01-03 09:00:00 EST  │
│ 2022-01-03 09:05:00 UTC ┆ 2022-01-03 09:05:00 EST  │
│ 2022-01-03 09:10:00 UTC ┆ 2022-01-03 09:10:00 EST  │
│ 2022-01-03 09:15:00 UTC ┆ 2022-01-03 09:15:00 EST  │
│ …                       ┆ …                        │
│ 2022-03-11 15:45:00 UTC ┆ 2022-03-11 15:45:00 EST  │
│ 2022-03-11 15:50:00 UTC ┆ 2022-03-11 15:50:00 EST  │
│ 2022-03-11 15:55:00 UTC ┆ 2022-03-11 15:55:00 EST  │
│ 2022-03-11 16:00:00 UTC ┆ 2022-03-11 16:00:00 EST  │
└─────────────────────────┴──────────────────────────┘
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Polars的replace_time_zone和convert_time_zone对于相同的时区转换显示不同的字符串表示形式 的相关文章

随机推荐