如何在熊猫中快速计算跨多个假期日历的两个日期列之间的工作日数?

时间:2019-02-11 10:49:17

标签: python pandas numpy dataframe

是否有一种方法可以使用向量计算来根据给定列中的内容在熊猫中应用多个假期日历?

对于我来说,我有一个带有开始日期和结束日期的货币对,并且想算出给定货币对的日期之间有多少个工作日。

任何帮助将不胜感激,因为我现在已经尝试了多种方法而没有成功。

我的数据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 

我想得到类似于以下的输出,请注意,美元假期意味着工作日的数量比欧元或日元少一个工作日。

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

解决方法

这是我目前可以使用的解决方案,但是使用非最佳做法的实施速度很慢:

为了以后可以使用holidaydates值,这些值随后被拆分为称为holidaydatesusdholidaydatesgbp等的不同数据框。 尽管可以在需要时将其循环,但已手动实现了该操作:

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,在最终取两个返回的计数中的最小值之前。

矢量解决方案?

尽管上面的代码当前可以得到正确的答案,但我正在寻找一种更Python化的方法来实现该解决方案。

由于代码采用了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函数选择部分USD数据框,创建一个名为'Trade Date - Settle Date'的新列,然后将该值设置为工作日范围使用美元假期日历,.count()得出交易日期和结算日期之间的工作日数的结果。相反,我似乎将整个对象传递给时间戳,并在下面收到错误。

  

TypeError:无法将类型的输入[#(我的数据帧的大列表放在此处)...名称:TRADE_DATE,dtype:object]类型转换为时间戳

经过几个小时的尝试不同的方法,包括在numpy支持但不支持的datetime64(天)或(纳秒)之间获得各种datetime64错误,我用尽了所有的选择。我还意识到,在很多情况下,我要截取的数据框都将包含相同的行,因此,我必须找出一种方法,将其重新应用到数据框中,并选择最高日期。

有没有人可以协助您最好地在多个日历中应用工作日计算?也许我的方法是错误的,并且可以更简单地实现?

更新

使用以下代码将假期代码导入到系统中

# 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 :('不能安全地将提供的假期输入转换为日期数组,'发生在索引0')

错误中引用的特定行是

holidays=holidaydates.Date[holidaydates.Currency.isin([row['TO_CRRNCY'],row['FROM_CRRNCY']])]), axis=1)

0 个答案:

没有答案