time-split 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.
- time_split-0.1.0/LICENSE.md +21 -0
- time_split-0.1.0/PKG-INFO +97 -0
- time_split-0.1.0/README.md +65 -0
- time_split-0.1.0/pyproject.toml +190 -0
- time_split-0.1.0/src/time_split/__init__.py +16 -0
- time_split-0.1.0/src/time_split/_backend/__init__.py +4 -0
- time_split-0.1.0/src/time_split/_backend/_available.py +57 -0
- time_split-0.1.0/src/time_split/_backend/_datetime_index_like.py +19 -0
- time_split-0.1.0/src/time_split/_backend/_limits.py +151 -0
- time_split-0.1.0/src/time_split/_backend/_schedule.py +66 -0
- time_split-0.1.0/src/time_split/_backend/_span.py +128 -0
- time_split-0.1.0/src/time_split/_backend/_splitter.py +162 -0
- time_split-0.1.0/src/time_split/_docstrings.py +36 -0
- time_split-0.1.0/src/time_split/_frontend/__init__.py +13 -0
- time_split-0.1.0/src/time_split/_frontend/_plot.py +355 -0
- time_split-0.1.0/src/time_split/_frontend/_progress.py +125 -0
- time_split-0.1.0/src/time_split/_frontend/_split.py +45 -0
- time_split-0.1.0/src/time_split/_frontend/_to_string.py +103 -0
- time_split-0.1.0/src/time_split/_frontend/_weight.py +66 -0
- time_split-0.1.0/src/time_split/_support.py +14 -0
- time_split-0.1.0/src/time_split/integration/__init__.py +1 -0
- time_split-0.1.0/src/time_split/integration/_log_progress.py +25 -0
- time_split-0.1.0/src/time_split/integration/pandas/__init__.py +42 -0
- time_split-0.1.0/src/time_split/integration/pandas/_impl.py +81 -0
- time_split-0.1.0/src/time_split/integration/polars/__init__.py +57 -0
- time_split-0.1.0/src/time_split/integration/polars/_impl.py +69 -0
- time_split-0.1.0/src/time_split/integration/sklearn/__init__.py +7 -0
- time_split-0.1.0/src/time_split/integration/sklearn/_impl.py +170 -0
- time_split-0.1.0/src/time_split/integration/split_data.py +72 -0
- time_split-0.1.0/src/time_split/py.typed +0 -0
- time_split-0.1.0/src/time_split/settings.py +172 -0
- time_split-0.1.0/src/time_split/support/__init__.py +21 -0
- time_split-0.1.0/src/time_split/types.py +61 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Richard Sundqvist
|
|
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,97 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: time-split
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: My personal little ML engineering library.
|
|
5
|
+
Home-page: https://github.com/rsundqvist/time-split
|
|
6
|
+
Author: Richard Sundqvist
|
|
7
|
+
Author-email: richard.sundqvist@live.se
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Provides-Extra: plotting
|
|
22
|
+
Requires-Dist: matplotlib ; extra == "plotting"
|
|
23
|
+
Requires-Dist: pandas (>=2.0.3)
|
|
24
|
+
Requires-Dist: rics (>=3.1.0)
|
|
25
|
+
Requires-Dist: seaborn ; extra == "plotting"
|
|
26
|
+
Project-URL: Bug Tracker, https://github.com/rsundqvist/time-split/issues
|
|
27
|
+
Project-URL: Changelog, https://github.com/rsundqvist/time-split/blob/master/CHANGELOG.md
|
|
28
|
+
Project-URL: Documentation, https://time-split.readthedocs.io
|
|
29
|
+
Project-URL: Repository, https://github.com/rsundqvist/time-split
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Time Split <!-- omit in toc -->
|
|
33
|
+
Flexible k-fold validation splits for heterogeneous data.
|
|
34
|
+
|
|
35
|
+
-----------------
|
|
36
|
+
[](https://pypi.python.org/pypi/time-split)
|
|
37
|
+
[](https://pypi.python.org/pypi/time-split)
|
|
38
|
+
[](https://github.com/rsundqvist/time-split/actions?workflow=tests)
|
|
39
|
+
[](https://codecov.io/gh/rsundqvist/time-split)
|
|
40
|
+
[](https://time-split.readthedocs.io/)
|
|
41
|
+
[](https://pypi.python.org/pypi/time-split)
|
|
42
|
+
|
|
43
|
+
<div align="center">
|
|
44
|
+
<img alt="Plotted folds on a two-by-two grid."
|
|
45
|
+
title="Examples" height="300" width="1200"
|
|
46
|
+
src="https://raw.githubusercontent.com/rsundqvist/time-split/master/docs/2x2-examples.jpg"><br>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
Plotted folds on a two-by-two grid. See the
|
|
50
|
+
[examples](https://time-split.readthedocs.io/en/stable/auto_examples/index.html) page for more.
|
|
51
|
+
|
|
52
|
+
## What is it?
|
|
53
|
+
A library for creating cross-validation splits of _heterogeneous_ data, such as raw transaction data with strong
|
|
54
|
+
non-stationary characteristics.
|
|
55
|
+
|
|
56
|
+
## Highlighted Features
|
|
57
|
+
- [Splitting schedules](https://time-split.readthedocs.io/en/stable/guide/schedules.html) based on a fixed interval,
|
|
58
|
+
a CRON-expression, or a pre-defined list.
|
|
59
|
+
- [Data selection](https://time-split.readthedocs.io/en/stable/guide/spans.html) based on a timedelta, or the splitting
|
|
60
|
+
schedule itself.
|
|
61
|
+
- Automatically extract and [normalize](https://time-split.readthedocs.io/en/stable/guide/flex.html) data limits for
|
|
62
|
+
supported data types.
|
|
63
|
+
- [Plotting function](https://time-split.readthedocs.io/en/stable/generated/time_split.html#time_split.plot) for
|
|
64
|
+
visualization of folds. Display fold sizes (hour/row count), or
|
|
65
|
+
[use custom text](https://time-split.readthedocs.io/en/stable/auto_examples/plotting_with_metrics.html).
|
|
66
|
+
- [Integrations](https://time-split.readthedocs.io/en/stable/generated/time_split.integration.html) for popular
|
|
67
|
+
libraries such as `pandas`, `polars` and `scikit-learn`.
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
The package is published through the [Python Package Index (PyPI)]. Source code
|
|
71
|
+
is available on GitHub: https://github.com/rsundqvist/time-split
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
pip install -U time-split
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This is the preferred method to install ``time-split``, as it will always install the
|
|
78
|
+
most recent stable release.
|
|
79
|
+
|
|
80
|
+
If you don't have [pip] installed, this [Python installation guide] can guide
|
|
81
|
+
you through the process.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
[MIT](LICENSE.md)
|
|
85
|
+
|
|
86
|
+
## Documentation
|
|
87
|
+
Hosted on Read the Docs: https://time-split.readthedocs.io
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
|
|
91
|
+
All contributions, bug reports, bug fixes, documentation improvements, enhancements, and ideas are welcome. To get
|
|
92
|
+
started, see the [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
93
|
+
|
|
94
|
+
[Python Package Index (PyPI)]: https://pypi.org/project/time-split
|
|
95
|
+
[pip]: https://pip.pypa.io
|
|
96
|
+
[Python installation guide]: http://docs.python-guide.org/en/stable/starting/installation/
|
|
97
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Time Split <!-- omit in toc -->
|
|
2
|
+
Flexible k-fold validation splits for heterogeneous data.
|
|
3
|
+
|
|
4
|
+
-----------------
|
|
5
|
+
[](https://pypi.python.org/pypi/time-split)
|
|
6
|
+
[](https://pypi.python.org/pypi/time-split)
|
|
7
|
+
[](https://github.com/rsundqvist/time-split/actions?workflow=tests)
|
|
8
|
+
[](https://codecov.io/gh/rsundqvist/time-split)
|
|
9
|
+
[](https://time-split.readthedocs.io/)
|
|
10
|
+
[](https://pypi.python.org/pypi/time-split)
|
|
11
|
+
|
|
12
|
+
<div align="center">
|
|
13
|
+
<img alt="Plotted folds on a two-by-two grid."
|
|
14
|
+
title="Examples" height="300" width="1200"
|
|
15
|
+
src="https://raw.githubusercontent.com/rsundqvist/time-split/master/docs/2x2-examples.jpg"><br>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
Plotted folds on a two-by-two grid. See the
|
|
19
|
+
[examples](https://time-split.readthedocs.io/en/stable/auto_examples/index.html) page for more.
|
|
20
|
+
|
|
21
|
+
## What is it?
|
|
22
|
+
A library for creating cross-validation splits of _heterogeneous_ data, such as raw transaction data with strong
|
|
23
|
+
non-stationary characteristics.
|
|
24
|
+
|
|
25
|
+
## Highlighted Features
|
|
26
|
+
- [Splitting schedules](https://time-split.readthedocs.io/en/stable/guide/schedules.html) based on a fixed interval,
|
|
27
|
+
a CRON-expression, or a pre-defined list.
|
|
28
|
+
- [Data selection](https://time-split.readthedocs.io/en/stable/guide/spans.html) based on a timedelta, or the splitting
|
|
29
|
+
schedule itself.
|
|
30
|
+
- Automatically extract and [normalize](https://time-split.readthedocs.io/en/stable/guide/flex.html) data limits for
|
|
31
|
+
supported data types.
|
|
32
|
+
- [Plotting function](https://time-split.readthedocs.io/en/stable/generated/time_split.html#time_split.plot) for
|
|
33
|
+
visualization of folds. Display fold sizes (hour/row count), or
|
|
34
|
+
[use custom text](https://time-split.readthedocs.io/en/stable/auto_examples/plotting_with_metrics.html).
|
|
35
|
+
- [Integrations](https://time-split.readthedocs.io/en/stable/generated/time_split.integration.html) for popular
|
|
36
|
+
libraries such as `pandas`, `polars` and `scikit-learn`.
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
The package is published through the [Python Package Index (PyPI)]. Source code
|
|
40
|
+
is available on GitHub: https://github.com/rsundqvist/time-split
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
pip install -U time-split
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is the preferred method to install ``time-split``, as it will always install the
|
|
47
|
+
most recent stable release.
|
|
48
|
+
|
|
49
|
+
If you don't have [pip] installed, this [Python installation guide] can guide
|
|
50
|
+
you through the process.
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
[MIT](LICENSE.md)
|
|
54
|
+
|
|
55
|
+
## Documentation
|
|
56
|
+
Hosted on Read the Docs: https://time-split.readthedocs.io
|
|
57
|
+
|
|
58
|
+
## Contributing
|
|
59
|
+
|
|
60
|
+
All contributions, bug reports, bug fixes, documentation improvements, enhancements, and ideas are welcome. To get
|
|
61
|
+
started, see the [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
62
|
+
|
|
63
|
+
[Python Package Index (PyPI)]: https://pypi.org/project/time-split
|
|
64
|
+
[pip]: https://pip.pypa.io
|
|
65
|
+
[Python installation guide]: http://docs.python-guide.org/en/stable/starting/installation/
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "time-split"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "My personal little ML engineering library."
|
|
5
|
+
authors = ["Richard Sundqvist <richard.sundqvist@live.se>"]
|
|
6
|
+
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
homepage = "https://github.com/rsundqvist/time-split"
|
|
9
|
+
repository = "https://github.com/rsundqvist/time-split"
|
|
10
|
+
documentation = "https://time-split.readthedocs.io"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"Operating System :: POSIX :: Linux",
|
|
16
|
+
"Operating System :: MacOS",
|
|
17
|
+
"Operating System :: Microsoft :: Windows",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Natural Language :: English",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
'Programming Language :: Python :: 3 :: Only',
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[tool.poetry.urls]
|
|
28
|
+
"Bug Tracker" = "https://github.com/rsundqvist/time-split/issues"
|
|
29
|
+
"Changelog" = "https://github.com/rsundqvist/time-split/blob/master/CHANGELOG.md"
|
|
30
|
+
|
|
31
|
+
[tool.poetry.scripts]
|
|
32
|
+
|
|
33
|
+
[tool.poetry.dependencies]
|
|
34
|
+
python = ">=3.11"
|
|
35
|
+
|
|
36
|
+
pandas = ">=2.0.3"
|
|
37
|
+
rics = ">=3.1.0"
|
|
38
|
+
|
|
39
|
+
# plotting
|
|
40
|
+
matplotlib = { version = "*", optional = true }
|
|
41
|
+
seaborn = { version = "*", optional = true }
|
|
42
|
+
|
|
43
|
+
[tool.poetry.extras]
|
|
44
|
+
plotting = ["matplotlib", "seaborn"]
|
|
45
|
+
|
|
46
|
+
[tool.poetry.group.manual-extras.dependencies]
|
|
47
|
+
# Extras that must be available for docs + tests go here. Doc/test-only deps go in their respective groups.
|
|
48
|
+
croniter = "^2.0.3"
|
|
49
|
+
pyarrow = "^15.0.2"
|
|
50
|
+
polars = { version = "^0.20.19", extras = ["pandas"] }
|
|
51
|
+
|
|
52
|
+
[tool.poetry.group.docs]
|
|
53
|
+
[tool.poetry.group.docs.dependencies]
|
|
54
|
+
# duplicate object warnings for sphinx>=4
|
|
55
|
+
# description: https://github.com/astropy/astropy/issues/11723
|
|
56
|
+
# https://github.com/astropy/astropy/pull/12270
|
|
57
|
+
sphinx = "^7.2.6"
|
|
58
|
+
sphinx-gallery = "^0.15.0"
|
|
59
|
+
pydata-sphinx-theme = "^0.15.2"
|
|
60
|
+
myst-parser = "^2.0.0"
|
|
61
|
+
nbsphinx = "^0.9.3"
|
|
62
|
+
ipython = "^8.23.0"
|
|
63
|
+
|
|
64
|
+
[tool.poetry.group.notebooks]
|
|
65
|
+
optional = true
|
|
66
|
+
[tool.poetry.group.notebooks.dependencies]
|
|
67
|
+
jupyterlab = "^4"
|
|
68
|
+
ipywidgets = "*"
|
|
69
|
+
jupyterlab-execute-time = "*"
|
|
70
|
+
tqdm = "*"
|
|
71
|
+
|
|
72
|
+
[tool.poetry.group.test.dependencies]
|
|
73
|
+
pytest = "^8.1.1"
|
|
74
|
+
xdoctest = "^1.1.3"
|
|
75
|
+
coverage = "^7.4.4"
|
|
76
|
+
pytest-cov = "^5.0.0"
|
|
77
|
+
|
|
78
|
+
scikit-learn = "^1.4.2"
|
|
79
|
+
# TODO Unpin.
|
|
80
|
+
# dask.datasets.timeseries() warns:
|
|
81
|
+
# UserWarning: dask_expr does not support the DataFrameIOFunction protocol for column projection
|
|
82
|
+
# on dask == 2024.3.1
|
|
83
|
+
dask = { version = "==2024.4.1", extras = ["dataframe"] }
|
|
84
|
+
|
|
85
|
+
[tool.poetry.group.devops.dependencies]
|
|
86
|
+
invoke = "^2.2.0"
|
|
87
|
+
mypy = "^1.9.0"
|
|
88
|
+
ruff = "^0.3.7"
|
|
89
|
+
|
|
90
|
+
safety = "^3.1.0"
|
|
91
|
+
codespell = "^2.2.6"
|
|
92
|
+
bump2version = "^1.0.1"
|
|
93
|
+
|
|
94
|
+
[tool.ruff]
|
|
95
|
+
line-length = 120
|
|
96
|
+
fix = true
|
|
97
|
+
unsafe-fixes = false
|
|
98
|
+
output-format = "grouped"
|
|
99
|
+
|
|
100
|
+
[tool.ruff.format]
|
|
101
|
+
docstring-code-format = true
|
|
102
|
+
docstring-code-line-length = 80
|
|
103
|
+
line-ending = "lf"
|
|
104
|
+
|
|
105
|
+
[tool.ruff.lint]
|
|
106
|
+
exclude = ["notebooks/*"]
|
|
107
|
+
select = [
|
|
108
|
+
"ANN",
|
|
109
|
+
"S",
|
|
110
|
+
"I",
|
|
111
|
+
"B",
|
|
112
|
+
"D",
|
|
113
|
+
"F",
|
|
114
|
+
"W",
|
|
115
|
+
"E",
|
|
116
|
+
"SLOT",
|
|
117
|
+
"SIM",
|
|
118
|
+
"TID",
|
|
119
|
+
"TD",
|
|
120
|
+
"TCH",
|
|
121
|
+
"ARG",
|
|
122
|
+
"PTH",
|
|
123
|
+
"NPY",
|
|
124
|
+
"PD",
|
|
125
|
+
"N",
|
|
126
|
+
"PERF",
|
|
127
|
+
"PLC",
|
|
128
|
+
"PLE",
|
|
129
|
+
"PLR",
|
|
130
|
+
"PLW",
|
|
131
|
+
"UP",
|
|
132
|
+
"FURB",
|
|
133
|
+
"RUF",
|
|
134
|
+
"TRY",
|
|
135
|
+
]
|
|
136
|
+
ignore = [
|
|
137
|
+
"ANN101", # Missing type annotation for `self` in method
|
|
138
|
+
"ANN102", # Missing type annotation for `cls` in method
|
|
139
|
+
"D105", # Missing docstring in magic method
|
|
140
|
+
"D107", # Missing docstring in `__init__`
|
|
141
|
+
"ANN401",
|
|
142
|
+
"TID252", # Prefer absolute imports over relative imports from parent modules
|
|
143
|
+
"TRY003",
|
|
144
|
+
"PD901", # Avoid using the generic variable name `df` for DataFrames
|
|
145
|
+
"PLC0415", # `import` should be at the top-level of a file
|
|
146
|
+
"PLR0913", #
|
|
147
|
+
"SIM108", # Use ternary operator - coverage issue, see https://github.com/nedbat/coveragepy/issues/509
|
|
148
|
+
"TD003", # Missing issue link
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
[tool.ruff.lint.per-file-ignores]
|
|
152
|
+
"tests/*" = [
|
|
153
|
+
"D",
|
|
154
|
+
"S101", # Allow 'assert' keyword
|
|
155
|
+
"ANN",
|
|
156
|
+
"PLR2004", # Allow magic values
|
|
157
|
+
]
|
|
158
|
+
"examples/*" = [
|
|
159
|
+
"D205", # Clashes with sphinx_gallery (generated examples)
|
|
160
|
+
"B007",
|
|
161
|
+
]
|
|
162
|
+
"**/time_split/*" = [
|
|
163
|
+
"PLR0913", # Too many arguments in function definition
|
|
164
|
+
]
|
|
165
|
+
"**/time_split/integration/sklearn/*" = [
|
|
166
|
+
"N803", # Argument name `X` should be lowercase
|
|
167
|
+
"ARG002", # Unused method argument: `groups`
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
[tool.ruff.lint.pydocstyle]
|
|
171
|
+
convention = "google"
|
|
172
|
+
[tool.ruff.lint.pycodestyle]
|
|
173
|
+
max-line-length = 150
|
|
174
|
+
|
|
175
|
+
[tool.pytest]
|
|
176
|
+
[tool.pytest.ini_options]
|
|
177
|
+
filterwarnings = [
|
|
178
|
+
"error",
|
|
179
|
+
"ignore:Matplotlib is currently using agg:UserWarning",
|
|
180
|
+
"ignore:.*utcfromtimestamp.*:DeprecationWarning", # Detta är farligt - ta bort
|
|
181
|
+
]
|
|
182
|
+
log_file_level = 10
|
|
183
|
+
log_file = "pytest.log"
|
|
184
|
+
norecursedirs = [
|
|
185
|
+
"**/cli_modules/*",
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
[build-system]
|
|
189
|
+
requires = ["poetry-core>=1.0.8"]
|
|
190
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Create temporal k-folds for cross-validation with heterogeneous data."""
|
|
2
|
+
|
|
3
|
+
import logging as _logging
|
|
4
|
+
|
|
5
|
+
from ._frontend import log_split_progress, plot, split
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"log_split_progress",
|
|
9
|
+
"plot",
|
|
10
|
+
"split",
|
|
11
|
+
"__version__", # Make MyPy happy
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
_logging.getLogger(__name__).addHandler(_logging.NullHandler())
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import NamedTuple
|
|
2
|
+
|
|
3
|
+
from pandas import DatetimeIndex, Timestamp, isna
|
|
4
|
+
|
|
5
|
+
from .._support import handle_dask
|
|
6
|
+
from ..types import DatetimeIterable, Flex
|
|
7
|
+
from ._datetime_index_like import DatetimeIndexLike
|
|
8
|
+
from ._limits import LimitsTuple, expand_limits
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AvailableMetadata(NamedTuple):
|
|
12
|
+
"""Output of :func:`process_available`."""
|
|
13
|
+
|
|
14
|
+
available_as_index: DatetimeIndexLike | None
|
|
15
|
+
limits: LimitsTuple
|
|
16
|
+
expanded_limits: LimitsTuple
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def process_available(available: DatetimeIterable, *, flex: Flex) -> AvailableMetadata:
|
|
20
|
+
"""Process a user-given `available` argument.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
available: Available data from user. May be ``None``
|
|
24
|
+
flex: Flex-argument. Determines how much (if at all) to expand limits.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
A tuple ``(available, limits)``. Note that `available` will be ``None``, it has not been iterated over. This
|
|
28
|
+
assures that iterables are not consumed unless needed.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
ValueError: For invalid `available` arguments.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
if isinstance(available, DatetimeIndexLike):
|
|
35
|
+
# Avoids conversion, which may be expensive (or impossible on the current machine),
|
|
36
|
+
# and leverages additional non-Pandas types that do vectorized min/max.
|
|
37
|
+
limits = _compute_data_limits(available)
|
|
38
|
+
available_retval = available
|
|
39
|
+
else:
|
|
40
|
+
available_retval = DatetimeIndex(available)
|
|
41
|
+
limits = _compute_data_limits(available_retval)
|
|
42
|
+
|
|
43
|
+
min_dt, max_dt = limits
|
|
44
|
+
if min_dt == max_dt or isna(min_dt) or isna(max_dt):
|
|
45
|
+
raise ValueError("Not enough data in 'available'; at least two unique elements are required.")
|
|
46
|
+
|
|
47
|
+
if not (isinstance(min_dt, Timestamp) and isinstance(max_dt, Timestamp)):
|
|
48
|
+
# Enforce pandas types; we might have a numpy.datetime64
|
|
49
|
+
# (croniter 1.4.1 + numpy 1.25.2): croniter cannot handle numpy.datetime64
|
|
50
|
+
limits = Timestamp(min_dt), Timestamp(max_dt)
|
|
51
|
+
|
|
52
|
+
expanded_limits = expand_limits(limits, flex=flex)
|
|
53
|
+
return AvailableMetadata(available_retval, limits=limits, expanded_limits=expanded_limits)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _compute_data_limits(available: DatetimeIndexLike) -> LimitsTuple:
|
|
57
|
+
return handle_dask(available.min()), handle_dask(available.max())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from collections.abc import Iterable, Iterator
|
|
2
|
+
from typing import Protocol, runtime_checkable
|
|
3
|
+
|
|
4
|
+
from pandas import Series
|
|
5
|
+
|
|
6
|
+
from ..types import DatetimeTypes
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@runtime_checkable
|
|
10
|
+
class DatetimeIndexLike(Protocol, Iterable[DatetimeTypes]):
|
|
11
|
+
"""A type that behaves (sort of) like a pandas DatetimeIndex."""
|
|
12
|
+
|
|
13
|
+
def __iter__(self) -> Iterator[DatetimeTypes]: ...
|
|
14
|
+
|
|
15
|
+
def value_counts(self) -> Series: ...
|
|
16
|
+
|
|
17
|
+
def min(self) -> DatetimeTypes: ...
|
|
18
|
+
|
|
19
|
+
def max(self) -> DatetimeTypes: ...
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from itertools import starmap
|
|
3
|
+
from typing import NamedTuple
|
|
4
|
+
|
|
5
|
+
from pandas import Timedelta, Timestamp
|
|
6
|
+
|
|
7
|
+
from ..settings import auto_flex
|
|
8
|
+
from ..types import Flex, TimedeltaTypes
|
|
9
|
+
|
|
10
|
+
LimitsTuple = tuple[Timestamp, Timestamp]
|
|
11
|
+
LevelTuple = tuple[TimedeltaTypes, TimedeltaTypes, TimedeltaTypes]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _TimedeltaTuple(NamedTuple):
|
|
15
|
+
start_at: Timedelta
|
|
16
|
+
round_to: Timedelta
|
|
17
|
+
tolerance: Timedelta
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def expand_limits(limits: LimitsTuple, *, flex: Flex | LevelTuple | Iterable[LevelTuple] = "auto") -> LimitsTuple:
|
|
21
|
+
"""Derive the `"real"` bounds of `limits`.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
limits: A tuple ``(lo, hi)`` of timestamps.
|
|
25
|
+
flex: Flex arguments as described in the :ref:`User guide`. Also supports level-tuples
|
|
26
|
+
``[(start_at, round_to, tolerance)...]``. Passing ``flex=[settings.auto_flex.day, settings.auto_flex.hour]``
|
|
27
|
+
is equivalent to ``flex='auto'``.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Limits rounded according to the `flex`-argument.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: For invalid limits.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
>>> from pandas import Timestamp
|
|
37
|
+
>>> limits = Timestamp("2019-05-11"), Timestamp("2019-05-11 22:05:30")
|
|
38
|
+
|
|
39
|
+
Basic usage.
|
|
40
|
+
|
|
41
|
+
>>> expand_limits(limits, flex="d")
|
|
42
|
+
(Timestamp('2019-05-11 00:00:00'), Timestamp('2019-05-12 00:00:00'))
|
|
43
|
+
|
|
44
|
+
You may specify a maximum "distance" that limits may be expanded.
|
|
45
|
+
|
|
46
|
+
>>> expand_limits(limits, flex="d<1h")
|
|
47
|
+
(Timestamp('2019-05-11 00:00:00'), Timestamp('2019-05-11 22:05:30'))
|
|
48
|
+
|
|
49
|
+
Limits will never be rounded in the "wrong" direction...
|
|
50
|
+
|
|
51
|
+
>>> limits = Timestamp("2019-05-11"), Timestamp("2019-05-11 11:05:30")
|
|
52
|
+
>>> expand_limits(limits, flex="d")
|
|
53
|
+
(Timestamp('2019-05-11 00:00:00'), Timestamp('2019-05-11 11:05:30'))
|
|
54
|
+
|
|
55
|
+
...even if you make the tolerance large enough.
|
|
56
|
+
|
|
57
|
+
>>> expand_limits(limits, flex="d<14h")
|
|
58
|
+
(Timestamp('2019-05-11 00:00:00'), Timestamp('2019-05-11 11:05:30'))
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
if limits[0] >= limits[1]:
|
|
62
|
+
msg = f"Bad limits. Expected limits[1] > limits[0], but got {limits=}."
|
|
63
|
+
raise ValueError(msg)
|
|
64
|
+
|
|
65
|
+
if flex is False:
|
|
66
|
+
return limits
|
|
67
|
+
|
|
68
|
+
if flex is True or flex == "auto":
|
|
69
|
+
return _from_levels(limits)
|
|
70
|
+
|
|
71
|
+
if isinstance(flex, str):
|
|
72
|
+
round_to, _, tolerance = flex.partition("<")
|
|
73
|
+
level = _make_level(None, round_to=round_to.strip(), tolerance=tolerance.strip())
|
|
74
|
+
return _apply(limits, level=level)
|
|
75
|
+
if isinstance(flex, tuple):
|
|
76
|
+
return _from_levels(limits, levels=[_make_level(*flex)])
|
|
77
|
+
|
|
78
|
+
return _from_levels(limits, levels=starmap(_make_level, flex))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _from_levels(limits: LimitsTuple, *, levels: Iterable[_TimedeltaTuple] | None = None) -> LimitsTuple:
|
|
82
|
+
if levels is None:
|
|
83
|
+
levels = _levels_from_settings()
|
|
84
|
+
else:
|
|
85
|
+
levels = sorted(levels, reverse=True, key=lambda level: level[0])
|
|
86
|
+
|
|
87
|
+
lo, hi = limits
|
|
88
|
+
diff = hi - lo
|
|
89
|
+
|
|
90
|
+
for level in levels:
|
|
91
|
+
start_at, _round_to, _tolerance = level
|
|
92
|
+
if diff >= start_at:
|
|
93
|
+
return _apply(limits, level=level)
|
|
94
|
+
|
|
95
|
+
return limits
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _apply(limits: LimitsTuple, *, level: _TimedeltaTuple) -> LimitsTuple:
|
|
99
|
+
lo, hi = limits
|
|
100
|
+
|
|
101
|
+
lo_floor = lo.floor(level.round_to)
|
|
102
|
+
if abs(lo_floor - lo) > level.tolerance:
|
|
103
|
+
return limits
|
|
104
|
+
|
|
105
|
+
hi_ceil = hi.ceil(level.round_to)
|
|
106
|
+
if abs(hi_ceil - hi) > level.tolerance:
|
|
107
|
+
return limits
|
|
108
|
+
|
|
109
|
+
if auto_flex.SANITY_CHECK:
|
|
110
|
+
if lo.round(level.round_to) != lo_floor:
|
|
111
|
+
return limits
|
|
112
|
+
if hi.round(level.round_to) != hi_ceil:
|
|
113
|
+
return limits
|
|
114
|
+
|
|
115
|
+
return lo_floor, hi_ceil
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _levels_from_settings() -> list[_TimedeltaTuple]:
|
|
119
|
+
day, hour = _make_level(*auto_flex.day), _make_level(*auto_flex.hour)
|
|
120
|
+
if day.round_to != Timedelta(days=1):
|
|
121
|
+
msg = f"Invalid settings: {auto_flex.day=} must have round_to=1 day."
|
|
122
|
+
raise ValueError(msg)
|
|
123
|
+
if hour.round_to != Timedelta(hours=1):
|
|
124
|
+
msg = f"Invalid settings: {auto_flex.hour=} must have round_to=1 hour."
|
|
125
|
+
raise ValueError(msg)
|
|
126
|
+
return [day, hour]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _make_level(
|
|
130
|
+
start_at: TimedeltaTypes | None,
|
|
131
|
+
round_to: TimedeltaTypes,
|
|
132
|
+
tolerance: TimedeltaTypes,
|
|
133
|
+
) -> _TimedeltaTuple:
|
|
134
|
+
start_at = Timedelta(start_at) # Will be NaT if None, making all ifs False.
|
|
135
|
+
if start_at <= Timedelta(0):
|
|
136
|
+
raise ValueError(f"Bad {start_at=}; must be non-negative.")
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
round_to = Timedelta(1, unit=round_to)
|
|
140
|
+
except ValueError as e:
|
|
141
|
+
raise ValueError(f"Got {round_to=}, which is not a valid frequency.") from e
|
|
142
|
+
|
|
143
|
+
tolerance = Timedelta(tolerance)
|
|
144
|
+
if tolerance <= Timedelta(0):
|
|
145
|
+
raise ValueError(f"Bad {tolerance=}; must be non-negative.")
|
|
146
|
+
|
|
147
|
+
if start_at < round_to:
|
|
148
|
+
raise ValueError(f"{start_at=} < {round_to=}")
|
|
149
|
+
if round_to < tolerance:
|
|
150
|
+
raise ValueError(f"{round_to=} < {tolerance=}")
|
|
151
|
+
return _TimedeltaTuple(start_at, round_to, tolerance)
|