From 82aa9eee06b0d2b3377dbf94975844708319593b Mon Sep 17 00:00:00 2001 From: Brendan Wells Date: Tue, 24 Mar 2026 11:50:24 -0700 Subject: [PATCH 1/4] Modify models for April forecast --- mozaic/models.py | 61 +++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/mozaic/models.py b/mozaic/models.py index 63106bc..183cf2f 100644 --- a/mozaic/models.py +++ b/mozaic/models.py @@ -6,13 +6,20 @@ logging.getLogger("cmdstanpy").disabled = True -def desktop_forecast_model(historical_data, historical_dates, forecast_dates): +import logging +import numpy as np +import pandas as pd +import prophet + +logging.getLogger("cmdstanpy").disabled = True + +def desktop_forecast_model_(historical_data, historical_dates, forecast_dates): params = { "daily_seasonality": False, "weekly_seasonality": True, - "yearly_seasonality": False, + "yearly_seasonality": True, "uncertainty_samples": 1000, - "changepoint_range": 0.8, + "changepoint_range": 0.7, "seasonality_prior_scale": 0.00825, "changepoint_prior_scale": 0.15983, "growth": "logistic", @@ -20,15 +27,9 @@ def desktop_forecast_model(historical_data, historical_dates, forecast_dates): x = historical_data - # if x.max() >= 10e6: - # params["growth"] = "logistic" - if (x.abs().corr(x.diff().abs()) or 0) > 0.0: params["seasonality_mode"] = "multiplicative" params["growth"] = "linear" - # params["seasonality_prior_scale"] = 20 - # params["changepoint_prior_scale"] = 0.05 - # params["growth"] = "logistic" if (len(x.dropna()) > (365 * 2)) and ( np.quantile(x.dropna(), 0.5) / (np.quantile(x.dropna(), 0.1) + 1e-8) < 5 @@ -49,25 +50,13 @@ def desktop_forecast_model(historical_data, historical_dates, forecast_dates): ) future = pd.DataFrame({"ds": forecast_dates}) - # if "growth" in params: - # if historical_data.max() >= 10e6: - # cap = observed["y"].max() * 1.2 - # floor = observed["y"].min() * 0.8 - # observed["cap"] = cap - # observed["floor"] = floor - # future["cap"] = cap - # future["floor"] = floor - # else: - # cap = observed["y"].max() * 1.2 - # floor = 0.0 - # observed["cap"] = cap - # observed["floor"] = floor - # future["cap"] = cap - # future["floor"] = floor - if params["growth"] == "logistic": - cap = observed["y"].max() * 1.2 - floor = observed["y"].min() * 0.8 + cap = observed["y"].tail(366).max() * 1.05 + if cap > 100e6: + floor = observed["y"].tail(366).min() * 1 + else: + floor = observed["y"].tail(366).min() * 0.92 + observed["cap"] = cap observed["floor"] = floor future["cap"] = cap @@ -90,19 +79,27 @@ def desktop_forecast_model(historical_data, historical_dates, forecast_dates): return predictive_samples, m, prophet_forecast -def mobile_forecast_model(historical_data, historical_dates, forecast_dates): +import logging +import numpy as np +import pandas as pd +import prophet + +logging.getLogger("cmdstanpy").disabled = True + +def mobile_forecast_model_(historical_data, historical_dates, forecast_dates): params = { "daily_seasonality": False, "weekly_seasonality": True, "yearly_seasonality": len(historical_data.dropna()) > (365 * 2), "uncertainty_samples": 1000, - "changepoint_range": 0.8, + "changepoint_range": 0.82, "growth": "logistic", } if historical_data.max() >= 1e6: params["seasonality_prior_scale"] = 0.1 params["changepoint_prior_scale"] = 0.1 + params["growth"] = "linear" if historical_data.max() <= 2e6: params["seasonality_mode"] = "multiplicative" @@ -118,14 +115,14 @@ def mobile_forecast_model(historical_data, historical_dates, forecast_dates): if "growth" in params: if historical_data.max() >= 10e6: - cap = historical_data.max() * 2.0 - floor = historical_data.min() * 0.8 + cap = observed["y"].tail(366).max() * 1.10 + floor = observed["y"].tail(366).min() * 1.05 observed["cap"] = cap observed["floor"] = floor future["cap"] = cap future["floor"] = floor else: - cap = historical_data.max() * 2.0 + cap = historical_data.max() * 1.1 floor = 0.0 observed["cap"] = cap observed["floor"] = floor From 46fe15c3898364cafd7ce2ec76f81cb4367713ae Mon Sep 17 00:00:00 2001 From: Brendan Wells Date: Tue, 24 Mar 2026 12:02:51 -0700 Subject: [PATCH 2/4] Modify titles --- mozaic/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mozaic/models.py b/mozaic/models.py index 183cf2f..896b5fa 100644 --- a/mozaic/models.py +++ b/mozaic/models.py @@ -13,7 +13,7 @@ logging.getLogger("cmdstanpy").disabled = True -def desktop_forecast_model_(historical_data, historical_dates, forecast_dates): +def desktop_forecast_model(historical_data, historical_dates, forecast_dates): params = { "daily_seasonality": False, "weekly_seasonality": True, @@ -86,7 +86,7 @@ def desktop_forecast_model_(historical_data, historical_dates, forecast_dates): logging.getLogger("cmdstanpy").disabled = True -def mobile_forecast_model_(historical_data, historical_dates, forecast_dates): +def mobile_forecast_model(historical_data, historical_dates, forecast_dates): params = { "daily_seasonality": False, "weekly_seasonality": True, From 4c3eff55290881923d9ce1b2da20c275c1b70472 Mon Sep 17 00:00:00 2001 From: Brendan Wells Date: Fri, 27 Mar 2026 11:59:25 -0700 Subject: [PATCH 3/4] Clean up --- mozaic/models.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/mozaic/models.py b/mozaic/models.py index 896b5fa..ee065dd 100644 --- a/mozaic/models.py +++ b/mozaic/models.py @@ -6,13 +6,6 @@ logging.getLogger("cmdstanpy").disabled = True -import logging -import numpy as np -import pandas as pd -import prophet - -logging.getLogger("cmdstanpy").disabled = True - def desktop_forecast_model(historical_data, historical_dates, forecast_dates): params = { "daily_seasonality": False, @@ -79,13 +72,6 @@ def desktop_forecast_model(historical_data, historical_dates, forecast_dates): return predictive_samples, m, prophet_forecast -import logging -import numpy as np -import pandas as pd -import prophet - -logging.getLogger("cmdstanpy").disabled = True - def mobile_forecast_model(historical_data, historical_dates, forecast_dates): params = { "daily_seasonality": False, From 4c61f9d2045c88b887f4f93ca62e5ee70dc6ff61 Mon Sep 17 00:00:00 2001 From: Brendan Wells Date: Fri, 27 Mar 2026 23:52:30 -0700 Subject: [PATCH 4/4] Warn if any holiday effects will be clipped at the -0.6 boundary --- mozaic/core.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mozaic/core.py b/mozaic/core.py index e5a1b64..4e7ca3c 100644 --- a/mozaic/core.py +++ b/mozaic/core.py @@ -1,3 +1,5 @@ +import warnings + import numpy as np import pandas as pd @@ -291,6 +293,19 @@ def _predict_holiday_effects(self): .sum() .reindex(self.forecast_dates, fill_value=0) ) + # warn if any holiday effects will be clipped at the -0.6 boundary + clipped = self.proportional_holiday_effects[self.proportional_holiday_effects < -0.6] + if len(clipped) > 0: + details = ", ".join( + f"{date.strftime('%Y-%m-%d')} ({value:.3f})" + for date, value in clipped.items() + ) + warnings.warn( + f"Holiday effects clipped at -0.6 boundary for dates: {details}", + UserWarning, + stacklevel=2, + ) + # ensure no single-day impact becomes too large self.proportional_holiday_effects.clip(lower=-0.6, upper=0, inplace=True)