Есть ли способ использовать векторные вычисления для применения нескольких праздничных календарей в Пандах в зависимости от содержимого данного столбца?
В моем случае у меня есть валютные пары с начальной и конечной датами, иЯ хочу выяснить, сколько рабочих дней находится между датами для данной валютной пары.
Любая помощь будет принята с благодарностью, поскольку я пробовал несколько подходов сейчас безуспешно.
Мои данные df
структурирован таким образом:
TRADE_DATE SETTLE_DATE FROM_CRRNCY TO_CRRNCY
18/01/2019 22/01/2019 USD GBP
18/01/2019 22/01/2019 EUR GBP
18/01/2019 22/01/2019 JPY GBP
Отсюда у меня есть отдельный фрейм данных holidaydates
, который объявляет все полные праздничные дни для данной валюты, которая структурирована таким образом:
Currency Date
GBP 01/01/2019
USD 01/01/2019
USD 21/01/2019
Я хотел бы перейти к выводу, аналогичному приведенному ниже, обратите внимание, что отпуск в долларах США означает, что количество рабочих дней на один меньше, чем для EUR или JPY.
TRADE_DATE SETTLE_DATE FROM_CRRNCY TO_CRRNCY Trade Date - Settle Date
18/01/2019 22/01/2019 USD GBP 2
18/01/2019 22/01/2019 EUR GBP 3
18/01/2019 22/01/2019 JPY GBP 3
Обходной путь
Это мое решение, которое в настоящее время работает, но реализуется медленно, с использованием не лучшей практики:
Чтобы иметь возможность использовать holidaydaЗатем значения tes затем разделяются на различные кадры данных, которые называются holidaydatesusd
, holidaydatesgbp
и т. д. Это было реализовано вручную, хотя при необходимости его можно зациклить:
holidaydatesusd = holidaydates.loc[holidaydates['Currency'] == 'USD','Date'].tolist()
Чтобы определить количество рабочих дней между двумя значениями, я использовал numpy busday_count
, применяя результат в качестве нового столбца в кадре данных.Код для этого показан ниже, опять же, это очень ручная реализация:
df['Trade Date - Settle Date'] = df.apply(lambda row: \
np.busday_count(row['TRADE_DATE'], row['SETTLE_DATE'],holidays=holidaydatesusd) \
if row['FROM_CRRNCY'] == 'USD' else \
np.busday_count(row['TRADE_DATE'], row['SETTLE_DATE'],holidays=holidaydatesgbp) \
if row['FROM_CRRNCY'] == 'GBP' else \
np.busday_count(row['TRADE_DATE'], row['SETTLE_DATE'],holidays=holidaydatesjpy) \
if row['FROM_CRRNCY'] == 'JPY' else \
np.busday_count(row['TRADE_DATE'], row['SETTLE_DATE'],holidays=holidaydateseur) \
if row['FROM_CRRNCY'] == 'EUR' else
np.busday_count(row['TRADE_DATE'], row['SETTLE_DATE']), axis=1)
Чтобы получить обе стороны вычисления, я затем повторяю приведенный выше код, но для FROM_CRRNCY, прежде чем, наконец, взятьминимум двух возвращенных отсчетов.
Vector Solution?
Хотя приведенный выше код в настоящее время получает правильный ответ, я ищу более Pythonic способ реализации решения.
Поскольку код прибегает к логике строки цикла if ... else, это заставляет функцию оценивать каждую отдельную строку, а не просматривать весь фрейм данных.
Вместо этого естьспособ извлечь логику сопоставления из функции и выполнить ее по всему фрейму данных за один раз, предоставив необходимые ответы, не прибегая к оценке на уровне строк?
Есть предложения о том, как я мог бы написать этобыло бы приветствоваться также размещение сотен валютных пар, поскольку я не хочу реализовывать это в том виде, в каком оно существует в настоящее время.
То, что я пробовал до сих пор ...
До сих пор я пытался сделать следующее:
# Split the dataframe into separate frames with the same currency
df_usd = df.loc[((df['FROM_CRRNCY'] == 'USD') | (df['TO_CRRNCY'] == 'USD'))]
df_eur = df.loc[((df['FROM_CRRNCY'] == 'EUR') | (df['TO_CRRNCY'] == 'EUR'))]
df_gbp = df.loc[((df['FROM_CRRNCY'] == 'GBP') | (df['TO_CRRNCY'] == 'GBP'))]
df_jpy = df.loc[((df['FROM_CRRNCY'] == 'JPY') | (df['TO_CRRNCY'] == 'JPY'))]
В следующей части я не могу успешно продолжить.
# Now attempt to use the Pandas business day range method, calling count after
df.loc[df_usd, 'Trade Date - Settle Date'] = \
df.loc[df_usd, pd.bdate_range(start = df.loc['TRADE_DATE'], end = df.loc['SETTLE_DATE'], holidays=holidaydatesusd).count()]
Что я думаю, что пытаюсь сделатьсделать сейчас - использовать функцию .loc
, чтобы выбрать частичный фрейм данных в долларах США, создать новый столбец с именем 'Trade Date - Settle Date'
, а затем установить это значение равным диапазону рабочего дня .count()
результат для числа of рабочих дней между датой сделки и датой расчета с использованием календаря праздников в долларах СШАВместо этого я, кажется, передаю целые объекты в метку времени и получаю сообщение об ошибке ниже.
TypeError: Невозможно преобразовать входные данные [# (большой список моего фрейма данных находится здесь) ... Name: TRADE_DATE,dtype: object] типа Timestamp
После нескольких часов попыток использования различных подходов, включая получение различных ошибок datetime64 между datetime64 (day) или (наносекундами), которые поддерживает numpy, но pandas не поддерживает, язакончились варианты.Я также понимаю, что выполняемые мною срезы данных во многих случаях будут содержать одни и те же строки, поэтому мне нужно было бы разработать метод для повторного применения их обратно в кадр данных, выбирая самую высокую дату.
Есть ли кто-нибудь, кто может помочь, как лучше применить расчеты рабочего дня для нескольких календарей?Возможно, мой подход неверен, и это может быть достигнуто гораздо проще?
Обновление
Коды праздников были импортированы в систему с использованием следующего кода
# Define the Lookup Date function for speed
def lookup(s, **args):
dates = {date:pandas.to_datetime(date, dayfirst='True', **args) for date in s.unique()}
return s.map(dates)
# Read in the Holiday Name Lookup
holidaydates = pd.read_csv(holidaydates_file,
dtype={'Currency': str,
'Date': str
}
)
# Convert to dates
holidaydates['Date'] = lookup(holidaydates['Date'])
CSV-файл с датами праздников содержит два столбца «Валюта» и «Дата» и имеет содержимое, аналогичное приведенному выше примеру.Выходные даты начинаются с формы данных (1464,2).
Затем выполняется с использованием этого кода:
df['Trade Date - Settle Date'] = df.apply(lambda row: np.busday_count(row['TRADE_DATE'], row['SETTLE_DATE'], \
holidays=holidaydates.Date[holidaydates.Currency.isin([row['TO_CRRNCY'],row['FROM_CRRNCY']])]), axis=1)
Это приводит к следующей ошибке:
ValueError: («Невозможно безопасно преобразовать входные данные выходных дней в массив дат», u 'возникли с индексом 0')
Конкретная строка, указанная в ошибке,
holidays=holidaydates.Date[holidaydates.Currency.isin([row['TO_CRRNCY'],row['FROM_CRRNCY']])]), axis=1)