在多索引中移动时间以合并

Posted

技术标签:

【中文标题】在多索引中移动时间以合并【英文标题】:Shift time in multi-index to merge 【发布时间】:2018-11-09 12:51:00 【问题描述】:

我想合并两个由timeid 索引的数据集。问题是,每个数据集中的时间略有不同。在一个数据集中,时间(每月)是月中,所以是每个月的 15 号。在另一个数据集中,这是最后一个工作日。这应该仍然是一对一的匹配,但日期并不完全相同。

我的方法是将月中日期更改为工作日月末日期。

数据:

dt = pd.date_range('1/1/2011','12/31/2011', freq='D')
dt = dt[dt.day == 15]
lst = [1,2,3]
idx = pd.MultiIndex.from_product([dt,lst],names=['date','id'])
df = pd.DataFrame(np.random.randn(len(idx)), index=idx)
df.head()

输出:

                      0
date       id
2011-01-15 1  -0.598584
           2  -0.484455
           3  -2.044912
2011-02-15 1  -0.017512
           2   0.852843

这就是我想要的(我删除了性能警告):

In[83]:df.index.levels[0] + BMonthEnd()
Out[83]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
               '2011-05-31', '2011-06-30', '2011-07-29', '2011-08-31',
               '2011-09-30', '2011-10-31', '2011-11-30', '2011-12-30'],
              dtype='datetime64[ns]', freq='BM')

但是,索引是不可变的,所以这不起作用:

In: df.index.levels[0] = df.index.levels[0] + BMonthEnd()

TypeError: 'FrozenList' does not support mutable operations.

我唯一的解决方案是重置索引(),更改日期,然后再次设置索引():

df.reset_index(inplace=True)
df['date'] = df['date'] + BMonthEnd()
df.set_index(['date','id'], inplace=True)

这给了我想要的,但这是最好的方法吗?是否有 set_level_values() 函数(我在 API 中没有看到)?

或者我可能采用了错误的合并方法。我可以将数据集与键 df.index.get_level_values(0).yeardf.index.get_level_values(0).monthid 合并,但这似乎并没有好多少。

【问题讨论】:

【参考方案1】:

您可以使用set_levels 来设置多索引级别:

df.index.set_levels(df.index.levels[0] + pd.tseries.offsets.BMonthEnd(),
                    level='date', inplace=True)


>>> df.head()
                      0
date       id          
2011-01-31 1  -1.410646
           2   0.642618
           3  -0.537930
2011-02-28 1  -0.418943
           2   0.983186

【讨论】:

我没有选票,但我清除了一个并投了赞成票,因为我也喜欢这个解决方案。 谢谢,非常感谢!我也喜欢你的解决方案! 这是对重新索引问题的出色、干净的解决方案,但我将下面的解决方案标记为答案,因为它解决了我以更好的性能合并的整体问题。通过跳过重新索引,整体速度更快。【参考方案2】:

你可以重新构建它:

df.index = pd.MultiIndex.from_arrays(
    [
       df.index.get_level_values(0) + BMonthEnd(),
       df.index.get_level_values(1)
])

set_levels 隐式重建索引。如果您有两个以上的级别,则此解决方案会变得笨拙,因此请考虑使用set_levels 来简化输入。

【讨论】:

这也可以。我没有想到这种方法。【参考方案3】:

既然你无论如何都想合并,你可以忘记更改索引并使用pandas.merge_asof()

数据

df1

                      0
date       id          
2011-01-15 1  -0.810581
           2   1.177235
           3   0.083883
2011-02-15 1   1.217419
           2  -0.970804
           3   1.262364
2011-03-15 1  -0.026136
           2  -0.036250
           3  -1.103929
2011-04-15 1  -1.303298

这是一个月的最后一个工作日,df2

                      0
date       id          
2011-01-31 1  -0.277675
           2   0.086539
           3   1.441449
2011-02-28 1   1.330212
           2  -0.028398
           3  -0.114297
2011-03-31 1  -0.031264
           2  -0.787093
           3  -0.133088
2011-04-29 1   0.938732

合并

使用df1 作为左侧DataFrame,然后选择合并方向为前进,因为最后一个工作日总是在 15 日之后。或者,您可以设置容差。这在您在右侧缺少一个月 DataFrame 的情况下很有用,并且如果您缺少 2 月最后一个工作日的数据,这将阻止您将 03-31-2011 合并到 02-15-2011

import pandas as pd
pd.merge_asof(df1.reset_index(), df2.reset_index(), by='id', on='date', 
              direction='forward', tolerance=pd.Timedelta(days=20)).set_index(['date', 'id'])

结果

                    0_x       0_y
date       id                    
2011-01-15 1  -0.810581 -0.277675
           2   1.177235  0.086539
           3   0.083883  1.441449
2011-02-15 1   1.217419  1.330212
           2  -0.970804 -0.028398
           3   1.262364 -0.114297
2011-03-15 1  -0.026136 -0.031264
           2  -0.036250 -0.787093
           3  -1.103929 -0.133088
2011-04-15 1  -1.303298  0.938732

【讨论】:

这太棒了——在我更大的数据上,这种合并比重新设置索引然后合并要快。 另外,我想保留月末日期,所以我只是更改了direction='backward' 和数据框的顺序,这样就可以了。

以上是关于在多索引中移动时间以合并的主要内容,如果未能解决你的问题,请参考以下文章

从隐藏在多索引中的年月创建日期时间

Pandas 多索引数据框 - 从多索引中的一个索引中选择最大值

熊猫数据框检查索引是不是存在于多索引中

将 numpy 数组复制到 Panda 多索引中(大小相同)

多索引中的标签输出

熊猫列多索引中的缺失值