Python DataFrame выбирает строки с ежемесячным приращением от ежедневных данных - PullRequest
0 голосов
/ 04 октября 2018

Давайте сразу перейдем к вопросу.Ниже приведены данные за день:

             AAA    BBB    CCC
date                           
2012-04-16  44.48  28.48  17.65
2012-04-17  44.59  28.74  17.65
2012-04-18  44.92  28.74  17.72
2012-04-19  44.92  28.62  17.72
2012-04-20  45.09  28.68  17.71
2012-04-23  45.09  28.40  17.76
2012-04-24  45.09  28.51  17.73
2012-04-25  45.01  28.76  17.73
2012-04-26  45.40  28.94  17.76
2012-04-27  45.57  29.02  17.79
2012-04-30  45.45  28.90  17.80
2012-05-01  45.79  29.07  17.80
2012-05-02  45.71  28.98  17.77
2012-05-03  45.44  28.81  17.79
2012-05-04  45.05  28.48  17.79
2012-05-07  45.05  28.48  17.79
2012-05-08  45.00  28.40  17.93
2012-05-09  44.87  28.30  17.94
2012-05-10  44.93  28.34  17.85
2012-05-11  44.86  28.30  17.96
           ...    ...    ...

Я хочу выбрать строки, начиная с первой строки с ежемесячным приращением , то есть строки с индексом 2012-04-16, 2012-05-16, 2012-06-16, ... .Я могу просто использовать относительную дельту и добавить их вручную, но мне интересно, есть ли более эффективный метод.Я попытался выполнить повторную выборку, но я мог выбрать только первое или последнее число каждого месяца, как в df.resample('M').first().

. Проблема усложняется тем, что некоторые даты отсутствуют;это рабочие дни, но не те, что в США.Существует несколько способов решения этой проблемы:

  1. Выберите точную дату или более раннюю, ближайшую к дате.Если такой даты не существует, начните искать более поздние даты.

  2. Выберите точную дату или более позднюю, ближайшую к дате.Если такой даты не существует, начните искать более ранние даты.

  3. Выберите самую близкую дату к точной дате независимо от того, была ли она ранней или поздней;Я могу использовать min(df.index, key=lambda x: abs(x - (df.index[0] + relativedelta(months=1))).

И в каждом из этих случаев мне интересно, какой метод является наиболее эффективным и простым для чтения.В последнем примере кода месяц является переменной, поэтому я не уверен, смогу ли я сделать это как лямбда-процедуру и использовать «apply».

Заранее спасибо.

1 Ответ

0 голосов
/ 12 октября 2018

Прежде чем мы рассмотрим ваши данные, давайте сначала посмотрим, как мы можем создать DatetimeIndex для определенного дня каждого месяца.Поскольку обычный pd.date_range с ежемесячной периодичностью принимает последний день каждого месяца, мы можем просто добавить фиксированное количество дней:

idx = pd.date_range('2018-04-01', '2018-07-01', freq='1M') + pd.DateOffset(days=16)

DatetimeIndex(['2018-05-16', '2018-06-16', '2018-07-16'],
              dtype='datetime64[ns]', freq=None)

Теперь давайтевозьмем пример кадра данных, в котором пропущено около 16 th дней:

              AAA    BBB    CCC
date                           
2012-04-16  44.48  28.48  17.65
2012-04-17  44.59  28.74  17.65
2012-05-15  45.79  29.07  17.80
2012-05-16  45.71  28.98  17.77
2012-05-17  45.44  28.81  17.79
2012-06-15  44.87  28.30  17.94
2012-06-17  44.95  28.50  17.98
2012-07-14  44.65  28.25  17.87
2012-07-17  44.55  28.75  17.75

Как вы упомянули, существует несколько способов выбора несоответствующих дней:идти назад, вперед или искать ближайший без предпочтений. Вы должны рассмотреть, что наиболее уместно в контексте вашего проекта. Ниже приведено решение, которое придерживается функциональности Pandas и избегает пользовательских lambda функций.

Определите фрейм данных с помощью DatetimeIndex

Сначала создайте фрейм данных с указанием только обязательных индексов:

offset = pd.DateOffset(days=16)
start_date = df.index[0]-pd.DateOffset(months=1)
idx = pd.date_range(start_date, df.index[-1], freq='1M') + offset

df_idx = pd.DataFrame(index=idx)

Обратите внимание, нам нужно вычесть месяц из аргумента начала, чтобы первый месяц не был пропущенпосле добавления 16 дней.Теперь вы можете использовать pd.merge_asof с различными вариантами: -

Совпадение назад / вперед / ближайший через merge_asof

Укажите direction аргумент как 'backward' (по умолчанию), 'forward' или 'nearest' в зависимости от ситуации.Например, используя 'forward':

print(pd.merge_asof(df_idx, df, left_index=True, right_index=True, direction='forward'))

              AAA    BBB    CCC
2012-04-16  44.48  28.48  17.65
2012-05-16  45.71  28.98  17.77
2012-06-16  44.95  28.50  17.98
2012-07-16  44.55  28.75  17.75

Теперь этого может быть достаточно для ваших нужд.


Редактировать: Если вы хотите сохранить индексиз кадра данных вы можете изменить направление слияния и использовать 'backward' вместо 'forward':

res = pd.merge_asof(df.reset_index(),
                    df_idx.reset_index().rename(columns={'index': 'date_idx'}),
                    left_on='date', right_on='date_idx', direction='backward')

res['diff'] = (res['date'] - res['date_idx']).dt.days.abs()
grouper = res['date'].dt.strftime('%Y-%m')
res = res[res['diff'] == res.groupby(grouper)['diff'].transform('min')]

print(res)

        date    AAA    BBB    CCC   date_idx  diff
0 2012-04-16  44.48  28.48  17.65 2012-04-16     0
3 2012-05-16  45.71  28.98  17.77 2012-05-16     0
6 2012-06-17  44.95  28.50  17.98 2012-06-16     1
8 2012-07-17  44.55  28.75  17.75 2012-07-16     1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...