Skip to content

Commit

Permalink
Merge pull request #253 from winedarksea/dev
Browse files Browse the repository at this point in the history
0.6.18
  • Loading branch information
winedarksea authored Feb 11, 2025
2 parents 236ab0d + 64fc360 commit 787d3e7
Show file tree
Hide file tree
Showing 64 changed files with 3,024 additions and 441 deletions.
12 changes: 9 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@
* Forecasts are desired for the future immediately following the most recent data.
* trimmed_mean to AverageValueNaive

# 0.6.17 🇺🇦 🇺🇦 🇺🇦
* minor adjustments and bug fixes for scalability
* added BallTreeRegressionMotif
# 0.6.18 🇺🇦 🇺🇦 🇺🇦
* minor tweaks and bug fixes
* updated python version required to 3.9 to allow more typing options
* validation_indexes can now be a list of tuples with mixed forecast lengths
* "mixed_length" validation option, for limited testing of longer forecast lengths
* kalman method for ensemble aggregation (ala sensor fusion)
* UpscaleDownscaleTransformer added
* PreprocessingExperts model added (experimental)

### Unstable Upstream Pacakges (those that are frequently broken by maintainers)
* Pytorch-Forecasting
Expand All @@ -27,6 +32,7 @@
* add to appropriate model_lists: all, recombination_approved if so, no_shared if so
* add to model table in extended_tutorial.md (most columns here have an equivalent model_list)
* if model has regressors, make sure it meets Simulation Forecasting needs (method=="regressor", fails on no regressor if "User")
* if model has result_windows, add to appropriate model_list noting also diff_window_motif_list

## New Transformer Checklist:
* Make sure that if it modifies the size (more/fewer columns or rows) it returns pd.DataFrame with proper index/columns
Expand Down
2 changes: 1 addition & 1 deletion autots/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from autots.models.cassandra import Cassandra


__version__ = '0.6.17'
__version__ = '0.6.18'

TransformTS = GeneralTransformer

Expand Down
1 change: 1 addition & 0 deletions autots/evaluator/anomaly_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ def detect(self, df):
use_hebrew_holidays=self.use_hebrew_holidays,
use_hindu_holidays=self.use_hindu_holidays,
)
return self

def plot_anomaly(self, kwargs={}):
self.anomaly_model.plot(**kwargs)
Expand Down
74 changes: 66 additions & 8 deletions autots/evaluator/auto_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
ComponentAnalysis,
PreprocessingRegression,
)
from autots.models.composite import PreprocessingExperts


def create_model_id(
Expand Down Expand Up @@ -744,6 +745,17 @@ def ModelMonster(
n_jobs=n_jobs,
**parameters,
)
elif model == 'PreprocessingExperts':
return PreprocessingExperts(
frequency=frequency,
prediction_interval=prediction_interval,
holiday_country=holiday_country,
random_seed=random_seed,
verbose=verbose,
forecast_length=forecast_length,
n_jobs=n_jobs,
**parameters,
)
elif model == "":
raise AttributeError(
("Model name is empty. Likely this means AutoTS has not been fit.")
Expand Down Expand Up @@ -815,6 +827,7 @@ def __init__(
self.current_model_file = current_model_file
self.model_count = model_count
self.force_gc = force_gc
self.fore_len_increase = False
# handle still in JSON form
if isinstance(transformation_dict, str):
if transformation_dict == "":
Expand All @@ -830,17 +843,32 @@ def __init__(
self.parameter_dict = json.loads(parameter_dict)
else:
self.parameter_dict = parameter_dict
if model_str == "PreprocessingRegression":
if self.transformation_dict is None:
self.transformation_dict = {}
# special models that get fed in the transformation parameters from the main definition
if model_str in ["PreprocessingRegression", "PreprocessingExperts"]:
# this is messy and likely to be broken indirectly, basically overwriting inner transformations with empty
self.parameter_dict['transformation_dict'] = self.transformation_dict
self.transformation_dict = {
use_trans_params = {
'fillna': None,
'transformations': {},
'transformation_params': {},
}
if self.transformation_dict is None:
self.transformation_dict = {}
else:
use_trans_params = self.transformation_dict
# this extends the length for the upscaled forecasts as necessary
transformations = self.transformation_dict.get("transformations", {})
transformers_used = list(transformations.values())
self.forecast_length_needed = self.forecast_length
if "UpscaleDownscaleTransformer" in transformers_used:
params = self.transformation_dict.get("transformation_params", {})
for x, y in transformations.items():
if y == "UpscaleDownscaleTransformer":
if params[x].get("mode") == "upscale":
self.fore_len_increase = params[x]["factor"] + 1
self.forecast_length_needed *= self.fore_len_increase
self.transformer_object = GeneralTransformer(
**self.transformation_dict,
**use_trans_params,
n_jobs=self.n_jobs,
holiday_country=self.holiday_country,
verbose=self.verbose,
Expand All @@ -864,7 +892,7 @@ def fit(self, df, future_regressor=None):
holiday_country=self.holiday_country,
random_seed=self.random_seed,
verbose=self.verbose,
forecast_length=self.forecast_length,
forecast_length=self.forecast_length_needed,
n_jobs=self.n_jobs,
)
transformationStartTime = datetime.datetime.now()
Expand Down Expand Up @@ -907,7 +935,9 @@ def fit(self, df, future_regressor=None):

def predict(self, forecast_length=None, future_regressor=None):
if forecast_length is None:
forecast_length = self.forecast_length
forecast_length = self.forecast_length_needed
elif self.fore_len_increase:
forecast_length *= self.fore_len_increase
if not self._fit_complete:
raise ValueError("Model not yet fit.")
df_forecast = self.model.predict(
Expand Down Expand Up @@ -938,7 +968,7 @@ def predict(self, forecast_length=None, future_regressor=None):
)

# CHECK Forecasts are proper length!
if df_forecast.forecast.shape[0] != self.forecast_length:
if df_forecast.forecast.shape[0] < self.forecast_length:
raise ValueError(
f"Model {self.model_str} returned improper forecast_length. Returned: {df_forecast.forecast.shape[0]} and requested: {self.forecast_length}"
)
Expand Down Expand Up @@ -3038,6 +3068,34 @@ def validation_aggregation(
return validation_results


# uses this for a test of user input
all_valid_weightings = [
'smape_weighting',
'mae_weighting',
'rmse_weighting',
'containment_weighting',
'runtime_weighting',
'spl_weighting',
'contour_weighting',
'made_weighting',
'mage_weighting',
'custom_weighting',
'mle_weighting',
'imle_weighting',
'maxe_weighting',
'oda_weighting',
'mqae_weighting',
'dwae_weighting',
'ewmae_weighting',
'uwmse_weighting',
'smoothness_weighting',
'mate_weighting',
'wasserstein_weighting',
'dwd_weighting',
'matse_weighting',
]


def generate_score(
model_results,
metric_weighting: dict = {},
Expand Down
Loading

0 comments on commit 787d3e7

Please sign in to comment.