diff --git a/pts/feature/__init__.py b/pts/feature/__init__.py index e658636..b061e76 100644 --- a/pts/feature/__init__.py +++ b/pts/feature/__init__.py @@ -1,4 +1,11 @@ -from .holiday import SPECIAL_DATE_FEATURES, SpecialDateFeatureSet, CustomDateFeatureSet, CustomHolidayFeatureSet +from .holiday import ( + SPECIAL_DATE_FEATURES, + SpecialDateFeatureSet, + CustomDateFeatureSet, + CustomHolidayFeatureSet, + squared_exponential_kernel, + exponential_kernel, +) from .lag import get_lags_for_frequency, get_fourier_lags_for_frequency from .time_feature import ( DayOfMonth, diff --git a/pts/feature/holiday.py b/pts/feature/holiday.py index 4097ee3..14f262a 100644 --- a/pts/feature/holiday.py +++ b/pts/feature/holiday.py @@ -63,10 +63,7 @@ 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)], + "Cyber Monday", month=11, day=1, offset=[pd.DateOffset(weekday=TH(4)), Day(4)], ) @@ -151,7 +148,7 @@ class SpecialDateFeatureSet: Example use: - >>> from gluonts.time_feature.holiday import ( + >>> from pts.features import ( ... squared_exponential_kernel, ... SpecialDateFeatureSet, ... CHRISTMAS_DAY, @@ -219,13 +216,13 @@ class SpecialDateFeatureSet: for feat_name in self.feature_names ] ) - + + class CustomDateFeatureSet: """ - Implements calculation of holiday features. The CustomDateFeatureSet is - applied on a pandas Series with Datetimeindex and returns a 2D array of - the shape (len(dates), num_features), where num_features are the number - of holidays. + Implements calculation of date features. The CustomDateFeatureSet is + applied on a pandas Series with Datetimeindex and returns a 1D array of + the shape (1, len(date_indices)). Note that for lower than daily granularity the distance to the holiday is still computed on a per-day basis. @@ -233,26 +230,34 @@ class CustomDateFeatureSet: Example use: >>> import pandas as pd - >>> cfs = CustomDateFeatureSet([pd.to_datetime('20191129', format='%Y%m%d'), pd.to_datetime('20200101', format='%Y%m%d')], kernel) + >>> cfs = CustomDateFeatureSet([pd.to_datetime('20191129', format='%Y%m%d'), + ... pd.to_datetime('20200101', format='%Y%m%d')]) >>> date_indices = pd.date_range( ... start="2019-11-24", ... end="2019-12-31", ... freq='D' ... ) >>> cfs(date_indices) - array([[1., 0., 0., 0., 0., 0., 0., 0.], - [0., 1., 0., 0., 0., 0., 0., 0.]]) + array([[0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., + 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., + 0., 0., 0., 0., 0., 0.]]) Example use for using a squared exponential kernel: - >>> kernel = squared_exponential_kernel(alpha=1.0) - >>> cfs = CustomDateFeatureSet([pd.to_datetime('20191129', format='%Y%m%d'), pd.to_datetime('20200101', format='%Y%m%d')], kernel) + >>> kernel = squared_exponential_kernel(alpha=0.5) + >>> cfs = CustomDateFeatureSet([pd.to_datetime('20191129', format='%Y%m%d'), + ... pd.to_datetime('20200101', format='%Y%m%d')], kernel) >>> cfs(date_indices) - array([[1.00000000e+00, 3.67879441e-01, 1.83156389e-02, 1.23409804e-04, - 1.12535175e-07, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], - [3.67879441e-01, 1.00000000e+00, 3.67879441e-01, 1.83156389e-02, - 1.23409804e-04, 1.12535175e-07, 0.00000000e+00, 0.00000000e+00]]) - + array([[3.72665317e-06, 3.35462628e-04, 1.11089965e-02, 1.35335283e-01, + 6.06530660e-01, 1.00000000e+00, 6.06530660e-01, 1.35335283e-01, + 1.11089965e-02, 3.35462628e-04, 3.72665317e-06, 1.52299797e-08, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 1.52299797e-08, 3.72665317e-06, 3.35462628e-04, 1.11089965e-02, + 1.35335283e-01, 6.06530660e-01]]) """ def __init__( @@ -282,18 +287,22 @@ class CustomDateFeatureSet: dates Pandas series with Datetimeindex timestamps. """ - return np.vstack( - [ - np.hstack( - [ - self.kernel_function((index - ref_date).days) - for index in dates - ] - ) - for ref_date in self.reference_dates - ] + return ( + np.vstack( + [ + np.hstack( + [ + self.kernel_function((index - ref_date).days) + for index in dates + ] + ) + for ref_date in self.reference_dates + ] + ) + .sum(0, keepdims=True) ) - + + class CustomHolidayFeatureSet: """ Implements calculation of holiday features. The CustomHolidayFeatureSet is @@ -306,7 +315,7 @@ class CustomHolidayFeatureSet: Example use: - >>> from gluonts.time_feature.holiday import ( + >>> from pts.features import ( ... squared_exponential_kernel, ... SpecialDateFeatureSet, ... CHRISTMAS_DAY, @@ -373,4 +382,5 @@ class CustomHolidayFeatureSet: ) for custom_holiday in self.custom_holidays ] - ) + ) + diff --git a/test/feature/test_holiday.py b/test/feature/test_holiday.py index 6c582bb..33dc62b 100644 --- a/test/feature/test_holiday.py +++ b/test/feature/test_holiday.py @@ -43,7 +43,7 @@ from pts.feature.holiday import ( squared_exponential_kernel, exponential_kernel, CustomDateFeatureSet, - CustomHolidayFeatureSet + CustomHolidayFeatureSet, ) test_dates = { @@ -105,7 +105,13 @@ test_dates = { 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"], + 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"], } @@ -258,10 +264,14 @@ def test_special_date_feature_set_daily_squared_exponential(): sfs = SpecialDateFeatureSet([CHRISTMAS_EVE, CHRISTMAS_DAY], squared_exp_kernel) computed_features = sfs(date_indices) np.testing.assert_almost_equal(computed_features, reference_features, decimal=6) - + + def test_custom_date_feature_set(): - ref_dates = [pd.to_datetime('20191129', format='%Y%m%d'), pd.to_datetime('20200101', format='%Y%m%d')] + ref_dates = [ + pd.to_datetime("20191129", format="%Y%m%d"), + pd.to_datetime("20200101", format="%Y%m%d"), + ] kernel = exponential_kernel(alpha=1.0) @@ -269,16 +279,22 @@ def test_custom_date_feature_set(): sfs = SpecialDateFeatureSet([BLACK_FRIDAY, NEW_YEARS_DAY], kernel) date_indices = pd.date_range( - start=pd.to_datetime('20191101', format='%Y%m%d'), - end=pd.to_datetime('20200131', format='%Y%m%d'), - freq='D') + start=pd.to_datetime("20191101", format="%Y%m%d"), + end=pd.to_datetime("20200131", format="%Y%m%d"), + freq="D", + ) + + assert ( + np.sum(cfs(date_indices) - sfs(date_indices).sum(0, keepdims=True)) == 0 + ), "Features don't match" - assert(np.sum(cfs(date_indices) - sfs(date_indices)) == 0), "Features don't match" - def test_custom_holiday_feature_set(): - custom_holidays = [Holiday("New Years Day", month=1, day=1), Holiday("Christmas Day", month=12, day=25)] + custom_holidays = [ + Holiday("New Years Day", month=1, day=1), + Holiday("Christmas Day", month=12, day=25), + ] kernel = exponential_kernel(alpha=1.0) @@ -286,8 +302,9 @@ def test_custom_holiday_feature_set(): sfs = SpecialDateFeatureSet([NEW_YEARS_DAY, CHRISTMAS_DAY], kernel) date_indices = pd.date_range( - start=pd.to_datetime('20191101', format='%Y%m%d'), - end=pd.to_datetime('20200131', format='%Y%m%d'), - freq='D') + start=pd.to_datetime("20191101", format="%Y%m%d"), + end=pd.to_datetime("20200131", format="%Y%m%d"), + freq="D", + ) - assert(np.sum(cfs(date_indices) - sfs(date_indices)) == 0), "Features don't match" + assert np.sum(cfs(date_indices) - sfs(date_indices)) == 0, "Features don't match"