@@ -44,6 +44,7 @@ def __init__(
4444 activation = 'ReLU' ,
4545 scaler_type = 'robust' ,
4646 model_args = {},
47+ point_quantile = None ,
4748 ** kwargs ,
4849 ):
4950 ModelObject .__init__ (
@@ -66,11 +67,18 @@ def __init__(
6667 self .activation = activation
6768 self .scaler_type = scaler_type
6869 self .model_args = model_args
70+ self .point_quantile = point_quantile
6971 self .forecast_length = forecast_length
7072 self .df_train = None
7173 self .static_regressor = None
7274
73- def fit (self , df , future_regressor = None , static_regressor = None ):
75+ def fit (
76+ self ,
77+ df ,
78+ future_regressor = None ,
79+ static_regressor = None ,
80+ regressor_per_series = None ,
81+ ):
7482 """Train algorithm given data supplied.
7583
7684 Args:
@@ -86,6 +94,9 @@ def fit(self, df, future_regressor=None, static_regressor=None):
8694 self .static_regressor = static_regressor
8795 if isinstance (self .static_regressor , pd .DataFrame ):
8896 static_cols = static_regressor .columns .tolist ()
97+ if regressor_per_series is not None :
98+ if not isinstance (regressor_per_series , dict ):
99+ raise ValueError ("regressor_per_series in incorrect format" )
89100
90101 from neuralforecast import NeuralForecast
91102 from neuralforecast .losses .pytorch import (
@@ -121,7 +132,12 @@ def fit(self, df, future_regressor=None, static_regressor=None):
121132 logging .getLogger ("pytorch_lightning" ).setLevel (logging .CRITICAL )
122133 loss = self .loss
123134 if loss == "MQLoss" :
124- loss = MQLoss (level = levels )
135+ if self .point_quantile is None :
136+ loss = MQLoss (level = levels )
137+ else :
138+ div = (1 - self .prediction_interval ) / 2
139+ quantiles = [div , 1 - div , self .point_quantile ]
140+ loss = MQLoss (quantiles = quantiles )
125141 elif loss == "Poisson" :
126142 loss = DistributionLoss (
127143 distribution = 'Poisson' , level = levels , return_params = False
@@ -187,7 +203,10 @@ def fit(self, df, future_regressor=None, static_regressor=None):
187203 models = self .model
188204 model_args = self .model_args
189205 if self .regression_type in ['User' , 'user' , True ]:
190- self .base_args ["futr_exog_list" ] = future_regressor .columns .tolist ()
206+ regr_cols = future_regressor .columns .tolist ()
207+ if regressor_per_series is not None :
208+ regr_cols + next (iter (regressor_per_series .values ())).columns .tolist ()
209+ self .base_args ["futr_exog_list" ] = regr_cols
191210 self .base_args ['stat_exog_list' ] = static_cols
192211
193212 if isinstance (models , list ):
@@ -221,6 +240,17 @@ def fit(self, df, future_regressor=None, static_regressor=None):
221240 silly_format = silly_format .merge (
222241 future_regressor , left_on = 'ds' , right_index = True
223242 )
243+ if regressor_per_series is not None :
244+ full_df = []
245+ for key , value in regressor_per_series .items ():
246+ local_copy = value .copy ().reindex (df .index )
247+ local_copy .index .name = 'ds'
248+ local_copy = local_copy .reset_index ()
249+ local_copy ['unique_id' ] = str (key )
250+ full_df .append (local_copy )
251+ silly_format = silly_format .merge (
252+ pd .concat (full_df ), on = ['unique_id' , 'ds' ], how = 'left'
253+ ).fillna (0 )
224254 self .nf = NeuralForecast (models = models , freq = freq )
225255 if self .static_regressor is None :
226256 self .nf .fit (df = silly_format )
@@ -234,7 +264,11 @@ def fit(self, df, future_regressor=None, static_regressor=None):
234264 return self
235265
236266 def predict (
237- self , forecast_length = None , future_regressor = None , just_point_forecast = False
267+ self ,
268+ forecast_length = None ,
269+ future_regressor = None ,
270+ just_point_forecast = False ,
271+ regressor_per_series = None ,
238272 ):
239273 predictStartTime = datetime .datetime .now ()
240274 if self .regression_type in ['User' , 'user' , True ]:
@@ -249,6 +283,17 @@ def predict(
249283 future_regressor , left_index = True , right_index = True
250284 )
251285 futr_df = futr_df .reset_index (names = 'ds' )
286+ if regressor_per_series is not None :
287+ full_df = []
288+ for key , value in regressor_per_series .items ():
289+ local_copy = value .copy ().reindex (index )
290+ local_copy .index .name = 'ds'
291+ local_copy = local_copy .reset_index ()
292+ local_copy ['unique_id' ] = str (key )
293+ full_df .append (local_copy )
294+ futr_df = futr_df .merge (
295+ pd .concat (full_df ), on = ['unique_id' , 'ds' ], how = 'left'
296+ ).fillna (0 )
252297 self .futr_df = futr_df
253298 long_forecast = self .nf .predict (futr_df = futr_df )
254299 else :
@@ -259,6 +304,9 @@ def predict(
259304 target_col = long_forecast .columns [- 1 ]
260305 else :
261306 target_col = target_col [0 ]
307+ if self .point_quantile is not None :
308+ # print(long_forecast.columns)
309+ target_col = long_forecast .columns [- 1 ]
262310 forecast = long_forecast .reset_index ().pivot_table (
263311 index = 'ds' , columns = 'unique_id' , values = target_col
264312 )[self .column_names ]
@@ -274,10 +322,12 @@ def predict(
274322 )
275323 else :
276324 target_col = [x for x in long_forecast .columns if "hi-" in x ][0 ]
325+ # print(f"upper target col: {target_col}")
277326 upper_forecast = long_forecast .reset_index ().pivot_table (
278327 index = 'ds' , columns = 'unique_id' , values = target_col
279328 )[self .column_names ]
280329 target_col = [x for x in long_forecast .columns if "lo-" in x ][0 ]
330+ # print(f"lower target col {target_col}")
281331 lower_forecast = long_forecast .reset_index ().pivot_table (
282332 index = 'ds' , columns = 'unique_id' , values = target_col
283333 )[self .column_names ]
@@ -311,6 +361,16 @@ def get_new_params(self, method: str = 'random'):
311361 regression_type_choice = random .choices ([None , "User" ], weights = [0.8 , 0.2 ])[
312362 0
313363 ]
364+ if "deep" in method :
365+ max_steps = random .choices (
366+ [40 , 80 , 100 , 1000 , 5000 , 10000 , 50000 ],
367+ [0.2 , 0.2 , 0.2 , 0.1 , 0.05 , 0.05 , 0.01 ],
368+ )[0 ]
369+ else :
370+ max_steps = random .choices (
371+ [40 , 80 , 100 , 1000 , 5000 ],
372+ [0.2 , 0.2 , 0.2 , 0.05 , 0.03 ],
373+ )[0 ]
314374 activation = random .choices (
315375 ['ReLU' , 'Softplus' , 'Tanh' , 'SELU' , 'LeakyReLU' , 'PReLU' , 'Sigmoid' ],
316376 [0.5 , 0.1 , 0.1 , 0.1 , 0.1 , 0.1 , 0.1 ],
@@ -328,8 +388,13 @@ def get_new_params(self, method: str = 'random'):
328388 "SMAPE" ,
329389 "StudentT" ,
330390 ],
331- [0.3 , 0.1 , 0.01 , 0.1 , 0.1 , 0.01 , 0.1 , 0.1 , 0.1 , 0.01 ],
391+ [0.5 , 0.1 , 0.01 , 0.1 , 0.1 , 0.01 , 0.1 , 0.1 , 0.1 , 0.01 ],
332392 )[0 ]
393+ point_quantile = None
394+ if loss == "MQLoss" :
395+ point_quantile = random .choices (
396+ [None , 0.35 , 0.45 , 0.55 , 0.65 , 0.7 ], [0.5 , 0.1 , 0.1 , 0.1 , 0.1 , 0.1 ]
397+ )[0 ]
333398 if models == "TFT" :
334399 model_args = {
335400 "n_head" : random .choice ([2 , 4 ]),
@@ -368,14 +433,12 @@ def get_new_params(self, method: str = 'random'):
368433 'learning_rate' : random .choices (
369434 [0.001 , 0.1 , 0.01 , 0.0003 , 0.00001 ], [0.4 , 0.1 , 0.1 , 0.1 , 0.1 ]
370435 )[0 ],
371- "max_steps" : random .choices (
372- [40 , 80 , 100 , 1000 ],
373- [0.2 , 0.2 , 0.2 , 0.05 ],
374- )[0 ],
436+ "max_steps" : max_steps ,
375437 'input_size' : random .choices (
376438 [10 , 28 , "2ForecastLength" , "3ForecastLength" ], [0.2 , 0.2 , 0.2 , 0.2 ]
377439 )[0 ],
378440 # "early_stop_patience_steps": random.choice([1, 3, 5]),
441+ "point_quantile" : point_quantile ,
379442 "model_args" : model_args ,
380443 'regression_type' : regression_type_choice ,
381444 }
@@ -390,13 +453,14 @@ def get_params(self):
390453 'learning_rate' : self .learning_rate ,
391454 "max_steps" : self .max_steps ,
392455 'input_size' : self .input_size ,
456+ 'point_quantile' : self .point_quantile ,
393457 "model_args" : self .model_args ,
394458 'regression_type' : self .regression_type ,
395459 }
396460
397461
398462if False :
399- from autots .models .neural_forecast import NeuralForecast
463+ # from autots.models.neural_forecast import NeuralForecast
400464 from autots import load_daily , create_regressor , infer_frequency
401465
402466 df = load_daily (long = False )
0 commit comments