pyforesight 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyforesight-0.1.0/.github/workflows/ci.yml +41 -0
- pyforesight-0.1.0/.github/workflows/pages.yml +38 -0
- pyforesight-0.1.0/.github/workflows/workflow.yml +68 -0
- pyforesight-0.1.0/.gitignore +8 -0
- pyforesight-0.1.0/CITATION.cff +24 -0
- pyforesight-0.1.0/Cargo.lock +139 -0
- pyforesight-0.1.0/Cargo.toml +21 -0
- pyforesight-0.1.0/LICENSE +21 -0
- pyforesight-0.1.0/PKG-INFO +172 -0
- pyforesight-0.1.0/README.md +145 -0
- pyforesight-0.1.0/README.pt-BR.md +62 -0
- pyforesight-0.1.0/pyproject.toml +49 -0
- pyforesight-0.1.0/python/foresight/__init__.py +128 -0
- pyforesight-0.1.0/python/foresight/_foresight.pyi +312 -0
- pyforesight-0.1.0/python/foresight/py.typed +0 -0
- pyforesight-0.1.0/site/assets/how-it-works.svg +69 -0
- pyforesight-0.1.0/site/index.html +520 -0
- pyforesight-0.1.0/src/lib.rs +1850 -0
- pyforesight-0.1.0/stubtest-allowlist.txt +2 -0
- pyforesight-0.1.0/tests/conftest.py +64 -0
- pyforesight-0.1.0/tests/data/air_passengers.csv +147 -0
- pyforesight-0.1.0/tests/data/piaui_revenue.csv +115 -0
- pyforesight-0.1.0/tests/data/rust_models.json +1 -0
- pyforesight-0.1.0/tests/data/rust_more.json +1 -0
- pyforesight-0.1.0/tests/test_api.py +97 -0
- pyforesight-0.1.0/tests/test_models.py +141 -0
- pyforesight-0.1.0/tests/test_more.py +156 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
rust:
|
|
9
|
+
name: fmt and clippy
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
14
|
+
with:
|
|
15
|
+
components: rustfmt, clippy
|
|
16
|
+
- run: cargo fmt --check
|
|
17
|
+
- run: cargo clippy --all-targets -- -D warnings
|
|
18
|
+
|
|
19
|
+
test:
|
|
20
|
+
name: test (${{ matrix.os }}, python ${{ matrix.python }})
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
26
|
+
python: ["3.9", "3.13"]
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: ${{ matrix.python }}
|
|
32
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
33
|
+
- name: Build and install
|
|
34
|
+
run: pip install ".[test,pandas]" numpy
|
|
35
|
+
- name: Test
|
|
36
|
+
run: pytest -v
|
|
37
|
+
- name: Type stubs match the module
|
|
38
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
|
|
39
|
+
run: |
|
|
40
|
+
pip install mypy
|
|
41
|
+
python -m mypy.stubtest foresight._foresight --ignore-disjoint-bases --allowlist stubtest-allowlist.txt
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: pages
|
|
2
|
+
|
|
3
|
+
# Publishes the static landing page in site/ to GitHub Pages on every push to
|
|
4
|
+
# main. Pages must be set to "GitHub Actions" as its source (Settings → Pages).
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths:
|
|
10
|
+
- "site/**"
|
|
11
|
+
- ".github/workflows/pages.yml"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
pages: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: pages
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
deploy:
|
|
25
|
+
environment:
|
|
26
|
+
name: github-pages
|
|
27
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- name: Disable Jekyll
|
|
32
|
+
run: touch site/.nojekyll
|
|
33
|
+
- uses: actions/configure-pages@v5
|
|
34
|
+
- uses: actions/upload-pages-artifact@v3
|
|
35
|
+
with:
|
|
36
|
+
path: site
|
|
37
|
+
- id: deployment
|
|
38
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# The file name workflow.yml is the one registered on PyPI as trusted
|
|
4
|
+
# publisher: do not rename it.
|
|
5
|
+
#
|
|
6
|
+
# Wheels for every platform on demand; on a tag vX.Y.Z they are also
|
|
7
|
+
# published to PyPI by trusted publishing (the project must be registered as
|
|
8
|
+
# a trusted publisher on pypi.org, environment "pypi").
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
tags: ["v*"]
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
wheels:
|
|
20
|
+
name: wheel (${{ matrix.os }}, ${{ matrix.target }})
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
include:
|
|
26
|
+
- { os: ubuntu-latest, target: x86_64 }
|
|
27
|
+
- { os: ubuntu-latest, target: aarch64 }
|
|
28
|
+
- { os: macos-latest, target: aarch64 }
|
|
29
|
+
- { os: macos-latest, target: x86_64 }
|
|
30
|
+
- { os: windows-latest, target: x64 }
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: PyO3/maturin-action@v1
|
|
34
|
+
with:
|
|
35
|
+
target: ${{ matrix.target }}
|
|
36
|
+
args: --release --out dist
|
|
37
|
+
manylinux: auto
|
|
38
|
+
- uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: wheel-${{ matrix.os }}-${{ matrix.target }}
|
|
41
|
+
path: dist
|
|
42
|
+
|
|
43
|
+
sdist:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: PyO3/maturin-action@v1
|
|
48
|
+
with:
|
|
49
|
+
command: sdist
|
|
50
|
+
args: --out dist
|
|
51
|
+
- uses: actions/upload-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: sdist
|
|
54
|
+
path: dist
|
|
55
|
+
|
|
56
|
+
publish:
|
|
57
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
58
|
+
needs: [wheels, sdist]
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
environment: pypi
|
|
61
|
+
permissions:
|
|
62
|
+
id-token: write
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/download-artifact@v4
|
|
65
|
+
with:
|
|
66
|
+
path: dist
|
|
67
|
+
merge-multiple: true
|
|
68
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this package, please cite it as below."
|
|
3
|
+
title: "pyforesight: time series forecasting that picks its model by what would have worked"
|
|
4
|
+
type: software
|
|
5
|
+
license: MIT
|
|
6
|
+
repository-code: "https://github.com/StrategicProjects/pyforesight"
|
|
7
|
+
url: "https://strategicprojects.github.io/pyforesight/"
|
|
8
|
+
authors:
|
|
9
|
+
- family-names: Leite
|
|
10
|
+
given-names: André
|
|
11
|
+
email: leite@castlab.org
|
|
12
|
+
orcid: "https://orcid.org/0000-0002-4718-9766"
|
|
13
|
+
- family-names: Wasiliew
|
|
14
|
+
given-names: Marcos
|
|
15
|
+
orcid: "https://orcid.org/0009-0004-4694-3159"
|
|
16
|
+
- family-names: Vasconcelos
|
|
17
|
+
given-names: Hugo
|
|
18
|
+
orcid: "https://orcid.org/0000-0001-6249-0920"
|
|
19
|
+
- family-names: Amorim
|
|
20
|
+
given-names: Carlos
|
|
21
|
+
orcid: "https://orcid.org/0000-0001-6315-8305"
|
|
22
|
+
- family-names: Bezerra
|
|
23
|
+
given-names: Diogo
|
|
24
|
+
orcid: "https://orcid.org/0000-0002-1216-8674"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "foresight"
|
|
7
|
+
version = "0.7.1"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "87da2acde51ebebfa0f2e5608b7998b917e101a43d91822c1254c768bc8dc6a0"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "heck"
|
|
13
|
+
version = "0.5.0"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "libc"
|
|
19
|
+
version = "0.2.189"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "once_cell"
|
|
25
|
+
version = "1.21.4"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
28
|
+
|
|
29
|
+
[[package]]
|
|
30
|
+
name = "portable-atomic"
|
|
31
|
+
version = "1.15.0"
|
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
33
|
+
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "proc-macro2"
|
|
37
|
+
version = "1.0.107"
|
|
38
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
39
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
40
|
+
dependencies = [
|
|
41
|
+
"unicode-ident",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "pyforesight"
|
|
46
|
+
version = "0.1.0"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"foresight",
|
|
49
|
+
"pyo3",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[[package]]
|
|
53
|
+
name = "pyo3"
|
|
54
|
+
version = "0.29.2"
|
|
55
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
56
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
57
|
+
dependencies = [
|
|
58
|
+
"libc",
|
|
59
|
+
"once_cell",
|
|
60
|
+
"portable-atomic",
|
|
61
|
+
"pyo3-build-config",
|
|
62
|
+
"pyo3-ffi",
|
|
63
|
+
"pyo3-macros",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[[package]]
|
|
67
|
+
name = "pyo3-build-config"
|
|
68
|
+
version = "0.29.2"
|
|
69
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
70
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
71
|
+
dependencies = [
|
|
72
|
+
"target-lexicon",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[[package]]
|
|
76
|
+
name = "pyo3-ffi"
|
|
77
|
+
version = "0.29.2"
|
|
78
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
79
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
80
|
+
dependencies = [
|
|
81
|
+
"libc",
|
|
82
|
+
"pyo3-build-config",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[[package]]
|
|
86
|
+
name = "pyo3-macros"
|
|
87
|
+
version = "0.29.2"
|
|
88
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
89
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
90
|
+
dependencies = [
|
|
91
|
+
"proc-macro2",
|
|
92
|
+
"pyo3-macros-backend",
|
|
93
|
+
"quote",
|
|
94
|
+
"syn",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "pyo3-macros-backend"
|
|
99
|
+
version = "0.29.2"
|
|
100
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
102
|
+
dependencies = [
|
|
103
|
+
"heck",
|
|
104
|
+
"proc-macro2",
|
|
105
|
+
"quote",
|
|
106
|
+
"syn",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
[[package]]
|
|
110
|
+
name = "quote"
|
|
111
|
+
version = "1.0.47"
|
|
112
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
113
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
114
|
+
dependencies = [
|
|
115
|
+
"proc-macro2",
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
[[package]]
|
|
119
|
+
name = "syn"
|
|
120
|
+
version = "2.0.119"
|
|
121
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
122
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
123
|
+
dependencies = [
|
|
124
|
+
"proc-macro2",
|
|
125
|
+
"quote",
|
|
126
|
+
"unicode-ident",
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
[[package]]
|
|
130
|
+
name = "target-lexicon"
|
|
131
|
+
version = "0.13.5"
|
|
132
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
133
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
134
|
+
|
|
135
|
+
[[package]]
|
|
136
|
+
name = "unicode-ident"
|
|
137
|
+
version = "1.0.26"
|
|
138
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
139
|
+
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "pyforesight"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Python bindings of the foresight forecasting crate"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
repository = "https://github.com/StrategicProjects/pyforesight"
|
|
8
|
+
publish = false
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
|
|
11
|
+
[lib]
|
|
12
|
+
name = "_foresight"
|
|
13
|
+
crate-type = ["cdylib"]
|
|
14
|
+
|
|
15
|
+
[dependencies]
|
|
16
|
+
foresight = "=0.7.1"
|
|
17
|
+
pyo3 = { version = "0.29", features = ["abi3-py39"] }
|
|
18
|
+
|
|
19
|
+
[profile.release]
|
|
20
|
+
lto = "fat"
|
|
21
|
+
codegen-units = 1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 André Leite, Marcos Wasiliew, Hugo Vasconcelos, Carlos Amorim and Diogo Bezerra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyforesight
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Science/Research
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Rust
|
|
8
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
9
|
+
Classifier: Typing :: Typed
|
|
10
|
+
Requires-Dist: pandas>=1.5 ; extra == 'pandas'
|
|
11
|
+
Requires-Dist: pytest>=7 ; extra == 'test'
|
|
12
|
+
Provides-Extra: pandas
|
|
13
|
+
Provides-Extra: test
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Summary: Time series forecasting that picks its model by what would have worked: rolling-origin backtests, empirical intervals, ARIMA, ETS, Prophet, TBATS, STL and ensembles, in Rust.
|
|
16
|
+
Keywords: forecasting,time series,arima,ets,prophet,backtest
|
|
17
|
+
Author-email: André Leite <leite@castlab.org>, Marcos Wasiliew <marcos.wasiliew@gmail.com>, Hugo Vasconcelos <hugo.vasconcelos@ufpe.br>, Carlos Amorim <carlos.agaf@ufpe.br>, Diogo Bezerra <diogo.bezerra@ufpe.br>
|
|
18
|
+
Maintainer-email: André Leite <leite@castlab.org>
|
|
19
|
+
License-Expression: MIT
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
22
|
+
Project-URL: Homepage, https://strategicprojects.github.io/pyforesight/
|
|
23
|
+
Project-URL: Issues, https://github.com/StrategicProjects/pyforesight/issues
|
|
24
|
+
Project-URL: Rust crate, https://crates.io/crates/foresight
|
|
25
|
+
Project-URL: Source, https://github.com/StrategicProjects/pyforesight
|
|
26
|
+
|
|
27
|
+
# pyforesight
|
|
28
|
+
|
|
29
|
+
[](https://github.com/StrategicProjects/pyforesight/actions/workflows/ci.yml)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
[](pyproject.toml)
|
|
32
|
+
|
|
33
|
+
Time series forecasting in Python that picks its model by what would have
|
|
34
|
+
worked.
|
|
35
|
+
|
|
36
|
+
`foresight` fits several models to a series, replays the past to see how each
|
|
37
|
+
would have done, chooses by out-of-sample error and reports intervals taken
|
|
38
|
+
from the errors actually observed, including intervals for the total of the
|
|
39
|
+
next k periods.
|
|
40
|
+
|
|
41
|
+
The models, the backtest and the utilities are the Rust crate
|
|
42
|
+
[foresight](https://github.com/milkway/foresight), compiled into the package:
|
|
43
|
+
the numbers are the crate's, the backtest runs on all cores, and nothing else
|
|
44
|
+
is needed at run time. NumPy and pandas are accepted and, for pandas, produced
|
|
45
|
+
on request, but neither is required.
|
|
46
|
+
|
|
47
|
+
**Website:** <https://strategicprojects.github.io/pyforesight/> ·
|
|
48
|
+
[Português](README.pt-BR.md)
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install git+https://github.com/StrategicProjects/pyforesight
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Python 3.9 or later. Installing from the repository compiles the Rust code,
|
|
57
|
+
so it needs a Rust toolchain (<https://rustup.rs>).
|
|
58
|
+
|
|
59
|
+
## Use
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import foresight as fs
|
|
63
|
+
|
|
64
|
+
# monthly data whose first observation is in March
|
|
65
|
+
y = fs.monthly(values, first_month=3)
|
|
66
|
+
|
|
67
|
+
# replay the last 36 months, 12 months ahead, with every built-in model
|
|
68
|
+
report = fs.backtest(y)
|
|
69
|
+
|
|
70
|
+
best = report.best
|
|
71
|
+
print(f"{best.name}: MAPE {best.score:.1f}%")
|
|
72
|
+
for p in best.forecast:
|
|
73
|
+
lo, hi = p.interval(0.80)
|
|
74
|
+
print(p.horizon, round(p.mean), round(lo), round(hi))
|
|
75
|
+
|
|
76
|
+
half_year = best.cumulative(6) # the total of the next six months, with its own interval
|
|
77
|
+
report.to_pandas() # one row per candidate (needs pandas)
|
|
78
|
+
best.to_pandas() # the forecast with its intervals
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
One model on its own:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# seasonal ARIMA on the log scale
|
|
85
|
+
fit = fs.log(fs.Arima.airline()).fit(y)
|
|
86
|
+
next_year = fit.forecast(12)
|
|
87
|
+
|
|
88
|
+
# orders chosen from the data, inspected
|
|
89
|
+
auto = fs.AutoArima().fit(fs.monthly(log_values, first_month=3))
|
|
90
|
+
auto.details["order"], auto.details["seasonal_order"], auto.aicc
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
A trend that bends, with dated events:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
model = fs.Prophet(
|
|
97
|
+
events={"campaign": [10, 34, 58, 82, 106, 130]}, # future ones included
|
|
98
|
+
steps={"new_law": 80}, # a lasting change of level
|
|
99
|
+
)
|
|
100
|
+
fit = model.fit(y)
|
|
101
|
+
fit.details["changepoints"], fit.details["effects"]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Several models combined, and the wider set of candidates:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
ensemble = fs.Ensemble(fs.defaults(), weighting="stacked")
|
|
108
|
+
report = fs.backtest(y, fs.thorough() + [ensemble.named("my_ensemble")])
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Any sequence of numbers works where a series is expected: a list, a NumPy
|
|
112
|
+
array, a pandas Series. Without `fs.Series` (or `fs.monthly`,
|
|
113
|
+
`fs.quarterly`), pass the seasonal period: `fs.Theta().fit(values, period=12)`.
|
|
114
|
+
Missing values (`None`, NaN) are only accepted by the cleaning functions.
|
|
115
|
+
|
|
116
|
+
## What is in it
|
|
117
|
+
|
|
118
|
+
| Piece | What it does |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `Series`, `monthly`, `quarterly` | values + seasonal period + season of the first observation |
|
|
121
|
+
| `Model` / `Fit` | fit once, forecast any horizon, inspect `params`, `details`, likelihood and residuals |
|
|
122
|
+
| Models | `Mean`, `Naive`, `Drift`, `SeasonalNaive`, `Theta`, `HoltWinters`, `LogLinear` (optionally deflated by a price index), `Arima` (seasonal, exact maximum likelihood, optionally with regressors), `AutoArima`, `Ets`, `AutoEts`, `Prophet` (changepoints, Fourier seasonality, dated events and steps), `Tbats` (several seasonal periods, not necessarily whole numbers), `Croston` (with SBA and TSB) |
|
|
123
|
+
| `Transformed`, `log` | any model on the log or another Box-Cox scale, λ fixed or by Guerrero's method |
|
|
124
|
+
| `Decomposed`, `stl`, `mstl` | trend, seasonal patterns and remainder by LOESS; any model on the seasonally adjusted series |
|
|
125
|
+
| `Ensemble` | average, median, weights by inverse error or stacked weights |
|
|
126
|
+
| `Regressors` | external variables, Fourier terms, seasonal dummies |
|
|
127
|
+
| `defaults`, `thorough` | ready sets of 11 and 18 candidates |
|
|
128
|
+
| `backtest` | rolling origin (expanding or fixed window) on all cores; MAPE, MAE, RMSE, MASE and bias by horizon; average of the best models; choice by out-of-sample error; empirical intervals by horizon and for totals |
|
|
129
|
+
| `interpolate`, `outliers`, `clean` | gaps filled and outliers found and replaced, with the season taken into account |
|
|
130
|
+
| Measures and tests | `mape`, `bias`, `mae`, `rmse`, `mase`, `acf`, `difference`, `kpss`, `ndiffs`, `nsdiffs`, `seasonal_strength`, `box_cox`, `inv_box_cox`, `guerrero` |
|
|
131
|
+
|
|
132
|
+
## How it differs from the usual toolkits
|
|
133
|
+
|
|
134
|
+
Most forecasting libraries choose a model by an in-sample information
|
|
135
|
+
criterion and derive intervals from distributional assumptions. Here the
|
|
136
|
+
choice and the intervals both come from forecasts made without seeing the
|
|
137
|
+
future they are judged against. The interval for a total (say, the rest of a
|
|
138
|
+
fiscal year) is measured on totals, because adding up monthly limits
|
|
139
|
+
overstates its uncertainty.
|
|
140
|
+
|
|
141
|
+
## Checked
|
|
142
|
+
|
|
143
|
+
The package runs the Rust crate, so its numbers are the crate's; the tests
|
|
144
|
+
check that nothing is lost on the way, against results recorded by the crate:
|
|
145
|
+
ARIMA, regression with ARIMA errors, ETS, Prophet, TBATS, STL and MSTL,
|
|
146
|
+
Croston, cleaning, ensembles, tests of stationarity and seasonality, and the
|
|
147
|
+
backtests of 11 and 18 candidates on three public series. The crate itself is
|
|
148
|
+
compared with the R packages `forecast` 9.0.2 and `prophet` 1.1.7, and
|
|
149
|
+
reproduced independently by the Go edition
|
|
150
|
+
[foresight-go](https://github.com/milkway/foresight-go).
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pip install maturin pytest
|
|
154
|
+
maturin develop --release
|
|
155
|
+
pytest # about 30 s; pytest -m "not slow" skips TBATS and the thorough backtest
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Data
|
|
159
|
+
|
|
160
|
+
`tests/data` has two public series: the monthly ICMS and FPE revenue of the
|
|
161
|
+
state of Piauí, Brazil (Siconfi/STN, with the IPCA price index from the
|
|
162
|
+
Central Bank of Brazil), and the airline passengers of Box & Jenkins.
|
|
163
|
+
|
|
164
|
+
## Authors
|
|
165
|
+
|
|
166
|
+
André Leite, Marcos Wasiliew, Hugo Vasconcelos, Carlos Amorim and Diogo
|
|
167
|
+
Bezerra.
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT.
|
|
172
|
+
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# pyforesight
|
|
2
|
+
|
|
3
|
+
[](https://github.com/StrategicProjects/pyforesight/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](pyproject.toml)
|
|
6
|
+
|
|
7
|
+
Time series forecasting in Python that picks its model by what would have
|
|
8
|
+
worked.
|
|
9
|
+
|
|
10
|
+
`foresight` fits several models to a series, replays the past to see how each
|
|
11
|
+
would have done, chooses by out-of-sample error and reports intervals taken
|
|
12
|
+
from the errors actually observed, including intervals for the total of the
|
|
13
|
+
next k periods.
|
|
14
|
+
|
|
15
|
+
The models, the backtest and the utilities are the Rust crate
|
|
16
|
+
[foresight](https://github.com/milkway/foresight), compiled into the package:
|
|
17
|
+
the numbers are the crate's, the backtest runs on all cores, and nothing else
|
|
18
|
+
is needed at run time. NumPy and pandas are accepted and, for pandas, produced
|
|
19
|
+
on request, but neither is required.
|
|
20
|
+
|
|
21
|
+
**Website:** <https://strategicprojects.github.io/pyforesight/> ·
|
|
22
|
+
[Português](README.pt-BR.md)
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install git+https://github.com/StrategicProjects/pyforesight
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Python 3.9 or later. Installing from the repository compiles the Rust code,
|
|
31
|
+
so it needs a Rust toolchain (<https://rustup.rs>).
|
|
32
|
+
|
|
33
|
+
## Use
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import foresight as fs
|
|
37
|
+
|
|
38
|
+
# monthly data whose first observation is in March
|
|
39
|
+
y = fs.monthly(values, first_month=3)
|
|
40
|
+
|
|
41
|
+
# replay the last 36 months, 12 months ahead, with every built-in model
|
|
42
|
+
report = fs.backtest(y)
|
|
43
|
+
|
|
44
|
+
best = report.best
|
|
45
|
+
print(f"{best.name}: MAPE {best.score:.1f}%")
|
|
46
|
+
for p in best.forecast:
|
|
47
|
+
lo, hi = p.interval(0.80)
|
|
48
|
+
print(p.horizon, round(p.mean), round(lo), round(hi))
|
|
49
|
+
|
|
50
|
+
half_year = best.cumulative(6) # the total of the next six months, with its own interval
|
|
51
|
+
report.to_pandas() # one row per candidate (needs pandas)
|
|
52
|
+
best.to_pandas() # the forecast with its intervals
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
One model on its own:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
# seasonal ARIMA on the log scale
|
|
59
|
+
fit = fs.log(fs.Arima.airline()).fit(y)
|
|
60
|
+
next_year = fit.forecast(12)
|
|
61
|
+
|
|
62
|
+
# orders chosen from the data, inspected
|
|
63
|
+
auto = fs.AutoArima().fit(fs.monthly(log_values, first_month=3))
|
|
64
|
+
auto.details["order"], auto.details["seasonal_order"], auto.aicc
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
A trend that bends, with dated events:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
model = fs.Prophet(
|
|
71
|
+
events={"campaign": [10, 34, 58, 82, 106, 130]}, # future ones included
|
|
72
|
+
steps={"new_law": 80}, # a lasting change of level
|
|
73
|
+
)
|
|
74
|
+
fit = model.fit(y)
|
|
75
|
+
fit.details["changepoints"], fit.details["effects"]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Several models combined, and the wider set of candidates:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
ensemble = fs.Ensemble(fs.defaults(), weighting="stacked")
|
|
82
|
+
report = fs.backtest(y, fs.thorough() + [ensemble.named("my_ensemble")])
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Any sequence of numbers works where a series is expected: a list, a NumPy
|
|
86
|
+
array, a pandas Series. Without `fs.Series` (or `fs.monthly`,
|
|
87
|
+
`fs.quarterly`), pass the seasonal period: `fs.Theta().fit(values, period=12)`.
|
|
88
|
+
Missing values (`None`, NaN) are only accepted by the cleaning functions.
|
|
89
|
+
|
|
90
|
+
## What is in it
|
|
91
|
+
|
|
92
|
+
| Piece | What it does |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `Series`, `monthly`, `quarterly` | values + seasonal period + season of the first observation |
|
|
95
|
+
| `Model` / `Fit` | fit once, forecast any horizon, inspect `params`, `details`, likelihood and residuals |
|
|
96
|
+
| Models | `Mean`, `Naive`, `Drift`, `SeasonalNaive`, `Theta`, `HoltWinters`, `LogLinear` (optionally deflated by a price index), `Arima` (seasonal, exact maximum likelihood, optionally with regressors), `AutoArima`, `Ets`, `AutoEts`, `Prophet` (changepoints, Fourier seasonality, dated events and steps), `Tbats` (several seasonal periods, not necessarily whole numbers), `Croston` (with SBA and TSB) |
|
|
97
|
+
| `Transformed`, `log` | any model on the log or another Box-Cox scale, λ fixed or by Guerrero's method |
|
|
98
|
+
| `Decomposed`, `stl`, `mstl` | trend, seasonal patterns and remainder by LOESS; any model on the seasonally adjusted series |
|
|
99
|
+
| `Ensemble` | average, median, weights by inverse error or stacked weights |
|
|
100
|
+
| `Regressors` | external variables, Fourier terms, seasonal dummies |
|
|
101
|
+
| `defaults`, `thorough` | ready sets of 11 and 18 candidates |
|
|
102
|
+
| `backtest` | rolling origin (expanding or fixed window) on all cores; MAPE, MAE, RMSE, MASE and bias by horizon; average of the best models; choice by out-of-sample error; empirical intervals by horizon and for totals |
|
|
103
|
+
| `interpolate`, `outliers`, `clean` | gaps filled and outliers found and replaced, with the season taken into account |
|
|
104
|
+
| Measures and tests | `mape`, `bias`, `mae`, `rmse`, `mase`, `acf`, `difference`, `kpss`, `ndiffs`, `nsdiffs`, `seasonal_strength`, `box_cox`, `inv_box_cox`, `guerrero` |
|
|
105
|
+
|
|
106
|
+
## How it differs from the usual toolkits
|
|
107
|
+
|
|
108
|
+
Most forecasting libraries choose a model by an in-sample information
|
|
109
|
+
criterion and derive intervals from distributional assumptions. Here the
|
|
110
|
+
choice and the intervals both come from forecasts made without seeing the
|
|
111
|
+
future they are judged against. The interval for a total (say, the rest of a
|
|
112
|
+
fiscal year) is measured on totals, because adding up monthly limits
|
|
113
|
+
overstates its uncertainty.
|
|
114
|
+
|
|
115
|
+
## Checked
|
|
116
|
+
|
|
117
|
+
The package runs the Rust crate, so its numbers are the crate's; the tests
|
|
118
|
+
check that nothing is lost on the way, against results recorded by the crate:
|
|
119
|
+
ARIMA, regression with ARIMA errors, ETS, Prophet, TBATS, STL and MSTL,
|
|
120
|
+
Croston, cleaning, ensembles, tests of stationarity and seasonality, and the
|
|
121
|
+
backtests of 11 and 18 candidates on three public series. The crate itself is
|
|
122
|
+
compared with the R packages `forecast` 9.0.2 and `prophet` 1.1.7, and
|
|
123
|
+
reproduced independently by the Go edition
|
|
124
|
+
[foresight-go](https://github.com/milkway/foresight-go).
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pip install maturin pytest
|
|
128
|
+
maturin develop --release
|
|
129
|
+
pytest # about 30 s; pytest -m "not slow" skips TBATS and the thorough backtest
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Data
|
|
133
|
+
|
|
134
|
+
`tests/data` has two public series: the monthly ICMS and FPE revenue of the
|
|
135
|
+
state of Piauí, Brazil (Siconfi/STN, with the IPCA price index from the
|
|
136
|
+
Central Bank of Brazil), and the airline passengers of Box & Jenkins.
|
|
137
|
+
|
|
138
|
+
## Authors
|
|
139
|
+
|
|
140
|
+
André Leite, Marcos Wasiliew, Hugo Vasconcelos, Carlos Amorim and Diogo
|
|
141
|
+
Bezerra.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT.
|