首页 > 解决方案 > 倒计时假期

问题描述

我在数据框中有一列告诉我日期是否是假期。这是一个例子:

dates   is_holiday
2019-02-14  NaN
2019-02-15  NaN
2019-02-16  NaN
2019-02-17  NaN
2019-02-18  Family Day
2019-02-19  NaN
2019-02-20  NaN
2019-02-21  NaN
2019-02-22  NaN
2019-02-23  NaN
2019-02-24  NaN

我想有一列倒计时距离下一个假期还有多少天,以及距离上一个假期有多少天,最多 5 天。上面示例的所需输出

dates          days_to_holiday
    2019-02-14  4
    2019-02-15  3
    2019-02-16  2
    2019-02-17  1
    2019-02-18  0
    2019-02-19  -1
    2019-02-20  -2
    2019-02-21  -3
    2019-02-22  -4
    2019-02-23  -5
    2019-02-24  NaN

距离 2019 年 2 月 14 日的家庭日(2019 年 2 月 18 日)还有 4 天,距离 2019 年 2 月 20 日的家庭日还有 2 天。由于我不想计算超过 5 天的假期,因此 2018-02-24 仍然是 NaN。

为了澄清,该列is_holiday在该列中有几个假期。这是一个更大的例子。

dates   is_holiday
2018-01-01  New Year's Day
2018-01-02  NaN
2018-01-03  NaN
2018-01-04  NaN
2018-01-05  NaN
2018-01-06  NaN
2018-01-07  NaN
2018-01-08  NaN
2018-01-09  NaN
2018-01-10  NaN
2018-01-11  NaN
2018-01-12  NaN
2018-01-13  NaN
2018-01-14  NaN
2018-01-15  NaN
2018-01-16  NaN
2018-01-17  NaN
2018-01-18  NaN
2018-01-19  NaN
2018-01-20  NaN
2018-01-21  NaN
2018-01-22  NaN
2018-01-23  NaN
2018-01-24  NaN
2018-01-25  NaN
2018-01-26  NaN
2018-01-27  NaN
2018-01-28  NaN
2018-01-29  NaN
2018-01-30  NaN
2018-01-31  NaN
2018-02-01  NaN
2018-02-02  NaN
2018-02-03  NaN
2018-02-04  NaN
2018-02-05  NaN
2018-02-06  NaN
2018-02-07  NaN
2018-02-08  NaN
2018-02-09  NaN
2018-02-10  NaN
2018-02-11  NaN
2018-02-12  NaN
2018-02-13  NaN
2018-02-14  NaN
2018-02-15  NaN
2018-02-16  NaN
2018-02-17  NaN
2018-02-18  NaN
2018-02-19  Family Day
2018-02-20  NaN
2018-02-21  NaN
2018-02-22  NaN
2018-02-23  NaN
2018-02-24  NaN
2018-02-25  NaN
2018-02-26  NaN
2018-02-27  NaN
2018-02-28  NaN
2018-03-01  NaN
2018-03-02  NaN
2018-03-03  NaN
2018-03-04  NaN
2018-03-05  NaN
2018-03-06  NaN
2018-03-07  NaN
2018-03-08  NaN
2018-03-09  NaN
2018-03-10  NaN
2018-03-11  NaN
2018-03-12  NaN
2018-03-13  NaN
2018-03-14  NaN
2018-03-15  NaN
2018-03-16  NaN
2018-03-17  NaN
2018-03-18  NaN
2018-03-19  NaN
2018-03-20  NaN
2018-03-21  NaN
2018-03-22  NaN
2018-03-23  NaN
2018-03-24  NaN
2018-03-25  NaN
2018-03-26  NaN
2018-03-27  NaN
2018-03-28  NaN
2018-03-29  NaN
2018-03-30  Good Friday
2018-03-31  NaN
2018-04-01  NaN
2018-04-02  NaN
2018-04-03  NaN
2018-04-04  NaN
2018-04-05  NaN
2018-04-06  NaN
2018-04-07  NaN
2018-04-08  NaN
2018-04-09  NaN
2018-04-10  NaN
2018-04-11  NaN
2018-04-12  NaN
2018-04-13  NaN
2018-04-14  NaN
2018-04-15  NaN
2018-04-16  NaN
2018-04-17  NaN
2018-04-18  NaN
2018-04-19  NaN
2018-04-20  NaN
2018-04-21  NaN
2018-04-22  NaN
2018-04-23  NaN
2018-04-24  NaN
2018-04-25  NaN
2018-04-26  NaN
2018-04-27  NaN
2018-04-28  NaN
2018-04-29  NaN
2018-04-30  NaN
2018-05-01  NaN
2018-05-02  NaN
2018-05-03  NaN
2018-05-04  NaN
2018-05-05  NaN
2018-05-06  NaN
2018-05-07  NaN
2018-05-08  NaN
2018-05-09  NaN
2018-05-10  NaN
2018-05-11  NaN
2018-05-12  NaN
2018-05-13  NaN
2018-05-14  NaN
2018-05-15  NaN
2018-05-16  NaN
2018-05-17  NaN
2018-05-18  NaN
2018-05-19  NaN
2018-05-20  NaN
2018-05-21  Victoria Day
2018-05-22  NaN
2018-05-23  NaN
2018-05-24  NaN
2018-05-25  NaN
2018-05-26  NaN
2018-05-27  NaN
2018-05-28  NaN
2018-05-29  NaN
2018-05-30  NaN
2018-05-31  NaN
2018-06-01  NaN
2018-06-02  NaN
2018-06-03  NaN
2018-06-04  NaN
2018-06-05  NaN
2018-06-06  NaN
2018-06-07  NaN
2018-06-08  NaN
2018-06-09  NaN
2018-06-10  NaN
2018-06-11  NaN
2018-06-12  NaN
2018-06-13  NaN
2018-06-14  NaN
2018-06-15  NaN
2018-06-16  NaN
2018-06-17  NaN
2018-06-18  NaN
2018-06-19  NaN
2018-06-20  NaN
2018-06-21  NaN
2018-06-22  NaN
2018-06-23  NaN
2018-06-24  NaN
2018-06-25  NaN
2018-06-26  NaN
2018-06-27  NaN
2018-06-28  NaN
2018-06-29  NaN
2018-06-30  NaN
2018-07-01  Canada Day
2018-07-02  Canada Day (Observed)
2018-07-03  NaN
2018-07-04  NaN
2018-07-05  NaN
2018-07-06  NaN
2018-07-07  NaN
2018-07-08  NaN
2018-07-09  NaN
2018-07-10  NaN
2018-07-11  NaN
2018-07-12  NaN
2018-07-13  NaN
2018-07-14  NaN
2018-07-15  NaN
2018-07-16  NaN
2018-07-17  NaN
2018-07-18  NaN
2018-07-19  NaN
2018-07-20  NaN
2018-07-21  NaN
2018-07-22  NaN
2018-07-23  NaN
2018-07-24  NaN
2018-07-25  NaN
2018-07-26  NaN
2018-07-27  NaN
2018-07-28  NaN
2018-07-29  NaN
2018-07-30  NaN
2018-07-31  NaN
2018-08-01  NaN
2018-08-02  NaN
2018-08-03  NaN
2018-08-04  NaN
2018-08-05  NaN
2018-08-06  Civic Holiday
2018-08-07  NaN
2018-08-08  NaN
2018-08-09  NaN
2018-08-10  NaN
2018-08-11  NaN
2018-08-12  NaN
2018-08-13  NaN
2018-08-14  NaN
2018-08-15  NaN
2018-08-16  NaN
2018-08-17  NaN
2018-08-18  NaN
2018-08-19  NaN
2018-08-20  NaN
2018-08-21  NaN
2018-08-22  NaN
2018-08-23  NaN
2018-08-24  NaN
2018-08-25  NaN
2018-08-26  NaN
2018-08-27  NaN
2018-08-28  NaN
2018-08-29  NaN
2018-08-30  NaN
2018-08-31  NaN
2018-09-01  NaN
2018-09-02  NaN
2018-09-03  Labour Day
2018-09-04  NaN
2018-09-05  NaN
2018-09-06  NaN
2018-09-07  NaN
2018-09-08  NaN
2018-09-09  NaN
2018-09-10  NaN
2018-09-11  NaN
2018-09-12  NaN
2018-09-13  NaN
2018-09-14  NaN
2018-09-15  NaN
2018-09-16  NaN
2018-09-17  NaN
2018-09-18  NaN
2018-09-19  NaN
2018-09-20  NaN
2018-09-21  NaN
2018-09-22  NaN
2018-09-23  NaN
2018-09-24  NaN
2018-09-25  NaN
2018-09-26  NaN
2018-09-27  NaN
2018-09-28  NaN
2018-09-29  NaN
2018-09-30  NaN
2018-10-01  NaN
2018-10-02  NaN
2018-10-03  NaN
2018-10-04  NaN
2018-10-05  NaN
2018-10-06  NaN
2018-10-07  NaN
2018-10-08  Thanksgiving
2018-10-09  NaN
2018-10-10  NaN
2018-10-11  NaN
2018-10-12  NaN
2018-10-13  NaN
2018-10-14  NaN
2018-10-15  NaN
2018-10-16  NaN
2018-10-17  NaN
2018-10-18  NaN
2018-10-19  NaN
2018-10-20  NaN
2018-10-21  NaN
2018-10-22  NaN
2018-10-23  NaN
2018-10-24  NaN
2018-10-25  NaN
2018-10-26  NaN
2018-10-27  NaN
2018-10-28  NaN
2018-10-29  NaN
2018-10-30  NaN
2018-10-31  NaN
2018-11-01  NaN
2018-11-02  NaN
2018-11-03  NaN
2018-11-04  NaN
2018-11-05  NaN
2018-11-06  NaN
2018-11-07  NaN
2018-11-08  NaN
2018-11-09  NaN
2018-11-10  NaN
2018-11-11  NaN
2018-11-12  NaN
2018-11-13  NaN
2018-11-14  NaN
2018-11-15  NaN
2018-11-16  NaN
2018-11-17  NaN
2018-11-18  NaN
2018-11-19  NaN
2018-11-20  NaN
2018-11-21  NaN
2018-11-22  NaN
2018-11-23  NaN
2018-11-24  NaN
2018-11-25  NaN
2018-11-26  NaN
2018-11-27  NaN
2018-11-28  NaN
2018-11-29  NaN
2018-11-30  NaN
2018-12-01  NaN
2018-12-02  NaN
2018-12-03  NaN
2018-12-04  NaN
2018-12-05  NaN
2018-12-06  NaN
2018-12-07  NaN
2018-12-08  NaN
2018-12-09  NaN
2018-12-10  NaN
2018-12-11  NaN
2018-12-12  NaN
2018-12-13  NaN
2018-12-14  NaN
2018-12-15  NaN
2018-12-16  NaN
2018-12-17  NaN
2018-12-18  NaN
2018-12-19  NaN
2018-12-20  NaN
2018-12-21  NaN
2018-12-22  NaN
2018-12-23  NaN
2018-12-24  NaN
2018-12-25  Christmas Day
2018-12-26  Boxing Day
2018-12-27  NaN
2018-12-28  NaN
2018-12-29  NaN
2018-12-30  NaN
2018-12-31  NaN

我怎样才能做到这一点?

标签: pythonpandas

解决方案


一个相当冗长的方法适用于 2018 年更长的假期示例:

# Label each holiday and the following block of non-holiday days with
# a unique integer on which to group
df['grouper'] = df['is_holiday'].notnull().cumsum()

# Get days after last holiday, with each holiday as 0
df['aft'] = df.groupby('grouper').cumcount()

# Get days before next holiday. The + 1 is needed because
# cumcount(ascending=False) assigns 0 to the last row
df['bef'] = df.groupby('grouper').cumcount(ascending=False) + 1

df['days_to_holiday'] = df[['aft', 'bef']].min(axis=1)

# Wherever we chose a value from the "days after last holiday" column,
# multiply that value by -1
df.loc[df['bef'] == df['days_to_holiday'], 'days_to_holiday'] *= -1

# Get a boolean mask of days far away from holidays which we
# want to set to NaN
mask = df['is_holiday'].ffill(limit=5).bfill(limit=4).isnull()

df.loc[mask, 'days_to_holiday'] = pd.np.nan

df = df[['dates', 'is_holiday', 'days_to_holiday']]

# Spot check
df.iloc[40:60]

         dates  is_holiday  days_to_holiday
40  2018-02-10         NaN              NaN
41  2018-02-11         NaN              NaN
42  2018-02-12         NaN              NaN
43  2018-02-13         NaN              NaN
44  2018-02-14         NaN              NaN
45  2018-02-15         NaN              4.0
46  2018-02-16         NaN              3.0
47  2018-02-17         NaN              2.0
48  2018-02-18         NaN              1.0
49  2018-02-19  Family Day              0.0
50  2018-02-20         NaN             -1.0
51  2018-02-21         NaN             -2.0
52  2018-02-22         NaN             -3.0
53  2018-02-23         NaN             -4.0
54  2018-02-24         NaN             -5.0
55  2018-02-25         NaN              NaN
56  2018-02-26         NaN              NaN
57  2018-02-27         NaN              NaN
58  2018-02-28         NaN              NaN
59  2018-03-01         NaN              NaN

请注意,在一年的最后 2 天(2018-12-30和),此解决方案2018-12-31节礼日停止计数,并开始计数​​2019 年元旦。这对我来说似乎是正确的,但我认为值得一提的是,它偶然发生在解决方案:

df.tail(15)

          dates     is_holiday  days_to_holiday
350  2018-12-17            NaN              NaN
351  2018-12-18            NaN              NaN
352  2018-12-19            NaN              NaN
353  2018-12-20            NaN              NaN
354  2018-12-21            NaN              4.0
355  2018-12-22            NaN              3.0
356  2018-12-23            NaN              2.0
357  2018-12-24            NaN              1.0
358  2018-12-25  Christmas Day              0.0
359  2018-12-26     Boxing Day              0.0
360  2018-12-27            NaN             -1.0
361  2018-12-28            NaN             -2.0
362  2018-12-29            NaN             -3.0
363  2018-12-30            NaN              2.0
364  2018-12-31            NaN              1.0

推荐阅读