├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── workflows │ └── python-publish.yml ├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── ahead ├── ARMAGARCH │ ├── ArmaGarch.py │ └── __init__.py ├── Base │ ├── Base.py │ └── __init__.py ├── Basic │ ├── BasicForecaster.py │ └── __init__.py ├── DynamicRegressor │ ├── DynamicRegressor.py │ └── __init__.py ├── EAT │ ├── EAT.py │ └── __init__.py ├── FitForecast │ ├── FitForecasting.py │ └── __init__.py ├── Ridge2 │ ├── Ridge2Regressor.py │ └── __init__.py ├── VAR │ ├── VAR.py │ └── __init__.py ├── __init__.py ├── config.py ├── demo │ ├── thierrymoudiki_20240224_ahead_python_and_R.ipynb │ └── thierrymoudiki_20240408_conformal_bench.ipynb └── utils │ ├── __init__.py │ ├── multivariate.py │ ├── tscv_indices.py │ ├── unimultivariate.py │ └── univariate.py ├── datasets ├── AirPassengers.csv └── nile.csv ├── docs ├── error.html.jinja2 ├── frame.html.jinja2 ├── index.html.jinja2 ├── module.html.jinja2 ├── static │ ├── syntax-highlighting.css │ └── theme.css ├── t-logo.png └── t-logo2.png ├── examples ├── armagarch.py ├── basicforecaster.py ├── dynrm.py ├── eat.py ├── fitforecaster.py ├── ridge2regressor.py └── var.py ├── installr.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py └── test_ahead.py └── tox.ini /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Techtonique -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * ahead version: 2 | * Python version: 3 | * Operating System: 4 | 5 | ### Description 6 | 7 | Describe what you were trying to get done. 8 | Tell us what happened, what went wrong, and what you expected to happen. 9 | 10 | ### What I Did 11 | 12 | ``` 13 | Paste the command(s) you ran and the output. 14 | If there was a crash, please include the traceback here. 15 | ``` 16 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distribution 📦 to PyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main # Replace with your branch name 8 | 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' # Replace with your Python version 21 | 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install wheel setuptools twine 26 | 27 | - name: Build distribution 28 | run: python setup.py sdist bdist_wheel 29 | 30 | - name: Run examples 31 | run: pip install .&&find examples -maxdepth 2 -name "*.py" -exec python3 {} \; 32 | 33 | - name: Publish to PyPI 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | with: 36 | password: ${{ secrets.PYPI_AHEAD }} 37 | repository-url: https://upload.pypi.org/legacy/ 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | # IDE settings 104 | .vscode/ 105 | .Rproj.user 106 | 107 | # R artifacts 108 | R-package/.Rproj.* 109 | 110 | # Apple stuff 111 | .DS_Store 112 | ahead/.DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.com 2 | 3 | language: python 4 | python: 5 | - 3.8 6 | - 3.7 7 | - 3.6 8 | - 3.5 9 | 10 | # Command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 11 | install: pip install -U tox-travis 12 | 13 | # Command to run tests, e.g. python setup.py test 14 | script: tox 15 | 16 | 17 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development Lead 6 | ---------------- 7 | 8 | * T. Moudiki 9 | 10 | Contributors 11 | ------------ 12 | 13 | None yet. Why not be the first? 14 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # version 0.10.0 2 | 3 | - Naming series in input data frame 4 | - Plot method for all the objects 5 | - More about the code: begin refactoring and DRYing --> Base class 6 | 7 | # version 0.9.0 8 | 9 | - Align with R version 10 | - Progress bars for bootstrapping (independent, circular block, moving block) 11 | - See also [https://github.com/Techtonique/ahead/blob/main/NEWS.md](https://github.com/Techtonique/ahead/blob/main/NEWS.md) 12 | 13 | # version 0.8.2 14 | 15 | - plot ridge2 16 | 17 | # version 0.8.1 18 | 19 | - Align with R version (see [https://github.com/Techtonique/ahead/blob/main/NEWS.md#version-070](https://github.com/Techtonique/ahead/blob/main/NEWS.md#version-070) and [https://github.com/Techtonique/ahead/blob/main/NEWS.md#version-080](https://github.com/Techtonique/ahead/blob/main/NEWS.md#version-080)) as much as possible 20 | - moving block bootstrap in `ridge2f`, `basicf`, in addition to circular block bootstrap from 0.6.2 21 | - adjust R-Vine copulas on residuals for `ridge2f` simulation (with empirical and Gaussian marginals) 22 | 23 | # version 0.6.2 24 | 25 | - Add Block Bootstrap to `ridge2f` and `basicf` 26 | - Add external regressors to `ridge2f` 27 | - Add clustering with _K-Means_ and hierarchical clustering to `ridge2f` 28 | - Install R or rpy2 if necessary (? weird) 29 | - Refactor code for `rpy2` and R imports 30 | 31 | # version 0.6.1 32 | 33 | - Reduce number of required packages depencies 34 | - Fix rpy2 requirement (version 3.4.5) 35 | - Requires Python version >= 3.9 36 | 37 | # version 0.5.0 38 | 39 | - add dropout regularization to `Ridge2Regressor` 40 | - parallel execution for `type_pi == bootstrap` in `Ridge2Regressor` (done in R /!\, experimental) 41 | - ARMA(1, 1)-GARCH(1, 1) in Python 42 | 43 | # version 0.4.2 44 | 45 | - new attributes mean, lower bound, upper bound forecast as numpy arrays 46 | 47 | # version 0.4.1 48 | 49 | - use `get_frequency` to get series frequency as a number 50 | - create a function `get_tscv_indices` for getting time series cross-validation indices -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # `ahead`'s Code of Conduct 2 | 3 | ## 1. Purpose 4 | 5 | A primary goal of this project is to be __inclusive__ to the largest number of contributors, and most importantly __with the most varied and diverse backgrounds possible__. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion, or lack of religion thereof. 6 | 7 | This code of conduct outlines our expectations for all those who participate to the project, as well as the consequences for unacceptable behavior. 8 | 9 | We invite all those who participate in, to help us create safe and positive experiences for everyone. 10 | 11 | ## 2. Open [Source/Culture/Tech] Citizenship 12 | 13 | A supplemental goal of this Code of Conduct is to encourage participants to recognize and strengthen the relationships between our actions and their effects on other participants. 14 | 15 | Communities mirror the societies in which they exist, and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. 16 | 17 | ## 3. Expected Behavior 18 | 19 | The following behaviors are expected and requested of all contributors: 20 | 21 | * __Attempt collaboration before conflict__. 22 | * Participate in an __authentic__ and active way. In doing so, you contribute to the health and longevity of this project. 23 | * Exercise consideration and respect in your speech and actions. 24 | * Refrain from demeaning, discriminatory, or harassing behavior and speech. 25 | * Be mindful of your surroundings and of your fellow participants. 26 | 27 | ## 4. Unacceptable Behavior 28 | 29 | The following behaviors are considered harassment and are unacceptable: 30 | 31 | * Violence, threats of violence or violent language directed against another person. 32 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language. 33 | * Posting or displaying sexually explicit or violent material. 34 | * Posting or threatening to post other people's personally identifying information ("doxing"). 35 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. 36 | * Inappropriate photography or recording. 37 | * Unwelcome sexual attention. This includes, sexualized comments or jokes. 38 | * Deliberate intimidation, stalking or following (online or in person). 39 | * Advocating for, or encouraging, any of the above behavior. 40 | 41 | ## 5. Consequences of Unacceptable Behavior 42 | 43 | Unacceptable behavior from any contributor will not be tolerated. 44 | 45 | Anyone asked to stop unacceptable behavior is expected to comply immediately. 46 | 47 | If a contributor engages in unacceptable behavior, appropriate action will be taken, up to and including a temporary ban or permanent expulsion without warning. 48 | 49 | ## 6. Scope 50 | 51 | We expect all contributors to abide by this Code of Conduct in all venues, online and in-person. 52 | 53 | ## 7. Contact info 54 | 55 | thierry.moudiki AT gmail.com 56 | 57 | ## 8. License and attribution 58 | 59 | Portions of text derived from the [Citizen Code of Conduct](http://citizencodeofconduct.org/). 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Clear BSD License 2 | 3 | Copyright (c) [2021] [Thierry Moudiki] 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted (subject to the limitations in the disclaimer 8 | below) provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY 22 | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 23 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 25 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 26 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 29 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 30 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include CONTRIBUTING.rst 3 | include HISTORY.rst 4 | include LICENSE 5 | include README.rst 6 | 7 | recursive-include tests * 8 | recursive-exclude * __pycache__ 9 | recursive-exclude * *.py[co] 10 | 11 | recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: clean clean-test clean-pyc clean-build docs help 3 | .DEFAULT_GOAL := help 4 | 5 | define BROWSER_PYSCRIPT 6 | import os, webbrowser, sys, mkdocs 7 | 8 | from urllib.request import pathname2url 9 | 10 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 11 | endef 12 | export BROWSER_PYSCRIPT 13 | 14 | define PRINT_HELP_PYSCRIPT 15 | import re, sys 16 | 17 | for line in sys.stdin: 18 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 19 | if match: 20 | target, help = match.groups() 21 | print("%-20s %s" % (target, help)) 22 | endef 23 | export PRINT_HELP_PYSCRIPT 24 | 25 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 26 | 27 | help: 28 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 29 | 30 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts 31 | 32 | clean-build: ## remove build artifacts 33 | rm -fr build/ 34 | rm -fr dist/ 35 | rm -fr .eggs/ 36 | find . -name '*.egg-info' -exec rm -fr {} + 37 | find . -name '*.egg' -exec rm -fr {} + 38 | 39 | clean-pyc: ## remove Python file artifacts 40 | find . -name '*.pyc' -exec rm -f {} + 41 | find . -name '*.pyo' -exec rm -f {} + 42 | find . -name '*~' -exec rm -f {} + 43 | find . -name '__pycache__' -exec rm -fr {} + 44 | 45 | clean-test: ## remove test and coverage artifacts 46 | rm -fr htmlcov 47 | 48 | lint: ## check style with flake8 49 | flake8 ahead tests 50 | 51 | coverage: ## check code coverage quickly with the default Python 52 | coverage run --source ahead setup.py test 53 | coverage report -m 54 | coverage html 55 | $(BROWSER) htmlcov/index.html 56 | 57 | docs: ## generate docs 58 | #pip install black pdoc 59 | #black ahead/* --line-length=80 60 | pdoc -t docs ahead/* --output-dir ahead-docs 61 | find . -name '__pycache__' -exec rm -fr {} + 62 | 63 | servedocs: ## compile the docs watching for change 64 | pip install black pdoc 65 | black ahead/* --line-length=80 66 | pdoc -t docs ahead/* 67 | find . -name '__pycache__' -exec rm -fr {} + 68 | 69 | release: dist ## package and upload a release 70 | pip install twine --ignore-installed 71 | python3 -m twine upload --repository pypi dist/* --verbose 72 | 73 | dist: clean ## builds source and wheel package 74 | python3 setup.py sdist 75 | python3 setup.py bdist_wheel 76 | ls -l dist 77 | 78 | install: clean ## install the package to the active Python's site-packages 79 | python3 -m pip install . --verbose 80 | 81 | build-site: docs ## put docs website in a directory 82 | cd docs&&mkdocs build 83 | cp -rf docs/site/* ../../Pro_Website/Techtonique.github.io/ahead_python 84 | cd .. 85 | 86 | run-examples: ## run all examples with one command 87 | find examples -maxdepth 2 -name "*.py" -exec python3 {} \; 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ahead 2 | =============================== 3 | 4 | 5 | ![PyPI](https://img.shields.io/pypi/v/ahead) [![PyPI - License](https://img.shields.io/pypi/l/ahead)](https://github.com/Techtonique/ahead_python/blob/main/LICENSE) [![PyPI Downloads](https://pepy.tech/badge/ahead)](https://pepy.tech/project/ahead) [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/ahead_python.svg)](https://anaconda.org/conda-forge/ahead_python) 6 | [![HitCount](https://hits.dwyl.com/Techtonique/ahead_python.svg?style=flat-square)](http://hits.dwyl.com/Techtonique/ahead_python) 7 | [![CodeFactor](https://www.codefactor.io/repository/github/techtonique/ahead_python/badge)](https://www.codefactor.io/repository/github/techtonique/ahead_python) 8 | [![Documentation](https://img.shields.io/badge/documentation-is_here-green)](https://techtonique.github.io/ahead_python/) 9 | 10 | 11 | Welcome to __ahead__ (Python version; the R version is [here](https://github.com/Techtonique/ahead)). 12 | 13 | `ahead` is a package for univariate and multivariate **time series forecasting**, with uncertainty quantification. The Python version is built on top of [the R package](https://techtonique.github.io/ahead/) with the same name. __ahead__'s source code is [available on GitHub](https://github.com/Techtonique/ahead_python). 14 | 15 | Currently, 6 forecasting methods are implemented in the Python package: 16 | 17 | - `DynamicRegressor`: **univariate** time series forecasting method adapted from [`forecast::nnetar`](https://otexts.com/fpp2/nnetar.html#neural-network-autoregression). 18 | The Python implementation contains only the [automatic version](https://thierrymoudiki.github.io/blog/2021/10/22/r/misc/ahead-ridge). 19 | - `EAT`: **univariate** time series forecasting method based on combinations of R's `forecast::ets`, `forecast::auto.arima`, and `forecast::thetaf` 20 | - `ArmaGarch`: **univariate** forecasting simulations of an ARMA(1, 1)-GARCH(1, 1) 21 | - `BasicForecaster`: **multivariate** time series forecasting methods; mean, median and random walk 22 | - `Ridge2Regressor`: **multivariate** time series forecasting method, based on __quasi-randomized networks__ and presented in [this paper](https://www.mdpi.com/2227-9091/6/1/22) 23 | - `VAR`: **multivariate** time series forecasting method using Vector AutoRegressive model (VAR, mostly here for benchmarking purpose) 24 | 25 | ## Installing 26 | 27 | - From Pypi, stable version: 28 | 29 | ```bash 30 | pip install ahead --verbose 31 | ``` 32 | 33 | - From Github, for the development version: 34 | 35 | ```bash 36 | pip install git+https://github.com/Techtonique/ahead_python.git --verbose 37 | ``` 38 | 39 | ## Quickstart 40 | 41 | ### Univariate time series 42 | 43 | ```python 44 | import pandas as pd 45 | from ahead import DynamicRegressor # might take some time, but ONLY the 1st time it's called 46 | 47 | # Data frame containing the time series 48 | dataset = { 49 | 'date' : ['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01', '2020-05-01'], 50 | 'value' : [34, 30, 35.6, 33.3, 38.1]} 51 | 52 | df = pd.DataFrame(dataset).set_index('date') 53 | print(df) 54 | 55 | # univariate time series forecasting 56 | d1 = DynamicRegressor(h = 5) 57 | d1.forecast(df) 58 | print(d1.result_df_) 59 | ``` 60 | 61 | ### Multivariate time series 62 | 63 | ```python 64 | import pandas as pd 65 | from ahead import Ridge2Regressor # might take some time, but ONLY the 1st time it's called 66 | 67 | # Data frame containing the (3) time series 68 | dataset = { 69 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 70 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 71 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 72 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 73 | df = pd.DataFrame(dataset).set_index('date') 74 | print(df) 75 | 76 | # multivariate time series forecasting 77 | r1 = Ridge2Regressor(h = 5) 78 | r1.forecast(df) 79 | print(r1.result_dfs_) 80 | ``` 81 | 82 | ## Contributing 83 | 84 | Want to contribute to __ahead__'s development on Github, [read this](CONTRIBUTING.md)! 85 | 86 | ## License 87 | 88 | [BSD 3-Clause](LICENSE) © Thierry Moudiki, 2021. 89 | -------------------------------------------------------------------------------- /ahead/ARMAGARCH/ArmaGarch.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from .. import config 3 | 4 | from ..utils import univariate as uv 5 | from ..utils import unimultivariate as umv 6 | 7 | 8 | class ArmaGarch(object): 9 | """ARMA(1, 1)-GARCH(1, 1) forecasting (with simulation) 10 | 11 | Parameters: 12 | 13 | h: an integer; 14 | forecasting horizon 15 | 16 | level: an integer; 17 | Confidence level for prediction intervals 18 | 19 | B: an integer; 20 | number of simulations for R's `stats::arima.sim` 21 | 22 | cl: an integer; 23 | the number of clusters for parallel execution (done in R /!\) 24 | 25 | dist: a string; 26 | distribution of innovations ("student" or "gaussian") 27 | 28 | seed: an integer; 29 | reproducibility seed 30 | 31 | date_formatting: a string; 32 | Currently: 33 | - "original": yyyy-mm-dd 34 | - "ms": milliseconds 35 | 36 | Attributes: 37 | 38 | fcast_: an object; 39 | raw result from fitting R's `ahead::armagarchf` through `rpy2` 40 | 41 | averages_: a list; 42 | mean forecast in a list 43 | 44 | ranges_: a list; 45 | lower and upper prediction intervals in a list 46 | 47 | output_dates_: a list; 48 | a list of output dates (associated to forecast) 49 | 50 | mean_: a numpy array 51 | contains series mean forecast as a numpy array 52 | 53 | lower_: a numpy array 54 | contains series lower bound forecast as a numpy array 55 | 56 | upper_: a numpy array 57 | contains series upper bound forecast as a numpy array 58 | 59 | result_df_: a data frame; 60 | contains 3 columns, mean forecast, lower + upper 61 | prediction intervals, and a date index 62 | 63 | sims_: a numpy array 64 | forecasting simulations 65 | 66 | """ 67 | 68 | def __init__( 69 | self, 70 | h=5, 71 | level=95, 72 | B=250, 73 | cl=1, 74 | dist="student", 75 | seed=123, 76 | date_formatting="original", 77 | ): 78 | 79 | self.h = h 80 | self.level = level 81 | self.B = B 82 | self.cl = cl 83 | self.dist = dist 84 | self.seed = seed 85 | self.date_formatting = date_formatting 86 | self.input_df = None 87 | 88 | self.fcast_ = None 89 | self.averages_ = None 90 | self.ranges_ = None 91 | self.output_dates_ = [] 92 | self.mean_ = [] 93 | self.lower_ = [] 94 | self.upper_ = [] 95 | self.result_df_ = None 96 | self.sims_ = None 97 | 98 | def forecast(self, df): 99 | """Forecasting method from `ArmaGarch` class 100 | 101 | Parameters: 102 | 103 | df: a data frame; 104 | a data frame containing the input time series (see example) 105 | 106 | """ 107 | 108 | # get input dates, output dates, number of series, series names, etc. 109 | self.init_forecasting_params(df) 110 | 111 | # obtain time series object ----- 112 | self.format_input() 113 | 114 | self.get_forecast("armagarch") 115 | 116 | # result ----- 117 | ( 118 | self.averages_, 119 | self.ranges_, 120 | self.output_dates_, 121 | ) = uv.format_univariate_forecast( 122 | date_formatting=self.date_formatting, 123 | output_dates=self.output_dates_, 124 | horizon=self.h, 125 | fcast=self.fcast_, 126 | ) 127 | 128 | self.mean_ = np.asarray(self.fcast_.rx2["mean"]) 129 | self.lower_ = np.asarray(self.fcast_.rx2["lower"]) 130 | self.upper_ = np.asarray(self.fcast_.rx2["upper"]) 131 | 132 | self.result_df_ = umv.compute_result_df(self.averages_, self.ranges_) 133 | 134 | self.sims_ = np.asarray(self.fcast_.rx2["sims"]) 135 | 136 | return self 137 | -------------------------------------------------------------------------------- /ahead/ARMAGARCH/__init__.py: -------------------------------------------------------------------------------- 1 | from .ArmaGarch import ArmaGarch 2 | 3 | __all__ = ["ArmaGarch"] 4 | -------------------------------------------------------------------------------- /ahead/Base/Base.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | from rpy2.robjects import numpy2ri, r 5 | from rpy2.robjects.vectors import FloatVector 6 | 7 | from subprocess import Popen, PIPE 8 | from .. import config 9 | from ..utils.univariate import compute_y_ts 10 | from ..utils.multivariate import compute_y_mts 11 | from ..utils.unimultivariate import compute_input_dates, compute_output_dates 12 | 13 | 14 | class Base(object): 15 | 16 | def __init__(self, h=5, level=95, date_formatting="ms", seed=123): 17 | 18 | self.h = h 19 | self.level = level 20 | self.date_formatting = date_formatting 21 | self.seed = seed 22 | self.frequency = None 23 | self.series_names = None 24 | self.n_series = None 25 | self.type_input = "univariate" # (or "multivariate") 26 | self.B = None 27 | self.input_df = None 28 | self.input_dates = None 29 | self.method = None 30 | self.weights = None 31 | 32 | self.input_ts_ = None # input time series 33 | self.mean_ = None 34 | self.lower_ = None 35 | self.upper_ = None 36 | self.sims_ = None 37 | self.output_dates_ = None 38 | self.result_dfs_ = None 39 | 40 | R_IS_INSTALLED = False 41 | 42 | try: 43 | proc = Popen(["which", "R"], stdout=PIPE, stderr=PIPE) 44 | R_IS_INSTALLED = proc.wait() == 0 45 | except Exception as e: 46 | pass 47 | 48 | if not R_IS_INSTALLED: 49 | raise ImportError("R is not installed! \n" + config.USAGE_MESSAGE) 50 | 51 | def format_input(self): 52 | if self.input_df.shape[1] > 0: 53 | self.input_ts_ = compute_y_mts(self.input_df, self.frequency) 54 | else: 55 | self.input_ts_ = compute_y_ts(self.input_df, self.frequency) 56 | 57 | def init_forecasting_params(self, df): 58 | self.input_df = df 59 | self.series_names = df.columns 60 | self.n_series = len(self.series_names) 61 | self.input_dates = compute_input_dates(df) 62 | self.type_input = "multivariate" if len(df.shape) > 0 else "univariate" 63 | self.output_dates_, self.frequency = compute_output_dates(df, self.h) 64 | 65 | def getsims(self, input_tuple, ix): 66 | n_sims = len(input_tuple) 67 | res = [input_tuple[i].iloc[:, ix].values for i in range(n_sims)] 68 | return np.asarray(res).T 69 | 70 | def get_forecast(self, method=None, xreg=None): 71 | 72 | if method != None: 73 | self.method = method 74 | 75 | if self.method == "armagarch": 76 | self.fcast_ = config.AHEAD_PACKAGE.armagarchf( 77 | y=self.input_ts_, 78 | h=self.h, 79 | level=self.level, 80 | B=self.B, 81 | cl=self.cl, 82 | dist=self.dist, 83 | seed=self.seed, 84 | ) 85 | 86 | if self.method in ("mean", "median", "rw"): 87 | self.fcast_ = config.AHEAD_PACKAGE.basicf( 88 | self.input_ts_, 89 | h=self.h, 90 | level=self.level, 91 | method=self.method, 92 | type_pi=self.type_pi, 93 | block_length=self.block_length, 94 | B=self.B, 95 | seed=self.seed, 96 | ) 97 | 98 | if self.method == "dynrm": 99 | self.fcast_ = config.AHEAD_PACKAGE.dynrmf( 100 | y=self.input_ts_, 101 | h=self.h, 102 | level=self.level, 103 | type_pi=self.type_pi, 104 | ) 105 | 106 | if self.method == "eat": 107 | self.fcast_ = config.AHEAD_PACKAGE.eatf( 108 | y=self.input_ts_, 109 | h=self.h, 110 | level=self.level, 111 | type_pi=self.type_pi, 112 | weights=config.FLOATVECTOR(self.weights), 113 | ) 114 | 115 | if self.method == "ridge2": 116 | if xreg is None: 117 | 118 | self.fcast_ = config.AHEAD_PACKAGE.ridge2f( 119 | self.input_ts_, 120 | h=self.h, 121 | level=self.level, 122 | lags=self.lags, 123 | nb_hidden=self.nb_hidden, 124 | nodes_sim=self.nodes_sim, 125 | activ=self.activation, 126 | a=self.a, 127 | lambda_1=self.lambda_1, 128 | lambda_2=self.lambda_2, 129 | dropout=self.dropout, 130 | type_pi=self.type_pi, 131 | margins=self.margins, 132 | # can be NULL, but in R (use 0 in R instead of NULL for v0.7.0) 133 | block_length=self.block_length, 134 | B=self.B, 135 | type_aggregation=self.type_aggregation, 136 | # can be NULL, but in R (use 0 in R instead of NULL for v0.7.0) 137 | centers=self.centers, 138 | type_clustering=self.type_clustering, 139 | cl=self.cl, 140 | seed=self.seed, 141 | ) 142 | 143 | else: # xreg is not None: 144 | 145 | try: 146 | self.xreg_ = xreg.values 147 | except: 148 | self.xreg_ = config.DEEP_COPY(xreg) 149 | 150 | is_matrix_xreg = len(self.xreg_.shape) > 1 151 | 152 | numpy2ri.activate() 153 | 154 | xreg_ = ( 155 | r.matrix( 156 | FloatVector(self.xreg_.flatten()), 157 | byrow=True, 158 | nrow=self.xreg_.shape[0], 159 | ncol=self.xreg_.shape[1], 160 | ) 161 | if is_matrix_xreg 162 | else r.matrix( 163 | FloatVector(self.xreg_.flatten()), 164 | byrow=True, 165 | nrow=self.xreg_.shape[0], 166 | ncol=1, 167 | ) 168 | ) 169 | 170 | self.fcast_ = config.AHEAD_PACKAGE.ridge2f( 171 | self.input_ts_, 172 | xreg=xreg_, 173 | h=self.h, 174 | level=self.level, 175 | lags=self.lags, 176 | nb_hidden=self.nb_hidden, 177 | nodes_sim=self.nodes_sim, 178 | activ=self.activation, 179 | a=self.a, 180 | lambda_1=self.lambda_1, 181 | lambda_2=self.lambda_2, 182 | dropout=self.dropout, 183 | type_pi=self.type_pi, 184 | margins=self.margins, 185 | # can be NULL, but in R (use 0 in R instead of NULL for v0.7.0) 186 | block_length=self.block_length, 187 | B=self.B, 188 | type_aggregation=self.type_aggregation, 189 | # can be NULL, but in R (use 0 in R instead of NULL for v0.7.0) 190 | centers=self.centers, 191 | type_clustering=self.type_clustering, 192 | cl=self.cl, 193 | seed=self.seed, 194 | ) 195 | 196 | if self.method == "var": 197 | self.fcast_ = config.AHEAD_PACKAGE.varf( 198 | self.input_ts_, 199 | h=self.h, 200 | level=self.level, 201 | lags=self.lags, 202 | type_VAR=self.type_VAR, 203 | ) 204 | 205 | def plot(self, series, type_axis="dates", type_plot="pi"): 206 | """Plot time series forecast 207 | 208 | Parameters: 209 | 210 | series: {integer} or {string} 211 | series index or name 212 | """ 213 | assert all( 214 | [ 215 | self.mean_ is not None, 216 | self.lower_ is not None, 217 | self.upper_ is not None, 218 | self.output_dates_ is not None, 219 | ] 220 | ), "model forecasting must be obtained first (with `forecast` method)" 221 | 222 | if isinstance(series, str): 223 | assert ( 224 | series in self.series_names 225 | ), f"series {series} doesn't exist in the input dataset" 226 | series_idx = self.input_df.columns.get_loc(series) 227 | else: 228 | assert isinstance(series, int) and ( 229 | 0 <= series < self.n_series 230 | ), f"check series index (< {self.n_series})" 231 | series_idx = series 232 | 233 | y_all = list(self.input_df.iloc[:, series_idx]) + list( 234 | self.result_dfs_[series_idx]["mean"].values 235 | ) 236 | 237 | y_test = list(self.result_dfs_[series_idx]["mean"].values) 238 | n_points_all = len(y_all) 239 | n_points_train = self.input_df.shape[0] 240 | 241 | if type_axis == "numeric": 242 | x_all = [i for i in range(n_points_all)] 243 | x_test = [i for i in range(n_points_train, n_points_all)] 244 | 245 | if type_axis == "dates": # use dates 246 | x_train = [date.strftime("%Y-%m-%d") for date in self.input_dates] 247 | x_test = [date.strftime("%Y-%m-%d") for date in self.output_dates_] 248 | x_all = np.concatenate((x_train, x_test), axis=None) 249 | 250 | if type_plot == "pi": 251 | fig, ax = plt.subplots() 252 | ax.plot(x_all, y_all, "-") 253 | ax.plot(x_test, y_test, "-", color="orange") 254 | ax.fill_between( 255 | x_test, 256 | self.result_dfs_[series_idx]["lower"].values, 257 | self.result_dfs_[series_idx]["upper"].values, 258 | alpha=0.2, 259 | color="orange", 260 | ) 261 | plt.title( 262 | f"prediction intervals for {series}", 263 | loc="left", 264 | fontsize=12, 265 | fontweight=0, 266 | color="black", 267 | ) 268 | plt.show() 269 | 270 | if type_plot == "spaghetti": 271 | palette = plt.get_cmap("Set1") 272 | sims_ix = self.getsims(self.sims_, series_idx) 273 | plt.plot(x_all, y_all, "-") 274 | for col_ix in range( 275 | sims_ix.shape[1] 276 | ): # avoid this when there are thousands of simulations 277 | plt.plot( 278 | x_test, 279 | sims_ix[:, col_ix], 280 | "-", 281 | color=palette(col_ix), 282 | linewidth=1, 283 | alpha=0.9, 284 | ) 285 | plt.plot(x_all, y_all, "-", color="black") 286 | plt.plot(x_test, y_test, "-", color="blue") 287 | # Add titles 288 | plt.title( 289 | f"{self.B} simulations of {series}", 290 | loc="left", 291 | fontsize=12, 292 | fontweight=0, 293 | color="black", 294 | ) 295 | plt.xlabel("Time") 296 | plt.ylabel("Values") 297 | # Show the graph 298 | plt.show() 299 | -------------------------------------------------------------------------------- /ahead/Base/__init__.py: -------------------------------------------------------------------------------- 1 | from .Base import Base 2 | 3 | __all__ = ["Base"] 4 | -------------------------------------------------------------------------------- /ahead/Basic/BasicForecaster.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ..Base import Base 4 | from ..utils import multivariate as mv 5 | from ..utils import unimultivariate as umv 6 | from .. import config 7 | 8 | 9 | class BasicForecaster(Base): 10 | """Basic forecasting functions for multivariate time series (mean, median, random walk) 11 | 12 | Parameters: 13 | 14 | h: an integer; 15 | forecasting horizon 16 | 17 | level: an integer; 18 | Confidence level for prediction intervals 19 | 20 | method: a string; 21 | Forecasting method, either "mean", "median", or random walk ("rw") 22 | 23 | type_pi: a string; 24 | Type of prediction interval (currently "gaussian", 25 | "bootstrap" (independent), "blockbootstrap" (circular), 26 | "movingblockbootstrap") 27 | 28 | block_length: an integer 29 | length of block for multivariate block bootstrap (`type_pi == blockbootstrap` 30 | or `type_pi == movingblockbootstrap`) 31 | 32 | B: an integer; 33 | Number of replications 34 | 35 | date_formatting: a string; 36 | Currently: 37 | - "original": yyyy-mm-dd 38 | - "ms": milliseconds 39 | 40 | seed: an integer; 41 | reproducibility seed 42 | 43 | Attributes: 44 | 45 | fcast_: an object; 46 | raw result from fitting R's `ahead::ridge2f` through `rpy2` 47 | 48 | averages_: a list of lists; 49 | mean forecast in a list for each series 50 | 51 | ranges_: a list of lists; 52 | lower and upper prediction intervals in a list for each series 53 | 54 | output_dates_: a list; 55 | a list of output dates (associated to forecast) 56 | 57 | mean_: a numpy array 58 | contains series mean forecast as a numpy array 59 | 60 | lower_: a numpy array 61 | contains series lower bound forecast as a numpy array 62 | 63 | upper_: a numpy array 64 | contains series upper bound forecast as a numpy array 65 | 66 | result_dfs_: a tuple of data frames; 67 | each element of the tuple contains 3 columns, 68 | mean forecast, lower + upper prediction intervals, 69 | and a date index 70 | 71 | sims_: currently a tuple of numpy arrays 72 | for `type_pi == bootstrap`, simulations for each series 73 | 74 | Examples: 75 | 76 | ```python 77 | import pandas as pd 78 | from ahead import BasicForecaster 79 | 80 | # Data frame containing the time series 81 | dataset = { 82 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 83 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 84 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 85 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 86 | df = pd.DataFrame(dataset).set_index('date') 87 | print(df) 88 | 89 | # multivariate time series forecasting 90 | r1 = BasicForecaster(h = 5) 91 | r1.forecast(df) 92 | print(r1.result_dfs_) 93 | ``` 94 | 95 | """ 96 | 97 | def __init__( 98 | self, 99 | h=5, 100 | level=95, 101 | method="mean", 102 | type_pi="gaussian", 103 | block_length=5, 104 | B=100, 105 | date_formatting="original", 106 | seed=123, 107 | ): 108 | 109 | super().__init__( 110 | h=h, 111 | level=level, 112 | seed=seed, 113 | ) 114 | 115 | self.method = method 116 | self.type_pi = type_pi 117 | self.block_length = block_length 118 | self.B = B 119 | self.date_formatting = date_formatting 120 | self.input_df = None 121 | 122 | self.fcast_ = None 123 | self.averages_ = None 124 | self.ranges_ = None 125 | self.output_dates_ = [] 126 | self.mean_ = None 127 | self.lower_ = None 128 | self.upper_ = None 129 | self.result_dfs_ = None 130 | self.sims_ = None 131 | 132 | def forecast(self, df): 133 | """Forecasting method from `BasicForecaster` class 134 | 135 | Parameters: 136 | 137 | df: a data frame; 138 | a data frame containing the input time series (see example) 139 | 140 | """ 141 | 142 | # get input dates, output dates, number of series, series names, etc. 143 | self.init_forecasting_params(df) 144 | 145 | # obtain time series object ----- 146 | self.format_input() 147 | 148 | if self.type_pi in ("blockbootstrap", "movingblockbootstrap"): 149 | assert ( 150 | self.block_length is not None 151 | ), "For `type_pi in ('blockbootstrap', 'movingblockbootstrap')`, `block_length` must be not None" 152 | 153 | self.get_forecast() 154 | 155 | # result ----- 156 | ( 157 | self.averages_, 158 | self.ranges_, 159 | _, 160 | ) = mv.format_multivariate_forecast( 161 | n_series=self.n_series, 162 | date_formatting=self.date_formatting, 163 | output_dates=self.output_dates_, 164 | horizon=self.h, 165 | fcast=self.fcast_, 166 | ) 167 | 168 | self.mean_ = np.asarray(self.fcast_.rx2["mean"]) 169 | self.lower_ = np.asarray(self.fcast_.rx2["lower"]) 170 | self.upper_ = np.asarray(self.fcast_.rx2["upper"]) 171 | 172 | self.result_dfs_ = tuple( 173 | umv.compute_result_df(self.averages_[i], self.ranges_[i]) 174 | for i in range(self.n_series) 175 | ) 176 | 177 | if self.type_pi in ( 178 | "bootstrap", 179 | "blockbootstrap", 180 | "movingblockbootstrap", 181 | ): 182 | self.sims_ = tuple( 183 | np.asarray(self.fcast_.rx2["sims"][i]) for i in range(self.B) 184 | ) 185 | 186 | return self 187 | -------------------------------------------------------------------------------- /ahead/Basic/__init__.py: -------------------------------------------------------------------------------- 1 | from .BasicForecaster import BasicForecaster 2 | 3 | __all__ = ["BasicForecaster"] 4 | -------------------------------------------------------------------------------- /ahead/DynamicRegressor/DynamicRegressor.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ..Base import Base 4 | from ..utils import univariate as uv 5 | from ..utils import unimultivariate as umv 6 | from .. import config 7 | 8 | 9 | class DynamicRegressor(Base): 10 | """Dynamic Regression Model adapted from R's `forecast::nnetar` 11 | 12 | Parameters: 13 | 14 | h: an integer; 15 | forecasting horizon 16 | 17 | level: an integer; 18 | Confidence level for prediction intervals 19 | 20 | type_pi: a string; 21 | Type of prediction interval (currently "gaussian", 22 | ETS: "E", Arima: "A" or Theta: "T") 23 | 24 | date_formatting: a string; 25 | Currently: 26 | - "original": yyyy-mm-dd 27 | - "ms": milliseconds 28 | 29 | Attributes: 30 | 31 | fcast_: an object; 32 | raw result from fitting R's `ahead::dynrmf` through `rpy2` 33 | 34 | averages_: a list; 35 | mean forecast in a list 36 | 37 | ranges_: a list; 38 | lower and upper prediction intervals in a list 39 | 40 | output_dates_: a list; 41 | a list of output dates (associated to forecast) 42 | 43 | mean_: a numpy array 44 | contains series mean forecast as a numpy array 45 | 46 | lower_: a numpy array 47 | contains series lower bound forecast as a numpy array 48 | 49 | upper_: a numpy array 50 | contains series upper bound forecast as a numpy array 51 | 52 | result_df_: a data frame; 53 | contains 3 columns, mean forecast, lower + upper 54 | prediction intervals, and a date index 55 | 56 | Examples: 57 | 58 | ```python 59 | import pandas as pd 60 | from ahead import DynamicRegressor 61 | 62 | # Data frame containing the time series 63 | dataset = { 64 | 'date' : ['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01', '2020-05-01'], 65 | 'value' : [34, 30, 35.6, 33.3, 38.1]} 66 | 67 | df = pd.DataFrame(dataset).set_index('date') 68 | print(df) 69 | 70 | # univariate time series forecasting 71 | d1 = DynamicRegressor(h = 5) 72 | d1.forecast(df) 73 | print(d1.result_df_) 74 | ``` 75 | 76 | """ 77 | 78 | def __init__(self, h=5, level=95, type_pi="E", date_formatting="original"): 79 | 80 | super().__init__( 81 | h=h, 82 | level=level, 83 | ) 84 | 85 | self.type_pi = type_pi 86 | self.date_formatting = date_formatting 87 | self.input_df = None 88 | self.type_input = "univariate" 89 | 90 | self.fcast_ = None 91 | self.averages_ = None 92 | self.ranges_ = None 93 | self.output_dates_ = [] 94 | self.mean_ = [] 95 | self.lower_ = [] 96 | self.upper_ = [] 97 | self.result_df_ = None 98 | 99 | def forecast(self, df): 100 | """Forecasting method from `DynamicRegressor` class 101 | 102 | Parameters: 103 | 104 | df: a data frame; 105 | a data frame containing the input time series (see example) 106 | 107 | """ 108 | 109 | # get input dates, output dates, number of series, series names, etc. 110 | self.init_forecasting_params(df) 111 | 112 | # obtain time series object ----- 113 | self.format_input() 114 | 115 | self.get_forecast("dynrm") 116 | 117 | # result ----- 118 | ( 119 | self.averages_, 120 | self.ranges_, 121 | _, 122 | ) = uv.format_univariate_forecast( 123 | date_formatting=self.date_formatting, 124 | output_dates=self.output_dates_, 125 | horizon=self.h, 126 | fcast=self.fcast_, 127 | ) 128 | 129 | self.mean_ = np.asarray(self.fcast_.rx2["mean"]) 130 | self.lower_ = np.asarray(self.fcast_.rx2["lower"]) 131 | self.upper_ = np.asarray(self.fcast_.rx2["upper"]) 132 | 133 | self.result_df_ = umv.compute_result_df(self.averages_, self.ranges_) 134 | 135 | return self 136 | -------------------------------------------------------------------------------- /ahead/DynamicRegressor/__init__.py: -------------------------------------------------------------------------------- 1 | from .DynamicRegressor import DynamicRegressor 2 | 3 | __all__ = ["DynamicRegressor"] 4 | -------------------------------------------------------------------------------- /ahead/EAT/EAT.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ..Base import Base 4 | from ..utils import univariate as uv 5 | from ..utils import unimultivariate as umv 6 | from .. import config 7 | 8 | 9 | class EAT(Base): 10 | """Combinations of ETS (exponential smoothing), auto.arima and Theta models 11 | 12 | Parameters: 13 | 14 | h: an integer; 15 | forecasting horizon 16 | 17 | level: an integer; 18 | Confidence level for prediction intervals 19 | 20 | weights: a list; 21 | coefficients assigned to each method in the ensemble 22 | 23 | type_pi: a string; 24 | Type of prediction interval (currently "gaussian", 25 | ETS: "E", Arima: "A" or Theta: "T") 26 | 27 | date_formatting: a string; 28 | Currently: 29 | - "original": yyyy-mm-dd 30 | - "ms": milliseconds 31 | 32 | Attributes: 33 | 34 | fcast_: an object; 35 | raw result from fitting R's `ahead::eatf` through `rpy2` 36 | 37 | averages_: a list; 38 | mean forecast in a list 39 | 40 | ranges_: a list; 41 | lower and upper prediction intervals in a list 42 | 43 | output_dates_: a list; 44 | a list of output dates (associated to forecast) 45 | 46 | mean_: a numpy array 47 | contains series mean forecast as a numpy array 48 | 49 | lower_: a numpy array 50 | contains series lower bound forecast as a numpy array 51 | 52 | upper_: a numpy array 53 | contains series upper bound forecast as a numpy array 54 | 55 | result_df_: a data frame; 56 | contains 3 columns, mean forecast, lower + upper 57 | prediction intervals, and a date index 58 | 59 | Examples: 60 | 61 | ```python 62 | import pandas as pd 63 | from ahead import EAT 64 | 65 | # Data frame containing the time series 66 | dataset = { 67 | 'date' : ['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01', '2020-05-01'], 68 | 'value' : [34, 30, 35.6, 33.3, 38.1]} 69 | 70 | df = pd.DataFrame(dataset).set_index('date') 71 | print(df) 72 | 73 | # univariate time series forecasting 74 | e1 = EAT(h = 5) # default, equal weights for each model=[1/3, 1/3, 1/3] 75 | e1.forecast(df) 76 | print(e1.result_df_) 77 | ``` 78 | 79 | """ 80 | 81 | def __init__( 82 | self, 83 | h=5, 84 | level=95, 85 | weights=None, 86 | type_pi="E", 87 | date_formatting="original", 88 | ): 89 | 90 | super().__init__(h=h, level=level) 91 | 92 | if weights is None: 93 | weights = [1 / 3, 1 / 3, 1 / 3] 94 | 95 | assert len(weights) == 3, "must have 'len(weights) == 3'" 96 | 97 | self.weights = weights 98 | self.type_pi = type_pi 99 | self.date_formatting = date_formatting 100 | self.input_df = None 101 | self.type_input = "univariate" 102 | 103 | self.fcast_ = None 104 | self.averages_ = None 105 | self.ranges_ = None 106 | self.output_dates_ = [] 107 | self.mean_ = [] 108 | self.lower_ = [] 109 | self.upper_ = [] 110 | self.result_df_ = None 111 | 112 | def forecast(self, df): 113 | """Forecasting method from `EAT` class 114 | 115 | Parameters: 116 | 117 | df: a data frame; 118 | a data frame containing the input time series (see example) 119 | 120 | """ 121 | 122 | # get input dates, output dates, number of series, series names, etc. 123 | self.init_forecasting_params(df) 124 | 125 | # obtain time series object ----- 126 | self.format_input() 127 | 128 | self.get_forecast("eat") 129 | 130 | # result ----- 131 | ( 132 | self.averages_, 133 | self.ranges_, 134 | _, 135 | ) = uv.format_univariate_forecast( 136 | date_formatting=self.date_formatting, 137 | output_dates=self.output_dates_, 138 | horizon=self.h, 139 | fcast=self.fcast_, 140 | ) 141 | 142 | self.mean_ = np.asarray(self.fcast_.rx2["mean"]) 143 | self.lower_ = np.asarray(self.fcast_.rx2["lower"]) 144 | self.upper_ = np.asarray(self.fcast_.rx2["upper"]) 145 | 146 | self.result_df_ = umv.compute_result_df(self.averages_, self.ranges_) 147 | 148 | return self 149 | -------------------------------------------------------------------------------- /ahead/EAT/__init__.py: -------------------------------------------------------------------------------- 1 | from .EAT import EAT 2 | 3 | __all__ = ["EAT"] 4 | -------------------------------------------------------------------------------- /ahead/FitForecast/FitForecasting.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ..Base import Base 4 | from ..utils import univariate as uv 5 | from ..utils import unimultivariate as umv 6 | from ..utils import multivariate as mv 7 | from .. import config 8 | from rpy2.robjects import NULL as rNULL 9 | 10 | 11 | class FitForecaster(Base): 12 | """Fit and forecast time series with uncertainty quantification 13 | 14 | See https://techtonique.r-universe.dev/ahead/doc/manual.html#fitforecast 15 | 16 | Examples: 17 | 18 | ```python 19 | url = "https://raw.githubusercontent.com/Techtonique/" 20 | url += "datasets/main/time_series/univariate/" 21 | url += "a10.csv" 22 | 23 | df = pd.read_csv(url) 24 | df.index = pd.DatetimeIndex(df.date) # must have 25 | df.drop(columns=['date'], inplace=True) 26 | 27 | # univariate ts forecasting 28 | d1 = FitForecaster() 29 | 30 | print(d1) 31 | 32 | start = time() 33 | print(d1.fit_forecast(df)) 34 | print(f"Elapsed: {time()-start} \n") 35 | 36 | print(f"after: {d1.mean_}") 37 | print(f"after: {d1.lower_}") 38 | print(f"after: {d1.upper_}") 39 | ``` 40 | 41 | """ 42 | 43 | def __init__( 44 | self, 45 | h=None, 46 | level=95, 47 | pct_train=0.9, 48 | pct_calibration=0.5, 49 | B=1000, 50 | seed=17223, 51 | conformalize=False, 52 | type_calibration="splitconformal", 53 | gap=3, 54 | agg="mean", 55 | vol="constant", 56 | type_sim="kde", 57 | date_formatting="original", 58 | ): 59 | 60 | super().__init__( 61 | h=h, 62 | level=level, 63 | ) 64 | 65 | self.pct_train = pct_train 66 | self.pct_calibration = pct_calibration 67 | self.B = B 68 | self.seed = seed 69 | self.conformalize = conformalize 70 | self.type_calibration = type_calibration 71 | self.gap = gap 72 | self.agg = agg 73 | self.vol = vol 74 | self.type_sim = type_sim 75 | self.date_formatting = date_formatting 76 | self.input_df = None 77 | 78 | self.fcast_ = None 79 | self.averages_ = None 80 | self.ranges_ = None 81 | self.output_dates_ = [] 82 | self.mean_ = [] 83 | self.lower_ = [] 84 | self.upper_ = [] 85 | self.result_df_ = None 86 | 87 | def fit_forecast(self, df, method="thetaf"): 88 | 89 | assert method in ( 90 | "thetaf", 91 | "arima", 92 | "ets", 93 | "te", 94 | "tbats", 95 | "tslm", 96 | "dynrmf", 97 | "ridge2f", 98 | "naive", 99 | "snaive", 100 | ), 'must have method in ("thetaf", "arima", "ets", "te", "tbats", "tslm", "dynrmf", "ridge2f", "naive", "snaive")' 101 | 102 | # keep it in this order 103 | h = None 104 | if self.h is not None: 105 | h = self.h 106 | else: 107 | self.h = df.shape[0] - int(np.floor(df.shape[0] * self.pct_train)) 108 | 109 | # get input dates, output dates, number of series, series names, etc. 110 | self.init_forecasting_params(df) 111 | 112 | # obtain time series object ----- 113 | self.format_input() 114 | 115 | self.method = method 116 | 117 | self.fcast_ = config.AHEAD_PACKAGE.fitforecast( 118 | y=self.input_ts_, 119 | h=rNULL if h is None else h, 120 | pct_train=self.pct_train, 121 | pct_calibration=self.pct_calibration, 122 | method=self.method, 123 | level=self.level, 124 | B=self.B, 125 | seed=self.seed, 126 | conformalize=self.conformalize, 127 | type_calibration=self.type_calibration, 128 | ) 129 | 130 | # result ----- 131 | if df.shape[1] > 1: 132 | ( 133 | self.averages_, 134 | self.ranges_, 135 | _, 136 | ) = mv.format_multivariate_forecast( 137 | n_series=self.n_series, 138 | date_formatting=self.date_formatting, 139 | output_dates=self.output_dates_, 140 | horizon=self.h, 141 | fcast=self.fcast_, 142 | ) 143 | else: 144 | ( 145 | self.averages_, 146 | self.ranges_, 147 | _, 148 | ) = uv.format_univariate_forecast( 149 | date_formatting=self.date_formatting, 150 | output_dates=self.output_dates_, 151 | horizon=self.h, 152 | fcast=self.fcast_, 153 | ) 154 | 155 | self.mean_ = np.asarray(self.fcast_.rx2["mean"]) 156 | self.lower_ = np.asarray(self.fcast_.rx2["lower"]) 157 | self.upper_ = np.asarray(self.fcast_.rx2["upper"]) 158 | 159 | self.result_dfs_ = umv.compute_result_df(self.averages_, self.ranges_) 160 | 161 | if "sims" in list(self.fcast_.names): 162 | self.sims_ = tuple( 163 | np.asarray(self.fcast_.rx2["sims"][i]) for i in range(self.B) 164 | ) 165 | 166 | return self 167 | -------------------------------------------------------------------------------- /ahead/FitForecast/__init__.py: -------------------------------------------------------------------------------- 1 | from .FitForecasting import FitForecaster 2 | 3 | __all__ = ["FitForecaster"] 4 | -------------------------------------------------------------------------------- /ahead/Ridge2/Ridge2Regressor.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import rpy2.robjects.conversion as cv 4 | from rpy2.robjects import ( 5 | default_converter, 6 | FloatVector, 7 | ListVector, 8 | numpy2ri, 9 | r, 10 | ) 11 | 12 | from ..Base import Base 13 | from ..utils import multivariate as mv 14 | from ..utils import unimultivariate as umv 15 | from .. import config 16 | 17 | 18 | class Ridge2Regressor(Base): 19 | """Random Vector functional link network model with 2 regularization parameters 20 | 21 | Parameters: 22 | 23 | h: an integer; 24 | forecasting horizon 25 | 26 | level: an integer; 27 | Confidence level for prediction intervals 28 | 29 | lags: an integer; 30 | Number of lags 31 | 32 | nb_hidden: an integer; 33 | Number of nodes in hidden layer 34 | 35 | nodes_sim: an integer; 36 | Type of simulation for nodes in the hidden layer 37 | ("sobol", "halton", "unif") 38 | 39 | activation: a string; 40 | Activation function ("relu", "sigmoid", "tanh", 41 | "leakyrelu", "elu", "linear") 42 | 43 | a: a float; 44 | hyperparameter for activation function "leakyrelu", "elu" 45 | 46 | lambda_1: a float; 47 | Regularization parameter for original predictors 48 | 49 | lambda_2: a float; 50 | Regularization parameter for transformed predictors 51 | 52 | dropout: a float; 53 | dropout regularization parameter (dropping nodes in hidden layer) 54 | 55 | type_pi: a string; 56 | Type of prediction interval (currently "gaussian", 57 | "bootstrap", (circular) "blockbootstrap", "movingblockbootstrap", or "rvinecopula") 58 | 59 | block_length: an integer 60 | length of block for multivariate block bootstrap (`type_pi == blockbootstrap` or 61 | `type_pi == movingblockbootstrap`) 62 | 63 | margins: a string; 64 | distribution of residuals' marginals for `type_pi == rvinecopula`: "empirical" (default), 65 | "gaussian" 66 | 67 | B: an integer; 68 | Number of bootstrap replications for `type_pi == bootstrap`, "blockbootstrap", 69 | "movingblockbootstrap", or "rvinecopula" 70 | 71 | type_aggregation: a string; 72 | Type of aggregation, ONLY for bootstrapping; either "mean" or "median" 73 | 74 | centers: an integer; 75 | Number of clusters for \code{type_clustering} 76 | 77 | type_clustering: a string; 78 | "kmeans" (K-Means clustering) or "hclust" (Hierarchical clustering) 79 | 80 | cl: an integer; 81 | The number of clusters for parallel execution (done in R), for `type_pi == bootstrap` 82 | 83 | date_formatting: a string; 84 | Currently: 85 | - "original": yyyy-mm-dd 86 | - "ms": milliseconds 87 | 88 | seed: an integer; 89 | reproducibility seed for type_pi == 'bootstrap' 90 | 91 | Attributes: 92 | 93 | fcast_: an object; 94 | raw result from fitting R's `ahead::ridge2f` through `rpy2` 95 | 96 | averages_: a list of lists; 97 | mean forecast in a list for each series 98 | 99 | ranges_: a list of lists; 100 | lower and upper prediction intervals in a list for each series 101 | 102 | output_dates_: a list; 103 | a list of output dates (associated to forecast) 104 | 105 | mean_: a numpy array 106 | contains series mean forecast as a numpy array 107 | 108 | lower_: a numpy array 109 | contains series lower bound forecast as a numpy array 110 | 111 | upper_: a numpy array 112 | contains series upper bound forecast as a numpy array 113 | 114 | result_dfs_: a tuple of data frames; 115 | each element of the tuple contains 3 columns, 116 | mean forecast, lower + upper prediction intervals, 117 | and a date index 118 | 119 | sims_: currently a tuple of numpy arrays 120 | for `type_pi == bootstrap`, simulations for each series 121 | 122 | Examples: 123 | 124 | ```python 125 | import pandas as pd 126 | from ahead import Ridge2Regressor 127 | 128 | # Data frame containing the time series 129 | dataset = { 130 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 131 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 132 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 133 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 134 | df = pd.DataFrame(dataset).set_index('date') 135 | print(df) 136 | 137 | # multivariate time series forecasting 138 | r1 = Ridge2Regressor(h = 5) 139 | r1.forecast(df) 140 | print(r1.result_dfs_) 141 | ``` 142 | 143 | """ 144 | 145 | def __init__( 146 | self, 147 | h=5, 148 | level=95, 149 | lags=1, 150 | nb_hidden=5, 151 | nodes_sim="sobol", 152 | activation="relu", 153 | a=0.01, 154 | lambda_1=0.1, 155 | lambda_2=0.1, 156 | dropout=0, 157 | type_pi="gaussian", 158 | # can be NULL, but in R (use 0 in R instead of NULL for v0.7.0) 159 | block_length=3, 160 | margins="empirical", 161 | B=100, 162 | type_aggregation="mean", 163 | centers=2, 164 | type_clustering="kmeans", 165 | cl=1, 166 | date_formatting="original", 167 | seed=123, 168 | ): 169 | 170 | super().__init__( 171 | h=h, 172 | level=level, 173 | seed=seed, 174 | ) 175 | 176 | self.lags = lags 177 | self.nb_hidden = nb_hidden 178 | self.nodes_sim = nodes_sim 179 | self.activation = activation 180 | self.a = a 181 | self.lambda_1 = lambda_1 182 | self.lambda_2 = lambda_2 183 | self.dropout = dropout 184 | self.type_pi = type_pi 185 | self.block_length = block_length 186 | self.margins = margins 187 | self.B = B 188 | self.type_aggregation = type_aggregation 189 | # can be NULL, but in R (use 0 in R instead of NULL for v0.7.0) 190 | self.centers = centers 191 | self.type_clustering = type_clustering 192 | self.cl = cl 193 | self.date_formatting = date_formatting 194 | self.seed = seed 195 | self.input_df = None 196 | self.type_input = "multivariate" 197 | 198 | self.fcast_ = None 199 | self.averages_ = None 200 | self.ranges_ = None 201 | self.output_dates_ = [] 202 | self.mean_ = None 203 | self.lower_ = None 204 | self.upper_ = None 205 | self.result_dfs_ = None 206 | self.sims_ = None 207 | self.xreg_ = None 208 | 209 | def forecast(self, df, xreg=None): 210 | """Forecasting method from `Ridge2Regressor` class 211 | 212 | Parameters: 213 | 214 | df: a data frame; 215 | a data frame containing the input time series (see example) 216 | 217 | xreg: a numpy array or a data frame; 218 | external regressors 219 | 220 | """ 221 | 222 | # get input dates, output dates, number of series, series names, etc. 223 | self.init_forecasting_params(df) 224 | 225 | # obtain time series object ----- 226 | self.format_input() 227 | 228 | self.get_forecast("ridge2") 229 | 230 | # result ----- 231 | ( 232 | self.averages_, 233 | self.ranges_, 234 | _, 235 | ) = mv.format_multivariate_forecast( 236 | n_series=self.n_series, 237 | date_formatting=self.date_formatting, 238 | output_dates=self.output_dates_, 239 | horizon=self.h, 240 | fcast=self.fcast_, 241 | ) 242 | 243 | self.mean_ = np.asarray(self.fcast_.rx2["mean"]) 244 | self.lower_ = np.asarray(self.fcast_.rx2["lower"]) 245 | self.upper_ = np.asarray(self.fcast_.rx2["upper"]) 246 | 247 | self.result_dfs_ = tuple( 248 | umv.compute_result_df(self.averages_[i], self.ranges_[i]) 249 | for i in range(self.n_series) 250 | ) 251 | 252 | if self.type_pi in ( 253 | "bootstrap", 254 | "blockbootstrap", 255 | "movingblockbootstrap", 256 | "rvinecopula", 257 | ): 258 | self.sims_ = tuple( 259 | np.asarray(self.fcast_.rx2["sims"][i]) for i in range(self.B) 260 | ) 261 | 262 | return self 263 | -------------------------------------------------------------------------------- /ahead/Ridge2/__init__.py: -------------------------------------------------------------------------------- 1 | from .Ridge2Regressor import Ridge2Regressor 2 | 3 | __all__ = ["Ridge2Regressor"] 4 | -------------------------------------------------------------------------------- /ahead/VAR/VAR.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ..Base import Base 4 | from ..utils import multivariate as mv 5 | from ..utils import unimultivariate as umv 6 | from .. import config 7 | 8 | 9 | class VAR(Base): 10 | """Vector AutoRegressive model 11 | 12 | Parameters: 13 | 14 | h: an integer; 15 | forecasting horizon 16 | 17 | level: an integer; 18 | Confidence level for prediction intervals 19 | 20 | lags: an integer; 21 | the lag order 22 | 23 | type_VAR: a string; 24 | Type of deterministic regressors to include 25 | ("const", "trend", "both", "none") 26 | 27 | date_formatting: a string; 28 | Currently: 29 | - "original": yyyy-mm-dd 30 | - "ms": milliseconds 31 | 32 | Attributes: 33 | 34 | fcast_: an object; 35 | raw result from fitting R's `ahead::varf` through `rpy2` 36 | 37 | averages_: a list of lists; 38 | mean forecast in a list for each series 39 | 40 | ranges_: a list of lists; 41 | lower and upper prediction intervals in a list for each series 42 | 43 | output_dates_: a list; 44 | a list of output dates (associated to forecast) 45 | 46 | mean_: a numpy array 47 | contains series mean forecast as a numpy array 48 | 49 | lower_: a numpy array 50 | contains series lower bound forecast as a numpy array 51 | 52 | upper_: a numpy array 53 | contains series upper bound forecast as a numpy array 54 | 55 | result_dfs_: a tuple of data frames; 56 | each element of the tuple contains 3 columns, 57 | mean forecast, lower + upper prediction intervals, 58 | and a date index 59 | 60 | Examples: 61 | 62 | ```python 63 | import pandas as pd 64 | from ahead import VAR 65 | 66 | # Data frame containing the time series 67 | dataset = { 68 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 69 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 70 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 71 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 72 | df = pd.DataFrame(dataset).set_index('date') 73 | print(df) 74 | 75 | # multivariate time series forecasting 76 | v1 = VAR(h = 5, date_formatting = "original", type_VAR="none") 77 | v1.forecast(df) 78 | print(v1.result_dfs_) 79 | ``` 80 | 81 | """ 82 | 83 | def __init__( 84 | self, h=5, level=95, lags=1, type_VAR="none", date_formatting="original" 85 | ): # type_VAR = "const", "trend", "both", "none" 86 | 87 | assert type_VAR in ( 88 | "const", 89 | "trend", 90 | "both", 91 | "none", 92 | ), "must have: type_VAR in ('const', 'trend', 'both', 'none')" 93 | 94 | super().__init__( 95 | h=h, 96 | level=level, 97 | ) 98 | 99 | self.lags = lags 100 | self.type_VAR = type_VAR 101 | self.date_formatting = date_formatting 102 | self.input_df = None 103 | 104 | self.fcast_ = None 105 | self.averages_ = None 106 | self.ranges_ = None 107 | self.output_dates_ = [] 108 | self.mean_ = None 109 | self.lower_ = None 110 | self.upper_ = None 111 | self.result_dfs_ = None 112 | 113 | def forecast(self, df): 114 | """Forecasting method from `VAR` class 115 | 116 | Parameters: 117 | 118 | df: a data frame; 119 | a data frame containing the input time series (see example) 120 | 121 | """ 122 | 123 | # get input dates, output dates, number of series, series names, etc. 124 | self.init_forecasting_params(df) 125 | 126 | # obtain time series object ----- 127 | self.format_input() 128 | 129 | self.get_forecast("var") 130 | 131 | # result ----- 132 | ( 133 | self.averages_, 134 | self.ranges_, 135 | _, 136 | ) = mv.format_multivariate_forecast( 137 | n_series=self.n_series, 138 | date_formatting=self.date_formatting, 139 | output_dates=self.output_dates_, 140 | horizon=self.h, 141 | fcast=self.fcast_, 142 | ) 143 | 144 | self.mean_ = np.asarray(self.fcast_.rx2["mean"]) 145 | self.lower_ = np.asarray(self.fcast_.rx2["lower"]) 146 | self.upper_ = np.asarray(self.fcast_.rx2["upper"]) 147 | 148 | self.result_dfs_ = tuple( 149 | umv.compute_result_df(self.averages_[i], self.ranges_[i]) 150 | for i in range(self.n_series) 151 | ) 152 | 153 | return self 154 | -------------------------------------------------------------------------------- /ahead/VAR/__init__.py: -------------------------------------------------------------------------------- 1 | from .VAR import VAR 2 | 3 | __all__ = ["VAR"] 4 | -------------------------------------------------------------------------------- /ahead/__init__.py: -------------------------------------------------------------------------------- 1 | """Top-level package for ahead.""" 2 | 3 | __author__ = """T. Moudiki""" 4 | __email__ = "thierry.moudiki@gmail.com" 5 | __version__ = "0.10.0" 6 | 7 | from .ARMAGARCH import ArmaGarch 8 | from .Basic import BasicForecaster 9 | from .DynamicRegressor import DynamicRegressor 10 | from .EAT import EAT 11 | from .FitForecast import FitForecaster 12 | from .Ridge2 import Ridge2Regressor 13 | from .VAR import VAR 14 | 15 | 16 | __all__ = [ 17 | "ArmaGarch", 18 | "BasicForecaster", 19 | "DynamicRegressor", 20 | "EAT", 21 | "FitForecaster", 22 | "Ridge2Regressor", 23 | "VAR", 24 | ] 25 | -------------------------------------------------------------------------------- /ahead/config.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import rpy2 3 | 4 | try: 5 | import rpy2.robjects.packages as rpackages 6 | from rpy2.robjects.packages import importr 7 | from rpy2.robjects.vectors import FloatVector, StrVector 8 | from rpy2 import rinterface, robjects 9 | from rpy2.robjects import r, default_converter 10 | from rpy2.rinterface import RRuntimeWarning 11 | from rpy2.rinterface_lib import callbacks 12 | from rpy2.rinterface_lib.embedded import RRuntimeError 13 | import rpy2.robjects.conversion as cv 14 | except ImportError as e: 15 | RPY2_ERROR_MESSAGE = str(e) 16 | RPY2_IS_INSTALLED = False 17 | else: 18 | RPY2_IS_INSTALLED = True 19 | 20 | USAGE_MESSAGE = """ 21 | This Python class is based on R package 'ahead' (https://techtonique.github.io/ahead/). 22 | You need to install R (https://www.r-project.org/) and rpy2 (https://pypi.org/project/rpy2/). 23 | 24 | Then, install R package 'ahead' (if necessary): 25 | >> R -e 'install.packages("ahead", repos = https://techtonique.r-universe.dev)' 26 | """ 27 | 28 | r["options"](warn=-1) 29 | 30 | 31 | def _none2null(none_obj): 32 | return r("NULL") 33 | 34 | 35 | none_converter = cv.Converter("None converter") 36 | none_converter.py2rpy.register(type(None), _none2null) 37 | 38 | required_packages = ["ahead"] # list of required R packages 39 | 40 | packages_to_install = [ 41 | x for x in required_packages if not rpackages.isinstalled(x) 42 | ] 43 | 44 | utils = importr("utils") 45 | graphics = importr("graphics") 46 | base = importr("base") 47 | 48 | # print(f" required_packages: {required_packages} \n") 49 | # print(f" packages_to_install: {packages_to_install} \n") 50 | # print(f" len(packages_to_install): {len(packages_to_install)} \n") 51 | 52 | if len(packages_to_install) > 0: 53 | base.options( 54 | repos=base.c( 55 | techtonique="https://techtonique.r-universe.dev", 56 | CRAN="https://cloud.r-project.org", 57 | ) 58 | ) 59 | utils.install_packages( 60 | StrVector(packages_to_install) 61 | ) # dependencies of dependencies nightmare... 62 | 63 | # check R version 64 | # print(f"R version: {utils.packageVersion('ahead')}") 65 | 66 | FLOATVECTOR = FloatVector 67 | AHEAD_PACKAGE = importr("ahead") 68 | CHECK_PACKAGES = True 69 | 70 | 71 | def DEEP_COPY(x): 72 | return pickle.loads(pickle.dumps(x, -1)) 73 | 74 | 75 | NONE_CONVERTER = none_converter 76 | -------------------------------------------------------------------------------- /ahead/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .unimultivariate import ( 2 | compute_output_dates, 3 | compute_result_df, 4 | get_frequency, 5 | ) 6 | from .univariate import compute_y_ts, format_univariate_forecast 7 | from .multivariate import compute_y_mts, format_multivariate_forecast 8 | from .tscv_indices import get_tscv_indices 9 | 10 | __all__ = [ 11 | "compute_output_dates", 12 | "compute_y_ts", 13 | "format_univariate_forecast", 14 | "compute_y_mts", 15 | "format_multivariate_forecast", 16 | "compute_result_df", 17 | "get_frequency", 18 | "get_tscv_indices", 19 | ] 20 | -------------------------------------------------------------------------------- /ahead/utils/multivariate.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | 5 | import rpy2.robjects as robjects 6 | import rpy2.robjects.packages as rpackages 7 | from rpy2.robjects.packages import importr 8 | from rpy2.robjects import FloatVector, r 9 | from datetime import datetime 10 | from rpy2.robjects.vectors import StrVector 11 | from .unimultivariate import get_frequency 12 | 13 | base = importr("base") 14 | stats = importr("stats") 15 | 16 | 17 | def compute_y_mts(df, df_frequency): 18 | 19 | input_series = df.to_numpy() 20 | 21 | input_series_tolist = input_series.tolist() 22 | xx = [item for sublist in input_series_tolist for item in sublist] 23 | 24 | ts = r.matrix( 25 | FloatVector(xx), 26 | byrow=True, 27 | nrow=len(input_series_tolist), 28 | ncol=df.shape[1], 29 | ) 30 | 31 | # ts.colnames = StrVector(df.columns.tolist()) 32 | 33 | return stats.ts(ts, frequency=get_frequency(df_frequency)) 34 | 35 | 36 | def format_multivariate_forecast( 37 | n_series, date_formatting, output_dates, horizon, fcast 38 | ): 39 | if date_formatting == "original": 40 | output_dates_ = [ 41 | datetime.strftime(output_dates[i], "%Y-%m-%d") 42 | for i in range(horizon) 43 | ] 44 | 45 | if date_formatting == "ms": 46 | output_dates_ = [ 47 | int( 48 | datetime.strptime(str(output_dates[i]), "%Y-%m-%d").timestamp() 49 | * 1000 50 | ) 51 | for i in range(horizon) 52 | ] 53 | 54 | mean_array = np.asarray(fcast.rx2["mean"], dtype="float64") 55 | lower_array = np.asarray(fcast.rx2["lower"], dtype="float64") 56 | upper_array = np.asarray(fcast.rx2["upper"], dtype="float64") 57 | 58 | averages = [] 59 | ranges = [] 60 | 61 | for j in range(n_series): 62 | averages_series_j = [] 63 | ranges_series_j = [] 64 | for i in range(horizon): 65 | date_i = output_dates_[i] 66 | averages_series_j.append([date_i, mean_array[i, j]]) 67 | ranges_series_j.append( 68 | [ 69 | date_i, 70 | lower_array[i, j], 71 | upper_array[i, j], 72 | ] 73 | ) 74 | averages.append(averages_series_j) 75 | ranges.append(ranges_series_j) 76 | 77 | return averages, ranges, output_dates_ 78 | -------------------------------------------------------------------------------- /ahead/utils/tscv_indices.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def get_tscv_indices( 5 | n=25, p=0.8, initial_window=5, horizon=3, fixed_window=False 6 | ): 7 | """Generate indices to split data into training and test set. 8 | 9 | Parameters 10 | ---------- 11 | n : int, input series length. 12 | 13 | p : float, proportion of data in the training set 14 | 15 | initial_window : int, initial number of consecutive values in each 16 | training set sample 17 | 18 | horizon : int, number of consecutive values in test set sample 19 | 20 | fixed_window : boolean, fixed window or increasing window 21 | 22 | """ 23 | 24 | # Initialization of indices ----- 25 | 26 | indices = np.arange(n) 27 | train_test_indices = [] 28 | hold_out_indices = [] 29 | 30 | # train index 31 | min_index_train = 0 32 | max_index_train = initial_window 33 | 34 | # test index 35 | min_index_test = max_index_train 36 | max_index_test = initial_window + horizon 37 | 38 | # Main loop ----- 39 | 40 | if p == 1: 41 | 42 | if fixed_window == True: 43 | 44 | while max_index_test <= n: 45 | 46 | train_test_indices.append( 47 | { 48 | "train": indices[min_index_train:max_index_train], 49 | "test": indices[min_index_test:max_index_test], 50 | } 51 | ) 52 | 53 | min_index_train += 1 54 | min_index_test += 1 55 | max_index_train += 1 56 | max_index_test += 1 57 | 58 | else: # fixed_window == False 59 | 60 | while max_index_test <= n: 61 | 62 | train_test_indices.append( 63 | { 64 | "train": indices[min_index_train:max_index_train], 65 | "test": indices[min_index_test:max_index_test], 66 | } 67 | ) 68 | 69 | max_index_train += 1 70 | min_index_test += 1 71 | max_index_test += 1 72 | 73 | return train_test_indices 74 | 75 | # else if p < 1 76 | 77 | if fixed_window == True: 78 | 79 | while max_index_test <= n: 80 | 81 | if max_index_test <= int(p * n): 82 | 83 | train_test_indices.append( 84 | { 85 | "train": indices[min_index_train:max_index_train], 86 | "test": indices[min_index_test:max_index_test], 87 | } 88 | ) 89 | 90 | else: 91 | 92 | hold_out_indices.append( 93 | { 94 | "train": indices[min_index_train:max_index_train], 95 | "test": indices[min_index_test:max_index_test], 96 | } 97 | ) 98 | 99 | min_index_train += 1 100 | min_index_test += 1 101 | max_index_train += 1 102 | max_index_test += 1 103 | 104 | else: # fixed_window == False 105 | 106 | while max_index_test <= n: 107 | 108 | if max_index_test <= int(p * n): 109 | 110 | train_test_indices.append( 111 | { 112 | "train": indices[min_index_train:max_index_train], 113 | "test": indices[min_index_test:max_index_test], 114 | } 115 | ) 116 | 117 | else: 118 | 119 | hold_out_indices.append( 120 | { 121 | "train": indices[min_index_train:max_index_train], 122 | "test": indices[min_index_test:max_index_test], 123 | } 124 | ) 125 | 126 | max_index_train += 1 127 | min_index_test += 1 128 | max_index_test += 1 129 | 130 | return train_test_indices, hold_out_indices 131 | -------------------------------------------------------------------------------- /ahead/utils/unimultivariate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | from difflib import SequenceMatcher 4 | 5 | 6 | # compute input dates from data frame's index 7 | def compute_input_dates(df): 8 | 9 | input_dates = df.index.values 10 | 11 | frequency = pd.infer_freq(pd.DatetimeIndex(input_dates)) 12 | 13 | input_dates = pd.date_range( 14 | start=input_dates[0], periods=len(input_dates), freq=frequency 15 | ).values.tolist() 16 | 17 | df_input_dates = pd.DataFrame({"date": input_dates}) 18 | 19 | input_dates = pd.to_datetime(df_input_dates["date"]).dt.date 20 | 21 | return input_dates 22 | 23 | 24 | # compute output dates from data frame's index 25 | def compute_output_dates(df, horizon): 26 | input_dates = df.index.values 27 | 28 | if input_dates[0] == 0: 29 | input_dates = pd.date_range( 30 | start=pd.Timestamp.today().strftime("%Y-%m-%d"), periods=horizon 31 | ) 32 | 33 | # print(f"\n in nnetsauce.utils.timeseries 1: {input_dates} \n") 34 | 35 | frequency = pd.infer_freq(pd.DatetimeIndex(input_dates)) 36 | 37 | # print(f"\n in nnetsauce.utils.timeseries 2: {frequency} \n") 38 | 39 | output_dates = np.delete( 40 | pd.date_range( 41 | start=input_dates[-1], periods=horizon + 1, freq=frequency 42 | ).values, 43 | 0, 44 | ).tolist() 45 | 46 | # print(f"\n in nnetsauce.utils.timeseries 3: {output_dates} \n") 47 | 48 | df_output_dates = pd.DataFrame({"date": output_dates}) 49 | 50 | output_dates = pd.to_datetime(df_output_dates["date"]).dt.date 51 | 52 | return output_dates, frequency 53 | 54 | 55 | def compute_result_df(averages, ranges): 56 | try: 57 | pred_mean = pd.Series(dict(averages)).to_frame("mean") 58 | except Exception: 59 | pred_mean = pd.Series(averages).to_frame("mean") 60 | pred_ci = pd.DataFrame( 61 | ranges, columns=["date", "lower", "upper"] 62 | ).set_index("date") 63 | return pd.concat([pred_mean, pred_ci], axis=1) 64 | 65 | 66 | def get_closest_str(input_str, list_choices): 67 | scores = np.asarray( 68 | [ 69 | SequenceMatcher(None, a=input_str, b=elt).ratio() 70 | for elt in list_choices 71 | ] 72 | ) 73 | return list_choices[np.where(scores == np.max(scores))[0][0]] 74 | 75 | 76 | def get_frequency(input_str): 77 | # https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases 78 | """https://otexts.com/fpp2/ts-objects.html#frequency-of-a-time-series 79 | Data frequency 80 | Annual 1 81 | Quarterly 4 82 | Monthly 12 83 | Weekly 52 84 | """ 85 | frequency_choices = { 86 | "A": 1, 87 | "Y": 1, 88 | "BA": 1, 89 | "BY": 1, 90 | "AS": 1, 91 | "YS": 1, 92 | "BAS": 1, 93 | "BYS": 1, 94 | "Q": 4, 95 | "BQ": 4, 96 | "QS": 4, 97 | "BQS": 4, 98 | "M": 12, 99 | "BM": 12, 100 | "CBM": 12, 101 | "MS": 12, 102 | "BMS": 12, 103 | "CBMS": 12, 104 | "W": 52, 105 | "B": 365, 106 | "C": 365, 107 | "D": 365, 108 | } 109 | 110 | try: 111 | 112 | return frequency_choices[input_str] 113 | 114 | except: 115 | 116 | list_frequency_choices = list(frequency_choices.keys()) 117 | 118 | closest_str = get_closest_str( 119 | input_str=input_str, list_choices=list_frequency_choices 120 | ) 121 | 122 | return frequency_choices[closest_str] 123 | -------------------------------------------------------------------------------- /ahead/utils/univariate.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | 5 | import rpy2.robjects as robjects 6 | import rpy2.robjects.packages as rpackages 7 | from rpy2.robjects.packages import importr 8 | from rpy2.robjects import FloatVector 9 | from datetime import datetime 10 | from rpy2.robjects.vectors import StrVector 11 | from .unimultivariate import get_frequency 12 | 13 | base = importr("base") 14 | stats = importr("stats") 15 | 16 | 17 | def compute_y_ts(df, df_frequency): 18 | 19 | input_series = df.to_numpy() 20 | 21 | ts = stats.ts( 22 | FloatVector(input_series.flatten()), 23 | frequency=get_frequency(df_frequency), 24 | ) 25 | 26 | return ts 27 | 28 | 29 | def format_univariate_forecast(date_formatting, output_dates, horizon, fcast): 30 | 31 | if date_formatting == "original": 32 | output_dates_ = [ 33 | datetime.strftime(output_dates[i], "%Y-%m-%d") 34 | for i in range(horizon) 35 | ] 36 | 37 | if date_formatting == "ms": 38 | output_dates_ = [ 39 | int( 40 | datetime.strptime(str(output_dates[i]), "%Y-%m-%d").timestamp() 41 | * 1000 42 | ) 43 | for i in range(horizon) 44 | ] 45 | 46 | averages = [ 47 | [output_dates_[i], fcast.rx2["mean"][i]] for i in range(horizon) 48 | ] 49 | ranges = [ 50 | [output_dates_[i], fcast.rx2["lower"][i], fcast.rx2["upper"][i]] 51 | for i in range(horizon) 52 | ] 53 | 54 | return averages, ranges, output_dates_ 55 | -------------------------------------------------------------------------------- /datasets/AirPassengers.csv: -------------------------------------------------------------------------------- 1 | "date","value" 2 | 1949-01-01,112.0 3 | 1949-02-01,118.0 4 | 1949-03-01,132.0 5 | 1949-04-01,129.0 6 | 1949-05-01,121.0 7 | 1949-06-01,135.0 8 | 1949-07-01,148.0 9 | 1949-08-01,148.0 10 | 1949-09-01,136.0 11 | 1949-10-01,119.0 12 | 1949-11-01,104.0 13 | 1949-12-01,118.0 14 | 1950-01-01,115.0 15 | 1950-02-01,126.0 16 | 1950-03-01,141.0 17 | 1950-04-01,135.0 18 | 1950-05-01,125.0 19 | 1950-06-01,149.0 20 | 1950-07-01,170.0 21 | 1950-08-01,170.0 22 | 1950-09-01,158.0 23 | 1950-10-01,133.0 24 | 1950-11-01,114.0 25 | 1950-12-01,140.0 26 | 1951-01-01,145.0 27 | 1951-02-01,150.0 28 | 1951-03-01,178.0 29 | 1951-04-01,163.0 30 | 1951-05-01,172.0 31 | 1951-06-01,178.0 32 | 1951-07-01,199.0 33 | 1951-08-01,199.0 34 | 1951-09-01,184.0 35 | 1951-10-01,162.0 36 | 1951-11-01,146.0 37 | 1951-12-01,166.0 38 | 1952-01-01,171.0 39 | 1952-02-01,180.0 40 | 1952-03-01,193.0 41 | 1952-04-01,181.0 42 | 1952-05-01,183.0 43 | 1952-06-01,218.0 44 | 1952-07-01,230.0 45 | 1952-08-01,242.0 46 | 1952-09-01,209.0 47 | 1952-10-01,191.0 48 | 1952-11-01,172.0 49 | 1952-12-01,194.0 50 | 1953-01-01,196.0 51 | 1953-02-01,196.0 52 | 1953-03-01,236.0 53 | 1953-04-01,235.0 54 | 1953-05-01,229.0 55 | 1953-06-01,243.0 56 | 1953-07-01,264.0 57 | 1953-08-01,272.0 58 | 1953-09-01,237.0 59 | 1953-10-01,211.0 60 | 1953-11-01,180.0 61 | 1953-12-01,201.0 62 | 1954-01-01,204.0 63 | 1954-02-01,188.0 64 | 1954-03-01,235.0 65 | 1954-04-01,227.0 66 | 1954-05-01,234.0 67 | 1954-06-01,264.0 68 | 1954-07-01,302.0 69 | 1954-08-01,293.0 70 | 1954-09-01,259.0 71 | 1954-10-01,229.0 72 | 1954-11-01,203.0 73 | 1954-12-01,229.0 74 | 1955-01-01,242.0 75 | 1955-02-01,233.0 76 | 1955-03-01,267.0 77 | 1955-04-01,269.0 78 | 1955-05-01,270.0 79 | 1955-06-01,315.0 80 | 1955-07-01,364.0 81 | 1955-08-01,347.0 82 | 1955-09-01,312.0 83 | 1955-10-01,274.0 84 | 1955-11-01,237.0 85 | 1955-12-01,278.0 86 | 1956-01-01,284.0 87 | 1956-02-01,277.0 88 | 1956-03-01,317.0 89 | 1956-04-01,313.0 90 | 1956-05-01,318.0 91 | 1956-06-01,374.0 92 | 1956-07-01,413.0 93 | 1956-08-01,405.0 94 | 1956-09-01,355.0 95 | 1956-10-01,306.0 96 | 1956-11-01,271.0 97 | 1956-12-01,306.0 98 | 1957-01-01,315.0 99 | 1957-02-01,301.0 100 | 1957-03-01,356.0 101 | 1957-04-01,348.0 102 | 1957-05-01,355.0 103 | 1957-06-01,422.0 104 | 1957-07-01,465.0 105 | 1957-08-01,467.0 106 | 1957-09-01,404.0 107 | 1957-10-01,347.0 108 | 1957-11-01,305.0 109 | 1957-12-01,336.0 110 | 1958-01-01,340.0 111 | 1958-02-01,318.0 112 | 1958-03-01,362.0 113 | 1958-04-01,348.0 114 | 1958-05-01,363.0 115 | 1958-06-01,435.0 116 | 1958-07-01,491.0 117 | 1958-08-01,505.0 118 | 1958-09-01,404.0 119 | 1958-10-01,359.0 120 | 1958-11-01,310.0 121 | 1958-12-01,337.0 122 | 1959-01-01,360.0 123 | 1959-02-01,342.0 124 | 1959-03-01,406.0 125 | 1959-04-01,396.0 126 | 1959-05-01,420.0 127 | 1959-06-01,472.0 128 | 1959-07-01,548.0 129 | 1959-08-01,559.0 130 | 1959-09-01,463.0 131 | 1959-10-01,407.0 132 | 1959-11-01,362.0 133 | 1959-12-01,405.0 134 | 1960-01-01,417.0 135 | 1960-02-01,391.0 136 | 1960-03-01,419.0 137 | 1960-04-01,461.0 138 | 1960-05-01,472.0 139 | 1960-06-01,535.0 140 | 1960-07-01,622.0 141 | 1960-08-01,606.0 142 | 1960-09-01,508.0 143 | 1960-10-01,461.0 144 | 1960-11-01,390.0 145 | 1960-12-01,432.0 146 | -------------------------------------------------------------------------------- /datasets/nile.csv: -------------------------------------------------------------------------------- 1 | "date","value" 2 | 1919-01-01,764.0 3 | 1920-01-01,821.0 4 | 1921-01-01,768.0 5 | 1922-01-01,845.0 6 | 1923-01-01,864.0 7 | 1924-01-01,862.0 8 | 1925-01-01,698.0 9 | 1926-01-01,845.0 10 | 1927-01-01,744.0 11 | 1928-01-01,796.0 12 | 1929-01-01,1040.0 13 | 1930-01-01,759.0 14 | 1931-01-01,781.0 15 | 1932-01-01,865.0 16 | 1933-01-01,845.0 17 | 1934-01-01,944.0 18 | 1935-01-01,984.0 19 | 1936-01-01,897.0 20 | 1937-01-01,822.0 21 | 1938-01-01,1010.0 22 | 1939-01-01,771.0 23 | 1940-01-01,676.0 24 | 1941-01-01,649.0 25 | 1942-01-01,846.0 26 | 1943-01-01,812.0 27 | 1944-01-01,742.0 28 | 1945-01-01,801.0 29 | 1946-01-01,1040.0 30 | 1947-01-01,860.0 31 | 1948-01-01,874.0 32 | 1949-01-01,848.0 33 | 1950-01-01,890.0 34 | 1951-01-01,744.0 35 | 1952-01-01,749.0 36 | 1953-01-01,838.0 37 | 1954-01-01,1050.0 38 | 1955-01-01,918.0 39 | 1956-01-01,986.0 40 | 1957-01-01,797.0 41 | 1958-01-01,923.0 42 | 1959-01-01,975.0 43 | 1960-01-01,815.0 44 | 1961-01-01,1020.0 45 | 1962-01-01,906.0 46 | 1963-01-01,901.0 47 | 1964-01-01,1170.0 48 | 1965-01-01,912.0 49 | 1966-01-01,746.0 50 | 1967-01-01,919.0 51 | 1968-01-01,718.0 52 | 1969-01-01,714.0 53 | 1970-01-01,740.0 -------------------------------------------------------------------------------- /docs/error.html.jinja2: -------------------------------------------------------------------------------- 1 | {# this template is used by pdoc's integrated web server to display runtime errors. #} 2 | {% extends "frame.html.jinja2" %} 3 | {% block title %}{{ error }}{% endblock %} 4 | {% block style %} 5 | 6 | 31 | {% endblock %} 32 | {% block body %} 33 |

{{ error }}

34 |
{{ details }}
35 |
36 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /docs/frame.html.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% block title %}{% endblock %} 8 | {% block favicon %} 9 | {% if favicon %} 10 | 11 | {% endif %} 12 | {% endblock %} 13 | {% block head %}{% endblock %} 14 | {% filter minify_css | indent %} 15 | {% block style %} 16 | 17 | 18 | 19 | {# 20 | The style_pdoc, style_theme, style_layout, and style_content Jinja2 blocks are deprecated and will be 21 | removed in a future release. Custom templates should either provide alternatives for the specific CSS files, 22 | or append their own styles by providing `custom.css` (see examples/custom-template/). 23 | #} 24 | {% block style_pdoc %} 25 | {% block style_theme %}{% endblock %} 26 | {% block style_syntax %}{% endblock %} 27 | {% block style_layout %}{% endblock %} 28 | {% block style_content %}{% endblock %} 29 | {# Use this file in your custom template directory to add additional CSS styling: #} 30 | 31 | {% endblock %} 32 | {% endblock %} 33 | {% endfilter %} 34 | {% if math %}{% include "math.html.jinja2" %}{% endif %} 35 | {% if mermaid %}{% include "mermaid.html.jinja2" %}{% endif %} 36 | 37 | 38 | {% block body %} 39 | 44 | {% block content %}{% endblock %} 45 | {% endblock body %} 46 | 47 | 48 | -------------------------------------------------------------------------------- /docs/index.html.jinja2: -------------------------------------------------------------------------------- 1 | {# this template is used to render the top-level index.html. #} 2 | {% if root_module_name %} 3 | {# 4 | If there is one common parent module for all documented modules, redirect there immediately. 5 | This makes a package's `__init__.py` the main entrypoint by default. 6 | A custom template could override this by setting root_module_name to false before `{% extend ... %}`. 7 | #} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% else %} 16 | {% extends "frame.html.jinja2" %} 17 | {% block title %}Module List – pdoc {{ __version__ }}{% endblock %} 18 | {% block style %} 19 | {{ super() | safe }} 20 | 42 | {% endblock %} 43 | {% block nav %} 44 |

Available Modules

45 | 50 | {% endblock %} 51 | {% block content %} 52 |
53 | {% block logo %} 54 | {% if logo %} 55 | {% set logo_link = "https://github.com/thierrymoudiki/thierrymoudiki.github.io/blob/master/images/t-logo2.png" %} 56 | {% if logo_link %}{% endif %} 57 | project logo 58 | {% if logo_link %}{% endif %} 59 | {% else %} 60 | 61 | pdoc 63 | 64 | {% endif %} 65 | {% endblock %} 66 | {% if search %} 67 | 68 | {% endif %} 69 |
70 |
71 | {% if search %} 72 | {% include "search.html.jinja2" %} 73 | {% endif %} 74 | {% endblock %} 75 | {% endif %} 76 | -------------------------------------------------------------------------------- /docs/module.html.jinja2: -------------------------------------------------------------------------------- 1 | {% extends "frame.html.jinja2" %} 2 | {% block title %}{{ module.modulename }} API documentation{% endblock %} 3 | {% block nav %} 4 | {% block module_list_link %} 5 | {% set parentmodule = ".".join(module.modulename.split(".")[:-1]) %} 6 | {% if parentmodule and parentmodule in all_modules %} 7 | 8 | {% include "resources/box-arrow-in-left.svg" %} 9 |   10 | {{- parentmodule -}} 11 | 12 | {% elif not root_module_name %} 13 | 14 | {% include "resources/box-arrow-in-left.svg" %} 15 |   16 | Module Index 17 | 18 | {% endif %} 19 | {% endblock %} 20 | 21 | {% block nav_title %} 22 | {% if logo %} 23 | {% set logo_link = "https://github.com/thierrymoudiki/thierrymoudiki.github.io/blob/master/images/t-logo2.png" %} 24 | {% if logo_link %}{% endif %} 25 | 26 | {% if logo_link %}{% endif %} 27 | {% endif %} 28 | {% endblock %} 29 | 30 | {% block search_box %} 31 | {% if search and all_modules|length > 1 %} 32 | {# we set a pattern here so that we can use the :valid CSS selector #} 33 | 35 | {% endif %} 36 | {% endblock %} 37 | 38 | {% block nav_index %} 39 | {% set index = module.docstring | to_markdown | to_html | attr("toc_html") %} 40 | {% if index %} 41 |

Contents

42 | {{ index | safe }} 43 | {% endif %} 44 | {% endblock %} 45 | 46 | {% block nav_submodules %} 47 | {% if module.submodules %} 48 |

Submodules

49 | 54 | {% endif %} 55 | {% endblock %} 56 | 57 | {% block nav_members %} 58 | {% if module.members %} 59 |

API Documentation

60 | {{ nav_members(module.members.values()) }} 61 | {% endif %} 62 | {% endblock %} 63 | 64 | {% block nav_footer %} 65 | {% if footer_text %} 66 | 67 | {% endif %} 68 | {% endblock %} 69 | 70 | {% block attribution %} 71 | 72 | built with pdocpdoc logo 76 | 77 | {% endblock %} 78 | {% endblock nav %} 79 | {% block content %} 80 |
81 | {% block module_info %} 82 |
83 | {% block edit_button %} 84 | {% if edit_url %} 85 | {% if "github.com" in edit_url %} 86 | {% set edit_text = "Edit on GitHub" %} 87 | {% elif "gitlab" in edit_url %} 88 | {% set edit_text = "Edit on GitLab" %} 89 | {% else %} 90 | {% set edit_text = "Edit Source" %} 91 | {% endif %} 92 | {{ edit_text }} 93 | {% endif %} 94 | {% endblock %} 95 | {{ module_name() }} 96 | {{ docstring(module) }} 97 | {{ view_source_state(module) }} 98 | {{ view_source_button(module) }} 99 | {{ view_source_code(module) }} 100 |
101 | {% endblock %} 102 | {% block module_contents %} 103 | {% for m in module.flattened_own_members if is_public(m) | trim %} 104 |
105 | {{ member(m) }} 106 | {% if m.kind == "class" %} 107 | {% for m in m.own_members if m.kind != "class" and is_public(m) | trim %} 108 | {% if (m.name in ["fit", "predict", "predict_proba", "score", "create_layer", "cook_training_set", "cook_test_set", "encode_clusters", "subsample", "download", "provide_models"]) %} 109 |
110 | {{ member(m) }} 111 |
112 | {% endif %} 113 | {% endfor %} 114 | {% endif %} 115 |
116 | {% endfor %} 117 | {% endblock %} 118 |
119 | {% if mtime %} 120 | {% include "livereload.html.jinja2" %} 121 | {% endif %} 122 | {% block search_js %} 123 | {% if search and all_modules|length > 1 %} 124 | {% include "search.html.jinja2" %} 125 | {% endif %} 126 | {% endblock %} 127 | {% endblock content %} 128 | {# 129 | End of content, beginning of helper macros. 130 | See https://pdoc.dev/docs/pdoc/render_helpers.html#DefaultMacroExtension for an explanation of defaultmacro. 131 | #} 132 | {% defaultmacro bases(cls) %} 133 | {%- if cls.bases -%} 134 | ( 135 | {%- for base in cls.bases -%} 136 | {{ base[:2] | link(text=base[2]) }} 137 | {%- if loop.nextitem %}, {% endif %} 138 | {%- endfor -%} 139 | ) 140 | {%- endif -%} 141 | {% enddefaultmacro %} 142 | {% defaultmacro default_value(var) -%} 143 | {%- if var.default_value_str %} 144 | = 145 | {% if var.default_value_str | length > 100 -%} 146 | 147 | 148 | {%- endif -%} 149 | {{ var.default_value_str | escape | linkify }} 150 | {%- endif -%} 151 | {% enddefaultmacro %} 152 | {% defaultmacro annotation(var) %} 153 | {%- if var.annotation_str -%} 154 | {{ var.annotation_str | escape | linkify }} 155 | {%- endif -%} 156 | {% enddefaultmacro %} 157 | {% defaultmacro decorators(doc) %} 158 | {% for d in doc.decorators if not d.startswith("@_") %} 159 |
{{ d }}
160 | {% endfor %} 161 | {% enddefaultmacro %} 162 | {% defaultmacro function(fn) -%} 163 | {{ decorators(fn) }} 164 | {% if fn.name == "__init__" %} 165 | {{ ".".join(fn.qualname.split(".")[:-1]) }} 166 | {{- fn.signature_without_self | format_signature(colon=False) | linkify }} 167 | {% else %} 168 | {{ fn.funcdef }} 169 | {{ fn.name }} 170 | {{- fn.signature | format_signature(colon=True) | linkify }} 171 | {% endif %} 172 | {% enddefaultmacro %} 173 | {% defaultmacro variable(var) -%} 174 | {%- if var.is_type_alias_type %}type {% endif -%} 175 | {{ var.name }}{{ annotation(var) }}{{ default_value(var) }} 176 | {% enddefaultmacro %} 177 | {% defaultmacro submodule(mod) -%} 178 | {{ mod.taken_from | link }} 179 | {% enddefaultmacro %} 180 | {% defaultmacro class(cls) -%} 181 | {{ decorators(cls) }} 182 | class 183 | {{ cls.qualname }} 184 | {{- bases(cls) -}}: 185 | {% enddefaultmacro %} 186 | {% defaultmacro member(doc) %} 187 | {{- view_source_state(doc) -}} 188 |
189 | {% if doc.kind == "class" %} 190 | {{ class(doc) }} 191 | {% elif doc.kind == "function" %} 192 | {{ function(doc) }} 193 | {% elif doc.kind == "module" %} 194 | {{ submodule(doc) }} 195 | {% else %} 196 | {{ variable(doc) }} 197 | {% endif %} 198 | {{ view_source_button(doc) }} 199 |
200 | 201 | {{ view_source_code(doc) }} 202 | {{ docstring(doc) }} 203 | {% enddefaultmacro %} 204 | {% defaultmacro docstring(var) %} 205 | {% if var.docstring %} 206 |
{{ var.docstring | replace("@public", "") | to_markdown | to_html | linkify(namespace=var.qualname) }}
207 | {% endif %} 208 | {% enddefaultmacro %} 209 | {% defaultmacro nav_members(members) %} 210 | 222 | {% enddefaultmacro %} 223 | {% defaultmacro is_public(doc) %} 224 | {# 225 | This macro is a bit unconventional in that its output is not rendered, but treated as a boolean: 226 | Returning no text is interpreted as false, returning any other text is iterpreted as true. 227 | Implementing this as a macro makes it very easy to override with a custom template, see 228 | https://github.com/mitmproxy/pdoc/tree/main/examples/custom-template. 229 | #} 230 | {% if "@private" in doc.docstring %} 231 | {# hide members explicitly marked as @private #} 232 | {% elif "@public" in doc.docstring %} 233 | {# show members explicitly marked as @public #} 234 | true 235 | {% elif not include_undocumented and not doc.docstring %} 236 | {# hide members that are undocumented if include_undocumented has been toggled off. #} 237 | {% elif doc.name == "__init__" and (doc.docstring or (doc.kind == "function" and doc.signature_without_self.parameters)) %} 238 | {# show constructors that have a docstring or at least one extra argument #} 239 | true 240 | {% elif doc.name == "__doc__" %} 241 | {# We don't want to document __doc__ itself, https://github.com/mitmproxy/pdoc/issues/235 #} 242 | {% elif doc.kind == "variable" and doc.is_typevar and not doc.docstring %} 243 | {# do not document TypeVars, that only clutters the docs. #} 244 | {% elif doc.kind == "module" and doc.fullname not in all_modules %} 245 | {# Skip modules that were manually excluded, https://github.com/mitmproxy/pdoc/issues/334 #} 246 | {% elif (doc.qualname or doc.name) is in(module.obj.__all__ or []) %} 247 | {# members starting with an underscore are still public if mentioned in __all__ #} 248 | true 249 | {% elif not doc.name.startswith("_") %} 250 | {# members not starting with an underscore are considered public by default #} 251 | true 252 | {% endif %} 253 | {% enddefaultmacro %} 254 | {# fmt: off #} 255 | {% defaultmacro inherited(cls) %} 256 | {% for base, members in cls.inherited_members.items() %} 257 | {% set m = None %}{# workaround for https://github.com/pallets/jinja/issues/1427 #} 258 | {% set member_html %} 259 | {% for m in members if is_public(m) | trim %} 260 |
261 | {{- m.taken_from | link(text=m.name.replace("__init__",base[1])) -}} 262 |
263 | {% endfor %} 264 | {% endset %} 265 | {# we may not have any public members, in which case we don't want to print anything. #} 266 | {% if member_html %} 267 |
{{ base | link }}
268 | {{ member_html }} 269 |
270 | {% endif %} 271 | {% endfor %} 272 | {% enddefaultmacro %} 273 | {# fmt: on #} 274 | {% defaultmacro view_source_state(doc) %} 275 | {% if show_source and doc.source %} 276 | 277 | {% endif %} 278 | {% enddefaultmacro %} 279 | {% defaultmacro view_source_button(doc) %} 280 | {% if show_source and doc.source %} 281 | 282 | {% endif %} 283 | {% enddefaultmacro %} 284 | {% defaultmacro view_source_code(doc) %} 285 | {% if show_source and doc.source %} 286 | {{ doc | highlight }} 287 | {% endif %} 288 | {% enddefaultmacro %} 289 | {% defaultmacro module_name() %} 290 |

291 | {% set parts = module.modulename.split(".") %} 292 | {% for part in parts %} 293 | {%- set fullname = ".".join(parts[:loop.index]) -%} 294 | {%- if fullname in all_modules and fullname != module.modulename -%} 295 | {{ part }} 296 | {%- else -%} 297 | {{ part }} 298 | {%- endif -%} 299 | {%- if loop.nextitem -%} 300 | . 301 | {%- endif -%} 302 | {% endfor %} 303 |

304 | {% enddefaultmacro %} 305 | -------------------------------------------------------------------------------- /docs/static/syntax-highlighting.css: -------------------------------------------------------------------------------- 1 | /* monokai color scheme, see pdoc/template/README.md */ 2 | pre { line-height: 125%; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 20px; } 4 | .pdoc-code .hll { background-color: #49483e } 5 | .pdoc-code { background: #272822; color: #f8f8f2 } 6 | .pdoc-code .c { color: #75715e } /* Comment */ 7 | .pdoc-code .err { color: #960050; background-color: #1e0010 } /* Error */ 8 | .pdoc-code .esc { color: #f8f8f2 } /* Escape */ 9 | .pdoc-code .g { color: #f8f8f2 } /* Generic */ 10 | .pdoc-code .k { color: #66d9ef } /* Keyword */ 11 | .pdoc-code .l { color: #ae81ff } /* Literal */ 12 | .pdoc-code .n { color: #f8f8f2 } /* Name */ 13 | .pdoc-code .o { color: #f92672 } /* Operator */ 14 | .pdoc-code .x { color: #f8f8f2 } /* Other */ 15 | .pdoc-code .p { color: #f8f8f2 } /* Punctuation */ 16 | .pdoc-code .ch { color: #75715e } /* Comment.Hashbang */ 17 | .pdoc-code .cm { color: #75715e } /* Comment.Multiline */ 18 | .pdoc-code .cp { color: #75715e } /* Comment.Preproc */ 19 | .pdoc-code .cpf { color: #75715e } /* Comment.PreprocFile */ 20 | .pdoc-code .c1 { color: #75715e } /* Comment.Single */ 21 | .pdoc-code .cs { color: #75715e } /* Comment.Special */ 22 | .pdoc-code .gd { color: #f92672 } /* Generic.Deleted */ 23 | .pdoc-code .ge { color: #f8f8f2; font-style: italic } /* Generic.Emph */ 24 | .pdoc-code .gr { color: #f8f8f2 } /* Generic.Error */ 25 | .pdoc-code .gh { color: #f8f8f2 } /* Generic.Heading */ 26 | .pdoc-code .gi { color: #a6e22e } /* Generic.Inserted */ 27 | .pdoc-code .go { color: #66d9ef } /* Generic.Output */ 28 | .pdoc-code .gp { color: #f92672; font-weight: bold } /* Generic.Prompt */ 29 | .pdoc-code .gs { color: #f8f8f2; font-weight: bold } /* Generic.Strong */ 30 | .pdoc-code .gu { color: #75715e } /* Generic.Subheading */ 31 | .pdoc-code .gt { color: #f8f8f2 } /* Generic.Traceback */ 32 | .pdoc-code .kc { color: #66d9ef } /* Keyword.Constant */ 33 | .pdoc-code .kd { color: #66d9ef } /* Keyword.Declaration */ 34 | .pdoc-code .kn { color: #f92672 } /* Keyword.Namespace */ 35 | .pdoc-code .kp { color: #66d9ef } /* Keyword.Pseudo */ 36 | .pdoc-code .kr { color: #66d9ef } /* Keyword.Reserved */ 37 | .pdoc-code .kt { color: #66d9ef } /* Keyword.Type */ 38 | .pdoc-code .ld { color: #e6db74 } /* Literal.Date */ 39 | .pdoc-code .m { color: #ae81ff } /* Literal.Number */ 40 | .pdoc-code .s { color: #e6db74 } /* Literal.String */ 41 | .pdoc-code .na { color: #a6e22e } /* Name.Attribute */ 42 | .pdoc-code .nb { color: #f8f8f2 } /* Name.Builtin */ 43 | .pdoc-code .nc { color: #a6e22e } /* Name.Class */ 44 | .pdoc-code .no { color: #66d9ef } /* Name.Constant */ 45 | .pdoc-code .nd { color: #a6e22e } /* Name.Decorator */ 46 | .pdoc-code .ni { color: #f8f8f2 } /* Name.Entity */ 47 | .pdoc-code .ne { color: #a6e22e } /* Name.Exception */ 48 | .pdoc-code .nf { color: #a6e22e } /* Name.Function */ 49 | .pdoc-code .nl { color: #f8f8f2 } /* Name.Label */ 50 | .pdoc-code .nn { color: #f8f8f2 } /* Name.Namespace */ 51 | .pdoc-code .nx { color: #a6e22e } /* Name.Other */ 52 | .pdoc-code .py { color: #f8f8f2 } /* Name.Property */ 53 | .pdoc-code .nt { color: #f92672 } /* Name.Tag */ 54 | .pdoc-code .nv { color: #f8f8f2 } /* Name.Variable */ 55 | .pdoc-code .ow { color: #f92672 } /* Operator.Word */ 56 | .pdoc-code .w { color: #f8f8f2 } /* Text.Whitespace */ 57 | .pdoc-code .mb { color: #ae81ff } /* Literal.Number.Bin */ 58 | .pdoc-code .mf { color: #ae81ff } /* Literal.Number.Float */ 59 | .pdoc-code .mh { color: #ae81ff } /* Literal.Number.Hex */ 60 | .pdoc-code .mi { color: #ae81ff } /* Literal.Number.Integer */ 61 | .pdoc-code .mo { color: #ae81ff } /* Literal.Number.Oct */ 62 | .pdoc-code .sa { color: #e6db74 } /* Literal.String.Affix */ 63 | .pdoc-code .sb { color: #e6db74 } /* Literal.String.Backtick */ 64 | .pdoc-code .sc { color: #e6db74 } /* Literal.String.Char */ 65 | .pdoc-code .dl { color: #e6db74 } /* Literal.String.Delimiter */ 66 | .pdoc-code .sd { color: #e6db74 } /* Literal.String.Doc */ 67 | .pdoc-code .s2 { color: #e6db74 } /* Literal.String.Double */ 68 | .pdoc-code .se { color: #ae81ff } /* Literal.String.Escape */ 69 | .pdoc-code .sh { color: #e6db74 } /* Literal.String.Heredoc */ 70 | .pdoc-code .si { color: #e6db74 } /* Literal.String.Interpol */ 71 | .pdoc-code .sx { color: #e6db74 } /* Literal.String.Other */ 72 | .pdoc-code .sr { color: #e6db74 } /* Literal.String.Regex */ 73 | .pdoc-code .s1 { color: #e6db74 } /* Literal.String.Single */ 74 | .pdoc-code .ss { color: #e6db74 } /* Literal.String.Symbol */ 75 | .pdoc-code .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ 76 | .pdoc-code .fm { color: #a6e22e } /* Name.Function.Magic */ 77 | .pdoc-code .vc { color: #f8f8f2 } /* Name.Variable.Class */ 78 | .pdoc-code .vg { color: #f8f8f2 } /* Name.Variable.Global */ 79 | .pdoc-code .vi { color: #f8f8f2 } /* Name.Variable.Instance */ 80 | .pdoc-code .vm { color: #f8f8f2 } /* Name.Variable.Magic */ -------------------------------------------------------------------------------- /docs/static/theme.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --pdoc-background: #212529; 3 | } 4 | 5 | .pdoc { 6 | --text: #f7f7f7; 7 | --muted: #9d9d9d; 8 | --link: #58a6ff; 9 | --link-hover: #3989ff; 10 | --code: #333; 11 | --active: #555; 12 | 13 | --accent: #343434; 14 | --accent2: #555; 15 | 16 | --nav-hover: rgba(0, 0, 0, 0.1); 17 | --name: #77C1FF; 18 | --def: #0cdd0c; 19 | --annotation: #00c037; 20 | } -------------------------------------------------------------------------------- /docs/t-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techtonique/ahead_python/c4b4acbf5b6eaf42118f6bd0173754a291723d89/docs/t-logo.png -------------------------------------------------------------------------------- /docs/t-logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techtonique/ahead_python/c4b4acbf5b6eaf42118f6bd0173754a291723d89/docs/t-logo2.png -------------------------------------------------------------------------------- /examples/armagarch.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from ahead import ArmaGarch 5 | from time import time 6 | 7 | print(f"\n ----- Running: {os.path.basename(__file__)}... ----- \n") 8 | 9 | # Forecasting horizon 10 | h = 5 11 | 12 | # univariate ts forecasting 13 | print("Example 1 -----") 14 | d1 = ArmaGarch(h = h) 15 | print(d1.__module__) 16 | print(dir(d1)) 17 | 18 | -------------------------------------------------------------------------------- /examples/basicforecaster.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from ahead import BasicForecaster 5 | from time import time 6 | 7 | print(f"\n ----- Running: {os.path.basename(__file__)}... ----- \n") 8 | 9 | # Forecasting horizon 10 | h = 5 11 | 12 | # Data frame containing the time series 13 | dataset = { 14 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 15 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 16 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 17 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 18 | df = pd.DataFrame(dataset).set_index('date') 19 | df.index = pd.DatetimeIndex(df.index) 20 | 21 | # univariate ts forecasting 22 | print("Example 1 -----") 23 | d1 = BasicForecaster(h = h, date_formatting = "ms") 24 | 25 | start = time() 26 | d1.forecast(df) 27 | print(f"Elapsed: {time()-start} \n") 28 | print("averages: \n") 29 | print(d1.averages_[0]) 30 | print("\n") 31 | print(d1.averages_[1]) 32 | print("\n") 33 | print(d1.averages_[2]) 34 | print("\n") 35 | print("ranges: \n") 36 | print(d1.ranges_[0]) 37 | print("\n") 38 | print(d1.ranges_[1]) 39 | print("\n") 40 | print(d1.ranges_[2]) 41 | print("\n") 42 | 43 | print("Example 2 -----") 44 | d2 = BasicForecaster(h = h, date_formatting = "original") 45 | start = time() 46 | d2.forecast(df) 47 | print(f"Elapsed: {time()-start} \n") 48 | print(d2.averages_[0]) 49 | print("\n") 50 | print(d2.averages_[1]) 51 | print("\n") 52 | print(d2.averages_[2]) 53 | print("\n") 54 | print("ranges: \n") 55 | print(d2.ranges_[0]) 56 | print("\n") 57 | print(d2.ranges_[1]) 58 | print("\n") 59 | print(d2.ranges_[2]) 60 | print("\n") 61 | 62 | 63 | print("Example 3 -----") 64 | 65 | d3 = BasicForecaster(h = h, date_formatting = "original", 66 | type_pi="bootstrap", B=5) 67 | 68 | start = time() 69 | d3.forecast(df) 70 | print(f"Elapsed: {time()-start} \n") 71 | 72 | print(d3.fcast_.rx2['mean']) 73 | print(d3.averages_[1]) 74 | print(np.asarray(d3.fcast_.rx2['mean'])) 75 | 76 | print(d3.fcast_.rx2['sims'][0]) 77 | res = np.asarray(d3.fcast_.rx2['sims'][1]) 78 | print(res) 79 | print(res.shape) 80 | print(res[0, 1]) 81 | 82 | print("\n result_dfs_: \n") 83 | print(d3.result_dfs_) 84 | 85 | print("\n sims_: \n") 86 | print(d3.sims_) 87 | 88 | print("\n output_dates_: \n") 89 | print(d3.output_dates_) 90 | 91 | print("\n mean, lower, upper as numpy arrays: \n") 92 | print(d3.mean_) 93 | print(d3.lower_) 94 | print(d3.upper_) 95 | 96 | 97 | print("Example 4 -----") 98 | 99 | d4 = BasicForecaster(h = h, date_formatting = "original", 100 | type_pi="blockbootstrap", B=5, block_length=3) 101 | 102 | start = time() 103 | d4.forecast(df) 104 | print(f"Elapsed: {time()-start} \n") 105 | 106 | print(d4.fcast_.rx2['mean']) 107 | print(d4.averages_[1]) 108 | print(np.asarray(d4.fcast_.rx2['mean'])) 109 | 110 | print(d4.fcast_.rx2['sims'][0]) 111 | res = np.asarray(d4.fcast_.rx2['sims'][1]) 112 | print(res) 113 | print(res.shape) 114 | print(res[0, 1]) 115 | 116 | print("\n result_dfs_: \n") 117 | print(d4.result_dfs_) 118 | 119 | print("\n sims_: \n") 120 | print(d4.sims_) 121 | 122 | print("\n output_dates_: \n") 123 | print(d4.output_dates_) 124 | 125 | print("\n mean, lower, upper as numpy arrays: \n") 126 | print(d4.mean_) 127 | print(d4.lower_) 128 | print(d4.upper_) 129 | 130 | print("\n") 131 | print("Example 5 -----") 132 | 133 | dataset = { 134 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', 135 | '2004-01-01', '2005-01-01', '2006-01-01', 136 | '2007-01-01'], 137 | 'series1' : [34, 30, 35.6, 33.3, 38.1, 34.4, 33.9], 138 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1, 4.9, 4.7], 139 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1, 99.9, 101.0]} 140 | df = pd.DataFrame(dataset).set_index('date') 141 | 142 | 143 | d5 = BasicForecaster(h = 5, date_formatting = "original", 144 | type_pi="movingblockbootstrap", B=20, block_length=3) 145 | 146 | start = time() 147 | d5.forecast(df) 148 | print(f"Elapsed: {time()-start} \n") 149 | 150 | print("\n output_dates_: \n") 151 | print(d5.output_dates_) 152 | 153 | print("\n mean, lower, upper as numpy arrays: \n") 154 | print(d5.mean_) 155 | print(d5.lower_) 156 | print(d5.upper_) 157 | 158 | print("\n result_dfs_: \n") 159 | print(d5.result_dfs_) 160 | 161 | print("\n sims_: \n") 162 | print(d5.sims_) 163 | -------------------------------------------------------------------------------- /examples/dynrm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from ahead import DynamicRegressor, EAT 5 | from time import time 6 | 7 | print(f"\n ----- Running: {os.path.basename(__file__)}... ----- \n") 8 | 9 | # Forecasting horizon 10 | h = 5 11 | 12 | 13 | # Data frame containing the time series 14 | dataset = { 15 | 'date' : ['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01', '2020-05-01'], 16 | 'value' : [34, 30, 35.6, 33.3, 38.1], 17 | } 18 | df = pd.DataFrame(dataset).set_index('date') 19 | df.index = pd.DatetimeIndex(df.index) 20 | print(df) 21 | 22 | 23 | # univariate ts forecasting 24 | print("Example 1 -----") 25 | d1 = DynamicRegressor(h = h, date_formatting = "ms") 26 | print(d1.__module__) 27 | 28 | start = time() 29 | d1.forecast(df) 30 | print(f"Elapsed: {time()-start} \n") 31 | print("averages: \n") 32 | print(d1.averages_) 33 | print("\n") 34 | print("ranges: \n") 35 | print(d1.ranges_) 36 | print("\n") 37 | 38 | print("Example 2 -----") 39 | d2 = DynamicRegressor(h = h, type_pi="T", date_formatting = "original") 40 | start = time() 41 | d2.forecast(df) 42 | print(f"Elapsed: {time()-start} \n") 43 | print("averages: \n") 44 | print(d2.averages_) 45 | print("\n") 46 | print("ranges: \n") 47 | print(d2.ranges_) 48 | print("\n") 49 | 50 | print("Example 3 -----") 51 | # https://stackoverflow.com/questions/42098126/mac-osx-python-ssl-sslerror-ssl-certificate-verify-failed-certificate-verify/42098127#42098127 52 | # compared to > ahead::dynrmf(y=window(Nile, start=1919), h=10, level=95, type_pi="gaussian") 53 | url = "https://raw.githubusercontent.com/Techtonique/ahead_python/main/datasets/nile.csv" 54 | df2 = pd.read_csv(url) 55 | df2 = df2.set_index('date') 56 | print(df2.head()) 57 | print(df2.tail()) 58 | 59 | 60 | d3 = DynamicRegressor(type_pi="gaussian", h=10) 61 | start = time() 62 | d3.forecast(df2) 63 | print(f"Elapsed: {time()-start} \n") 64 | print("averages: \n") 65 | print(d3.averages_) 66 | print("\n") 67 | print("ranges: \n") 68 | print(d3.ranges_) 69 | print("\n") 70 | print("data frame result: \n") 71 | print("\n") 72 | print(d3.result_df_) 73 | print("\n") 74 | print(d3.result_df_.loc[:,"lower"]) 75 | print("\n") 76 | print(d3.input_df) 77 | print("\n") 78 | print(d3.mean_) 79 | print(d3.lower_) 80 | print(d3.upper_) 81 | -------------------------------------------------------------------------------- /examples/eat.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from ahead import EAT 5 | from time import time 6 | 7 | print(f"\n ----- Running: {os.path.basename(__file__)}... ----- \n") 8 | 9 | # Forecasting horizon 10 | h = 5 11 | 12 | 13 | # Data frame containing the time series 14 | dataset = { 15 | 'date' : ['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01', '2020-05-01'], 16 | 'value' : [34, 30, 35.6, 33.3, 38.1], 17 | } 18 | df = pd.DataFrame(dataset).set_index('date') 19 | df.index = pd.DatetimeIndex(df.index) 20 | print(df) 21 | 22 | 23 | # univariate ts forecasting 24 | print("Example 1 -----") 25 | e1 = EAT(h = h, weights = [0.3, 0.4, 0.3], type_pi="T", date_formatting = "ms") 26 | 27 | start = time() 28 | e1.forecast(df) 29 | print(f"Elapsed: {time()-start} \n") 30 | print("averages: \n") 31 | print(e1.averages_) 32 | print("\n") 33 | print("ranges: \n") 34 | print(e1.ranges_) 35 | print("\n") 36 | print(e1.fcast_.rx2['mean']) 37 | print(e1.fcast_.rx2['lower']) 38 | print(e1.fcast_.rx2['upper']) 39 | 40 | print("Example 2 -----") 41 | e2 = EAT(h = h, type_pi="T", date_formatting = "original") 42 | start = time() 43 | e2.forecast(df) 44 | print(f"Elapsed: {time()-start} \n") 45 | print("averages: \n") 46 | print(e2.averages_) 47 | print("\n") 48 | print("ranges: \n") 49 | print(e2.ranges_) 50 | print("\n") 51 | print(e2.result_df_) 52 | print("\n") 53 | print(e2.mean_) 54 | print(e2.lower_) 55 | print(e2.upper_) 56 | -------------------------------------------------------------------------------- /examples/fitforecaster.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from ahead import FitForecaster 5 | from time import time 6 | 7 | print(f"\n ----- Running: {os.path.basename(__file__)}... ----- \n") 8 | 9 | url = "https://raw.githubusercontent.com/Techtonique/" 10 | url += "datasets/main/time_series/univariate/" 11 | url += "a10.csv" 12 | 13 | df = pd.read_csv(url) 14 | df.index = pd.DatetimeIndex(df.date) # must have 15 | df.drop(columns=['date'], inplace=True) 16 | 17 | # univariate ts forecasting 18 | print("Example 1 -----") 19 | d1 = FitForecaster() 20 | 21 | print(f"before: {d1}") 22 | 23 | start = time() 24 | print(d1.fit_forecast(df)) 25 | print(f"Elapsed: {time()-start} \n") 26 | 27 | print(f"after: {d1.result_dfs_}") -------------------------------------------------------------------------------- /examples/ridge2regressor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from ahead import Ridge2Regressor 5 | from time import time 6 | 7 | 8 | print(f"\n ----- Running: {os.path.basename(__file__)}... ----- \n") 9 | 10 | # Forecasting horizon 11 | h = 5 12 | 13 | 14 | # Data frame containing the time series 15 | dataset = { 16 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 17 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 18 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 19 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 20 | df = pd.DataFrame(dataset).set_index('date') 21 | df.index = pd.DatetimeIndex(df.index) 22 | 23 | # univariate ts forecasting 24 | print("Example 1 -----") 25 | d1 = Ridge2Regressor(h = h, date_formatting = "ms") 26 | 27 | start = time() 28 | d1.forecast(df) 29 | print(f"Elapsed: {time()-start} \n") 30 | print("averages: \n") 31 | print(d1.averages_[0]) 32 | print("\n") 33 | print(d1.averages_[1]) 34 | print("\n") 35 | print(d1.averages_[2]) 36 | print("\n") 37 | print("ranges: \n") 38 | print(d1.ranges_[0]) 39 | print("\n") 40 | print(d1.ranges_[1]) 41 | print("\n") 42 | print(d1.ranges_[2]) 43 | print("\n") 44 | #plot(d1, selected_series="series1") 45 | 46 | print("Example 2 -----") 47 | d2 = Ridge2Regressor(h = h, date_formatting = "original") 48 | start = time() 49 | d2.forecast(df) 50 | print(f"Elapsed: {time()-start} \n") 51 | print(d2.averages_[0]) 52 | print("\n") 53 | print(d2.averages_[1]) 54 | print("\n") 55 | print(d2.averages_[2]) 56 | print("\n") 57 | print("ranges: \n") 58 | print(d2.ranges_[0]) 59 | print("\n") 60 | print(d2.ranges_[1]) 61 | print("\n") 62 | print(d2.ranges_[2]) 63 | print("\n") 64 | #plot(d2, selected_series="series1") 65 | print("\n") 66 | 67 | print("Example 3 -----") 68 | 69 | d3 = Ridge2Regressor(h = h, date_formatting = "original", 70 | type_pi="bootstrap", B=5) 71 | 72 | start = time() 73 | d3.forecast(df) 74 | print(f"Elapsed: {time()-start} \n") 75 | 76 | print(d3.fcast_.rx2['mean']) 77 | print(d3.averages_[1]) 78 | print(np.asarray(d3.fcast_.rx2['mean'])) 79 | 80 | print(d3.fcast_.rx2['sims'][0]) 81 | res = np.asarray(d3.fcast_.rx2['sims'][1]) 82 | print(res) 83 | print(res.shape) 84 | print(res[0, 1]) 85 | 86 | print("\n result_dfs_: \n") 87 | print(d3.result_dfs_) 88 | 89 | print("\n sims_: \n") 90 | print(d3.sims_) 91 | 92 | print("\n output_dates_: \n") 93 | print(d3.output_dates_) 94 | 95 | print("\n mean, lower, upper as numpy arrays: \n") 96 | print(d3.mean_) 97 | print(d3.lower_) 98 | print(d3.upper_) 99 | print("\n") 100 | #plot(d3, selected_series="series1") 101 | print("\n") 102 | 103 | print("Example 4 -----") 104 | 105 | d4 = Ridge2Regressor(h = h, date_formatting = "original", 106 | type_pi="bootstrap", B=3, seed=1) 107 | 108 | # Data frame containing the time series 109 | dataset = { 110 | 'date' : ['2001-01-01', '2001-02-01', '2001-03-01', '2001-04-01', '2001-05-01'], 111 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 112 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 113 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 114 | df = pd.DataFrame(dataset).set_index('date') 115 | 116 | xreg = np.asarray(range(1, df.shape[0] + 1), dtype='float64') 117 | 118 | print(f"\n xreg: {xreg} \n") 119 | 120 | start = time() 121 | d4.forecast(df, xreg = xreg) 122 | print(f"Elapsed: {time()-start} \n") 123 | 124 | print(d4.fcast_.rx2['mean']) 125 | print(d4.averages_[1]) 126 | print(np.asarray(d4.fcast_.rx2['mean'])) 127 | 128 | print(d4.fcast_.rx2['sims'][0]) 129 | res = np.asarray(d4.fcast_.rx2['sims'][1]) 130 | print(res) 131 | print(res.shape) 132 | 133 | print("\n result_dfs_: \n") 134 | print(d4.result_dfs_) 135 | 136 | print("\n sims_: \n") 137 | print(d4.sims_) 138 | 139 | print("\n output_dates_: \n") 140 | print(d4.output_dates_) 141 | 142 | print("\n mean, lower, upper as numpy arrays: \n") 143 | print(d4.mean_) 144 | print(d4.lower_) 145 | print(d4.upper_) 146 | print("\n") 147 | d4.plot("series1") 148 | print("\n") 149 | 150 | -------------------------------------------------------------------------------- /examples/var.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from ahead import VAR 5 | from time import time 6 | 7 | print(f"\n ----- Running: {os.path.basename(__file__)}... ----- \n") 8 | 9 | # Forecasting horizon 10 | h = 5 11 | 12 | 13 | # Data frame containing the time series 14 | dataset = { 15 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 16 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 17 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 18 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 19 | df = pd.DataFrame(dataset).set_index('date') 20 | df.index = pd.DatetimeIndex(df.index) 21 | 22 | # univariate ts forecasting 23 | print("Example 1 -----") 24 | d1 = VAR(h = h, date_formatting = "ms") 25 | 26 | start = time() 27 | d1.forecast(df) 28 | print(f"Elapsed: {time()-start} \n") 29 | print("averages: \n") 30 | print(d1.averages_[0]) 31 | print("\n") 32 | print(d1.averages_[1]) 33 | print("\n") 34 | print(d1.averages_[2]) 35 | print("\n") 36 | print("ranges: \n") 37 | print(d1.ranges_[0]) 38 | print("\n") 39 | print(d1.ranges_[1]) 40 | print("\n") 41 | print(d1.ranges_[2]) 42 | print("\n") 43 | 44 | print("Example 2 -----") 45 | d2 = VAR(h = h, date_formatting = "original", type_VAR="none") 46 | start = time() 47 | d2.forecast(df) 48 | print(f"Elapsed: {time()-start} \n") 49 | print(d2.averages_[0]) 50 | print("\n") 51 | print(d2.averages_[1]) 52 | print("\n") 53 | print(d2.averages_[2]) 54 | print("\n") 55 | print("ranges: \n") 56 | print(d2.ranges_[0]) 57 | print("\n") 58 | print(d2.ranges_[1]) 59 | print("\n") 60 | print(d2.ranges_[2]) 61 | print("\n") 62 | print(d2.result_dfs_) 63 | print("\n") 64 | print("\n mean, lower, upper as numpy arrays: \n") 65 | print(d2.mean_) 66 | print(d2.lower_) 67 | print(d2.upper_) 68 | 69 | -------------------------------------------------------------------------------- /installr.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import platform 3 | 4 | 5 | def check_r_installed(): 6 | current_platform = platform.system() 7 | 8 | if current_platform == "Windows": 9 | # Check if R is installed on Windows by checking the registry 10 | try: 11 | subprocess.run( 12 | ["reg", "query", "HKLM\\Software\\R-core\\R"], check=True 13 | ) 14 | print("R is already installed on Windows.") 15 | return True 16 | except subprocess.CalledProcessError: 17 | print("R is not installed on Windows.") 18 | return False 19 | 20 | elif current_platform == "Linux": 21 | # Check if R is installed on Linux by checking if the 'R' executable is available 22 | try: 23 | subprocess.run(["which", "R"], check=True) 24 | print("R is already installed on Linux.") 25 | return True 26 | except subprocess.CalledProcessError: 27 | print("R is not installed on Linux.") 28 | return False 29 | 30 | elif current_platform == "Darwin": # macOS 31 | # Check if R is installed on macOS by checking if the 'R' executable is available 32 | try: 33 | subprocess.run(["which", "R"], check=True) 34 | print("R is already installed on macOS.") 35 | return True 36 | except subprocess.CalledProcessError: 37 | print("R is not installed on macOS.") 38 | return False 39 | 40 | else: 41 | print("Unsupported platform. Unable to check for R installation.") 42 | return False 43 | 44 | 45 | def install_r(): 46 | current_platform = platform.system() 47 | 48 | if current_platform == "Windows": 49 | # Install R on Windows using PowerShell 50 | install_command = "Start-Process powershell -Verb runAs -ArgumentList '-Command \"& {Invoke-WebRequest https://cran.r-project.org/bin/windows/base/R-4.1.2-win.exe -OutFile R.exe}; Start-Process R.exe -ArgumentList '/SILENT' -Wait}'" 51 | subprocess.run(install_command, shell=True) 52 | 53 | elif current_platform == "Linux": 54 | # Install R on Linux using the appropriate package manager (e.g., apt-get) 55 | install_command = ( 56 | "sudo apt update -qq && sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9" 57 | + "&& sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/'" 58 | + "&& sudo apt update" 59 | + "&& sudo apt install r-base" 60 | ) 61 | subprocess.run(install_command, shell=True) 62 | 63 | elif current_platform == "Darwin": # macOS 64 | # Install R on macOS using Homebrew 65 | install_command = "brew install r" 66 | subprocess.run(install_command, shell=True) 67 | 68 | else: 69 | print("Unsupported platform. Unable to install R.") -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | numpy 3 | scipy 4 | pandas 5 | scikit-learn 6 | rpy2==3.4.5 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | search = version='{current_version}' 8 | replace = version='{new_version}' 9 | 10 | [bumpversion:file:ahead/__init__.py] 11 | search = __version__ = '{current_version}' 12 | replace = __version__ = '{new_version}' 13 | 14 | [bdist_wheel] 15 | universal = 1 16 | 17 | [flake8] 18 | exclude = docs 19 | 20 | [aliases] 21 | # Define setup.py command aliases here 22 | 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import subprocess 3 | 4 | def check_r_installed(): 5 | current_platform = platform.system() 6 | 7 | if current_platform == "Windows": 8 | # Check if R is installed on Windows by checking the registry 9 | try: 10 | subprocess.run( 11 | ["reg", "query", "HKLM\\Software\\R-core\\R"], check=True 12 | ) 13 | print("R is already installed on Windows.") 14 | return True 15 | except subprocess.CalledProcessError: 16 | print("R is not installed on Windows.") 17 | return False 18 | 19 | elif current_platform == "Linux": 20 | # Check if R is installed on Linux by checking if the 'R' executable is available 21 | try: 22 | subprocess.run(["which", "R"], check=True) 23 | print("R is already installed on Linux.") 24 | return True 25 | except subprocess.CalledProcessError: 26 | print("R is not installed on Linux.") 27 | return False 28 | 29 | elif current_platform == "Darwin": # macOS 30 | # Check if R is installed on macOS by checking if the 'R' executable is available 31 | try: 32 | subprocess.run(["which", "R"], check=True) 33 | print("R is already installed on macOS.") 34 | return True 35 | except subprocess.CalledProcessError: 36 | print("R is not installed on macOS.") 37 | return False 38 | 39 | else: 40 | print("Unsupported platform. Unable to check for R installation.") 41 | return False 42 | 43 | def install_r(): 44 | 45 | current_platform = platform.system() 46 | 47 | if current_platform == "Windows": 48 | # Install R on Windows using PowerShell 49 | install_command = "Start-Process powershell -Verb subprocess.runAs -ArgumentList '-Command \"& {Invoke-WebRequest https://cran.r-project.org/bin/windows/base/R-4.1.2-win.exe -OutFile R.exe}; Start-Process R.exe -ArgumentList '/SILENT' -Wait}'" 50 | subprocess.run(install_command, shell=True) 51 | 52 | elif current_platform == "Linux": 53 | # Install R on Linux using the appropriate package manager (e.g., apt-get) 54 | install_command = ( 55 | "sudo apt update -qq && sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9" 56 | + "&& sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/'" 57 | + "&& sudo apt update" 58 | + "&& sudo apt -y install r-base" 59 | ) 60 | subprocess.run(install_command, shell=True) 61 | 62 | elif current_platform == "Darwin": # macOS 63 | # Install R on macOS using Homebrew 64 | install_command = "brew install r" 65 | subprocess.run(install_command, shell=True) 66 | 67 | else: 68 | 69 | print("Unsupported platform. Unable to install R.") 70 | 71 | def install_packages(): 72 | try: 73 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages(remotes, dependencies=TRUE)"]) 74 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('curl', dependencies=TRUE)"]) 75 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('e1071', dependencies=TRUE)"]) 76 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('ranger', dependencies=TRUE)"]) 77 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('fGarch', dependencies=TRUE)"]) 78 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('foreach', dependencies=TRUE)"]) 79 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('curl', dependencies=TRUE)"]) 80 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('randtoolbox', dependencies=TRUE)"]) 81 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('Rcpp', dependencies=TRUE)"]) 82 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('snow', dependencies=TRUE)"]) 83 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('VineCopula', dependencies=TRUE)"]) 84 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('tseries', dependencies=TRUE)"]) 85 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('forecast', dependencies=TRUE)"]) 86 | subprocess.run(["Rscript", "-e", "utils::install.packages('ahead', repos='https://techtonique.r-universe.dev', dependencies=TRUE)"]) 87 | except: 88 | # subprocess.run(["mkdir", "-p", "r-ahead"]) 89 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages(remotes, lib='r-ahead', dependencies=TRUE)"]) 90 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('curl', lib='r-ahead', dependencies=TRUE)"]) 91 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('e1071', lib='r-ahead', dependencies=TRUE)"]) 92 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('ranger', lib='r-ahead', dependencies=TRUE)"]) 93 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('fGarch', lib='r-ahead', dependencies=TRUE)"]) 94 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('foreach', lib='r-ahead', dependencies=TRUE)"]) 95 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('curl', lib='r-ahead', dependencies=TRUE)"]) 96 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('randtoolbox', lib='r-ahead', dependencies=TRUE)"]) 97 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('Rcpp', lib='r-ahead', dependencies=TRUE)"]) 98 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('snow', lib='r-ahead', dependencies=TRUE)"]) 99 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('VineCopula', lib='r-ahead', dependencies=TRUE)"]) 100 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('tseries', lib='r-ahead', dependencies=TRUE)"]) 101 | # subprocess.run(["sudo", "Rscript", "-e", "utils::install.packages('forecast', lib='r-ahead', dependencies=TRUE)"]) 102 | subprocess.run(["Rscript", "-e", "utils::install.packages('ahead', repos='https://techtonique.r-universe.dev', lib='r-ahead', dependencies=TRUE)"]) 103 | 104 | 105 | # Check if R is installed; if not, install it 106 | if not check_r_installed(): 107 | print("Installing R...") 108 | install_r() 109 | else: 110 | print("No installation needed.") 111 | 112 | install_packages() 113 | 114 | subprocess.run(["pip", "install", "rpy2"]) 115 | 116 | from setuptools import setup, find_packages 117 | from codecs import open 118 | from os import path 119 | 120 | __version__ = "0.13.0" 121 | 122 | here = path.abspath(path.dirname(__file__)) 123 | 124 | # get the dependencies and installs 125 | 126 | with open( 127 | path.join(here, "requirements.txt"), encoding="utf-8" 128 | ) as f: 129 | all_reqs = f.read().split("\n") 130 | 131 | install_requires = [ 132 | x.strip() for x in all_reqs if "git+" not in x 133 | ] 134 | dependency_links = [ 135 | x.strip().replace("git+", "") 136 | for x in all_reqs 137 | if x.startswith("git+") 138 | ] 139 | 140 | setup( 141 | name="ahead", 142 | version=__version__, 143 | description="Time series forecasting with Machine Learning and uncertainty quantification", 144 | long_description="A package for time series forecasting with Machine Learning and uncertainty quantification", 145 | license="BSD3 Clause Clear", 146 | classifiers=[ 147 | "Development Status :: 3 - Alpha", 148 | "Intended Audience :: Developers", 149 | "Programming Language :: Python :: 3", 150 | ], 151 | keywords="", 152 | packages=find_packages(exclude=["docs", "tests*"]), 153 | include_package_data=True, 154 | author="T. Moudiki", 155 | install_requires=install_requires, 156 | dependency_links=dependency_links, 157 | author_email="thierry.moudiki@gmail.com", 158 | python_requires=">=3.8" 159 | ) 160 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit test package for ahead.""" 2 | -------------------------------------------------------------------------------- /tests/test_ahead.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Tests for `ahead` package.""" 4 | 5 | # python -m unittest 6 | 7 | import unittest 8 | import pandas as pd 9 | from ahead import BasicForecaster, EAT, DynamicRegressor, Ridge2Regressor, VAR 10 | 11 | 12 | # Univariate dataset 13 | # Data frame containing the time series 14 | dataset_uni = { 15 | 'date' : ['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01', '2020-05-01'], 16 | 'value' : [34, 30, 35.6, 33.3, 38.1], 17 | } 18 | df_uni = pd.DataFrame(dataset_uni).set_index('date') 19 | 20 | # Multivariate dataset 21 | # Data frame containing the time series 22 | dataset_multi = { 23 | 'date' : ['2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01', '2005-01-01'], 24 | 'series1' : [34, 30, 35.6, 33.3, 38.1], 25 | 'series2' : [4, 5.5, 5.6, 6.3, 5.1], 26 | 'series3' : [100, 100.5, 100.6, 100.2, 100.1]} 27 | df_multi = pd.DataFrame(dataset_multi).set_index('date') 28 | 29 | 30 | # Forecasting horizon 31 | h = 5 32 | 33 | 34 | e1 = EAT(h = h, weights = [0.5, 0.4, 0.1], date_formatting = "ms") 35 | e2 = EAT(h = h, type_pi="T", date_formatting = "original") 36 | e1.forecast(df_uni) 37 | e2.forecast(df_uni) 38 | 39 | print("EAT ---------- \n") 40 | print(e1.averages_) 41 | print("\n") 42 | print(e1.ranges_) 43 | print("\n") 44 | print(e2.averages_) 45 | print("\n") 46 | print(e2.ranges_) 47 | print("\n") 48 | 49 | 50 | e3 = DynamicRegressor(h = h, date_formatting = "ms") 51 | e4 = DynamicRegressor(h = h, date_formatting = "original") 52 | e3.forecast(df_uni) 53 | e4.forecast(df_uni) 54 | 55 | print("DynamicRegressor ---------- \n") 56 | print(e3.averages_) 57 | print("\n") 58 | print(e3.ranges_) 59 | print("\n") 60 | print(e4.averages_) 61 | print("\n") 62 | print(e4.ranges_) 63 | print("\n") 64 | 65 | e5 = Ridge2Regressor(h = h, date_formatting = "ms") 66 | e6 = Ridge2Regressor(h = h, date_formatting = "original") 67 | e9 = Ridge2Regressor(h = h, date_formatting = "original", dropout=0.1) 68 | e10 = Ridge2Regressor(h = h, date_formatting = "original", dropout=0.1, 69 | type_pi='bootstrap', B=10, cl=2) 70 | e5.forecast(df_multi) 71 | e6.forecast(df_multi) 72 | e9.forecast(df_multi) 73 | e10.forecast(df_multi) 74 | print("Ridge2Regressor ---------- \n") 75 | print(e5.averages_) 76 | print("\n") 77 | print(e5.ranges_) 78 | print("\n") 79 | print(e6.averages_) 80 | print("\n") 81 | print(e6.ranges_) 82 | print("\n") 83 | print(e9.averages_) 84 | print("\n") 85 | print(e9.ranges_) 86 | print("\n") 87 | print(e10.averages_) 88 | print("\n") 89 | print(e10.ranges_) 90 | print("\n") 91 | 92 | 93 | e7 = VAR(h = h, date_formatting = "ms", type_VAR="none") 94 | e8 = VAR(h = h, date_formatting = "original", type_VAR="none") 95 | e7.forecast(df_multi) 96 | e8.forecast(df_multi) 97 | print("VAR ---------- \n") 98 | print(e7.averages_) 99 | print("\n") 100 | print(e7.ranges_) 101 | print("\n") 102 | print(e8.averages_) 103 | print("\n") 104 | print(e8.ranges_) 105 | print("\n") 106 | 107 | 108 | e11 = BasicForecaster(h = h, date_formatting = "ms") 109 | e12 = BasicForecaster(h = h, date_formatting = "original", 110 | type_pi='bootstrap', B=10) 111 | e11.forecast(df_multi) 112 | e12.forecast(df_multi) 113 | 114 | e13 = BasicForecaster(h = h, method="median", date_formatting = "ms") 115 | e14 = BasicForecaster(h = h, method="median", date_formatting = "original", 116 | type_pi='bootstrap', B=10) 117 | e13.forecast(df_multi) 118 | e14.forecast(df_multi) 119 | 120 | 121 | class TestAhead(unittest.TestCase): 122 | """Tests for `ahead` package.""" 123 | 124 | def setUp(self): 125 | """Set up test fixtures, if any.""" 126 | 127 | def tearDown(self): 128 | """Tear down test fixtures, if any.""" 129 | 130 | def test_eat(self): 131 | self.assertAlmostEqual(e1.averages_[0][1], 34.28750002378786) 132 | self.assertAlmostEqual(e1.ranges_[0][1], 28.95860083805203) 133 | self.assertAlmostEqual(e2.averages_[0][1], 37.833333650504755) 134 | self.assertAlmostEqual(e2.ranges_[0][1], 35.39276803598083) 135 | self.assertEqual(e1.averages_[0][0], 1590962400000) 136 | self.assertEqual(e2.averages_[0][0], '2020-06-01') 137 | 138 | def test_dynrm(self): 139 | self.assertAlmostEqual(e3.averages_[0][1], 36.79847159323566) 140 | self.assertAlmostEqual(e3.ranges_[0][1], 29.66346091111926) 141 | self.assertAlmostEqual(e4.averages_[0][1], 36.79847159323566) 142 | self.assertAlmostEqual(e4.ranges_[0][1], 29.66346091111926) 143 | self.assertEqual(e3.averages_[0][0], 1590962400000) 144 | self.assertEqual(e4.averages_[0][0], '2020-06-01') 145 | 146 | def test_basic(self): 147 | self.assertAlmostEqual(e11.averages_[0][0][1], 34.2) 148 | self.assertAlmostEqual(e12.averages_[0][0][1], 34.2) 149 | self.assertAlmostEqual(e13.averages_[0][0][1], 34.0) 150 | self.assertAlmostEqual(e14.averages_[0][0][1], 34.0) 151 | 152 | def test_ridge2(self): 153 | self.assertAlmostEqual(e5.averages_[0][0][1], 33.99538584327151) 154 | self.assertAlmostEqual(e6.averages_[0][0][1], 33.99538584327151) 155 | self.assertAlmostEqual(e5.averages_[0][0][0], 1136070000000) 156 | self.assertAlmostEqual(e6.averages_[0][0][0], '2006-01-01') 157 | self.assertAlmostEqual(e9.averages_[0][0][1], 26.80468657999291) 158 | self.assertAlmostEqual(e10.averages_[0][0][1], 27.02139325761657) 159 | 160 | def test_var(self): 161 | self.assertAlmostEqual(e7.averages_[0][0][1], 31.44666737744406) 162 | self.assertAlmostEqual(e8.averages_[0][0][1], 31.44666737744406) 163 | self.assertAlmostEqual(e7.averages_[0][0][0], 1136070000000) 164 | self.assertAlmostEqual(e8.averages_[0][0][0], '2006-01-01') 165 | 166 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py35, py36, py37, py38, flake8 3 | 4 | [travis] 5 | python = 6 | 3.8: py38 7 | 3.7: py37 8 | 3.6: py36 9 | 3.5: py35 10 | 11 | [testenv:flake8] 12 | basepython = python 13 | deps = flake8 14 | commands = flake8 ahead tests 15 | 16 | [testenv] 17 | setenv = 18 | PYTHONPATH = {toxinidir} 19 | 20 | commands = python setup.py test 21 | --------------------------------------------------------------------------------