【问题标题】:`combine_first` at DST changing day failsDST 更改日的“combine_first”失败
【发布时间】:2021-11-20 16:18:58
【问题描述】:

我正在尝试使用combine_first 加入两个熊猫系列,以便一个优先于另一个。但它在 DST 更改日失败。我整理了这个演示:

import pandas as pd
import numpy as np

fr1 = pd.date_range(pd.to_datetime('2020-10-25').tz_localize('Europe/Berlin'), pd.to_datetime('2020-10-26').tz_localize('Europe/Berlin'), freq='H')
fr2 = fr1 + pd.DateOffset(hours=12)
d1 = pd.Series(data=np.random.randint(0,10, len(fr1)), index = fr1)
d2 = pd.Series(data=np.random.randint(0,10, len(fr2)), index = fr2)
d2.combine_first(d1)

输出:

ValueError: cannot reindex from a duplicate axis

这是为什么?如何解决这个问题?我当然可以手动对系列进行切片并连接。

【问题讨论】:

  • 顺便说一句。你可以简化为pd.date_range('2020-10-25', '2020-10-26', tz='Europe/Berlin', freq='H')

标签: python-3.x pandas datetime timezone timestamp-with-timezone


【解决方案1】:

您可以看到DateTimeIndex 的第一项,+xxxx 的时区在 DST 更改时发生更改:

>>> date_range = pd.date_range(pd.to_datetime('2020-10-25').tz_localize('Europe/Berlin'), pd.to_datetime('2020-10-26').tz_localize('Europe/Berlin'), freq='H')
>>> date_range[:4]
DatetimeIndex(['2020-10-25 00:00:00+02:00', '2020-10-25 01:00:00+02:00',
               '2020-10-25 02:00:00+02:00', '2020-10-25 02:00:00+01:00'],

当您添加 12 小时时,日期也会更改其时区:

DatetimeIndex(['2020-10-25 12:00:00+01:00', '2020-10-25 13:00:00+01:00',
               '2020-10-25 14:00:00+01:00', '2020-10-25 14:00:00+01:00'],
              dtype='datetime64[ns, Europe/Berlin]', freq=None)

但时差不再是 12 小时,而是由于时区变化而变为 11 小时。这是因为在将DateOffset 添加到DatetimeIndex 时,您要求“明显”更改 12 小时,如“两个不同的时钟读数相隔 12 小时”。这意味着 2 个时间戳会映射到 12 小时后出现的同一个时间戳。

DateOffset 的一个特性是允许表达非常量的增量,即如果您添加 « 1 个月 »,您同样不会根据您所在的月份向日期添加相同的秒数。

如果您想在 12 个实际小时内换班,请使用Timedelta

>>> (date_range[:4] + pd.Timedelta(hours=12))
DatetimeIndex(['2020-10-25 11:00:00+01:00', '2020-10-25 12:00:00+01:00',
               '2020-10-25 13:00:00+01:00', '2020-10-25 14:00:00+01:00'],
              dtype='datetime64[ns, Europe/Berlin]', freq='H')
>>> (date_range + pd.Timedelta(hours=12)).is_unique
True

既然索引是唯一的,combine_first 也可以工作:

>>> d1 = pd.Series(data=np.random.randint(0, 10, 26), index=date_range)
>>> d2 = pd.Series(data=np.random.randint(0, 10, 26), index=date_range + pd.Timedelta(hours=12))
>>> d2.combine_first(d1)
2020-10-25 00:00:00+02:00    0.0
2020-10-25 01:00:00+02:00    1.0
2020-10-25 02:00:00+02:00    2.0
2020-10-25 02:00:00+01:00    1.0
2020-10-25 03:00:00+01:00    7.0
2020-10-25 04:00:00+01:00    4.0
2020-10-25 05:00:00+01:00    0.0
2020-10-25 06:00:00+01:00    1.0
2020-10-25 07:00:00+01:00    1.0
2020-10-25 08:00:00+01:00    1.0
2020-10-25 09:00:00+01:00    6.0
2020-10-25 10:00:00+01:00    7.0
2020-10-25 11:00:00+01:00    4.0
2020-10-25 12:00:00+01:00    8.0
2020-10-25 13:00:00+01:00    6.0
2020-10-25 14:00:00+01:00    0.0
2020-10-25 15:00:00+01:00    6.0
2020-10-25 16:00:00+01:00    0.0
2020-10-25 17:00:00+01:00    0.0
2020-10-25 18:00:00+01:00    9.0
2020-10-25 19:00:00+01:00    7.0
2020-10-25 20:00:00+01:00    9.0
2020-10-25 21:00:00+01:00    3.0
2020-10-25 22:00:00+01:00    4.0
2020-10-25 23:00:00+01:00    0.0
2020-10-26 00:00:00+01:00    6.0
2020-10-26 01:00:00+01:00    5.0
2020-10-26 02:00:00+01:00    9.0
2020-10-26 03:00:00+01:00    1.0
2020-10-26 04:00:00+01:00    4.0
2020-10-26 05:00:00+01:00    4.0
2020-10-26 06:00:00+01:00    3.0
2020-10-26 07:00:00+01:00    1.0
2020-10-26 08:00:00+01:00    8.0
2020-10-26 09:00:00+01:00    1.0
2020-10-26 10:00:00+01:00    6.0
2020-10-26 11:00:00+01:00    5.0
2020-10-26 12:00:00+01:00    6.0
Freq: H, dtype: float64

【讨论】:

  • pd.to_datetime('2020-10-25').tz_localize('Europe/Berlin') + pd.DateOffset(hours=3) 返回Timestamp('2020-10-25 02:00:00+0100', tz='Europe/Berlin')。这是否意味着pd.DateOffset 是实际的实际物理时间,而不是墙上的时间偏移?
  • @guyguyguy12345 这似乎只发生在DatetimeIndexes 上。 pd.DatetimeIndex([pd.to_datetime('2020-10-25').tz_localize('Europe/Berlin')]) + pd.DateOffset(hours=3) 返回2020-10-25 03:00:00+01:00
  • 来自文档:“但是,所有一小时或更小的 DateOffset 子类(小时、分钟、秒、毫、微、纳米)的行为类似于 Timedelta 并尊重绝对时间。” pandas.pydata.org/pandas-docs/stable/user_guide/…
  • 那么可能是一个错误?不确定。
  • 顺便说一句。 pd.DateOffset(hours=12) 的行为类似于 pd.Timedelta(hours=12) 如果您在 UTC 中进行添加:fr2 = (fr1.tz_convert('UTC') + pd.DateOffset(hours=12)).tz_convert('Europe/Berlin')
猜你喜欢
  • 2014-11-19
  • 2017-07-09
  • 2020-12-14
  • 2018-12-12
  • 2014-03-24
  • 2019-04-09
  • 1970-01-01
  • 1970-01-01
  • 2016-06-12
相关资源
最近更新 更多