Repository: awslabs/gluonts

Issue:
Best way to add holiday, weekend, weekend features
What's the best way to add holiday, weekend, weekend features to DeepAR?

I've seen one example that used feat_dynamic_real, but DeepAR has a parameter for time_features. Is time_features the better way? I have not seen examples of using the time_features parameter anywhere. If time_features is the best option, how would I use it?

Thanks!





Discussion:
@RXY3832 the `time_features` option in the estimator allows you to customize which features (such as the ones you mention) the model should use. The default settings will enable the features which are relevant for the specific frequency that you set: these are selected according to the logic here https://github.com/awslabs/gluon-ts/blob/2cb59f42fc933e931aa61d6519100eda07731441/src/gluonts/time_feature/_base.py#L126


So, in principle you don’t need to explicitly set them. However, if you really want to customize them you can explicitly provide them in a list:

```
time_features=[HourOfDay(), DayOfWeek()]
```

will make the model see the hour of day and day of week features. You can check the module I linked above to see how to import the necessary stuff.
This said, I agree we should add an example on this :-)
We also have a few holidays already implemented here: https://github.com/awslabs/gluon-ts/blob/master/src/gluonts/time_feature/holiday.py
Thanks for the information!

I tried adding Holidays, like in the link provided, and while Christmas and Christmas Eve worked, other date are giving me AssertionError: No closest holiday for the date index error. Below is the code and more detailed error.

Code:
```
from gluonts.time_feature.holiday import (
        squared_exponential_kernel,
        SpecialDateFeatureSet,
        NEW_YEARS_DAY,
        MARTIN_LUTHER_KING_DAY,
        SUPERBOWL,
        THANKSGIVING,
        CHRISTMAS_EVE,
        CHRISTMAS_DAY,
        NEW_YEARS_EVE
    )

h_days = [NEW_YEARS_DAY,
          MARTIN_LUTHER_KING_DAY,
          SUPERBOWL,
          THANKSGIVING,
          CHRISTMAS_EVE,
          CHRISTMAS_DAY,
          NEW_YEARS_EVE]

#import pandas as pd
sfs = SpecialDateFeatureSet(h_days)
date_indices = pd.date_range(
    start="2016-01-01",
    end="2019-11-30",
    freq='D'
    )

sfs(date_indices)
```

Error Details:
```
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-50-58f607c96e79> in <module>()
     27     )
     28 
---> 29 sfs(date_indices)

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/gluonts/time_feature/holiday.py in __call__(self, dates)
    211                     ]
    212                 )
--> 213                 for feat_name in self.feature_names
    214             ]
    215         )

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/gluonts/time_feature/holiday.py in <listcomp>(.0)
    211                     ]
    212                 )
--> 213                 for feat_name in self.feature_names
    214             ]
    215         )

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/gluonts/time_feature/holiday.py in <listcomp>(.0)
    208                             SPECIAL_DATE_FEATURES[feat_name](index)
    209                         )
--> 210                         for index in dates
    211                     ]
    212                 )

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/gluonts/time_feature/holiday.py in distance_to_day(index)
     43         assert (
     44             len(holiday_date) != 0
---> 45         ), f"No closest holiday for the date index {index} found."
     46         # It sometimes returns two dates if it is exactly half a year after the
     47         # holiday. In this case, the smaller distance (182 days) is returned.

AssertionError: No closest holiday for the date index 2018-07-18 00:00:00 found.
```

Let me know if I should move this over to another thread too.

Thanks!
Sorry for coming back to this so late. I'll look into this. 
i have the same error - 
code:
```
from gluonts.time_feature.holiday import (
        squared_exponential_kernel,
        SpecialDateFeatureSet,
        NEW_YEARS_DAY,
        MARTIN_LUTHER_KING_DAY,
        SUPERBOWL,
        THANKSGIVING,
        CHRISTMAS_EVE,
        CHRISTMAS_DAY,
        NEW_YEARS_EVE
    )

hdays = [NEW_YEARS_DAY,
          MARTIN_LUTHER_KING_DAY,
          SUPERBOWL,
          THANKSGIVING,
          CHRISTMAS_EVE,
          CHRISTMAS_DAY,
          NEW_YEARS_EVE]
kernel = squared_exponential_kernel(alpha=1.0)

sfs = SpecialDateFeatureSet(hdays, kernel)
date_indices = pd.date_range(
     start="2018-01-01",
     end="2019-12-31",
     freq='D')

sfs(date_indices)
```
error:

```
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-55-63f69419d55d> in <module>
     20 kernel = squared_exponential_kernel(alpha=1.0)
     21 sfs = SpecialDateFeatureSet(hdays, kernel)
---> 22 sfs(dfwide.index)

~/anaconda3/envs/btq/lib/python3.7/site-packages/gluonts/time_feature/holiday.py in __call__(self, dates)
    211                     ]
    212                 )
--> 213                 for feat_name in self.feature_names
    214             ]
    215         )

~/anaconda3/envs/btq/lib/python3.7/site-packages/gluonts/time_feature/holiday.py in <listcomp>(.0)
    211                     ]
    212                 )
--> 213                 for feat_name in self.feature_names
    214             ]
    215         )

~/anaconda3/envs/btq/lib/python3.7/site-packages/gluonts/time_feature/holiday.py in <listcomp>(.0)
    208                             SPECIAL_DATE_FEATURES[feat_name](index)
    209                         )
--> 210                         for index in dates
    211                     ]
    212                 )

~/anaconda3/envs/btq/lib/python3.7/site-packages/gluonts/time_feature/holiday.py in distance_to_day(index)
     43         assert (
     44             len(holiday_date) != 0
---> 45         ), f"No closest holiday for the date index {index} found."
     46         # It sometimes returns two dates if it is exactly half a year after the
     47         # holiday. In this case, the smaller distance (182 days) is returned.

AssertionError: No closest holiday for the date index 2018-07-18 00:00:00 found.```


Resolving patch:
diff --git a/src/gluonts/time_feature/holiday.py b/src/gluonts/time_feature/holiday.py
--- a/src/gluonts/time_feature/holiday.py
+++ b/src/gluonts/time_feature/holiday.py
@@ -16,6 +16,7 @@
 import numpy as np
 import pandas as pd
 from pandas.tseries.holiday import (
+    TH,
     SU,
     EasterMonday,
     GoodFriday,
@@ -29,9 +30,9 @@
 )
 from pandas.tseries.offsets import DateOffset, Day, Easter
 
-MAX_WINDOW = (
-    183
-)  # This is 183 to cover half a year (in both directions), also for leap years
+# This is 183 to cover half a year (in both directions), also for leap years
+# and half a week to cover holidays offset by a week
+MAX_WINDOW = 183 + 4
 
 
 def distance_to_holiday(holiday):
@@ -64,7 +65,18 @@ def distance_to_day(index):
 ChristmasEve = Holiday("Christmas", month=12, day=24)
 ChristmasDay = Holiday("Christmas", month=12, day=25)
 NewYearsEve = Holiday("New Years Eve", month=12, day=31)
-
+BlackFriday = Holiday(
+    "Black Friday",
+    month=11,
+    day=1,
+    offset=[pd.DateOffset(weekday=TH(4)), Day(1)],
+)
+CyberMonday = Holiday(
+    "Cyber Monday",
+    month=11,
+    day=1,
+    offset=[pd.DateOffset(weekday=TH(4)), Day(4)],
+)
 
 NEW_YEARS_DAY = "new_years_day"
 MARTIN_LUTHER_KING_DAY = "martin_luther_king_day"
@@ -82,7 +94,8 @@ def distance_to_day(index):
 CHRISTMAS_EVE = "christmas_eve"
 CHRISTMAS_DAY = "christmas_day"
 NEW_YEARS_EVE = "new_years_eve"
-
+BLACK_FRIDAY = "black_friday"
+CYBER_MONDAY = "cyber_monday"
 
 SPECIAL_DATE_FEATURES = {
     NEW_YEARS_DAY: distance_to_holiday(NewYearsDay),
@@ -101,6 +114,8 @@ def distance_to_day(index):
     CHRISTMAS_EVE: distance_to_holiday(ChristmasEve),
     CHRISTMAS_DAY: distance_to_holiday(ChristmasDay),
     NEW_YEARS_EVE: distance_to_holiday(NewYearsEve),
+    BLACK_FRIDAY: distance_to_holiday(BlackFriday),
+    CYBER_MONDAY: distance_to_holiday(CyberMonday),
 }
 
 


Test patch:
diff --git a/test/time_feature/test_holiday.py b/test/time_feature/test_holiday.py
--- a/test/time_feature/test_holiday.py
+++ b/test/time_feature/test_holiday.py
@@ -35,6 +35,8 @@
     SPECIAL_DATE_FEATURES,
     SUPERBOWL,
     THANKSGIVING,
+    BLACK_FRIDAY,
+    CYBER_MONDAY,
     SpecialDateFeatureSet,
     squared_exponential_kernel,
 )
@@ -98,6 +100,20 @@
     CHRISTMAS_EVE: ["2016-12-24", "2017-12-24", "2018-12-24", "2019-12-24"],
     CHRISTMAS_DAY: ["2016-12-25", "2017-12-25", "2018-12-25", "2019-12-25"],
     NEW_YEARS_EVE: ["2016-12-31", "2017-12-31", "2018-12-31", "2019-12-31"],
+    BLACK_FRIDAY: [
+        "2016-11-25",
+        "2017-11-24",
+        "2018-11-23",
+        "2019-11-29",
+        "2020-11-27",
+    ],
+    CYBER_MONDAY: [
+        "2016-11-28",
+        "2017-11-27",
+        "2018-11-26",
+        "2019-12-2",
+        "2020-11-30",
+    ],
 }