01. Início rápido — AutoHybTS¶
Previsão de uma série univariada: a AutoHybTS compara modelos individuais e híbridos residuais (modelo-base + corretor do erro) e só aceita o híbrido quando ele comprova ganho na validação temporal. Caso contrário, fica com o modelo mais simples.
Entrada:
pandas.Seriescom índice temporal regular.fhé o horizonte de previsão (número de passos à frente).
import autohybts
print("AutoHybTS", autohybts.__version__)
AutoHybTS 1.0.0rc2
1. Carregar dados¶
from autohybts.datasets import get_data
y = get_data("synthetic_daily")
y.tail()
2025-12-26 69.786655
2025-12-27 68.674617
2025-12-28 70.105403
2025-12-29 71.148468
2025-12-30 72.611886
Freq: D, Name: value, dtype: float64
2. Ajustar o experimento¶
fit valida a série (ordem, duplicatas, frequência), separa os últimos fh pontos como teste selado e congela as origens de validação. Nada do que vem depois enxerga o teste selado, até a avaliação final da seção 5.
from autohybts import AutoHybridExperiment
exp = AutoHybridExperiment(fh=14, session_id=42).fit(y)
exp.splits
| layer | train_start | origin | first_target | last_target | n_train | horizon | |
|---|---|---|---|---|---|---|---|
| fold_id | |||||||
| search-0 | search | 2024-01-01 | 2025-06-03 | 2025-06-04 | 2025-06-17 | 520 | 14 |
| search-1 | search | 2024-01-01 | 2025-06-17 | 2025-06-18 | 2025-07-01 | 534 | 14 |
| search-2 | search | 2024-01-01 | 2025-07-01 | 2025-07-02 | 2025-07-15 | 548 | 14 |
| search-3 | search | 2024-01-01 | 2025-07-15 | 2025-07-16 | 2025-07-29 | 562 | 14 |
| search-4 | search | 2024-01-01 | 2025-07-29 | 2025-07-30 | 2025-08-12 | 576 | 14 |
| search-5 | search | 2024-01-01 | 2025-08-12 | 2025-08-13 | 2025-08-26 | 590 | 14 |
| search-6 | search | 2024-01-01 | 2025-08-26 | 2025-08-27 | 2025-09-09 | 604 | 14 |
| select-0 | select | 2024-01-01 | 2025-09-09 | 2025-09-10 | 2025-09-23 | 618 | 14 |
| select-1 | select | 2024-01-01 | 2025-09-23 | 2025-09-24 | 2025-10-07 | 632 | 14 |
| select-2 | select | 2024-01-01 | 2025-10-07 | 2025-10-08 | 2025-10-21 | 646 | 14 |
| select-3 | select | 2024-01-01 | 2025-10-21 | 2025-10-22 | 2025-11-04 | 660 | 14 |
| select-4 | select | 2024-01-01 | 2025-11-04 | 2025-11-05 | 2025-11-18 | 674 | 14 |
| select-5 | select | 2024-01-01 | 2025-11-18 | 2025-11-19 | 2025-12-02 | 688 | 14 |
| select-6 | select | 2024-01-01 | 2025-12-02 | 2025-12-03 | 2025-12-16 | 702 | 14 |
exp.plot_data("series")
3. Comparar modelos¶
result = exp.compare_models()
result.leaderboard
| name | model | family | strategy | base | corrector | residual_source | window | alpha | mean_rank | ... | MAE | RMSE | sMAPE | coverage | n_units | n_failed | n_skipped | fit_seconds | predict_seconds | is_champion | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| candidate_id | |||||||||||||||||||||
| 7dd1082a4ee4fe9df20683376c5688b4a23322240929c4c44a8140e266a10ae2 | mlp | mlp(lags=7) | neural | standalone | mlp | NaN | NaN | NaN | NaN | 1.0 | ... | 1.352722 | 1.607530 | 0.020369 | 1.0 | 7 | 0 | 0 | 0.500670 | 0.009761 | False |
| c4821c0a39e39a26ca17be31d8298f1ebcaed90d98e0a1fd3722f7c6ed592f9b | linear-linear | linear(lags=7) + linear [w=7, alpha=0.5, histo... | residual | residual_additive | linear | linear | historical_oof_1step | 7.0 | 0.5 | 2.0 | ... | 1.372676 | 1.629758 | 0.020592 | 1.0 | 7 | 0 | 0 | 0.412645 | 0.015583 | False |
| e9e747a9648ad8d377f20122dce611a43dd3bf85a64c950a8e1ef246ebc032b4 | linear | linear(lags=7) | linear | standalone | linear | NaN | NaN | NaN | NaN | 3.0 | ... | 1.377221 | 1.630795 | 0.020681 | 1.0 | 7 | 0 | 0 | 1.074173 | 0.009563 | False |
| b678627c69f0bc5eb419617a2119103a8a5340ac96c0551898c62a1d76dc0179 | linear-linear | linear(lags=7) + linear [w=7, alpha=1.0, histo... | residual | residual_additive | linear | linear | historical_oof_1step | 7.0 | 1.0 | 4.0 | ... | 1.384831 | 1.654700 | 0.020753 | 1.0 | 7 | 0 | 0 | 0.410006 | 0.015403 | False |
| 773d3b8c8d625571d4d4b4f4aa234d9f88b9b7fc0ceb3101c126e49e100378fc | seasonal_naive-linear | seasonal_naive(season_length=7) + linear [w=7,... | residual | residual_additive | seasonal_naive | linear | historical_oof_1step | 7.0 | 0.5 | 5.0 | ... | 1.402583 | 1.724740 | 0.021025 | 1.0 | 7 | 0 | 0 | 0.206348 | 0.010221 | False |
| 70ad174e1cc49eb256646399fb3e4b7d9df82f14f2978d09d35dac0019c7ef37 | knn | knn(lags=7) | kernel | standalone | knn | NaN | NaN | NaN | NaN | 6.0 | ... | 1.411329 | 1.709356 | 0.021219 | 1.0 | 7 | 0 | 0 | 0.028072 | 0.044234 | False |
| 5eee0fd04c5d031149c009afa0f6ef16ed0881ef9bff723be77cb371c98c48f2 | arima-linear | arima + linear [w=7, alpha=0.5, historical_oof... | residual | residual_additive | arima | linear | historical_oof_1step | 7.0 | 0.5 | 7.0 | ... | 1.452390 | 1.678623 | 0.021683 | 1.0 | 7 | 0 | 0 | 7.780483 | 0.024590 | False |
| 7fd291011a690f1edb92d697b682dc473f7cfc3303579db95482c16613ca1af3 | arima | arima | statistical | standalone | arima | NaN | NaN | NaN | NaN | 8.0 | ... | 1.455084 | 1.684059 | 0.021726 | 1.0 | 7 | 0 | 0 | 13.203863 | 0.014928 | False |
| d90528888825a4459816afbcf249ff909747924f0a2bbaa6e09d027e31342238 | seasonal_naive | seasonal_naive(season_length=7) | statistical | standalone | seasonal_naive | NaN | NaN | NaN | NaN | 9.0 | ... | 1.458400 | 1.749381 | 0.021895 | 1.0 | 7 | 0 | 0 | 0.000101 | 0.000182 | False |
| 1f8d8f124a8728e54358f736b6cef1744bd8df58ebf5b13403a2dd2723163f06 | random_forest | random_forest(lags=7) | tree | standalone | random_forest | NaN | NaN | NaN | NaN | 10.0 | ... | 1.534356 | 1.836753 | 0.023028 | 1.0 | 7 | 0 | 0 | 1.502171 | 0.570004 | True |
| b6c78bca79005209fd794322aef3342d5365dc58c985cf82fe3f6a3f27d98d83 | svm | svm(lags=7) | kernel | standalone | svm | NaN | NaN | NaN | NaN | 11.0 | ... | 1.540783 | 1.820453 | 0.023218 | 1.0 | 7 | 0 | 0 | 0.071680 | 0.014335 | False |
| 9fd962cca51322c0e3697027ac96d1fa8d35c2672647b0030737ef663dde6df6 | mlp-linear | mlp(lags=7) + linear [w=7, alpha=0.5, historic... | residual | residual_additive | mlp | linear | historical_oof_1step | 7.0 | 0.5 | 12.0 | ... | 1.573721 | 1.820666 | 0.023585 | 1.0 | 7 | 0 | 0 | 1.692681 | 0.017822 | False |
| 972a9d57c62d5265bf431a410bf8ccd45ae226f2e6bb124bc9d763258cbdbae4 | mlp-linear | mlp(lags=7) + linear [w=7, alpha=1.0, historic... | residual | residual_additive | mlp | linear | historical_oof_1step | 7.0 | 1.0 | 13.0 | ... | 1.975629 | 2.258110 | 0.029415 | 1.0 | 7 | 0 | 0 | 1.704555 | 0.017772 | False |
| bbd8aa955229e78f79ee95ec40b350010922f578702fd6a464ea2ea729023f34 | seasonal_naive-linear | seasonal_naive(season_length=7) + linear [w=7,... | residual | residual_additive | seasonal_naive | linear | historical_oof_multistep | 7.0 | 0.5 | 14.0 | ... | 2.007921 | 2.273463 | 0.029742 | 1.0 | 7 | 0 | 0 | 0.156133 | 0.007449 | False |
| 08c7599c52fbdbc2c161c2dd01323ddd34d2d79995d98b76da3c3b2ee2afca7d | random_forest-linear | random_forest(lags=7) + linear [w=7, alpha=0.5... | residual | residual_additive | random_forest | linear | historical_oof_1step | 7.0 | 0.5 | 15.0 | ... | 2.084126 | 2.410305 | 0.030657 | 1.0 | 7 | 0 | 0 | 13.483639 | 0.554068 | False |
| 8bfe173d7025f4743a7c66088a637bd19599dd2eb3690ad7de406ccfd8c2609d | tree | tree(lags=7) | tree | standalone | tree | NaN | NaN | NaN | NaN | 16.0 | ... | 2.124931 | 2.572697 | 0.032101 | 1.0 | 7 | 0 | 0 | 0.025981 | 0.011808 | False |
| 312272b03c69d5b9c74c2cc83dab2109e632825b023ee15968f1df748b476bf1 | tree-linear | tree(lags=7) + linear [w=7, alpha=0.5, histori... | residual | residual_additive | tree | linear | historical_oof_1step | 7.0 | 0.5 | 17.0 | ... | 2.449863 | 2.878276 | 0.036210 | 1.0 | 7 | 0 | 0 | 0.506817 | 0.021105 | False |
| 2948929df89de24864d7dc328f6eaad73372295f0ac9c1a0b90ee60e1d5a5bf3 | linear-linear | linear(lags=7) + linear [w=7, alpha=0.5, histo... | residual | residual_additive | linear | linear | historical_oof_multistep | 7.0 | 0.5 | 18.0 | ... | 2.523833 | 2.852431 | 0.036878 | 1.0 | 7 | 0 | 0 | 0.327960 | 0.015786 | False |
| bf5bff5cb0bc3a6ce953ea9188dae5a44f9f780d655ab31857957fa89a1f2250 | knn-linear | knn(lags=7) + linear [w=7, alpha=0.5, historic... | residual | residual_additive | knn | linear | historical_oof_1step | 7.0 | 0.5 | 19.0 | ... | 2.886785 | 3.130541 | 0.042456 | 1.0 | 7 | 0 | 0 | 1.155241 | 0.050876 | False |
| 2117d1027abc8c33b855cff511914a6917df041e30dd98281ad26a716bae3b78 | naive | naive | statistical | standalone | naive | NaN | NaN | NaN | NaN | 20.0 | ... | 2.935143 | 3.458155 | 0.043580 | 1.0 | 7 | 0 | 0 | 0.000165 | 0.000178 | False |
| 05b85c303f6d97c721cc83ca4bcd4c9d6d37afd92f56fee4a1cf392f4e0a240f | svm-linear | svm(lags=7) + linear [w=7, alpha=0.5, historic... | residual | residual_additive | svm | linear | historical_oof_1step | 7.0 | 0.5 | 21.0 | ... | 5.357014 | 5.556529 | 0.077158 | 1.0 | 7 | 0 | 0 | 0.583178 | 0.021092 | False |
| df3ec82ac4a2623a390d9a7ee621e53fda49f4f8b54f4d411b38c569f7b2af05 | knn-linear | knn(lags=7) + linear [w=7, alpha=1.0, historic... | residual | residual_additive | knn | linear | historical_oof_1step | 7.0 | 1.0 | 22.0 | ... | 6.588538 | 6.722598 | 0.094099 | 1.0 | 7 | 0 | 0 | 0.987680 | 0.044472 | False |
22 rows × 21 columns
result.plot("pareto") # erro em V_search × custo de cada candidata
best = result.best
best
CandidateSpec(family='tree', strategy='standalone', base=ModelSpec(name='random_forest', params_json='{"lags":7}'), corrector=None, window_size=None, alpha=None, residual_source=None, variant='A', members=(), options_json='{}', components=(), candidate_id='1f8d8f124a8728e54358f736b6cef1744bd8df58ebf5b13403a2dd2723163f06')
exp.plot_model(best, "cv_forecasts") # previsões nas origens de validação
4. Prever¶
finalize_model reajusta a estrutura escolhida no histórico anterior ao teste, sem reabrir a seleção. predict_model prevê fh passos a partir do fim desse histórico.
final = exp.finalize_model(best)
forecast = exp.predict_model(final)
forecast.predictions
| series_id | origin | horizon | y_pred | |
|---|---|---|---|---|
| timestamp | ||||
| 2025-12-17 | __single__ | 2025-12-16 | 1 | 73.378733 |
| 2025-12-18 | __single__ | 2025-12-16 | 2 | 71.898770 |
| 2025-12-19 | __single__ | 2025-12-16 | 3 | 70.122112 |
| 2025-12-20 | __single__ | 2025-12-16 | 4 | 68.294473 |
| 2025-12-21 | __single__ | 2025-12-16 | 5 | 68.671440 |
| 2025-12-22 | __single__ | 2025-12-16 | 6 | 70.818109 |
| 2025-12-23 | __single__ | 2025-12-16 | 7 | 73.083289 |
| 2025-12-24 | __single__ | 2025-12-16 | 8 | 73.790339 |
| 2025-12-25 | __single__ | 2025-12-16 | 9 | 72.286542 |
| 2025-12-26 | __single__ | 2025-12-16 | 10 | 70.292672 |
| 2025-12-27 | __single__ | 2025-12-16 | 11 | 68.127478 |
| 2025-12-28 | __single__ | 2025-12-16 | 12 | 68.518635 |
| 2025-12-29 | __single__ | 2025-12-16 | 13 | 70.536078 |
| 2025-12-30 | __single__ | 2025-12-16 | 14 | 72.924148 |
5. Avaliar no teste selado¶
Feita uma única vez, depois que o modelo está congelado. O resultado não volta para a busca.
external = exp.evaluate_external(final)
external.metrics
| MASE | MAE | RMSE | sMAPE | |
|---|---|---|---|---|
| model | ||||
| random_forest(lags=7) | 0.704631 | 1.103425 | 1.273037 | 0.015608 |
import matplotlib.pyplot as plt
recent = y.iloc[-90:]
predictions = external.predictions
fig, ax = plt.subplots(figsize=(10, 3.6))
ax.plot(recent.index, recent.to_numpy(), color="#333333", linewidth=1, label="observado")
ax.plot(
predictions.index, predictions["y_pred"], "o-", color="#DD8452", markersize=3, label="previsto"
)
ax.axvline(predictions.index[0], color="gray", linestyle="--", linewidth=0.8)
ax.set_title(f"Teste selado ({len(predictions)} dias): {final.spec.label}")
ax.legend()
plt.show()
Próximos passos¶
02_hybrid_selection.ipynb: como a busca monta os híbridos e por que o campeão foi escolhido.03_finalize_and_persist.ipynb: salvar, carregar e prever sem teste selado.04_rolling_one_step.ipynb: variante B, previsão de um passo com observações chegando.05_benchmark_m4.ipynb: uma série do M4 com o teste oficial selado.