oracle-ads 2.13.2__py3-none-any.whl → 2.13.3__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.
- ads/aqua/app.py +2 -1
- ads/aqua/evaluation/evaluation.py +11 -10
- ads/aqua/finetuning/finetuning.py +2 -3
- ads/opctl/operator/lowcode/anomaly/model/base_model.py +3 -3
- ads/opctl/operator/lowcode/anomaly/model/randomcutforest.py +1 -1
- ads/opctl/operator/lowcode/anomaly/utils.py +1 -1
- ads/opctl/operator/lowcode/common/transformations.py +5 -1
- ads/opctl/operator/lowcode/common/utils.py +7 -2
- ads/opctl/operator/lowcode/forecast/model/arima.py +15 -10
- ads/opctl/operator/lowcode/forecast/model/automlx.py +31 -9
- ads/opctl/operator/lowcode/forecast/model/autots.py +7 -5
- ads/opctl/operator/lowcode/forecast/model/base_model.py +127 -101
- ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +14 -6
- ads/opctl/operator/lowcode/forecast/model/ml_forecast.py +2 -2
- ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +46 -32
- ads/opctl/operator/lowcode/forecast/model/prophet.py +82 -29
- ads/opctl/operator/lowcode/forecast/model_evaluator.py +136 -54
- ads/opctl/operator/lowcode/forecast/operator_config.py +29 -3
- ads/opctl/operator/lowcode/forecast/whatifserve/deployment_manager.py +103 -58
- {oracle_ads-2.13.2.dist-info → oracle_ads-2.13.3.dist-info}/METADATA +1 -1
- {oracle_ads-2.13.2.dist-info → oracle_ads-2.13.3.dist-info}/RECORD +24 -24
- {oracle_ads-2.13.2.dist-info → oracle_ads-2.13.3.dist-info}/WHEEL +0 -0
- {oracle_ads-2.13.2.dist-info → oracle_ads-2.13.3.dist-info}/entry_points.txt +0 -0
- {oracle_ads-2.13.2.dist-info → oracle_ads-2.13.3.dist-info}/licenses/LICENSE.txt +0 -0
@@ -28,8 +28,8 @@ from ads.opctl.operator.lowcode.common.utils import (
|
|
28
28
|
merged_category_column_name,
|
29
29
|
seconds_to_datetime,
|
30
30
|
write_data,
|
31
|
+
write_json,
|
31
32
|
)
|
32
|
-
from ads.opctl.operator.lowcode.forecast.model.forecast_datasets import TestData
|
33
33
|
from ads.opctl.operator.lowcode.forecast.utils import (
|
34
34
|
_build_metrics_df,
|
35
35
|
_build_metrics_per_horizon,
|
@@ -46,6 +46,7 @@ from ..const import (
|
|
46
46
|
AUTO_SELECT,
|
47
47
|
BACKTEST_REPORT_NAME,
|
48
48
|
SUMMARY_METRICS_HORIZON_LIMIT,
|
49
|
+
ForecastOutputColumns,
|
49
50
|
SpeedAccuracyMode,
|
50
51
|
SupportedMetrics,
|
51
52
|
SupportedModels,
|
@@ -132,11 +133,10 @@ class ForecastOperatorBaseModel(ABC):
|
|
132
133
|
|
133
134
|
if self.datasets.test_data is not None:
|
134
135
|
try:
|
135
|
-
(
|
136
|
-
self.
|
137
|
-
|
138
|
-
|
139
|
-
elapsed_time=elapsed_time,
|
136
|
+
(self.test_eval_metrics, summary_metrics) = (
|
137
|
+
self._test_evaluate_metrics(
|
138
|
+
elapsed_time=elapsed_time,
|
139
|
+
)
|
140
140
|
)
|
141
141
|
if not self.target_cat_col:
|
142
142
|
self.test_eval_metrics.rename(
|
@@ -145,7 +145,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
145
145
|
inplace=True,
|
146
146
|
)
|
147
147
|
except Exception:
|
148
|
-
logger.
|
148
|
+
logger.warning("Unable to generate Test Metrics.")
|
149
149
|
logger.debug(f"Full Traceback: {traceback.format_exc()}")
|
150
150
|
report_sections = []
|
151
151
|
|
@@ -155,9 +155,8 @@ class ForecastOperatorBaseModel(ABC):
|
|
155
155
|
model_description,
|
156
156
|
other_sections,
|
157
157
|
) = self._generate_report()
|
158
|
-
|
159
158
|
header_section = rc.Block(
|
160
|
-
rc.Heading(
|
159
|
+
rc.Heading(self.spec.report_title, level=1),
|
161
160
|
rc.Text(
|
162
161
|
f"You selected the {self.spec.model} model.\nBased on your dataset, you could have also selected any of the models: {SupportedModels.keys()}."
|
163
162
|
),
|
@@ -369,7 +368,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
369
368
|
-self.spec.horizon :
|
370
369
|
]
|
371
370
|
except KeyError as ke:
|
372
|
-
logger.
|
371
|
+
logger.warning(
|
373
372
|
f"Error Generating Metrics: Unable to find {s_id} in the test data. Error: {ke.args}"
|
374
373
|
)
|
375
374
|
y_pred = self.forecast_output.get_forecast(s_id)["forecast_value"].values[
|
@@ -478,10 +477,11 @@ class ForecastOperatorBaseModel(ABC):
|
|
478
477
|
unique_output_dir = self.spec.output_directory.url
|
479
478
|
results = ForecastResults()
|
480
479
|
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
480
|
+
storage_options = (
|
481
|
+
default_signer()
|
482
|
+
if ObjectStorageDetails.is_oci_path(unique_output_dir)
|
483
|
+
else {}
|
484
|
+
)
|
485
485
|
|
486
486
|
# report-creator html report
|
487
487
|
if self.spec.generate_report:
|
@@ -512,12 +512,13 @@ class ForecastOperatorBaseModel(ABC):
|
|
512
512
|
if self.target_cat_col
|
513
513
|
else result_df.drop(DataColumns.Series, axis=1)
|
514
514
|
)
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
515
|
+
if self.spec.generate_forecast_file:
|
516
|
+
write_data(
|
517
|
+
data=result_df,
|
518
|
+
filename=os.path.join(unique_output_dir, self.spec.forecast_filename),
|
519
|
+
format="csv",
|
520
|
+
storage_options=storage_options,
|
521
|
+
)
|
521
522
|
results.set_forecast(result_df)
|
522
523
|
|
523
524
|
# metrics csv report
|
@@ -531,18 +532,19 @@ class ForecastOperatorBaseModel(ABC):
|
|
531
532
|
metrics_df_formatted = metrics_df.reset_index().rename(
|
532
533
|
{"index": "metrics", "Series 1": metrics_col_name}, axis=1
|
533
534
|
)
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
535
|
+
if self.spec.generate_metrics_file:
|
536
|
+
write_data(
|
537
|
+
data=metrics_df_formatted,
|
538
|
+
filename=os.path.join(
|
539
|
+
unique_output_dir, self.spec.metrics_filename
|
540
|
+
),
|
541
|
+
format="csv",
|
542
|
+
storage_options=storage_options,
|
543
|
+
index=False,
|
544
|
+
)
|
543
545
|
results.set_metrics(metrics_df_formatted)
|
544
546
|
else:
|
545
|
-
logger.
|
547
|
+
logger.warning(
|
546
548
|
f"Attempted to generate the {self.spec.metrics_filename} file with the training metrics, however the training metrics could not be properly generated."
|
547
549
|
)
|
548
550
|
|
@@ -552,56 +554,59 @@ class ForecastOperatorBaseModel(ABC):
|
|
552
554
|
test_metrics_df_formatted = test_metrics_df.reset_index().rename(
|
553
555
|
{"index": "metrics", "Series 1": metrics_col_name}, axis=1
|
554
556
|
)
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
557
|
+
if self.spec.generate_metrics_file:
|
558
|
+
write_data(
|
559
|
+
data=test_metrics_df_formatted,
|
560
|
+
filename=os.path.join(
|
561
|
+
unique_output_dir, self.spec.test_metrics_filename
|
562
|
+
),
|
563
|
+
format="csv",
|
564
|
+
storage_options=storage_options,
|
565
|
+
index=False,
|
566
|
+
)
|
564
567
|
results.set_test_metrics(test_metrics_df_formatted)
|
565
568
|
else:
|
566
|
-
logger.
|
569
|
+
logger.warning(
|
567
570
|
f"Attempted to generate the {self.spec.test_metrics_filename} file with the test metrics, however the test metrics could not be properly generated."
|
568
571
|
)
|
569
572
|
# explanations csv reports
|
570
573
|
if self.spec.generate_explanations:
|
571
574
|
try:
|
572
575
|
if not self.formatted_global_explanation.empty:
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
576
|
+
if self.spec.generate_explanation_files:
|
577
|
+
write_data(
|
578
|
+
data=self.formatted_global_explanation,
|
579
|
+
filename=os.path.join(
|
580
|
+
unique_output_dir, self.spec.global_explanation_filename
|
581
|
+
),
|
582
|
+
format="csv",
|
583
|
+
storage_options=storage_options,
|
584
|
+
index=True,
|
585
|
+
)
|
582
586
|
results.set_global_explanations(self.formatted_global_explanation)
|
583
587
|
else:
|
584
|
-
logger.
|
588
|
+
logger.warning(
|
585
589
|
f"Attempted to generate global explanations for the {self.spec.global_explanation_filename} file, but an issue occured in formatting the explanations."
|
586
590
|
)
|
587
591
|
|
588
592
|
if not self.formatted_local_explanation.empty:
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
593
|
+
if self.spec.generate_explanation_files:
|
594
|
+
write_data(
|
595
|
+
data=self.formatted_local_explanation,
|
596
|
+
filename=os.path.join(
|
597
|
+
unique_output_dir, self.spec.local_explanation_filename
|
598
|
+
),
|
599
|
+
format="csv",
|
600
|
+
storage_options=storage_options,
|
601
|
+
index=True,
|
602
|
+
)
|
598
603
|
results.set_local_explanations(self.formatted_local_explanation)
|
599
604
|
else:
|
600
|
-
logger.
|
605
|
+
logger.warning(
|
601
606
|
f"Attempted to generate local explanations for the {self.spec.local_explanation_filename} file, but an issue occured in formatting the explanations."
|
602
607
|
)
|
603
608
|
except AttributeError as e:
|
604
|
-
logger.
|
609
|
+
logger.warning(
|
605
610
|
"Unable to generate explanations for this model type or for this dataset."
|
606
611
|
)
|
607
612
|
logger.debug(f"Got error: {e.args}")
|
@@ -631,15 +636,12 @@ class ForecastOperatorBaseModel(ABC):
|
|
631
636
|
f"The outputs have been successfully generated and placed into the directory: {unique_output_dir}."
|
632
637
|
)
|
633
638
|
if self.errors_dict:
|
634
|
-
|
635
|
-
|
639
|
+
write_json(
|
640
|
+
json_dict=self.errors_dict,
|
636
641
|
filename=os.path.join(
|
637
642
|
unique_output_dir, self.spec.errors_dict_filename
|
638
643
|
),
|
639
|
-
format="json",
|
640
644
|
storage_options=storage_options,
|
641
|
-
index=True,
|
642
|
-
indent=4,
|
643
645
|
)
|
644
646
|
results.set_errors_dict(self.errors_dict)
|
645
647
|
else:
|
@@ -742,45 +744,62 @@ class ForecastOperatorBaseModel(ABC):
|
|
742
744
|
include_horizon=False
|
743
745
|
).items():
|
744
746
|
if s_id in self.models:
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
kernel_explnr = PermutationExplainer(
|
761
|
-
model=explain_predict_fn, masker=data_trimmed_encoded
|
762
|
-
)
|
763
|
-
kernel_explnr_vals = kernel_explnr.shap_values(data_trimmed_encoded)
|
764
|
-
exp_end_time = time.time()
|
765
|
-
global_ex_time = global_ex_time + exp_end_time - exp_start_time
|
766
|
-
self.local_explainer(
|
767
|
-
kernel_explnr, series_id=s_id, datetime_col_name=datetime_col_name
|
768
|
-
)
|
769
|
-
local_ex_time = local_ex_time + time.time() - exp_end_time
|
747
|
+
try:
|
748
|
+
explain_predict_fn = self.get_explain_predict_fn(series_id=s_id)
|
749
|
+
data_trimmed = data_i.tail(
|
750
|
+
max(int(len(data_i) * ratio), 5)
|
751
|
+
).reset_index(drop=True)
|
752
|
+
data_trimmed[datetime_col_name] = data_trimmed[
|
753
|
+
datetime_col_name
|
754
|
+
].apply(lambda x: x.timestamp())
|
755
|
+
|
756
|
+
# Explainer fails when boolean columns are passed
|
757
|
+
|
758
|
+
_, data_trimmed_encoded = _label_encode_dataframe(
|
759
|
+
data_trimmed,
|
760
|
+
no_encode={datetime_col_name, self.original_target_column},
|
761
|
+
)
|
770
762
|
|
771
|
-
|
772
|
-
|
773
|
-
"No explanations generated. Ensure that additional data has been provided."
|
763
|
+
kernel_explnr = PermutationExplainer(
|
764
|
+
model=explain_predict_fn, masker=data_trimmed_encoded
|
774
765
|
)
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
766
|
+
kernel_explnr_vals = kernel_explnr.shap_values(data_trimmed_encoded)
|
767
|
+
exp_end_time = time.time()
|
768
|
+
global_ex_time = global_ex_time + exp_end_time - exp_start_time
|
769
|
+
self.local_explainer(
|
770
|
+
kernel_explnr,
|
771
|
+
series_id=s_id,
|
772
|
+
datetime_col_name=datetime_col_name,
|
781
773
|
)
|
774
|
+
local_ex_time = local_ex_time + time.time() - exp_end_time
|
775
|
+
|
776
|
+
if not len(kernel_explnr_vals):
|
777
|
+
logger.warning(
|
778
|
+
"No explanations generated. Ensure that additional data has been provided."
|
779
|
+
)
|
780
|
+
else:
|
781
|
+
self.global_explanation[s_id] = dict(
|
782
|
+
zip(
|
783
|
+
data_trimmed.columns[1:],
|
784
|
+
np.average(
|
785
|
+
np.absolute(kernel_explnr_vals[:, 1:]), axis=0
|
786
|
+
),
|
787
|
+
)
|
788
|
+
)
|
789
|
+
except Exception as e:
|
790
|
+
if s_id in self.errors_dict:
|
791
|
+
self.errors_dict[s_id]["explainer_error"] = str(e)
|
792
|
+
self.errors_dict[s_id]["explainer_error_trace"] = (
|
793
|
+
traceback.format_exc()
|
794
|
+
)
|
795
|
+
else:
|
796
|
+
self.errors_dict[s_id] = {
|
797
|
+
"model_name": self.spec.model,
|
798
|
+
"explainer_error": str(e),
|
799
|
+
"explainer_error_trace": traceback.format_exc(),
|
800
|
+
}
|
782
801
|
else:
|
783
|
-
logger.
|
802
|
+
logger.warning(
|
784
803
|
f"Skipping explanations for {s_id}, as forecast was not generated."
|
785
804
|
)
|
786
805
|
|
@@ -815,6 +834,13 @@ class ForecastOperatorBaseModel(ABC):
|
|
815
834
|
local_kernel_explnr_df = pd.DataFrame(
|
816
835
|
local_kernel_explnr_vals, columns=data.columns
|
817
836
|
)
|
837
|
+
|
838
|
+
# Add date column to local explanation DataFrame
|
839
|
+
local_kernel_explnr_df[ForecastOutputColumns.DATE] = (
|
840
|
+
self.datasets.get_horizon_at_series(
|
841
|
+
s_id=series_id
|
842
|
+
)[self.spec.datetime_column.name].reset_index(drop=True)
|
843
|
+
)
|
818
844
|
self.local_explanation[series_id] = local_kernel_explnr_df
|
819
845
|
|
820
846
|
def get_explain_predict_fn(self, series_id, fcst_col_name="yhat"):
|
@@ -23,14 +23,14 @@ from ..operator_config import ForecastOperatorConfig
|
|
23
23
|
|
24
24
|
|
25
25
|
class HistoricalData(AbstractData):
|
26
|
-
def __init__(self, spec, historical_data
|
26
|
+
def __init__(self, spec, historical_data=None):
|
27
27
|
super().__init__(spec=spec, name="historical_data", data=historical_data)
|
28
28
|
|
29
29
|
def _ingest_data(self, spec):
|
30
30
|
try:
|
31
31
|
self.freq = get_frequency_of_datetime(self.data.index.get_level_values(0))
|
32
32
|
except TypeError as e:
|
33
|
-
logger.
|
33
|
+
logger.warning(
|
34
34
|
f"Error determining frequency: {e.args}. Setting Frequency to None"
|
35
35
|
)
|
36
36
|
logger.debug(f"Full traceback: {e}")
|
@@ -106,7 +106,7 @@ class AdditionalData(AbstractData):
|
|
106
106
|
_spec = spec
|
107
107
|
self.additional_regressors = list(self.data.columns)
|
108
108
|
if not self.additional_regressors:
|
109
|
-
logger.
|
109
|
+
logger.warning(
|
110
110
|
f"No additional variables found in the additional_data. Only columns found: {self.data.columns}. Skipping for now."
|
111
111
|
)
|
112
112
|
# Check that datetime column matches historical datetime column
|
@@ -121,7 +121,13 @@ class TestData(AbstractData):
|
|
121
121
|
|
122
122
|
|
123
123
|
class ForecastDatasets:
|
124
|
-
def __init__(
|
124
|
+
def __init__(
|
125
|
+
self,
|
126
|
+
config: ForecastOperatorConfig,
|
127
|
+
historical_data=None,
|
128
|
+
additional_data=None,
|
129
|
+
test_data=None,
|
130
|
+
):
|
125
131
|
"""Instantiates the DataIO instance.
|
126
132
|
|
127
133
|
Properties
|
@@ -136,7 +142,9 @@ class ForecastDatasets:
|
|
136
142
|
self._target_col = config.spec.target_column
|
137
143
|
if historical_data is not None:
|
138
144
|
self.historical_data = HistoricalData(config.spec, historical_data)
|
139
|
-
self.additional_data = AdditionalData(
|
145
|
+
self.additional_data = AdditionalData(
|
146
|
+
config.spec, self.historical_data, additional_data
|
147
|
+
)
|
140
148
|
else:
|
141
149
|
self._load_data(config.spec)
|
142
150
|
self.test_data = TestData(config.spec, test_data)
|
@@ -147,7 +155,7 @@ class ForecastDatasets:
|
|
147
155
|
self.additional_data = AdditionalData(spec, self.historical_data)
|
148
156
|
|
149
157
|
if spec.generate_explanations and spec.additional_data is None:
|
150
|
-
logger.
|
158
|
+
logger.warning(
|
151
159
|
"Unable to generate explanations as there is no additional data passed in. Either set generate_explanations to False, or pass in additional data."
|
152
160
|
)
|
153
161
|
spec.generate_explanations = False
|
@@ -168,8 +168,8 @@ class MLForecastOperatorModel(ForecastOperatorBaseModel):
|
|
168
168
|
"error": str(e),
|
169
169
|
"error_trace": traceback.format_exc(),
|
170
170
|
}
|
171
|
-
logger.
|
172
|
-
logger.
|
171
|
+
logger.warning(f"Encountered Error: {e}. Skipping.")
|
172
|
+
logger.warning(traceback.format_exc())
|
173
173
|
raise e
|
174
174
|
|
175
175
|
def _build_model(self) -> pd.DataFrame:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
|
3
|
-
# Copyright (c) 2023,
|
3
|
+
# Copyright (c) 2023, 2025 Oracle and/or its affiliates.
|
4
4
|
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
|
5
5
|
|
6
6
|
import logging
|
@@ -40,7 +40,7 @@ from .forecast_datasets import ForecastDatasets, ForecastOutput
|
|
40
40
|
# "rmse": MeanSquaredError,
|
41
41
|
# }
|
42
42
|
# if selected_metric not in metric_translation.keys():
|
43
|
-
# logger.
|
43
|
+
# logger.warning(
|
44
44
|
# f"Could not find the metric: {selected_metric} in torchmetrics. Defaulting to MAE and RMSE"
|
45
45
|
# )
|
46
46
|
# return {"MAE": MeanAbsoluteError(), "RMSE": MeanSquaredError()}
|
@@ -149,28 +149,42 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
149
149
|
logger.debug(f"-----------------Model {i}----------------------")
|
150
150
|
logger.debug(forecast.tail())
|
151
151
|
|
152
|
-
# TODO; could also extract trend and seasonality?
|
153
|
-
cols_to_read = filter(
|
154
|
-
lambda x: x.startswith("future_regressor"), forecast.columns
|
155
|
-
)
|
156
|
-
self.explanations_info[s_id] = forecast[cols_to_read]
|
157
|
-
self.explanations_info[s_id]["Date"] = forecast["ds"]
|
158
|
-
self.explanations_info[s_id] = self.explanations_info[s_id].set_index(
|
159
|
-
"Date"
|
160
|
-
)
|
161
|
-
|
162
152
|
self.outputs[s_id] = forecast
|
153
|
+
upper_bound_col_name = f"yhat1 {model_kwargs['quantiles'][1]*100}%"
|
154
|
+
lower_bound_col_name = f"yhat1 {model_kwargs['quantiles'][0]*100}%"
|
163
155
|
self.forecast_output.populate_series_output(
|
164
156
|
series_id=s_id,
|
165
157
|
fit_val=self.drop_horizon(forecast["yhat1"]).values,
|
166
158
|
forecast_val=self.get_horizon(forecast["yhat1"]).values,
|
167
|
-
upper_bound=self.get_horizon(
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
159
|
+
upper_bound=self.get_horizon(forecast[upper_bound_col_name]).values,
|
160
|
+
lower_bound=self.get_horizon(forecast[lower_bound_col_name]).values,
|
161
|
+
)
|
162
|
+
core_columns = set(forecast.columns) - set(
|
163
|
+
[
|
164
|
+
"y",
|
165
|
+
"yhat1",
|
166
|
+
upper_bound_col_name,
|
167
|
+
lower_bound_col_name,
|
168
|
+
"future_regressors_additive",
|
169
|
+
"future_regressors_multiplicative",
|
170
|
+
]
|
171
|
+
)
|
172
|
+
exog_variables = set(
|
173
|
+
filter(lambda x: x.startswith("future_regressor_"), list(core_columns))
|
173
174
|
)
|
175
|
+
combine_terms = list(core_columns - exog_variables - set(["ds"]))
|
176
|
+
temp_df = (
|
177
|
+
forecast[list(core_columns)]
|
178
|
+
.rename({"ds": "Date"}, axis=1)
|
179
|
+
.set_index("Date")
|
180
|
+
)
|
181
|
+
if combine_terms:
|
182
|
+
temp_df[self.spec.target_column] = temp_df[combine_terms].sum(axis=1)
|
183
|
+
temp_df = temp_df.drop(combine_terms, axis=1)
|
184
|
+
else:
|
185
|
+
temp_df[self.spec.target_column] = 0
|
186
|
+
# Todo: check for columns that were dropped, and set them to 0
|
187
|
+
self.explanations_info[s_id] = temp_df
|
174
188
|
|
175
189
|
self.trainers[s_id] = model.trainer
|
176
190
|
self.models[s_id] = {}
|
@@ -207,7 +221,7 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
207
221
|
"error": str(e),
|
208
222
|
"error_trace": traceback.format_exc(),
|
209
223
|
}
|
210
|
-
logger.
|
224
|
+
logger.warning(traceback.format_exc())
|
211
225
|
raise e
|
212
226
|
|
213
227
|
def _build_model(self) -> pd.DataFrame:
|
@@ -215,7 +229,6 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
215
229
|
self.models = {}
|
216
230
|
self.trainers = {}
|
217
231
|
self.outputs = {}
|
218
|
-
self.errors_dict = {}
|
219
232
|
self.explanations_info = {}
|
220
233
|
self.accepted_regressors = {}
|
221
234
|
self.additional_regressors = self.datasets.get_additional_data_column_names()
|
@@ -363,7 +376,9 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
363
376
|
pd.Series(
|
364
377
|
m.state_dict(),
|
365
378
|
index=m.state_dict().keys(),
|
366
|
-
name=s_id
|
379
|
+
name=s_id
|
380
|
+
if self.target_cat_col
|
381
|
+
else self.original_target_column,
|
367
382
|
)
|
368
383
|
)
|
369
384
|
all_model_states = pd.concat(model_states, axis=1)
|
@@ -377,11 +392,15 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
377
392
|
self.explain_model()
|
378
393
|
|
379
394
|
if not self.target_cat_col:
|
380
|
-
self.formatted_global_explanation =
|
381
|
-
|
382
|
-
|
395
|
+
self.formatted_global_explanation = (
|
396
|
+
self.formatted_global_explanation.rename(
|
397
|
+
{"Series 1": self.original_target_column},
|
398
|
+
axis=1,
|
399
|
+
)
|
400
|
+
)
|
401
|
+
self.formatted_local_explanation.drop(
|
402
|
+
"Series", axis=1, inplace=True
|
383
403
|
)
|
384
|
-
self.formatted_local_explanation.drop("Series", axis=1, inplace=True)
|
385
404
|
|
386
405
|
# Create a markdown section for the global explainability
|
387
406
|
global_explanation_section = rc.Block(
|
@@ -412,7 +431,7 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
412
431
|
]
|
413
432
|
except Exception as e:
|
414
433
|
# Do not fail the whole run due to explanations failure
|
415
|
-
logger.
|
434
|
+
logger.warning(f"Failed to generate Explanations with error: {e}.")
|
416
435
|
logger.debug(f"Full Traceback: {traceback.format_exc()}")
|
417
436
|
|
418
437
|
model_description = rc.Text(
|
@@ -453,9 +472,7 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
453
472
|
for s_id, expl_df in self.explanations_info.items():
|
454
473
|
expl_df = expl_df.rename(rename_cols, axis=1)
|
455
474
|
# Local Expl
|
456
|
-
self.local_explanation[s_id] = self.get_horizon(expl_df)
|
457
|
-
["future_regressors_additive"], axis=1
|
458
|
-
)
|
475
|
+
self.local_explanation[s_id] = self.get_horizon(expl_df)
|
459
476
|
self.local_explanation[s_id]["Series"] = s_id
|
460
477
|
self.local_explanation[s_id].index.rename(self.dt_column_name, inplace=True)
|
461
478
|
# Global Expl
|
@@ -463,9 +480,6 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
463
480
|
g_expl.name = s_id
|
464
481
|
global_expl.append(g_expl)
|
465
482
|
self.global_explanation = pd.concat(global_expl, axis=1)
|
466
|
-
self.global_explanation = self.global_explanation.drop(
|
467
|
-
index=["future_regressors_additive"], axis=0
|
468
|
-
)
|
469
483
|
self.formatted_global_explanation = (
|
470
484
|
self.global_explanation / self.global_explanation.sum(axis=0) * 100
|
471
485
|
)
|