time-split 1.2.0__tar.gz → 1.2.2__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-1.2.0 → time_split-1.2.2}/LICENSE.md +1 -1
- {time_split-1.2.0 → time_split-1.2.2}/PKG-INFO +16 -19
- {time_split-1.2.0 → time_split-1.2.2}/README.md +0 -1
- time_split-1.2.2/pyproject.toml +203 -0
- time_split-1.2.0/pyproject.toml → time_split-1.2.2/pyproject.toml.orig +51 -50
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/__init__.py +1 -1
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_backend/_limits.py +11 -9
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_backend/_process_available.py +1 -1
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_backend/_schedule.py +5 -1
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_backend/_span.py +2 -1
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_backend/_splitter.py +2 -2
- time_split-1.2.2/src/time_split/_compat.py +19 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_frontend/_plot.py +8 -9
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_frontend/_progress.py +13 -6
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_frontend/_weight.py +9 -12
- time_split-1.2.2/src/time_split/_support.py +26 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/pandas/_impl.py +13 -6
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/polars/_impl.py +1 -1
- time_split-1.2.0/src/time_split/_support.py +0 -14
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/__main__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_backend/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_backend/_datetime_index_like.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_docstrings.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_frontend/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_frontend/_split.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/_frontend/_to_string.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/app/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/app/_create_explorer_link.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/app/reexport/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/cli.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/_log_progress.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/base.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/pandas/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/polars/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/sklearn/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/integration/sklearn/_impl.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/py.typed +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/settings.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/support/__init__.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/support/types.py +0 -0
- {time_split-1.2.0 → time_split-1.2.2}/src/time_split/types.py +0 -0
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: time-split
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Time-based k-fold validation splits for heterogeneous data.
|
|
5
|
-
License: MIT
|
|
6
|
-
License-File: LICENSE.md
|
|
7
5
|
Keywords: hyper,parameter,optimization,cross,validation,time,temporal,heterogenous,data
|
|
8
6
|
Author: Richard Sundqvist
|
|
9
|
-
Author-email: richard.sundqvist@live.se
|
|
10
|
-
|
|
7
|
+
Author-email: Richard Sundqvist <richard.sundqvist@live.se>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE.md
|
|
11
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
11
|
Classifier: Intended Audience :: Developers
|
|
13
12
|
Classifier: Intended Audience :: Science/Research
|
|
14
13
|
Classifier: Operating System :: POSIX :: Linux
|
|
15
14
|
Classifier: Operating System :: MacOS
|
|
16
15
|
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
18
16
|
Classifier: Natural Language :: English
|
|
19
17
|
Classifier: Typing :: Typed
|
|
20
18
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -23,19 +21,20 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
23
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
23
|
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Requires-Dist: click ; extra ==
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-
|
|
33
|
-
|
|
24
|
+
Requires-Dist: pandas>=2.0.3
|
|
25
|
+
Requires-Dist: rics>=6.0.1
|
|
26
|
+
Requires-Dist: click ; extra == 'app'
|
|
27
|
+
Requires-Dist: time-split-app>=1.1.0 ; extra == 'app'
|
|
28
|
+
Requires-Dist: matplotlib ; extra == 'plotting'
|
|
29
|
+
Requires-Dist: seaborn ; extra == 'plotting'
|
|
30
|
+
Requires-Python: >=3.11
|
|
31
|
+
Project-URL: homepage, https://github.com/rsundqvist/time-split
|
|
32
|
+
Project-URL: repository, https://github.com/rsundqvist/time-split
|
|
33
|
+
Project-URL: documentation, https://time-split
|
|
34
34
|
Project-URL: Bug Tracker, https://github.com/rsundqvist/time-split/issues
|
|
35
35
|
Project-URL: Changelog, https://github.com/rsundqvist/time-split/blob/master/CHANGELOG.md
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Project-URL: Repository, https://github.com/rsundqvist/time-split
|
|
36
|
+
Provides-Extra: app
|
|
37
|
+
Provides-Extra: plotting
|
|
39
38
|
Description-Content-Type: text/markdown
|
|
40
39
|
|
|
41
40
|
# Time Split <!-- omit in toc -->
|
|
@@ -116,9 +115,7 @@ most recent stable release.
|
|
|
116
115
|
Hosted on Read the Docs: https://time-split.readthedocs.io
|
|
117
116
|
|
|
118
117
|
## Contributing
|
|
119
|
-
|
|
120
118
|
All contributions, bug reports, bug fixes, documentation improvements, enhancements, and ideas are welcome. To get
|
|
121
119
|
started, see the [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
122
120
|
|
|
123
121
|
[Python Package Index (PyPI)]: https://pypi.org/project/time-split
|
|
124
|
-
|
|
@@ -76,7 +76,6 @@ most recent stable release.
|
|
|
76
76
|
Hosted on Read the Docs: https://time-split.readthedocs.io
|
|
77
77
|
|
|
78
78
|
## Contributing
|
|
79
|
-
|
|
80
79
|
All contributions, bug reports, bug fixes, documentation improvements, enhancements, and ideas are welcome. To get
|
|
81
80
|
started, see the [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
82
81
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "time-split"
|
|
3
|
+
version = "1.2.2"
|
|
4
|
+
description = "Time-based k-fold validation splits for heterogeneous data."
|
|
5
|
+
license = "MIT"
|
|
6
|
+
license-files = ["LICENSE.md"]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
keywords = [
|
|
9
|
+
"hyper",
|
|
10
|
+
"parameter",
|
|
11
|
+
"optimization",
|
|
12
|
+
"cross",
|
|
13
|
+
"validation",
|
|
14
|
+
"time",
|
|
15
|
+
"temporal",
|
|
16
|
+
"heterogenous",
|
|
17
|
+
"data",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 5 - Production/Stable",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Science/Research",
|
|
23
|
+
"Operating System :: POSIX :: Linux",
|
|
24
|
+
"Operating System :: MacOS",
|
|
25
|
+
"Operating System :: Microsoft :: Windows",
|
|
26
|
+
"Natural Language :: English",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
34
|
+
]
|
|
35
|
+
requires-python = ">=3.11"
|
|
36
|
+
dependencies = [
|
|
37
|
+
"pandas >= 2.0.3",
|
|
38
|
+
"rics >= 6.0.1",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[[project.authors]]
|
|
42
|
+
name = "Richard Sundqvist"
|
|
43
|
+
email = "richard.sundqvist@live.se"
|
|
44
|
+
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
app = [
|
|
47
|
+
"click",
|
|
48
|
+
"time-split-app >= 1.1.0",
|
|
49
|
+
]
|
|
50
|
+
plotting = [
|
|
51
|
+
"matplotlib",
|
|
52
|
+
"seaborn",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
homepage = "https://github.com/rsundqvist/time-split"
|
|
57
|
+
repository = "https://github.com/rsundqvist/time-split"
|
|
58
|
+
documentation = "https://time-split"
|
|
59
|
+
"Bug Tracker" = "https://github.com/rsundqvist/time-split/issues"
|
|
60
|
+
Changelog = "https://github.com/rsundqvist/time-split/blob/master/CHANGELOG.md"
|
|
61
|
+
|
|
62
|
+
[project.entry-points."rics.cli"]
|
|
63
|
+
time-split = "time_split.cli:main"
|
|
64
|
+
|
|
65
|
+
[dependency-groups]
|
|
66
|
+
manual-extras = [
|
|
67
|
+
"croniter~=6.2.4",
|
|
68
|
+
"pyarrow~=26.0.0",
|
|
69
|
+
"polars[pandas]~=2.0.0",
|
|
70
|
+
]
|
|
71
|
+
docs = [
|
|
72
|
+
"sphinx~=9.1.0 ; python_version >= '3.12'",
|
|
73
|
+
"sphinx-gallery~=0.22.1 ; python_version >= '3.12'",
|
|
74
|
+
"pydata-sphinx-theme~=0.23.0 ; python_version >= '3.12'",
|
|
75
|
+
"myst-parser~=5.1.0 ; python_version >= '3.12'",
|
|
76
|
+
"nbsphinx~=0.9.8 ; python_version >= '3.12'",
|
|
77
|
+
"ipython~=9.17.1 ; python_version >= '3.12'",
|
|
78
|
+
"sphinx-llm~=1.0.0 ; python_version >= '3.12'",
|
|
79
|
+
]
|
|
80
|
+
notebooks = [
|
|
81
|
+
"jupyterlab~=4.6.4",
|
|
82
|
+
"ipywidgets",
|
|
83
|
+
"jupyterlab-execute-time",
|
|
84
|
+
"tqdm",
|
|
85
|
+
]
|
|
86
|
+
test = [
|
|
87
|
+
"pytest~=9.1.1",
|
|
88
|
+
"xdoctest~=1.3.2",
|
|
89
|
+
"coverage~=7.16.2",
|
|
90
|
+
"pytest-cov~=7.1.0",
|
|
91
|
+
"scikit-learn~=1.9.1",
|
|
92
|
+
"dask[dataframe]~=2026.8.0",
|
|
93
|
+
]
|
|
94
|
+
devops = [
|
|
95
|
+
"nox~=2026.8.17",
|
|
96
|
+
"invoke~=3.0.3",
|
|
97
|
+
"mypy~=2.4.0",
|
|
98
|
+
"ruff~=0.17.1",
|
|
99
|
+
"codespell~=2.4.3",
|
|
100
|
+
"bump2version==1.0.1",
|
|
101
|
+
]
|
|
102
|
+
types = ["types-croniter"]
|
|
103
|
+
|
|
104
|
+
[tool.uv]
|
|
105
|
+
default-groups = [
|
|
106
|
+
"manual-extras",
|
|
107
|
+
"docs",
|
|
108
|
+
"test",
|
|
109
|
+
"devops",
|
|
110
|
+
"types",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
[tool.ruff]
|
|
114
|
+
line-length = 120
|
|
115
|
+
fix = true
|
|
116
|
+
unsafe-fixes = false
|
|
117
|
+
output-format = "grouped"
|
|
118
|
+
|
|
119
|
+
[tool.ruff.format]
|
|
120
|
+
docstring-code-format = true
|
|
121
|
+
docstring-code-line-length = 80
|
|
122
|
+
line-ending = "lf"
|
|
123
|
+
|
|
124
|
+
[tool.ruff.lint]
|
|
125
|
+
exclude = ["notebooks/*"]
|
|
126
|
+
select = [
|
|
127
|
+
"ANN",
|
|
128
|
+
"S",
|
|
129
|
+
"I",
|
|
130
|
+
"B",
|
|
131
|
+
"D",
|
|
132
|
+
"F",
|
|
133
|
+
"W",
|
|
134
|
+
"E",
|
|
135
|
+
"SLOT",
|
|
136
|
+
"SIM",
|
|
137
|
+
"TID",
|
|
138
|
+
"TD",
|
|
139
|
+
"TCH",
|
|
140
|
+
"ARG",
|
|
141
|
+
"PTH",
|
|
142
|
+
"NPY",
|
|
143
|
+
"PD",
|
|
144
|
+
"N",
|
|
145
|
+
"PERF",
|
|
146
|
+
"PLC",
|
|
147
|
+
"PLE",
|
|
148
|
+
"PLR",
|
|
149
|
+
"PLW",
|
|
150
|
+
"UP",
|
|
151
|
+
"FURB",
|
|
152
|
+
"RUF",
|
|
153
|
+
"TRY",
|
|
154
|
+
]
|
|
155
|
+
ignore = [
|
|
156
|
+
"D105",
|
|
157
|
+
"D107",
|
|
158
|
+
"ANN401",
|
|
159
|
+
"TID252",
|
|
160
|
+
"TRY003",
|
|
161
|
+
"PLC0415",
|
|
162
|
+
"PLR0913",
|
|
163
|
+
"SIM108",
|
|
164
|
+
"TD003",
|
|
165
|
+
"TC006",
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
[tool.ruff.lint.per-file-ignores]
|
|
169
|
+
"tests/*" = [
|
|
170
|
+
"D",
|
|
171
|
+
"S101",
|
|
172
|
+
"ANN",
|
|
173
|
+
"PLR2004",
|
|
174
|
+
]
|
|
175
|
+
"examples/*" = [
|
|
176
|
+
"D205",
|
|
177
|
+
"B007",
|
|
178
|
+
]
|
|
179
|
+
"**/time_split/*" = ["PLR0913"]
|
|
180
|
+
"**/time_split/integration/sklearn/*" = [
|
|
181
|
+
"N803",
|
|
182
|
+
"ARG002",
|
|
183
|
+
]
|
|
184
|
+
"**/time_split/app/*" = ["S101"]
|
|
185
|
+
|
|
186
|
+
[tool.ruff.lint.pydocstyle]
|
|
187
|
+
convention = "google"
|
|
188
|
+
|
|
189
|
+
[tool.ruff.lint.pycodestyle]
|
|
190
|
+
max-line-length = 150
|
|
191
|
+
|
|
192
|
+
[tool.pytest.ini_options]
|
|
193
|
+
filterwarnings = [
|
|
194
|
+
"error",
|
|
195
|
+
"ignore:Matplotlib is currently using agg:UserWarning",
|
|
196
|
+
"ignore:.*'mode' parameter.*Pillow 13.*:DeprecationWarning",
|
|
197
|
+
]
|
|
198
|
+
log_file_level = 10
|
|
199
|
+
log_file = "pytest.log"
|
|
200
|
+
|
|
201
|
+
[build-system]
|
|
202
|
+
requires = ["uv_build>=0.13.0,<0.14.0"]
|
|
203
|
+
build-backend = "uv_build"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "time-split"
|
|
3
|
-
version = "1.2.
|
|
3
|
+
version = "1.2.2"
|
|
4
4
|
description = "Time-based k-fold validation splits for heterogeneous data."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Richard Sundqvist", email = "richard.sundqvist@live.se" },
|
|
7
7
|
]
|
|
8
|
-
license =
|
|
8
|
+
license = "MIT"
|
|
9
|
+
license-files = ["LICENSE.md"] # uv_build only ships license files listed here.
|
|
9
10
|
readme = "README.md"
|
|
10
11
|
keywords = ["hyper", "parameter", "optimization", "cross", "validation", "time", "temporal", "heterogenous", "data"]
|
|
11
12
|
classifiers = [
|
|
@@ -15,7 +16,6 @@ classifiers = [
|
|
|
15
16
|
"Operating System :: POSIX :: Linux",
|
|
16
17
|
"Operating System :: MacOS",
|
|
17
18
|
"Operating System :: Microsoft :: Windows",
|
|
18
|
-
"License :: OSI Approved :: MIT License",
|
|
19
19
|
"Natural Language :: English",
|
|
20
20
|
"Typing :: Typed",
|
|
21
21
|
"Programming Language :: Python :: 3",
|
|
@@ -46,52 +46,53 @@ documentation = "https://time-split"
|
|
|
46
46
|
[project.entry-points."rics.cli"]
|
|
47
47
|
time-split = "time_split.cli:main"
|
|
48
48
|
|
|
49
|
-
[
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
49
|
+
[dependency-groups]
|
|
50
|
+
manual-extras = [
|
|
51
|
+
# Extras that must be available for docs + tests go here. Doc/test-only deps go in their respective groups.
|
|
52
|
+
"croniter~=6.2.4",
|
|
53
|
+
"pyarrow~=26.0.0",
|
|
54
|
+
"polars[pandas]~=2.0.0",
|
|
55
|
+
]
|
|
56
|
+
docs = [
|
|
57
|
+
# duplicate object warnings for sphinx>=4
|
|
58
|
+
# description: https://github.com/astropy/astropy/issues/11723
|
|
59
|
+
# https://github.com/astropy/astropy/pull/12270
|
|
60
|
+
"sphinx~=9.1.0 ; python_version >= '3.12'",
|
|
61
|
+
"sphinx-gallery~=0.22.1 ; python_version >= '3.12'",
|
|
62
|
+
"pydata-sphinx-theme~=0.23.0 ; python_version >= '3.12'",
|
|
63
|
+
"myst-parser~=5.1.0 ; python_version >= '3.12'", # https://github.com/executablebooks/MyST-Parser/issues/749
|
|
64
|
+
"nbsphinx~=0.9.8 ; python_version >= '3.12'",
|
|
65
|
+
"ipython~=9.17.1 ; python_version >= '3.12'",
|
|
66
|
+
"sphinx-llm~=1.0.0 ; python_version >= '3.12'", # 1.1.0 warns "unknown node type" on nbsphinx nodes, failing -W
|
|
67
|
+
]
|
|
68
|
+
notebooks = [
|
|
69
|
+
"jupyterlab~=4.6.4",
|
|
70
|
+
"ipywidgets",
|
|
71
|
+
"jupyterlab-execute-time",
|
|
72
|
+
"tqdm",
|
|
73
|
+
]
|
|
74
|
+
test = [
|
|
75
|
+
"pytest~=9.1.1",
|
|
76
|
+
"xdoctest~=1.3.2",
|
|
77
|
+
"coverage~=7.16.2",
|
|
78
|
+
"pytest-cov~=7.1.0",
|
|
79
|
+
"scikit-learn~=1.9.1",
|
|
80
|
+
"dask[dataframe]~=2026.8.0",
|
|
81
|
+
]
|
|
82
|
+
devops = [
|
|
83
|
+
"nox~=2026.8.17",
|
|
84
|
+
"invoke~=3.0.3",
|
|
85
|
+
"mypy~=2.4.0",
|
|
86
|
+
"ruff~=0.17.1",
|
|
87
|
+
"codespell~=2.4.3",
|
|
88
|
+
"bump2version==1.0.1",
|
|
89
|
+
]
|
|
90
|
+
types = [
|
|
91
|
+
"types-croniter",
|
|
92
|
+
]
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
bump2version = "~1.0.1"
|
|
94
|
+
[tool.uv]
|
|
95
|
+
default-groups = ["manual-extras", "docs", "test", "devops", "types"] # Not notebooks (opt-in).
|
|
95
96
|
|
|
96
97
|
[tool.ruff]
|
|
97
98
|
line-length = 120
|
|
@@ -187,5 +188,5 @@ log_file_level = 10
|
|
|
187
188
|
log_file = "pytest.log"
|
|
188
189
|
|
|
189
190
|
[build-system]
|
|
190
|
-
requires = ["
|
|
191
|
-
build-backend = "
|
|
191
|
+
requires = ["uv_build>=0.13.0,<0.14.0"]
|
|
192
|
+
build-backend = "uv_build"
|
|
@@ -4,6 +4,7 @@ from typing import Any, NamedTuple, TypeGuard
|
|
|
4
4
|
|
|
5
5
|
from pandas import Timedelta, Timestamp
|
|
6
6
|
|
|
7
|
+
from .._compat import fix_pandas4_warning, make_timedelta
|
|
7
8
|
from ..settings import auto_expand_limits, misc
|
|
8
9
|
from ..types import ExpandLimits, TimedeltaTypes
|
|
9
10
|
|
|
@@ -159,21 +160,22 @@ def _levels_from_settings() -> list[_TimedeltaTuple]:
|
|
|
159
160
|
|
|
160
161
|
def _make_level(
|
|
161
162
|
start_at: TimedeltaTypes | None,
|
|
162
|
-
round_to:
|
|
163
|
+
round_to: str | Timedelta,
|
|
163
164
|
tolerance: TimedeltaTypes,
|
|
164
165
|
) -> _TimedeltaTuple:
|
|
165
|
-
start_at =
|
|
166
|
+
start_at = make_timedelta(start_at) # Will be NaT if None, making all ifs False.
|
|
166
167
|
if start_at <= Timedelta(0):
|
|
167
|
-
raise ValueError(f"Bad {start_at=}; must be
|
|
168
|
+
raise ValueError(f"Bad {start_at=}; must be positive.")
|
|
168
169
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
if isinstance(round_to, str):
|
|
171
|
+
try:
|
|
172
|
+
round_to = Timedelta(1, unit=fix_pandas4_warning(round_to))
|
|
173
|
+
except ValueError as e:
|
|
174
|
+
raise ValueError(f"Got {round_to=}, which is not a valid frequency.") from e
|
|
173
175
|
|
|
174
|
-
tolerance =
|
|
176
|
+
tolerance = make_timedelta(tolerance)
|
|
175
177
|
if tolerance <= Timedelta(0):
|
|
176
|
-
raise ValueError(f"Bad {tolerance=}; must be
|
|
178
|
+
raise ValueError(f"Bad {tolerance=}; must be positive.")
|
|
177
179
|
|
|
178
180
|
if start_at < round_to:
|
|
179
181
|
raise ValueError(f"{start_at=} < {round_to=}")
|
|
@@ -56,4 +56,4 @@ def process_available(available: DatetimeIterable, *, expand_limits: ExpandLimit
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def _compute_data_limits(available: DatetimeIndexLike) -> LimitsTuple:
|
|
59
|
-
return handle_dask(available.min()
|
|
59
|
+
return handle_dask(available.min(), available.max())
|
|
@@ -2,6 +2,7 @@ from typing import Literal, NamedTuple, get_args
|
|
|
2
2
|
|
|
3
3
|
from pandas import DatetimeIndex, NaT, Timedelta, Timestamp, date_range
|
|
4
4
|
|
|
5
|
+
from .._compat import make_timedelta
|
|
5
6
|
from ..settings import misc as settings
|
|
6
7
|
from ..types import DatetimeIterable, ExpandLimits, Schedule, TimedeltaTypes
|
|
7
8
|
from ._limits import LimitsTuple
|
|
@@ -33,6 +34,9 @@ def materialize_schedule(
|
|
|
33
34
|
except TypeError as e:
|
|
34
35
|
raise ValueError("Schedule must be explicit when not bounded by an available range.") from e
|
|
35
36
|
|
|
37
|
+
if isinstance(schedule, str) and not schedule.strip():
|
|
38
|
+
raise ValueError(f"Schedule must not be blank: {schedule=}")
|
|
39
|
+
|
|
36
40
|
available_metadata = process_available(available, expand_limits=expand_limits)
|
|
37
41
|
min_dt, max_dt = available_metadata.expanded_limits
|
|
38
42
|
|
|
@@ -60,7 +64,7 @@ def _cron_like(schedule: str) -> bool:
|
|
|
60
64
|
|
|
61
65
|
|
|
62
66
|
def _from_timedelta(schedule: TimedeltaTypes, limits: LimitsTuple) -> DatetimeIndex:
|
|
63
|
-
timedelta =
|
|
67
|
+
timedelta = make_timedelta(schedule)
|
|
64
68
|
if timedelta <= Timedelta(0):
|
|
65
69
|
raise ValueError(f"unbounded {schedule=} must be greater than zero.")
|
|
66
70
|
|
|
@@ -4,6 +4,7 @@ from typing import Literal, TypeAlias
|
|
|
4
4
|
|
|
5
5
|
from pandas import DatetimeIndex, Timedelta, Timestamp
|
|
6
6
|
|
|
7
|
+
from .._compat import make_timedelta
|
|
7
8
|
from ..types import Span
|
|
8
9
|
from ._limits import LimitsTuple
|
|
9
10
|
from ._schedule import NO_LIMITS
|
|
@@ -27,7 +28,7 @@ class InvalidSpanError(ValueError):
|
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def _to_timedelta(span: str | timedelta | Timedelta, *, name: SpanArgumentName) -> Timedelta:
|
|
30
|
-
retval =
|
|
31
|
+
retval = make_timedelta(span)
|
|
31
32
|
if retval <= Timedelta(0):
|
|
32
33
|
raise InvalidSpanError(span, name=name)
|
|
33
34
|
return retval
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass
|
|
2
2
|
from typing import cast, get_args
|
|
3
3
|
|
|
4
|
-
from pandas import Timedelta
|
|
5
4
|
from rics.misc import format_kwargs, get_by_full_name
|
|
6
5
|
|
|
6
|
+
from .._compat import make_timedelta
|
|
7
7
|
from ..settings import misc as settings
|
|
8
8
|
from ..types import (
|
|
9
9
|
DatetimeIndexSplitterKwargs,
|
|
@@ -66,7 +66,7 @@ class DatetimeIndexSplitter:
|
|
|
66
66
|
|
|
67
67
|
from_end = data_end - schedule_end
|
|
68
68
|
if not isinstance(self.after, int):
|
|
69
|
-
after =
|
|
69
|
+
after = make_timedelta(self.after)
|
|
70
70
|
from_end -= after
|
|
71
71
|
|
|
72
72
|
return ms._replace(schedule=ms.schedule + from_end)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from pandas import Timedelta
|
|
4
|
+
|
|
5
|
+
from time_split.types import TimedeltaTypes
|
|
6
|
+
|
|
7
|
+
# TODO(pandas>=4): Remove this module.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def fix_pandas4_warning(arg: str) -> str:
|
|
11
|
+
# Uppercase a "d" unit that starts the string or follows a number or space ("d", "1d", "7 d", "1d2h"), not "days".
|
|
12
|
+
return re.sub(r"(?<![^\d\s])d(?![a-z])", "D", arg)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def make_timedelta(arg: TimedeltaTypes) -> Timedelta:
|
|
16
|
+
if isinstance(arg, str):
|
|
17
|
+
arg = fix_pandas4_warning(arg)
|
|
18
|
+
|
|
19
|
+
return Timedelta(arg)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from collections.abc import Sized
|
|
2
3
|
from contextlib import suppress
|
|
3
4
|
from dataclasses import asdict, dataclass, replace
|
|
@@ -11,6 +12,7 @@ from rics.strings import format_seconds as fmt_sec
|
|
|
11
12
|
from .._backend import DatetimeIndexSplitter
|
|
12
13
|
from .._backend._datetime_index_like import DatetimeIndexLike
|
|
13
14
|
from .._backend._limits import LimitsTuple
|
|
15
|
+
from .._compat import fix_pandas4_warning, make_timedelta
|
|
14
16
|
from .._docstrings import docs
|
|
15
17
|
from .._support import handle_dask
|
|
16
18
|
from ..settings import plot as settings
|
|
@@ -27,11 +29,8 @@ from ._split import split
|
|
|
27
29
|
from ._to_string import _PrettyTimestamp
|
|
28
30
|
from ._weight import fold_weight
|
|
29
31
|
|
|
30
|
-
if TYPE_CHECKING:
|
|
31
|
-
|
|
32
|
-
from matplotlib.pyplot import Axes # type: ignore[attr-defined]
|
|
33
|
-
except ModuleNotFoundError:
|
|
34
|
-
Axes = Any # type: ignore[misc, assignment]
|
|
32
|
+
if TYPE_CHECKING or os.environ.get("SPHINX_BUILD") is True:
|
|
33
|
+
from matplotlib.axes import Axes # noqa: TC002
|
|
35
34
|
|
|
36
35
|
Rows = Literal["rows"]
|
|
37
36
|
COUNT_ROWS: Literal["rows"] = "rows"
|
|
@@ -170,7 +169,7 @@ def plot(
|
|
|
170
169
|
ax.set_title(_make_title(available, split_kwargs))
|
|
171
170
|
|
|
172
171
|
if plot_data.available is None:
|
|
173
|
-
ax.legend(loc=
|
|
172
|
+
ax.legend(loc="lower right" if splitter.step > 0 else "upper right")
|
|
174
173
|
return ax
|
|
175
174
|
|
|
176
175
|
_plot_limits(ax, plot_data.available.expanded_limits)
|
|
@@ -179,7 +178,7 @@ def plot(
|
|
|
179
178
|
assert isinstance(row_count_bin, (str, pd.Series)) # noqa: S101
|
|
180
179
|
_plot_row_counts(ax, row_count_bin, plot_data.row_counts)
|
|
181
180
|
|
|
182
|
-
ax.legend(loc=
|
|
181
|
+
ax.legend(loc="lower right" if splitter.step > 0 else "upper right")
|
|
183
182
|
|
|
184
183
|
return ax
|
|
185
184
|
|
|
@@ -233,7 +232,7 @@ def _plot_row_counts(ax: "Axes", row_count_bin: str | pd.Series, row_counts: pd.
|
|
|
233
232
|
pretty = fmt_sec(diff(row_counts.index).min() / timedelta64(1, "s"))
|
|
234
233
|
else:
|
|
235
234
|
row_counts = row_counts.sort_index()
|
|
236
|
-
pretty = fmt_sec(
|
|
235
|
+
pretty = fmt_sec(make_timedelta(row_count_bin).total_seconds())
|
|
237
236
|
|
|
238
237
|
row_counts = row_counts * (max(ax.get_yticks()) / row_counts.max()) # Normalize to fold number yaxis
|
|
239
238
|
ax.fill_between(
|
|
@@ -356,7 +355,7 @@ def _compute_row_counts(
|
|
|
356
355
|
a_type = get_public_module(type(available), resolve_reexport=True, include_name=True)
|
|
357
356
|
raise TypeError(f"type(available)={a_type} must have one of `floor` and `dt` to use {row_count_bin=}")
|
|
358
357
|
|
|
359
|
-
return handle_dask(index_like.floor(row_count_bin).value_counts())
|
|
358
|
+
return handle_dask(index_like.floor(fix_pandas4_warning(row_count_bin)).value_counts())
|
|
360
359
|
|
|
361
360
|
|
|
362
361
|
def _make_title(available: Any | None, split_kwargs: dict[str, Any]) -> str:
|
|
@@ -93,9 +93,9 @@ def log_split_progress(
|
|
|
93
93
|
"""
|
|
94
94
|
logger = logging.getLogger(logger) if isinstance(logger, str) else logger
|
|
95
95
|
|
|
96
|
-
if isinstance(logger, logging.LoggerAdapter) and not
|
|
97
|
-
#
|
|
98
|
-
logger = _MergingLoggerAdapter(logger
|
|
96
|
+
if isinstance(logger, logging.LoggerAdapter) and not getattr(logger, "merge_extra", False):
|
|
97
|
+
# Stock adapters drop per-call extras (https://github.com/python/cpython/pull/107292); wrap to merge them.
|
|
98
|
+
logger = _MergingLoggerAdapter(logger)
|
|
99
99
|
|
|
100
100
|
return _ProgressTracker(
|
|
101
101
|
splits,
|
|
@@ -218,7 +218,7 @@ def default_metrics_formatter(end_message: str, metrics: dict[Any, Any] | pd.Ser
|
|
|
218
218
|
|
|
219
219
|
if isinstance(metrics, (pd.Series, pd.DataFrame)):
|
|
220
220
|
if isinstance(metrics, pd.DataFrame):
|
|
221
|
-
with_index = not isinstance(metrics, pd.RangeIndex)
|
|
221
|
+
with_index = not isinstance(metrics.index, pd.RangeIndex)
|
|
222
222
|
else:
|
|
223
223
|
with_index = True
|
|
224
224
|
return f"{end_message} Fold metrics:\n{metrics.round(3).to_string(index=with_index)}"
|
|
@@ -229,10 +229,17 @@ def default_metrics_formatter(end_message: str, metrics: dict[Any, Any] | pd.Ser
|
|
|
229
229
|
|
|
230
230
|
|
|
231
231
|
class _MergingLoggerAdapter(logging.LoggerAdapter[Any]):
|
|
232
|
-
|
|
232
|
+
def __init__(self, adapter: logging.LoggerAdapter[Any]) -> None:
|
|
233
|
+
super().__init__(adapter.logger, adapter.extra)
|
|
234
|
+
self._adapter = adapter
|
|
235
|
+
|
|
233
236
|
def process(self, msg: Any, kwargs: MutableMapping[str, Any]) -> tuple[Any, MutableMapping[str, Any]]:
|
|
234
237
|
"""See https://github.com/python/cpython/pull/107292."""
|
|
235
|
-
|
|
238
|
+
call_extra = kwargs.get("extra") or {}
|
|
239
|
+
# Let the wrapped adapter (possibly a user subclass) transform msg/kwargs first.
|
|
240
|
+
msg, kwargs = self._adapter.process(msg, kwargs)
|
|
241
|
+
adapter_extra = kwargs.get("extra") or {}
|
|
242
|
+
kwargs["extra"] = {**adapter_extra, **call_extra}
|
|
236
243
|
return msg, kwargs
|
|
237
244
|
|
|
238
245
|
|
|
@@ -2,6 +2,7 @@ from typing import Literal
|
|
|
2
2
|
|
|
3
3
|
from pandas import DatetimeIndex, Timedelta
|
|
4
4
|
|
|
5
|
+
from .._compat import fix_pandas4_warning
|
|
5
6
|
from .._support import handle_dask
|
|
6
7
|
from ..types import DatetimeIterable, DatetimeSplitCounts, DatetimeSplits
|
|
7
8
|
|
|
@@ -35,7 +36,7 @@ def fold_weight(
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def _from_unit(unit: str, splits: DatetimeSplits) -> list[DatetimeSplitCounts]:
|
|
38
|
-
resolution = Timedelta(1, unit=unit)
|
|
39
|
+
resolution = Timedelta(1, unit=fix_pandas4_warning(unit))
|
|
39
40
|
return [
|
|
40
41
|
DatetimeSplitCounts(
|
|
41
42
|
round((fold.mid - fold.start) / resolution),
|
|
@@ -53,14 +54,10 @@ def _from_data(available: DatetimeIterable, splits: DatetimeSplits) -> list[Date
|
|
|
53
54
|
else:
|
|
54
55
|
time = DatetimeIndex(available).to_series() # Coerce everything else.
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
retval.append(DatetimeSplitCounts(data, future_data=future_data))
|
|
65
|
-
|
|
66
|
-
return retval
|
|
57
|
+
counts = [
|
|
58
|
+
time.between(a, b, inclusive="left").sum()
|
|
59
|
+
for fold in splits
|
|
60
|
+
for a, b in ((fold.start, fold.mid), (fold.mid, fold.end))
|
|
61
|
+
]
|
|
62
|
+
computed = handle_dask(*counts)
|
|
63
|
+
return [DatetimeSplitCounts(d, future_data=fd) for d, fd in zip(computed[::2], computed[1::2], strict=True)]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Any, Protocol, TypeVar, overload, runtime_checkable
|
|
2
|
+
|
|
3
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@runtime_checkable
|
|
7
|
+
class _Computable(Protocol[T_co]):
|
|
8
|
+
def compute(self) -> T_co:
|
|
9
|
+
"""Compute results of a delayed Dask object."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@overload
|
|
13
|
+
def handle_dask(arg: T_co | _Computable[T_co], /) -> T_co: ...
|
|
14
|
+
@overload
|
|
15
|
+
def handle_dask(a: T_co | _Computable[T_co], b: T_co | _Computable[T_co], /) -> tuple[T_co, T_co]: ...
|
|
16
|
+
@overload
|
|
17
|
+
def handle_dask(*args: T_co | _Computable[T_co]) -> tuple[T_co, ...]: ...
|
|
18
|
+
def handle_dask(*args: Any) -> Any:
|
|
19
|
+
"""Run Dask compute on delayed objects, in a single pass."""
|
|
20
|
+
if any(isinstance(a, _Computable) for a in args):
|
|
21
|
+
# shortcut: non-Dask computables (e.g. xarray) now require dask; accepted, add a .compute() fallback if reported.
|
|
22
|
+
from dask import compute # type: ignore[attr-defined]
|
|
23
|
+
|
|
24
|
+
args = compute(*args) # type: ignore[no-untyped-call]
|
|
25
|
+
|
|
26
|
+
return args[0] if len(args) == 1 else args
|
|
@@ -2,7 +2,7 @@ from collections.abc import Hashable, Iterable
|
|
|
2
2
|
from datetime import date, datetime
|
|
3
3
|
from typing import Generic, TypeVar, Unpack
|
|
4
4
|
|
|
5
|
-
from pandas import DataFrame, DatetimeIndex, Series, Timestamp
|
|
5
|
+
from pandas import DataFrame, DatetimeIndex, Index, Series, Timestamp
|
|
6
6
|
from rics.misc import tname
|
|
7
7
|
|
|
8
8
|
from ..._docstrings import docs
|
|
@@ -60,6 +60,9 @@ class _Indexer(Generic[PandasT]):
|
|
|
60
60
|
def as_available(self, data: PandasT) -> Series | DatetimeIndex:
|
|
61
61
|
time = self._get_time(data)
|
|
62
62
|
|
|
63
|
+
if time.empty:
|
|
64
|
+
return time
|
|
65
|
+
|
|
63
66
|
first = time.iloc[0] if hasattr(time, "iloc") else time[0]
|
|
64
67
|
if isinstance(first, date):
|
|
65
68
|
return time
|
|
@@ -74,8 +77,12 @@ class _Indexer(Generic[PandasT]):
|
|
|
74
77
|
def select(self, data: PandasT, left: datetime, right: datetime) -> PandasT:
|
|
75
78
|
"""Select data based on the given bounds."""
|
|
76
79
|
time = self._get_time(data)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
|
|
81
|
+
if isinstance(time, Index):
|
|
82
|
+
if time.is_monotonic_increasing:
|
|
83
|
+
# Index slicing is a lot faster than boolean masks (empirically, seems to be a factor ~10).
|
|
84
|
+
return data[left : right - Timestamp.resolution]
|
|
85
|
+
|
|
86
|
+
time = time.to_series()
|
|
87
|
+
|
|
88
|
+
return data[time.between(left, right, inclusive="left")]
|
|
@@ -54,7 +54,7 @@ class _Indexer:
|
|
|
54
54
|
def as_available(self, data: DataFrame) -> Series:
|
|
55
55
|
time = self._get_time(data)
|
|
56
56
|
|
|
57
|
-
if isinstance(time[0], date):
|
|
57
|
+
if time.is_empty() or isinstance(time[0], date):
|
|
58
58
|
return time
|
|
59
59
|
|
|
60
60
|
msg = f"Elements of data[{self.time_column!r}] element are {tname(time[0])}, expected datetime-like."
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
from typing import Protocol, TypeVar, runtime_checkable
|
|
2
|
-
|
|
3
|
-
T_co = TypeVar("T_co", covariant=True)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@runtime_checkable
|
|
7
|
-
class _Computable(Protocol[T_co]):
|
|
8
|
-
def compute(self) -> T_co:
|
|
9
|
-
"""Compute results of a delayed Dask object."""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def handle_dask(arg: T_co | _Computable[T_co]) -> T_co:
|
|
13
|
-
"""Run Dask compute function on a delayed object."""
|
|
14
|
-
return arg.compute() if isinstance(arg, _Computable) else arg
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|