Сдвиг времени в мультииндекс для слияния - PullRequest
0 голосов
/ 30 мая 2018

Я хочу объединить два набора данных, которые проиндексированы time и id.Проблема в том, что время немного отличается в каждом наборе данных.В одном наборе данных время (Ежемесячно) - середина месяца, то есть 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.

Единственное решение, которое у меня есть, - это reset_index (), изменить даты, затем снова установить set_index ():

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).year, df.index.get_level_values(0).month и id, но это не намного лучше.

Ответы [ 3 ]

0 голосов
/ 30 мая 2018

Вы можете просто построить его снова:

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

set_levels неявно перестраивает индекс под прикрытием.Если у вас есть более двух уровней, это решение становится неудобным, поэтому рассмотрите возможность использования set_levels для краткости ввода.

0 голосов
/ 30 мая 2018

Поскольку вы хотите объединить в любом случае, вы можете забыть об изменении индекса и использовать команду 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 и не позволит вам объединить 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
0 голосов
/ 30 мая 2018

Вы можете использовать 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
...