将数据框中的月份数据的最后3个工作日替换为另一个

时间:2019-02-16 14:14:15

标签: python pandas dataframe

如何将另一个数据框的当月最后3个工作日(自定义日历)的数据替换为另一个?

我正在尝试对投资进行一些数据分析(具体交易恒指期货)。本月和下个月(数据框B)有两个用于期货结算的数据框。

数据框A看起来像这样

Date      Value

2/1/2019    123

3/1/2019    456

...

29/1/2019   785

30/1/2019   424

31/1/2019   456

1/2/2019    484

...

27/2/2019 465 (last day for available data)

数据框B看起来像这样

Date      Value

2/1/2019    654

3/1/2019    424

...

29/1/2019   544

30/1/2019   111

31/1/2019   222

1/2/2019    333

...

27/2/2019   444 (last day for available data)

我想在每个月的最后3个工作日修改数据框A。数据应替换为数据框B中的数据

Date      Value

2/1/2019    123

3/1/2019    456

...

29/1/2019   544

30/1/2019   111

31/1/2019   222

1/2/2019    484

...

27/2/2019   444 (last day for data available data)

我在网上查找并已经制作了一个自定义日历(如下所示,为简单起见,我删除了大部分假期)。

我也设法修改了上个月的数据。 但是如果今天是最后三个工作日之一,例如今天今天是2019年2月27日,我不知道如何替换数据框B中的数据(值= 444)

import pandas as pd
from pandas.tseries.offsets import CustomBusinessDay
from pandas.tseries.offsets import BMonthEnd
from datetime import date
from pandas.tseries.holiday import AbstractHolidayCalendar, nearest_workday, Holiday
import numpy as np
import quandl

=================Holiday=============================

class hktradingholiday2019(AbstractHolidayCalendar):
    rules = [
        Holiday('New Years Day', year=2019, month=1, day=1, observance=nearest_workday),
    ]

hktradeholiday2019 = CustomBusinessDay(calendar=hktradingholiday2019())

hktradeday2019 = pd.date_range(start="1/1/2019",end="31/12/2019", freq=hktradeholiday2019)

=======================================================

dataframeA = quandl.get("CHRIS/HKEX_HSI1")  
dataframeB = quandl.get("CHRIS/HKEX_HSI2")  

endoflastmonth= dataframeB.index[-1] - pd.tseries.offsets.MonthEnd(1)
df = dataframeB[:endoflastmonth]     
df = df.groupby(df.index.month).tail(3)

dataframeA.loc[df.index] = df  

有人会建议我如何获取2019年2月27日的数据,替换为数据框B中的值吗?

我的第一个想法是使用if语句检查最后一行是否为3个工作日之一(因此,我做了很多事情)。但是我不知道该怎么做。

2 个答案:

答案 0 :(得分:2)

我相信您需要:

class hktradingholiday2019(AbstractHolidayCalendar):
    rules = [
        Holiday('New Years Day', year=2019, month=1, day=1, observance=nearest_workday),
    ]

hktradeholiday2019 = CustomBusinessDay(calendar=hktradingholiday2019())

hktradeday2019 = pd.date_range(start="1/1/2019",end="31/12/2019", freq=hktradeholiday2019)
print (hktradeday2019)

#create DatetimeIndex
dfA['Date'] = pd.to_datetime(dfA['Date'], dayfirst=True)
dfB['Date'] = pd.to_datetime(dfB['Date'], dayfirst=True)

dfA = dfA.set_index('Date')
dfB = dfB.set_index('Date')

#print (dfA)
#print (dfB)

#get last 3 days per months for hktradeday2019    
s = hktradeday2019.to_series()
lastdays = s.groupby(s.index.month).tail(3)
#print (lastdays)

#replace only last 3 month values if exist
#same indices, so is possible assign by same idx in dfAb and dfB
idx = dfA.index.intersection(lastdays.index)
dfA.loc[idx] = dfB.loc[idx].combine_first(dfA.loc[idx])
print (dfA)
            Value
Date             
2019-01-02    123
2019-01-03    456
2019-01-29    544
2019-01-30    111
2019-01-31    222
2019-02-01    484
2019-02-27    444

答案 1 :(得分:0)

要将数据从A复制到B,可以使用:

A.mask(criterionlist, B)

但是成功的关键是如何定义以上criterionlist

要考虑自定义业务日历,必须对其进行定义。 为了演示的目的,我准备了一个与 Jan 30 的日历, 一个假期(此日期在以下示例中的日期中):

class ExBusinessCalendar(AbstractHolidayCalendar):
    rules = [
        Holiday('New Years Day', month=1, day=1),
        Holiday('My Extra Free Day', month=1, day=30),
        EasterMonday,
        Holiday('Labour Day', month=5, day=1)
    ]

然后,您必须定义以下辅助变量,与 我们的自定义日历:

  • offset- CustomBusinessMonthEnd 的偏移量。
  • cbd-一个 CustomBusinessDay 偏移量。
  • isBusinessDay-检查当前日期是否为 “按偏移量”(是否是我们日历中的实际工作日)。

我介绍了最后一种功能,可以过滤掉非企业业务 天,如果源数据包括在内。

有关如何定义它们的详细信息,请参见下面的脚本。

具有上述变量和两个DataFrame,条件列表可以 表示为:

A.Date.map(lambda d: offset.rollforward(d) <= d + 2 * cbd
    and isBusinessDay(d))

此功能:

  • 计算offset.rollforward(d)-的最后一个工作日 本月。
  • 然后计算d + 2 * cbd-当前日期+ 2个工作日。
  • 如果上述第一个日期早于或等于第二个日期, 我们在当月的最后3个工作日内。
  • 其他“过滤”条件是该日期(d)必须为 我们日历中的一个工作日。在下面的示例中,它过滤掉了 2019-01-30 的行。

因此整个脚本如下所示:

import pandas as pd
from pandas.tseries.holiday import AbstractHolidayCalendar, Holiday, EasterMonday, Easter

# Example business calendar
class ExBusinessCalendar(AbstractHolidayCalendar):
    rules = [
        Holiday('New Years Day', month=1, day=1),
        Holiday('My Extra Free Day', month=1, day=30),
        EasterMonday,
        Holiday('Labour Day', month=5, day=1)
    ]
# Source data for A and B
datA = [ ['2/1/2019',  120], ['3/1/2019',  450], ['26/1/2019', 760],
    ['27/1/2019', 770], ['28/1/2019', 780], ['29/1/2019', 790],
    ['30/1/2019', 420], ['31/1/2019', 450], ['1/2/2019',  480],
    ['2/2/2019',  480] ]
datB = [ ['2/1/2019',  122], ['3/1/2019',  453], ['26/1/2019', 764],
    ['27/1/2019', 775], ['28/1/2019', 786], ['29/1/2019', 797],
    ['30/1/2019', 428], ['31/1/2019', 451], ['1/2/2019',  482],
    ['2/2/2019',  483] ]

# Create DataFrames
A = pd.DataFrame(data=datA, columns=['Date', 'Value'])
A.Date = pd.to_datetime(A.Date, dayfirst=True)
B = pd.DataFrame(data=datB, columns=['Date', 'Value'])
B.Date = pd.to_datetime(B.Date, dayfirst=True)

# Auxiliary variables
offset = pd.offsets.CustomBusinessMonthEnd(calendar=ExBusinessCalendar())
cbd = pd.offsets.CustomBusinessDay(calendar=ExBusinessCalendar())
isBusinessDay = pd.offsets.CustomBusinessDay(calendar=ExBusinessCalendar()).onOffset
# Compute the result
A.mask(A.Date.map(lambda d: offset.rollforward(d) <= d + 2 * cbd
   and isBusinessDay(d)), B)

结果是:

        Date  Value
0 2019-01-02    120
1 2019-01-03    450
2 2019-01-26    760
3 2019-01-27    770
4 2019-01-28    786
5 2019-01-29    797
6 2019-01-30    420
7 2019-01-31    451
8 2019-02-01    480
9 2019-02-02    480    

如您所见,从B复制的值是 2019-01-28 2019-01-29 2019-01-31 (在我的示例数据中,结尾处没有 0 )。

2019-01-30 的值未复制,如在我的自定义业务日历中一样 这不是工作日。

要使用以上结果修改A,请将最后一条指令更改为:

A = A.mask(A.Date.map( ...