oracle-ads 2.11.6__py3-none-any.whl → 2.11.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. ads/catalog/model.py +3 -3
  2. ads/catalog/notebook.py +3 -3
  3. ads/catalog/project.py +2 -2
  4. ads/catalog/summary.py +2 -4
  5. ads/cli.py +2 -1
  6. ads/common/serializer.py +1 -1
  7. ads/data_labeling/metadata.py +2 -2
  8. ads/dataset/dataset.py +3 -5
  9. ads/dataset/factory.py +2 -3
  10. ads/dataset/label_encoder.py +1 -1
  11. ads/dataset/sampled_dataset.py +3 -5
  12. ads/jobs/ads_job.py +26 -2
  13. ads/jobs/builders/infrastructure/dsc_job.py +20 -7
  14. ads/model/model_artifact_boilerplate/artifact_introspection_test/model_artifact_validate.py +1 -1
  15. ads/opctl/operator/lowcode/anomaly/model/anomaly_dataset.py +8 -15
  16. ads/opctl/operator/lowcode/anomaly/model/automlx.py +2 -1
  17. ads/opctl/operator/lowcode/anomaly/model/base_model.py +2 -2
  18. ads/opctl/operator/lowcode/anomaly/operator_config.py +18 -1
  19. ads/opctl/operator/lowcode/anomaly/schema.yaml +16 -4
  20. ads/opctl/operator/lowcode/common/data.py +16 -2
  21. ads/opctl/operator/lowcode/common/transformations.py +48 -14
  22. ads/opctl/operator/lowcode/forecast/environment.yaml +1 -0
  23. ads/opctl/operator/lowcode/forecast/model/arima.py +21 -12
  24. ads/opctl/operator/lowcode/forecast/model/automlx.py +79 -72
  25. ads/opctl/operator/lowcode/forecast/model/autots.py +182 -164
  26. ads/opctl/operator/lowcode/forecast/model/base_model.py +59 -41
  27. ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +47 -47
  28. ads/opctl/operator/lowcode/forecast/model/prophet.py +48 -48
  29. ads/opctl/operator/lowcode/forecast/operator_config.py +18 -2
  30. ads/opctl/operator/lowcode/forecast/schema.yaml +20 -4
  31. ads/opctl/operator/lowcode/forecast/utils.py +4 -0
  32. ads/pipeline/ads_pipeline_step.py +11 -12
  33. {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/METADATA +4 -3
  34. {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/RECORD +37 -37
  35. {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/LICENSE.txt +0 -0
  36. {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/WHEEL +0 -0
  37. {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/entry_points.txt +0 -0
@@ -54,145 +54,154 @@ class AutoTSOperatorModel(ForecastOperatorBaseModel):
54
54
  target_column=self.original_target_column,
55
55
  dt_column=self.spec.datetime_column.name,
56
56
  )
57
- model = self.loaded_models if self.loaded_models is not None else None
58
- if model is None:
59
- # Initialize the AutoTS model with specified parameters
60
- model = AutoTS(
61
- forecast_length=self.spec.horizon,
62
- frequency=self.spec.model_kwargs.get(
63
- "frequency", "infer"
64
- ), # TODO: Use datasets.get_datetime_frequency ?
65
- prediction_interval=self.spec.confidence_interval_width,
66
- max_generations=self.spec.model_kwargs.get(
67
- "max_generations", AUTOTS_MAX_GENERATION
68
- ),
69
- no_negatives=self.spec.model_kwargs.get("no_negatives", False),
70
- constraint=self.spec.model_kwargs.get("constraint", None),
71
- ensemble=self.spec.model_kwargs.get("ensemble", "auto"),
72
- initial_template=self.spec.model_kwargs.get(
73
- "initial_template", "General+Random"
74
- ),
75
- random_seed=self.spec.model_kwargs.get("random_seed", 2022),
76
- holiday_country=self.spec.model_kwargs.get("holiday_country", "US"),
77
- subset=self.spec.model_kwargs.get("subset", None),
78
- aggfunc=self.spec.model_kwargs.get("aggfunc", "first"),
79
- na_tolerance=self.spec.model_kwargs.get("na_tolerance", 1),
80
- drop_most_recent=self.spec.model_kwargs.get("drop_most_recent", 0),
81
- drop_data_older_than_periods=self.spec.model_kwargs.get(
82
- "drop_data_older_than_periods", None
83
- ),
84
- model_list=self.spec.model_kwargs.get("model_list", "fast_parallel"),
85
- transformer_list=self.spec.model_kwargs.get("transformer_list", "auto"),
86
- transformer_max_depth=self.spec.model_kwargs.get(
87
- "transformer_max_depth", 6
88
- ),
89
- models_mode=self.spec.model_kwargs.get("models_mode", "random"),
90
- num_validations=self.spec.model_kwargs.get("num_validations", "auto"),
91
- models_to_validate=self.spec.model_kwargs.get(
92
- "models_to_validate", AUTOTS_MODELS_TO_VALIDATE
93
- ),
94
- max_per_model_class=self.spec.model_kwargs.get(
95
- "max_per_model_class", None
96
- ),
97
- validation_method=self.spec.model_kwargs.get(
98
- "validation_method", "backwards"
99
- ),
100
- min_allowed_train_percent=self.spec.model_kwargs.get(
101
- "min_allowed_train_percent", 0.5
102
- ),
103
- remove_leading_zeroes=self.spec.model_kwargs.get(
104
- "remove_leading_zeroes", False
105
- ),
106
- prefill_na=self.spec.model_kwargs.get("prefill_na", None),
107
- introduce_na=self.spec.model_kwargs.get("introduce_na", None),
108
- preclean=self.spec.model_kwargs.get("preclean", None),
109
- model_interrupt=self.spec.model_kwargs.get("model_interrupt", True),
110
- generation_timeout=self.spec.model_kwargs.get(
111
- "generation_timeout", None
112
- ),
113
- current_model_file=self.spec.model_kwargs.get(
114
- "current_model_file", None
115
- ),
116
- verbose=-1 if logger.level > 40 else 1,
117
- n_jobs=self.spec.model_kwargs.get("n_jobs", -1),
118
- )
119
-
120
- full_data_indexed = self.datasets.get_data_multi_indexed()
121
-
122
- dates = full_data_indexed.index.get_level_values(0).unique().tolist()
123
- train_idx = dates[: -self.spec.horizon]
124
-
125
- df_train = full_data_indexed[
126
- full_data_indexed.index.get_level_values(0).isin(train_idx)
127
- ][[self.original_target_column]].reset_index()
128
-
129
- # Future regressors need to be in wide format - (num_unique_dates x (num_unique_series x num_unique_cols))
130
- additional_regressors = list(
131
- set(full_data_indexed.columns) - {self.original_target_column}
132
- )
133
- if len(additional_regressors) > 1:
134
- future_regressor = full_data_indexed.reset_index().pivot(
135
- index=self.spec.datetime_column.name,
136
- columns=ForecastOutputColumns.SERIES,
137
- values=additional_regressors,
138
- )
139
- future_reg = future_regressor[: -self.spec.horizon]
140
- regr_fcst = future_regressor[-self.spec.horizon :]
141
- else:
142
- future_reg = None
143
- regr_fcst = None
144
-
145
- if self.loaded_models is None:
146
- model = model.fit(
147
- df_train,
148
- future_regressor=future_reg,
149
- date_col=self.spec.datetime_column.name,
150
- value_col=self.original_target_column,
151
- id_col=ForecastOutputColumns.SERIES,
152
- )
153
- # Store the trained model and generate forecasts
154
- self.models = copy.deepcopy(model)
155
- else:
156
- self.models = self.loaded_models
157
-
158
- self.outputs = model.predict(future_regressor=regr_fcst)
159
- logger.debug("===========Forecast Generated===========")
160
-
161
- hist_df = model.back_forecast().forecast
162
-
163
- params = vars(model).copy()
164
- for param in [
165
- "ens_copy",
166
- "df_wide_numeric",
167
- "future_regressor_train",
168
- "initial_results",
169
- "score_per_series",
170
- "validation_results",
171
- "validation_train_indexes",
172
- "validation_test_indexes",
173
- "validation_indexes",
174
- "best_model",
175
- ]:
176
- if param in params:
177
- params.pop(param)
178
-
179
- for s_id in self.datasets.list_series_ids():
180
- self.forecast_output.init_series_output(
181
- series_id=s_id, data_at_series=self.datasets.get_data_at_series(s_id)
182
- )
183
-
184
- self.forecast_output.populate_series_output(
185
- series_id=s_id,
186
- fit_val=hist_df[s_id].values,
187
- forecast_val=self.outputs.forecast[s_id].values,
188
- upper_bound=self.outputs.upper_forecast[s_id].values,
189
- lower_bound=self.outputs.lower_forecast[s_id].values,
57
+ try:
58
+ model = self.loaded_models if self.loaded_models is not None else None
59
+ if model is None:
60
+ # Initialize the AutoTS model with specified parameters
61
+ model = AutoTS(
62
+ forecast_length=self.spec.horizon,
63
+ frequency=self.spec.model_kwargs.get(
64
+ "frequency", "infer"
65
+ ), # TODO: Use datasets.get_datetime_frequency ?
66
+ prediction_interval=self.spec.confidence_interval_width,
67
+ max_generations=self.spec.model_kwargs.get(
68
+ "max_generations", AUTOTS_MAX_GENERATION
69
+ ),
70
+ no_negatives=self.spec.model_kwargs.get("no_negatives", False),
71
+ constraint=self.spec.model_kwargs.get("constraint", None),
72
+ ensemble=self.spec.model_kwargs.get("ensemble", "auto"),
73
+ initial_template=self.spec.model_kwargs.get(
74
+ "initial_template", "General+Random"
75
+ ),
76
+ random_seed=self.spec.model_kwargs.get("random_seed", 2022),
77
+ holiday_country=self.spec.model_kwargs.get("holiday_country", "US"),
78
+ subset=self.spec.model_kwargs.get("subset", None),
79
+ aggfunc=self.spec.model_kwargs.get("aggfunc", "first"),
80
+ na_tolerance=self.spec.model_kwargs.get("na_tolerance", 1),
81
+ drop_most_recent=self.spec.model_kwargs.get("drop_most_recent", 0),
82
+ drop_data_older_than_periods=self.spec.model_kwargs.get(
83
+ "drop_data_older_than_periods", None
84
+ ),
85
+ model_list=self.spec.model_kwargs.get("model_list", "fast_parallel"),
86
+ transformer_list=self.spec.model_kwargs.get("transformer_list", "auto"),
87
+ transformer_max_depth=self.spec.model_kwargs.get(
88
+ "transformer_max_depth", 6
89
+ ),
90
+ models_mode=self.spec.model_kwargs.get("models_mode", "random"),
91
+ num_validations=self.spec.model_kwargs.get("num_validations", "auto"),
92
+ models_to_validate=self.spec.model_kwargs.get(
93
+ "models_to_validate", AUTOTS_MODELS_TO_VALIDATE
94
+ ),
95
+ max_per_model_class=self.spec.model_kwargs.get(
96
+ "max_per_model_class", None
97
+ ),
98
+ validation_method=self.spec.model_kwargs.get(
99
+ "validation_method", "backwards"
100
+ ),
101
+ min_allowed_train_percent=self.spec.model_kwargs.get(
102
+ "min_allowed_train_percent", 0.5
103
+ ),
104
+ remove_leading_zeroes=self.spec.model_kwargs.get(
105
+ "remove_leading_zeroes", False
106
+ ),
107
+ prefill_na=self.spec.model_kwargs.get("prefill_na", None),
108
+ introduce_na=self.spec.model_kwargs.get("introduce_na", None),
109
+ preclean=self.spec.model_kwargs.get("preclean", None),
110
+ model_interrupt=self.spec.model_kwargs.get("model_interrupt", True),
111
+ generation_timeout=self.spec.model_kwargs.get(
112
+ "generation_timeout", None
113
+ ),
114
+ current_model_file=self.spec.model_kwargs.get(
115
+ "current_model_file", None
116
+ ),
117
+ verbose=-1 if logger.level > 40 else 1,
118
+ n_jobs=self.spec.model_kwargs.get("n_jobs", -1),
119
+ )
120
+
121
+ full_data_indexed = self.datasets.get_data_multi_indexed()
122
+
123
+ dates = full_data_indexed.index.get_level_values(0).unique().tolist()
124
+ train_idx = dates[: -self.spec.horizon]
125
+
126
+ df_train = full_data_indexed[
127
+ full_data_indexed.index.get_level_values(0).isin(train_idx)
128
+ ][[self.original_target_column]].reset_index()
129
+
130
+ # Future regressors need to be in wide format - (num_unique_dates x (num_unique_series x num_unique_cols))
131
+ additional_regressors = list(
132
+ set(full_data_indexed.columns) - {self.original_target_column}
190
133
  )
191
-
192
- self.model_parameters[s_id] = {
193
- "framework": SupportedModels.AutoTS,
194
- **params,
195
- }
134
+ if len(additional_regressors) > 1:
135
+ future_regressor = full_data_indexed.reset_index().pivot(
136
+ index=self.spec.datetime_column.name,
137
+ columns=ForecastOutputColumns.SERIES,
138
+ values=additional_regressors,
139
+ )
140
+ future_reg = future_regressor[: -self.spec.horizon]
141
+ regr_fcst = future_regressor[-self.spec.horizon:]
142
+ else:
143
+ future_reg = None
144
+ regr_fcst = None
145
+
146
+ for s_id in self.datasets.list_series_ids():
147
+ self.forecast_output.init_series_output(
148
+ series_id=s_id, data_at_series=self.datasets.get_data_at_series(s_id)
149
+ )
150
+
151
+ if self.loaded_models is None:
152
+ model = model.fit(
153
+ df_train,
154
+ future_regressor=future_reg,
155
+ date_col=self.spec.datetime_column.name,
156
+ value_col=self.original_target_column,
157
+ id_col=ForecastOutputColumns.SERIES,
158
+ )
159
+ # Store the trained model and generate forecasts
160
+ self.models = copy.deepcopy(model)
161
+ else:
162
+ self.models = self.loaded_models
163
+
164
+ self.outputs = model.predict(future_regressor=regr_fcst)
165
+ logger.debug("===========Forecast Generated===========")
166
+
167
+ hist_df = model.back_forecast().forecast
168
+
169
+ params = vars(model).copy()
170
+ for param in [
171
+ "ens_copy",
172
+ "df_wide_numeric",
173
+ "future_regressor_train",
174
+ "initial_results",
175
+ "score_per_series",
176
+ "validation_results",
177
+ "validation_train_indexes",
178
+ "validation_test_indexes",
179
+ "validation_indexes",
180
+ "best_model",
181
+ ]:
182
+ if param in params:
183
+ params.pop(param)
184
+
185
+ for s_id in self.datasets.list_series_ids():
186
+ self.forecast_output.populate_series_output(
187
+ series_id=s_id,
188
+ fit_val=hist_df[s_id].values,
189
+ forecast_val=self.outputs.forecast[s_id].values,
190
+ upper_bound=self.outputs.upper_forecast[s_id].values,
191
+ lower_bound=self.outputs.lower_forecast[s_id].values,
192
+ )
193
+
194
+ self.model_parameters[s_id] = {
195
+ "framework": SupportedModels.AutoTS,
196
+ **params,
197
+ }
198
+
199
+ except Exception as e:
200
+ for s_id in self.datasets.list_series_ids():
201
+ self.errors_dict[s_id] = {
202
+ "model_name": self.spec.model,
203
+ "error": str(e),
204
+ }
196
205
 
197
206
  logger.debug("===========Done===========")
198
207
 
@@ -213,29 +222,30 @@ class AutoTSOperatorModel(ForecastOperatorBaseModel):
213
222
  - ci_col_names (list): A list of column names for confidence intervals.
214
223
  """
215
224
  import datapane as dp
216
-
217
- # Section 1: Forecast Overview
218
- sec1_text = dp.Text(
219
- "## Forecast Overview \n"
220
- "These plots show your forecast in the context of historical data."
221
- )
222
- sec_1 = _select_plot_list(
223
- lambda s_id: self.outputs.plot(self.models.df_wide_numeric, series=s_id),
224
- self.datasets.list_series_ids(),
225
- )
226
-
227
- # Section 2: AutoTS Model Parameters
228
- sec2_text = dp.Text(f"## AutoTS Model Parameters")
229
- try:
230
- sec2 = dp.Code(
231
- code=yaml.dump(list(self.models.best_model.T.to_dict().values())[0]),
232
- language="yaml",
225
+ all_sections = []
226
+ if self.models:
227
+ # Section 1: Forecast Overview
228
+ sec1_text = dp.Text(
229
+ "## Forecast Overview \n"
230
+ "These plots show your forecast in the context of historical data."
233
231
  )
232
+ sec_1 = _select_plot_list(
233
+ lambda s_id: self.outputs.plot(self.models.df_wide_numeric, series=s_id),
234
+ self.datasets.list_series_ids(),
235
+ )
236
+
237
+ # Section 2: AutoTS Model Parameters
238
+ sec2_text = dp.Text(f"## AutoTS Model Parameters")
239
+ try:
240
+ sec2 = dp.Code(
241
+ code=yaml.dump(list(self.models.best_model.T.to_dict().values())[0]),
242
+ language="yaml",
243
+ )
234
244
 
235
- except KeyError as ke:
236
- logger.warn(f"Issue generating Model Parameters Table Section. Skipping")
237
- sec2 = dp.Text(f"Error generating model parameters.")
238
- all_sections = [sec1_text, sec_1, sec2_text, sec2]
245
+ except KeyError as ke:
246
+ logger.warn(f"Issue generating Model Parameters Table Section. Skipping")
247
+ sec2 = dp.Text(f"Error generating model parameters.")
248
+ all_sections = [sec1_text, sec_1, sec2_text, sec2]
239
249
 
240
250
  if self.spec.generate_explanations:
241
251
  logger.warn(f"Explanations not yet supported for the AutoTS Module")
@@ -266,9 +276,17 @@ class AutoTSOperatorModel(ForecastOperatorBaseModel):
266
276
  r2 -4.60E-06
267
277
  Explained Variance 0.002177087
268
278
  """
269
- mapes = pd.DataFrame(self.models.best_model_per_series_mape()).T
270
- scores = pd.DataFrame(
271
- self.models.best_model_per_series_score(), columns=["AutoTS Score"]
272
- ).T
273
- df = pd.concat([mapes, scores])
279
+ df = pd.DataFrame()
280
+ try:
281
+ mapes = pd.DataFrame(self.models.best_model_per_series_mape()).T
282
+ scores = pd.DataFrame(
283
+ self.models.best_model_per_series_score(), columns=["AutoTS Score"]
284
+ ).T
285
+ df = pd.concat([mapes, scores])
286
+ except Exception as e:
287
+ logger.debug(
288
+ f"Failed to generate training metrics"
289
+ )
290
+ logger.debug(f"Received Error Statement: {e}")
291
+
274
292
  return df
@@ -88,7 +88,7 @@ class ForecastOperatorBaseModel(ABC):
88
88
  self.formatted_local_explanation = None
89
89
 
90
90
  self.forecast_col_name = "yhat"
91
- self.perform_tuning = self.spec.tuning != None
91
+ self.perform_tuning = (self.spec.tuning != None) and (self.spec.tuning.n_trials != None)
92
92
 
93
93
  def generate_report(self):
94
94
  """Generates the forecasting report."""
@@ -270,17 +270,20 @@ class ForecastOperatorBaseModel(ABC):
270
270
  sec9 = dp.DataTable(self.eval_metrics)
271
271
  train_metrics_sections = [sec9_text, sec9]
272
272
 
273
- forecast_text = dp.Text(f"## Forecasted Data Overlaying Historical")
274
- forecast_sec = get_forecast_plots(
275
- self.forecast_output,
276
- horizon=self.spec.horizon,
277
- test_data=test_data,
278
- ci_interval_width=self.spec.confidence_interval_width,
279
- )
280
- if series_name is not None and len(self.datasets.list_series_ids()) > 1:
281
- forecast_plots = [forecast_text, series_subtext, forecast_sec]
282
- else:
283
- forecast_plots = [forecast_text, forecast_sec]
273
+
274
+ forecast_plots = []
275
+ if len(self.forecast_output.list_series_ids()) > 0:
276
+ forecast_text = dp.Text(f"## Forecasted Data Overlaying Historical")
277
+ forecast_sec = get_forecast_plots(
278
+ self.forecast_output,
279
+ horizon=self.spec.horizon,
280
+ test_data=test_data,
281
+ ci_interval_width=self.spec.confidence_interval_width,
282
+ )
283
+ if series_name is not None and len(self.datasets.list_series_ids()) > 1:
284
+ forecast_plots = [forecast_text, series_subtext, forecast_sec]
285
+ else:
286
+ forecast_plots = [forecast_text, forecast_sec]
284
287
 
285
288
  yaml_appendix_title = dp.Text(f"## Reference: YAML File")
286
289
  yaml_appendix = dp.Code(code=self.config.to_yaml(), language="yaml")
@@ -557,13 +560,14 @@ class ForecastOperatorBaseModel(ABC):
557
560
  )
558
561
  if self.errors_dict:
559
562
  write_data(
560
- data=pd.DataFrame(self.errors_dict.items(), columns=["model", "error"]),
563
+ data=pd.DataFrame.from_dict(self.errors_dict),
561
564
  filename=os.path.join(
562
565
  unique_output_dir, self.spec.errors_dict_filename
563
566
  ),
564
- format="csv",
567
+ format="json",
565
568
  storage_options=storage_options,
566
569
  index=True,
570
+ indent=4,
567
571
  )
568
572
  else:
569
573
  logger.info(f"All modeling completed successfully.")
@@ -650,38 +654,47 @@ class ForecastOperatorBaseModel(ABC):
650
654
  for s_id, data_i in self.datasets.get_data_by_series(
651
655
  include_horizon=False
652
656
  ).items():
653
- explain_predict_fn = self.get_explain_predict_fn(series_id=s_id)
657
+ if s_id in self.models:
654
658
 
655
- data_trimmed = data_i.tail(max(int(len(data_i) * ratio), 5)).reset_index(
656
- drop=True
657
- )
658
- data_trimmed[datetime_col_name] = data_trimmed[datetime_col_name].apply(
659
- lambda x: x.timestamp()
660
- )
661
-
662
- kernel_explnr = PermutationExplainer(
663
- model=explain_predict_fn, masker=data_trimmed
664
- )
665
- kernel_explnr_vals = kernel_explnr.shap_values(data_trimmed)
659
+ explain_predict_fn = self.get_explain_predict_fn(series_id=s_id)
660
+ data_trimmed = data_i.tail(max(int(len(data_i) * ratio), 5)).reset_index(
661
+ drop=True
662
+ )
663
+ data_trimmed[datetime_col_name] = data_trimmed[datetime_col_name].apply(
664
+ lambda x: x.timestamp()
665
+ )
666
666
 
667
- exp_end_time = time.time()
668
- global_ex_time = global_ex_time + exp_end_time - exp_start_time
667
+ # Explainer fails when boolean columns are passed
669
668
 
670
- self.local_explainer(
671
- kernel_explnr, series_id=s_id, datetime_col_name=datetime_col_name
672
- )
673
- local_ex_time = local_ex_time + time.time() - exp_end_time
669
+ _, data_trimmed_encoded = _label_encode_dataframe(
670
+ data_trimmed, no_encode={datetime_col_name, self.original_target_column}
671
+ )
674
672
 
675
- if not len(kernel_explnr_vals):
676
- logger.warn(
677
- f"No explanations generated. Ensure that additional data has been provided."
673
+ kernel_explnr = PermutationExplainer(
674
+ model=explain_predict_fn, masker=data_trimmed_encoded
678
675
  )
679
- else:
680
- self.global_explanation[s_id] = dict(
681
- zip(
682
- data_trimmed.columns[1:],
683
- np.average(np.absolute(kernel_explnr_vals[:, 1:]), axis=0),
676
+ kernel_explnr_vals = kernel_explnr.shap_values(data_trimmed_encoded)
677
+ exp_end_time = time.time()
678
+ global_ex_time = global_ex_time + exp_end_time - exp_start_time
679
+ self.local_explainer(
680
+ kernel_explnr, series_id=s_id, datetime_col_name=datetime_col_name
681
+ )
682
+ local_ex_time = local_ex_time + time.time() - exp_end_time
683
+
684
+ if not len(kernel_explnr_vals):
685
+ logger.warn(
686
+ f"No explanations generated. Ensure that additional data has been provided."
687
+ )
688
+ else:
689
+ self.global_explanation[s_id] = dict(
690
+ zip(
691
+ data_trimmed.columns[1:],
692
+ np.average(np.absolute(kernel_explnr_vals[:, 1:]), axis=0),
693
+ )
684
694
  )
695
+ else:
696
+ logger.warn(
697
+ f"Skipping explanations for {s_id}, as forecast was not generated."
685
698
  )
686
699
 
687
700
  logger.info(
@@ -700,9 +713,14 @@ class ForecastOperatorBaseModel(ABC):
700
713
  kernel_explainer: The kernel explainer object to use for generating explanations.
701
714
  """
702
715
  data = self.datasets.get_horizon_at_series(s_id=series_id)
703
-
716
+ # columns that were dropped in train_model in arima, should be dropped here as well
704
717
  data[datetime_col_name] = datetime_to_seconds(data[datetime_col_name])
705
718
  data = data.reset_index(drop=True)
719
+
720
+ # Explainer fails when boolean columns are passed
721
+ _, data = _label_encode_dataframe(
722
+ data, no_encode={datetime_col_name, self.original_target_column}
723
+ )
706
724
  # Generate local SHAP values using the kernel explainer
707
725
  local_kernel_explnr_vals = kernel_explainer.shap_values(data)
708
726
 
@@ -120,11 +120,11 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
120
120
  data = self.preprocess(df, s_id)
121
121
  data_i = self.drop_horizon(data)
122
122
 
123
- if self.loaded_models is not None:
123
+ if self.loaded_models is not None and s_id in self.loaded_models:
124
124
  model = self.loaded_models[s_id]
125
125
  accepted_regressors_config = model.config_regressors or dict()
126
126
  self.accepted_regressors[s_id] = list(accepted_regressors_config.keys())
127
- if self.loaded_trainers is not None:
127
+ if self.loaded_trainers is not None and s_id in self.loaded_trainers:
128
128
  model.trainer = self.loaded_trainers[s_id]
129
129
  else:
130
130
  if self.perform_tuning:
@@ -310,56 +310,56 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
310
310
 
311
311
  def _generate_report(self):
312
312
  import datapane as dp
313
-
313
+ series_ids = self.models.keys()
314
314
  all_sections = []
315
+ if len(series_ids) > 0:
316
+ try:
317
+ sec1_text = dp.Text(
318
+ "## Forecast Overview \nThese plots show your "
319
+ "forecast in the context of historical data."
320
+ )
321
+ sec1 = _select_plot_list(
322
+ lambda s_id: self.models[s_id].plot(self.outputs[s_id]),
323
+ series_ids=series_ids,
324
+ )
325
+ all_sections = all_sections + [sec1_text, sec1]
326
+ except Exception as e:
327
+ logger.debug(f"Failed to plot with exception: {e.args}")
315
328
 
316
- try:
317
- sec1_text = dp.Text(
318
- "## Forecast Overview \nThese plots show your "
319
- "forecast in the context of historical data."
320
- )
321
- sec1 = _select_plot_list(
322
- lambda s_id: self.models[s_id].plot(self.outputs[s_id]),
323
- series_ids=self.datasets.list_series_ids(),
324
- )
325
- all_sections = all_sections + [sec1_text, sec1]
326
- except Exception as e:
327
- logger.debug(f"Failed to plot with exception: {e.args}")
328
-
329
- try:
330
- sec2_text = dp.Text(f"## Forecast Broken Down by Trend Component")
331
- sec2 = _select_plot_list(
332
- lambda s_id: self.models[s_id].plot_components(self.outputs[s_id]),
333
- series_ids=self.datasets.list_series_ids(),
334
- )
335
- all_sections = all_sections + [sec2_text, sec2]
336
- except Exception as e:
337
- logger.debug(f"Failed to plot with exception: {e.args}")
329
+ try:
330
+ sec2_text = dp.Text(f"## Forecast Broken Down by Trend Component")
331
+ sec2 = _select_plot_list(
332
+ lambda s_id: self.models[s_id].plot_components(self.outputs[s_id]),
333
+ series_ids=series_ids,
334
+ )
335
+ all_sections = all_sections + [sec2_text, sec2]
336
+ except Exception as e:
337
+ logger.debug(f"Failed to plot with exception: {e.args}")
338
338
 
339
- try:
340
- sec3_text = dp.Text(f"## Forecast Parameter Plots")
341
- sec3 = _select_plot_list(
342
- lambda s_id: self.models[s_id].plot_parameters(),
343
- series_ids=self.datasets.list_series_ids(),
344
- )
345
- all_sections = all_sections + [sec3_text, sec3]
346
- except Exception as e:
347
- logger.debug(f"Failed to plot with exception: {e.args}")
348
-
349
- sec5_text = dp.Text(f"## Neural Prophet Model Parameters")
350
- model_states = []
351
- for i, (s_id, m) in enumerate(self.models.items()):
352
- model_states.append(
353
- pd.Series(
354
- m.state_dict(),
355
- index=m.state_dict().keys(),
356
- name=s_id,
339
+ try:
340
+ sec3_text = dp.Text(f"## Forecast Parameter Plots")
341
+ sec3 = _select_plot_list(
342
+ lambda s_id: self.models[s_id].plot_parameters(),
343
+ series_ids=series_ids,
357
344
  )
358
- )
359
- all_model_states = pd.concat(model_states, axis=1)
360
- sec5 = dp.DataTable(all_model_states)
345
+ all_sections = all_sections + [sec3_text, sec3]
346
+ except Exception as e:
347
+ logger.debug(f"Failed to plot with exception: {e.args}")
348
+
349
+ sec5_text = dp.Text(f"## Neural Prophet Model Parameters")
350
+ model_states = []
351
+ for i, (s_id, m) in enumerate(self.models.items()):
352
+ model_states.append(
353
+ pd.Series(
354
+ m.state_dict(),
355
+ index=m.state_dict().keys(),
356
+ name=s_id,
357
+ )
358
+ )
359
+ all_model_states = pd.concat(model_states, axis=1)
360
+ sec5 = dp.DataTable(all_model_states)
361
361
 
362
- all_sections = all_sections + [sec5_text, sec5]
362
+ all_sections = all_sections + [sec5_text, sec5]
363
363
 
364
364
  if self.spec.generate_explanations:
365
365
  try: