Праздничное свидание с пандами

Во время недавней работы над проектом мне пришлось работать с данными временных рядов за год. Я хотел добавить столбцы, указывающие, была ли конкретная дата праздником, а также хотел подсчитать количество дней от предыдущего праздника и дней до следующего праздника. Для человека, относительно плохо знакомого с Python, это было немного сложно. Однако после некоторого исследования в Интернете я обнаружил, что это довольно просто, и решил поделиться. Вот цели этой статьи:

  1. Проверьте, является ли день праздником или нет.
  2. Считайте дни от предыдущего праздника.
  3. Рассчитайте дни до следующего праздника.

Настройка:

Мы будем работать с фреймом данных pandas. Поэтому импортируем панд:

import pandas as pd

Теперь давайте создадим фрейм данных, который содержит даты за период в год, скажем, с 1 января 2019 года по 31 декабря 2019 года. Мы будем называть наш фрейм данных «диапазоном». Pandas позволяет очень легко создать диапазон дат.

dates = pd.DataFrame({'date':pd.date_range('2019-01-01', '2019-12-31')})

Однако к этому диапазону нужно добавить первый день 2020 года. Причина в том, что одна из наших целей - рассчитать дни до следующего праздника. Это усложняется в декабре, так как после 25-го следующим праздником будет 1 января, который не будет включен в наш диапазон дат, поэтому мы добавляем его к выходному диапазону дат.

dates = pd.DataFrame({'date':pd.date_range('2019-01-01', '2020-01-01')})

Давайте взглянем на созданный нами фрейм данных:

Всех праздников

Pandas поставляется со встроенным модулем, который содержит федеральные праздники США. Давайте импортируем это:

from pandas.tseries.holiday import USFederalHolidayCalendar as calendar

Мы можем передать диапазон дат в этот модуль, и он вернет все праздники в этом диапазоне.

cal = calendar()
holidays = cal.holidays(start=dates[‘date’].min(), end=dates[‘date’].max())

В приведенном выше коде модулю нужны дата начала и дата окончания. Вместо того, чтобы указывать даты вручную, мы можем делать это динамически, что будет полезно, когда ваш файл данных будет постоянно меняться, и данные добавляются на постоянной основе.

«Праздники» теперь представляют собой серию панд, содержащую все даты, которые являются федеральными праздниками США.

Праздник или нет

Предположим, мы хотим, чтобы заголовок столбца «праздник» в нашем фрейме данных содержал True, если дата является федеральным праздником США, и False, если это не так.

dates[‘holiday’] = dates[‘date’].isin(holidays)

Давайте посмотрим на наш фрейм данных

Дни от предыдущего праздника до следующего

Мы также хотели, чтобы столбец отображал количество дней от предыдущего праздника, а также еще один столбец, отображающий дни до следующего. Мы напишем функцию для каждого.

def days_prev_holiday(date, holidays):
    difference=[]
    for item in holidays:
        difference.append(int(str((item-date).days)))
    return abs(max([x for x in difference if x<=0]))

Давайте разберем код. Мы создаем список чисел, каждое из которых представляет количество дней до определенного праздника. Поскольку дата прошла, вычитание текущей даты из прошлой даты даст нам отрицательное число. Чтобы определить дни от ближайшего прошедшего праздника, мы возвращаем максимальное число среди всех отрицательных чисел. Поскольку мы смотрим на количество дней, нам не нужен знак «-», поэтому мы используем абс (число), чтобы избавиться от знака минус.

Эту функцию можно переписать на меньшие строки, используя понимание списков, однако для удобства чтения мы оставим наш цикл for.

Теперь, чтобы подсчитать количество дней до следующего праздника, мы используем почти ту же логику, однако на этот раз мы возвращаем минимальное число, количество всех чисел больше 0. Следовательно, функция становится такой:

def days_next_holiday(date, holidays):
    difference=[]
    for item in holidays:
        difference.append(int(str((item-date).days)))
    return min([x for x in difference if x>=0])

Теперь, когда у нас есть написанные функции, мы можем применить их ко всем строкам в соответствующем столбце в нашем фрейме данных.

dates['days_previous_holiday']= dates.apply(lambda row: days_prev_holiday(row['date'], holidays), axis=1)
dates['days_next_holiday']= dates.apply(lambda row: days_next_holiday(row['date'], holidays), axis=1)

Давайте в последний раз посмотрим на созданный нами фрейм данных: