在 Pandas MultiIndex 中移动 DateTime 索引
Posted
技术标签:
【中文标题】在 Pandas MultiIndex 中移动 DateTime 索引【英文标题】:Shift DateTime index within a Pandas MultiIndex 【发布时间】:2020-08-01 11:25:43 【问题描述】:我有一个 csv 文件,当我加载它时如下所示:
# generate example data
users = ['A', 'B', 'C', 'D']
#dates = pd.date_range("2020-02-01 00:00:00", "2020-04-04 20:00:00", freq="H")
dates = pd.date_range("2020-02-01 00:00:00", "2020-02-04 20:00:00", freq="H")
idx = pd.MultiIndex.from_product([users, dates])
idx.names = ["user", "datehour"]
y = pd.Series(np.random.choice(a=[0, 1], size=len(idx)), index=idx).rename('y')
# write to csv and reload (turns out this matters)
y.to_csv('reprod_example.csv')
y = pd.read_csv('reprod_example.csv', parse_dates=['datehour'])
y = y.set_index(['user', 'datehour']).y
>>> y.head()
user datehour
A 2020-02-01 00:00:00 0
2020-02-01 01:00:00 0
2020-02-01 02:00:00 1
2020-02-01 03:00:00 0
2020-02-01 04:00:00 0
Name: y, dtype: int64
我有以下函数来创建索引级别的滞后特征:
def shift_index(a, dt_idx_name, lag_freq, lag):
# get datetime index of relevant level
ac = a.copy()
dti = ac.index.get_level_values(dt_idx_name)
# shift it
dti_shifted = dti.shift(lag, freq=lag_freq)
# put it back where you found it
ac.index.set_levels(dti_shifted, level=dt_idx_name, inplace=True)
return ac
但是当我运行时:
y_lag = shift_index(y, 'datehour', 'H', 1)
,我收到以下错误:
ValueError: Level values must be unique...
(我实际上可以通过添加verify_integrity=False
来抑制这个错误
在函数中的.index.set_levels...
中,但这(可以预见)会导致问题)
这是奇怪的部分。如果您运行上面的示例但没有从 csv 保存/重新加载,它可以工作。我认为,原因似乎是y.index.get_level_value('datehour')
在创建后立即显示freq='H'
属性,但在从csv 重新加载后freq=None
。
这是有道理的,csv 显然不会保存该元数据。但是我发现为 MultiIndexed 系列设置 freq 属性非常困难。例如,这什么也没做。
df.index.freq = pd.tseries.frequencies.to_offset("H")
。 this answer 也不适用于我的 MultiIndex。
因此,如果我能够设置 MultiIndex 的 DateTime 组件的 freq
属性,我想我可以解决这个问题。但我的最终目标是创建我的 y
数据的一个版本,其中包含移位的 DateTime MultiIndex 组件,例如上面的 shift_index
函数。由于我通过 csv 接收数据,因此不能选择“只是不保存到 csv 并重新加载”。
【问题讨论】:
好问题。我有一个类似的问题,freq
在操作后设置为None
- 在我的情况下,当我使用df.index = pd.MultiIndex.from_arrays([qhour.index, qhour.index.year])
重新索引时。我有兴趣看看是否有更...直截了当的...做事方式(无意冒犯@mcskinner ;))
【参考方案1】:
经过一番折腾,我能够在分组数据上使用asfreq('H')
设置每小时频率,这样每个组的datehour
索引都有唯一值。
y = pd.read_csv('reprod_example.csv', parse_dates=['datehour'])
y = y.groupby('user').apply(lambda df: df.set_index('datehour').asfreq('H')).y
查看索引值显示正确的频率。
y.index[0]
# ('A', Timestamp('2020-02-01 00:00:00', freq='H'))
所做的只是将索引设置为两部分。 user
首先出现,以便嵌套的 datehour
索引在其中可以是唯一的。一旦datehour
索引是唯一的,那么asfreq
可以毫无困难地使用。
如果您在非唯一索引上尝试asfreq
,它将不起作用。
y_load.set_index('datehour').asfreq('H')
# ---------------------------------------------------------------------------
# ValueError Traceback (most recent call last)
# <ipython-input-433-3ba51b619417> in <module>
# ----> 1 y_load.set_index('datehour').asfreq('H')
# ...
# ValueError: cannot reindex from a duplicate axis
【讨论】:
哇,在 groupby/apply/lambda 中设置一个索引来设置频率,狂野。经过多次摆弄,我确信。我对您的发现印象深刻,我可以确认它绝对有效(+1)。希望你不介意我会等一两天,看看是否有人想出一个更简单/更惯用的方式,我认为它必须存在? 完全没问题。很高兴这有帮助!是的,非常狂野。开箱即用时,Pandas 非常棒,但如果不这样做,就会变得很尴尬。 在此处向 pandas 开发人员发布了一个问题,我们将看看他们是否有更简单的方法。 github.com/pandas-dev/pandas/issues/33647以上是关于在 Pandas MultiIndex 中移动 DateTime 索引的主要内容,如果未能解决你的问题,请参考以下文章
使用 Multiindex 从 Pandas DataFrame 中选择数据
如何从带有列表的嵌套字典构建 MultiIndex Pandas DataFrame
使用元组键从字典创建 MultiIndex pandas DataFrame