更改熊猫中日期时间列的时区并添加为分层索引

Posted

技术标签:

【中文标题】更改熊猫中日期时间列的时区并添加为分层索引【英文标题】:Change timezone of date-time column in pandas and add as hierarchical index 【发布时间】:2013-06-14 02:19:24 【问题描述】:

我有带有 UTC 时间戳的数据。我想将此时间戳的时区转换为“美国/太平洋”,并将其作为分层索引添加到 pandas DataFrame。我已经能够将时间戳转换为索引,但是当我尝试将它作为列或索引添加回 DataFrame 时,它​​会丢失时区格式。

>>> import pandas as pd
>>> dat = pd.DataFrame('label':['a', 'a', 'a', 'b', 'b', 'b'], 'datetime':['2011-07-19 07:00:00', '2011-07-19 08:00:00', '2011-07-19 09:00:00', '2011-07-19 07:00:00', '2011-07-19 08:00:00', '2011-07-19 09:00:00'], 'value':range(6))
>>> dat.dtypes
#datetime    object
#label       object
#value        int64
#dtype: object

现在,如果我尝试直接转换系列,我会遇到错误。

>>> times = pd.to_datetime(dat['datetime'])
>>> times.tz_localize('UTC')
#Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#  File "/Users/erikshilts/workspace/schedule-detection/python/pysched/env/lib/python2.7/site-packages/pandas/core/series.py", line 3170, in tz_localize
#    raise Exception('Cannot tz-localize non-time series')
#Exception: Cannot tz-localize non-time series

如果我将其转换为索引,那么我可以将其作为时间序列进行操作。请注意,索引现在具有太平洋时区。

>>> times_index = pd.Index(times)
>>> times_index_pacific = times_index.tz_localize('UTC').tz_convert('US/Pacific')
>>> times_index_pacific
#<class 'pandas.tseries.index.DatetimeIndex'>
#[2011-07-19 00:00:00, ..., 2011-07-19 02:00:00]
#Length: 6, Freq: None, Timezone: US/Pacific

但是,现在我在将索引添加回数据帧时遇到了问题,因为它失去了时区格式:

>>> dat_index = dat.set_index([dat['label'], times_index_pacific])
>>> dat_index
#                                      datetime label  value
#label                                                      
#a     2011-07-19 07:00:00  2011-07-19 07:00:00     a      0
#      2011-07-19 08:00:00  2011-07-19 08:00:00     a      1
#      2011-07-19 09:00:00  2011-07-19 09:00:00     a      2
#b     2011-07-19 07:00:00  2011-07-19 07:00:00     b      3
#      2011-07-19 08:00:00  2011-07-19 08:00:00     b      4
#      2011-07-19 09:00:00  2011-07-19 09:00:00     b      5

您会注意到索引返回到 UTC 时区,而不是转换后的太平洋时区。

如何更改时区并将其作为索引添加到 DataFrame?

【问题讨论】:

我认为这是一个错误...... 是的,这是一种奇怪的行为(时区是邪恶的)。可能值得创建an issue! 【参考方案1】:

现在这个问题已经解决了。例如,您现在可以调用:

dataframe.tz_localize('UTC', level=0)

但是,对于给定的示例,您必须调用它两次。 (即,每个级别一次。)

【讨论】:

【参考方案2】:

另一个适用于 pandas 0.13.1 的解决方法,解决了 FrozenList 无法分配的问题:

index.levels = pandas.core.base.FrozenList([
    index.levels[0].tz_localize('UTC').tz_convert(tz),
    index.levels[1].tz_localize('UTC').tz_convert(tz)
])

MultiIndex 在这个问题上苦苦挣扎,在许多其他情况下也失去了 tz。

【讨论】:

【参考方案3】:

解决方法似乎不起作用,因为分层索引的索引级别似乎是不可变的(FrozenList 是不可变的)。

从单数索引开始并追加也行不通。

创建转换为 Timestamp 并转换 to_datetime() 返回的 Series 的每个成员的 lambda 函数也不起作用。

有没有办法创建时区感知系列,然后将它们插入数据框/使它们成为索引?

joined_event_df = joined_event_df.set_index(['pandasTime'])
joined_event_df.index = joined_event_df.index.get_level_values(1).tz_localize('UTC').tz_convert('US/Central')
# we have tz-awareness above this line
joined_event_df = joined_event_df.set_index('sequence', append = True)
# we lose tz-awareness in the index as soon as we add another index
joined_event_df = joined_event_df.swaplevel(0,1)

【讨论】:

【参考方案4】:

如果设置为索引,它会自动转换为索引:

In [11]: dat.index = pd.to_datetime(dat.pop('datetime'), utc=True)

In [12]: dat
Out[12]:
                    label  value
datetime
2011-07-19 07:00:00     a      0
2011-07-19 08:00:00     a      1
2011-07-19 09:00:00     a      2
2011-07-19 07:00:00     b      3
2011-07-19 08:00:00     b      4
2011-07-19 09:00:00     b      5

然后做tz_localize:

In [12]: dat.index = dat.index.tz_localize('UTC').tz_convert('US/Pacific')

In [13]: dat
Out[13]:
                          label  value
datetime
2011-07-19 00:00:00-07:00     a      0
2011-07-19 01:00:00-07:00     a      1
2011-07-19 02:00:00-07:00     a      2
2011-07-19 00:00:00-07:00     b      3
2011-07-19 01:00:00-07:00     b      4
2011-07-19 02:00:00-07:00     b      5

然后你可以将标签列追加到索引中:

嗯,这绝对是一个错误!

In [14]: dat.set_index('label', append=True).swaplevel(0, 1)
Out[14]:
                           value
label datetime
a     2011-07-19 07:00:00      0
      2011-07-19 08:00:00      1
      2011-07-19 09:00:00      2
b     2011-07-19 07:00:00      3
      2011-07-19 08:00:00      4
      2011-07-19 09:00:00      5

一个 hacky 解决方法是直接转换 (datetime) 级别(当它已经是 MultiIndex 时):

In [15]: dat.index.levels[1] = dat.index.get_level_values(1).tz_localize('UTC').tz_convert('US/Pacific')

In [16]: dat1
Out[16]:
                                 value
label datetime
a     2011-07-19 00:00:00-07:00      0
      2011-07-19 01:00:00-07:00      1
      2011-07-19 02:00:00-07:00      2
b     2011-07-19 00:00:00-07:00      3
      2011-07-19 01:00:00-07:00      4
      2011-07-19 02:00:00-07:00      5

【讨论】:

我遇到了两个问题:1)我不能在 MultiIndex 上调用 tz_localize 或 tz_convert; 2)当我想要太平洋值(即[0, 1, 2, 0, 1, 2])时,从单个索引访问小时字段仍然会给我数组[7,8,9,7,8,9] 很抱歉,这绝对是一个错误(感谢您找到它)!我添加了一个解决方法(即在它是 MultiIndex 后转换日期时间级别)...

以上是关于更改熊猫中日期时间列的时区并添加为分层索引的主要内容,如果未能解决你的问题,请参考以下文章

如何在熊猫中使用 read_csv 将时区感知日期时间作为时区天真的本地 DatetimeIndex 读取?

将UTC时间戳转换为熊猫中的本地时区问题

熊猫以时间为索引获取特定日期的行数

熊猫:自某个日期以来经过的天数

如何使用熊猫更改数据框中的日期时间格式? [复制]

在熊猫中添加日期