time-split 0.6.0__tar.gz → 0.7.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.6.0 → time_split-0.7.0}/PKG-INFO +27 -17
- {time_split-0.6.0 → time_split-0.7.0}/README.md +10 -7
- {time_split-0.6.0 → time_split-0.7.0}/pyproject.toml +56 -56
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/__init__.py +2 -2
- time_split-0.7.0/src/time_split/__main__.py +9 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_backend/_limits.py +15 -7
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_backend/_schedule.py +2 -2
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_backend/_splitter.py +1 -23
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_docstrings.py +2 -9
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_frontend/_plot.py +15 -5
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_frontend/_progress.py +2 -3
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_frontend/_to_string.py +64 -26
- time_split-0.7.0/src/time_split/app/__init__.py +11 -0
- {time_split-0.6.0/src/time_split/support → time_split-0.7.0/src/time_split/app}/_create_explorer_link.py +16 -10
- time_split-0.7.0/src/time_split/cli.py +29 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/settings.py +30 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/support/__init__.py +3 -5
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/types.py +3 -1
- time_split-0.6.0/src/time_split/_compat.py +0 -49
- {time_split-0.6.0 → time_split-0.7.0}/LICENSE.md +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_backend/__init__.py +1 -1
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_backend/_datetime_index_like.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_backend/_process_available.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_backend/_span.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_frontend/__init__.py +2 -2
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_frontend/_split.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_frontend/_weight.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/_support.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/__init__.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/_log_progress.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/base.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/pandas/__init__.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/pandas/_impl.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/polars/__init__.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/polars/_impl.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/sklearn/__init__.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/integration/sklearn/_impl.py +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/py.typed +0 -0
- {time_split-0.6.0 → time_split-0.7.0}/src/time_split/support/types.py +0 -0
|
@@ -1,31 +1,38 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: time-split
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Time-based k-fold validation splits for heterogeneous data.
|
|
5
|
-
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: hyper,parameter,optimization,cross,validation,time,temporal,heterogenous,data
|
|
6
7
|
Author: Richard Sundqvist
|
|
7
8
|
Author-email: richard.sundqvist@live.se
|
|
8
9
|
Requires-Python: >=3.11
|
|
9
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
11
|
Classifier: Intended Audience :: Developers
|
|
11
12
|
Classifier: Intended Audience :: Science/Research
|
|
12
|
-
Classifier:
|
|
13
|
-
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
14
|
Classifier: Operating System :: MacOS
|
|
15
15
|
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
-
Classifier:
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Typing :: Typed
|
|
17
19
|
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Provides-Extra: app
|
|
21
25
|
Provides-Extra: plotting
|
|
26
|
+
Requires-Dist: click ; extra == "app"
|
|
22
27
|
Requires-Dist: matplotlib ; extra == "plotting"
|
|
23
28
|
Requires-Dist: pandas (>=2.0.3)
|
|
24
|
-
Requires-Dist: rics (>=
|
|
29
|
+
Requires-Dist: rics (>=6.0.1)
|
|
25
30
|
Requires-Dist: seaborn ; extra == "plotting"
|
|
31
|
+
Requires-Dist: time-split-app ; extra == "app"
|
|
26
32
|
Project-URL: Bug Tracker, https://github.com/rsundqvist/time-split/issues
|
|
27
33
|
Project-URL: Changelog, https://github.com/rsundqvist/time-split/blob/master/CHANGELOG.md
|
|
28
|
-
Project-URL: Documentation, https://time-split
|
|
34
|
+
Project-URL: Documentation, https://time-split
|
|
35
|
+
Project-URL: Homepage, https://github.com/rsundqvist/time-split
|
|
29
36
|
Project-URL: Repository, https://github.com/rsundqvist/time-split
|
|
30
37
|
Description-Content-Type: text/markdown
|
|
31
38
|
|
|
@@ -61,26 +68,29 @@ strong non-stationary characteristics.
|
|
|
61
68
|
schedule itself.
|
|
62
69
|
- Automatically extract and [normalize](https://time-split.readthedocs.io/en/stable/guide/expand_limits.html) data limits for
|
|
63
70
|
supported data types.
|
|
64
|
-
- [Plotting function](https://time-split.readthedocs.io/en/stable/
|
|
71
|
+
- [Plotting function](https://time-split.readthedocs.io/en/stable/api/time_split.html#time_split.plot) for
|
|
65
72
|
visualization of folds. Display fold sizes (hour/row count), or
|
|
66
73
|
[use custom text](https://time-split.readthedocs.io/en/stable/auto_examples/plotting_with_metrics.html).
|
|
67
|
-
- [Integrations](https://time-split.readthedocs.io/en/stable/
|
|
74
|
+
- [Integrations](https://time-split.readthedocs.io/en/stable/api/time_split.integration.html) for popular
|
|
68
75
|
libraries such as `pandas`, `polars` and `scikit-learn`.
|
|
69
76
|
- Convenient [web application](#experimenting-with-parameters) for exploring folds with different parameters.
|
|
70
77
|
|
|
71
78
|
## Experimenting with parameters
|
|
72
|
-
The **Time
|
|
79
|
+
The **Time Split** application
|
|
73
80
|
(available [here](https://time-split.streamlit.app/?data=1554942900-1557610200&schedule=0+0+%2A+%2A+MON%2CFRI&n_splits=2&step=2&show_removed=True))
|
|
74
81
|
is designed to help evaluate the effects of different
|
|
75
82
|
[parameters](https://time-split.readthedocs.io/en/stable/#parameter-overview).
|
|
76
|
-
To start it locally
|
|
77
|
-
[](https://hub.docker.com/r/rsundqvist/time-split/)
|
|
78
|
-
Docker, run
|
|
83
|
+
To start it locally, run
|
|
79
84
|
```sh
|
|
80
85
|
docker run -p 8501:8501 rsundqvist/time-split
|
|
81
86
|
```
|
|
87
|
+
or
|
|
88
|
+
```bash
|
|
89
|
+
pip install time-split[app]
|
|
90
|
+
python -m time_split app start
|
|
91
|
+
```
|
|
82
92
|
in the terminal. You may use
|
|
83
|
-
[`create_explorer_link()`](https://time-split.readthedocs.io/en/stable/
|
|
93
|
+
[`create_explorer_link()`](https://time-split.readthedocs.io/en/stable/api/time_split.app.html#time_split.app.create_explorer_link)
|
|
84
94
|
to build application URLs with preselected splitting parameters.
|
|
85
95
|
|
|
86
96
|
## Installation
|
|
@@ -30,26 +30,29 @@ strong non-stationary characteristics.
|
|
|
30
30
|
schedule itself.
|
|
31
31
|
- Automatically extract and [normalize](https://time-split.readthedocs.io/en/stable/guide/expand_limits.html) data limits for
|
|
32
32
|
supported data types.
|
|
33
|
-
- [Plotting function](https://time-split.readthedocs.io/en/stable/
|
|
33
|
+
- [Plotting function](https://time-split.readthedocs.io/en/stable/api/time_split.html#time_split.plot) for
|
|
34
34
|
visualization of folds. Display fold sizes (hour/row count), or
|
|
35
35
|
[use custom text](https://time-split.readthedocs.io/en/stable/auto_examples/plotting_with_metrics.html).
|
|
36
|
-
- [Integrations](https://time-split.readthedocs.io/en/stable/
|
|
36
|
+
- [Integrations](https://time-split.readthedocs.io/en/stable/api/time_split.integration.html) for popular
|
|
37
37
|
libraries such as `pandas`, `polars` and `scikit-learn`.
|
|
38
38
|
- Convenient [web application](#experimenting-with-parameters) for exploring folds with different parameters.
|
|
39
39
|
|
|
40
40
|
## Experimenting with parameters
|
|
41
|
-
The **Time
|
|
41
|
+
The **Time Split** application
|
|
42
42
|
(available [here](https://time-split.streamlit.app/?data=1554942900-1557610200&schedule=0+0+%2A+%2A+MON%2CFRI&n_splits=2&step=2&show_removed=True))
|
|
43
43
|
is designed to help evaluate the effects of different
|
|
44
44
|
[parameters](https://time-split.readthedocs.io/en/stable/#parameter-overview).
|
|
45
|
-
To start it locally
|
|
46
|
-
[](https://hub.docker.com/r/rsundqvist/time-split/)
|
|
47
|
-
Docker, run
|
|
45
|
+
To start it locally, run
|
|
48
46
|
```sh
|
|
49
47
|
docker run -p 8501:8501 rsundqvist/time-split
|
|
50
48
|
```
|
|
49
|
+
or
|
|
50
|
+
```bash
|
|
51
|
+
pip install time-split[app]
|
|
52
|
+
python -m time_split app start
|
|
53
|
+
```
|
|
51
54
|
in the terminal. You may use
|
|
52
|
-
[`create_explorer_link()`](https://time-split.readthedocs.io/en/stable/
|
|
55
|
+
[`create_explorer_link()`](https://time-split.readthedocs.io/en/stable/api/time_split.app.html#time_split.app.create_explorer_link)
|
|
53
56
|
to build application URLs with preselected splitting parameters.
|
|
54
57
|
|
|
55
58
|
## Installation
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
[
|
|
1
|
+
[project]
|
|
2
2
|
name = "time-split"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
description = "Time-based k-fold validation splits for heterogeneous data."
|
|
5
|
-
authors = [
|
|
6
|
-
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Richard Sundqvist", email = "richard.sundqvist@live.se" },
|
|
7
|
+
]
|
|
8
|
+
license = { text = "MIT" }
|
|
7
9
|
readme = "README.md"
|
|
8
|
-
|
|
9
|
-
repository = "https://github.com/rsundqvist/time-split"
|
|
10
|
-
documentation = "https://time-split.readthedocs.io"
|
|
10
|
+
keywords = ["hyper", "parameter", "optimization", "cross", "validation", "time", "temporal", "heterogenous", "data"]
|
|
11
11
|
classifiers = [
|
|
12
|
-
"Development Status ::
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
13
|
"Intended Audience :: Developers",
|
|
14
14
|
"Intended Audience :: Science/Research",
|
|
15
15
|
"Operating System :: POSIX :: Linux",
|
|
@@ -17,50 +17,54 @@ classifiers = [
|
|
|
17
17
|
"Operating System :: Microsoft :: Windows",
|
|
18
18
|
"License :: OSI Approved :: MIT License",
|
|
19
19
|
"Natural Language :: English",
|
|
20
|
+
"Typing :: Typed",
|
|
20
21
|
"Programming Language :: Python :: 3",
|
|
21
22
|
"Programming Language :: Python :: 3 :: Only",
|
|
22
23
|
"Programming Language :: Python :: 3.11",
|
|
23
24
|
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
24
26
|
]
|
|
27
|
+
requires-python = ">=3.11"
|
|
25
28
|
|
|
29
|
+
dependencies = [
|
|
30
|
+
"pandas >= 2.0.3",
|
|
31
|
+
"rics >= 6.0.1",
|
|
32
|
+
]
|
|
26
33
|
|
|
27
|
-
[
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
app = ["click", "time-split-app"] # Constraints managed by app.
|
|
36
|
+
plotting = ["matplotlib", "seaborn"]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
homepage = "https://github.com/rsundqvist/time-split"
|
|
40
|
+
repository = "https://github.com/rsundqvist/time-split"
|
|
41
|
+
documentation = "https://time-split"
|
|
28
42
|
"Bug Tracker" = "https://github.com/rsundqvist/time-split/issues"
|
|
29
43
|
"Changelog" = "https://github.com/rsundqvist/time-split/blob/master/CHANGELOG.md"
|
|
30
44
|
|
|
31
|
-
[
|
|
45
|
+
[project.entry-points."rics.cli"]
|
|
46
|
+
time-split = "time_split.cli:main"
|
|
32
47
|
|
|
33
|
-
[tool.poetry.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
pandas = ">=2.0.3"
|
|
37
|
-
rics = ">=4.1.1"
|
|
38
|
-
|
|
39
|
-
# plotting
|
|
40
|
-
matplotlib = { version = "*", optional = true }
|
|
41
|
-
seaborn = { version = "*", optional = true }
|
|
42
|
-
|
|
43
|
-
[tool.poetry.extras]
|
|
44
|
-
plotting = ["matplotlib", "seaborn"]
|
|
48
|
+
[tool.poetry.requires-plugins]
|
|
49
|
+
poetry-plugin-export = ">=1.8"
|
|
45
50
|
|
|
46
51
|
[tool.poetry.group.manual-extras.dependencies]
|
|
47
52
|
# Extras that must be available for docs + tests go here. Doc/test-only deps go in their respective groups.
|
|
48
|
-
croniter = "^
|
|
49
|
-
pyarrow = "
|
|
50
|
-
polars = { version = "^
|
|
51
|
-
streamlit = "*"
|
|
53
|
+
croniter = "^6.0.0"
|
|
54
|
+
pyarrow = "^19.0.0"
|
|
55
|
+
polars = { version = "^1.21.0", extras = ["pandas"] }
|
|
52
56
|
|
|
53
57
|
[tool.poetry.group.docs]
|
|
54
58
|
[tool.poetry.group.docs.dependencies]
|
|
55
59
|
# duplicate object warnings for sphinx>=4
|
|
56
60
|
# description: https://github.com/astropy/astropy/issues/11723
|
|
57
61
|
# https://github.com/astropy/astropy/pull/12270
|
|
58
|
-
sphinx = "
|
|
59
|
-
sphinx-gallery = "^0.
|
|
60
|
-
pydata-sphinx-theme = "^0.
|
|
61
|
-
myst-parser = "
|
|
62
|
-
nbsphinx = "^0.9.
|
|
63
|
-
ipython = "^8.
|
|
62
|
+
sphinx = "~8.1.3"
|
|
63
|
+
sphinx-gallery = "^0.18.0"
|
|
64
|
+
pydata-sphinx-theme = "^0.16.1"
|
|
65
|
+
myst-parser = "^4.0.0"
|
|
66
|
+
nbsphinx = "^0.9.6"
|
|
67
|
+
ipython = "^8.31.0"
|
|
64
68
|
|
|
65
69
|
[tool.poetry.group.notebooks]
|
|
66
70
|
optional = true
|
|
@@ -71,26 +75,22 @@ jupyterlab-execute-time = "*"
|
|
|
71
75
|
tqdm = "*"
|
|
72
76
|
|
|
73
77
|
[tool.poetry.group.test.dependencies]
|
|
74
|
-
pytest = "^8.
|
|
75
|
-
xdoctest = "^1.
|
|
76
|
-
coverage = "^7.
|
|
77
|
-
pytest-cov = "^
|
|
78
|
-
|
|
79
|
-
scikit-learn = "^1.
|
|
80
|
-
|
|
81
|
-
# dask.datasets.timeseries() warns:
|
|
82
|
-
# UserWarning: dask_expr does not support the DataFrameIOFunction protocol for column projection
|
|
83
|
-
# on dask == 2024.3.1
|
|
84
|
-
dask = { version = "==2024.5.0", extras = ["dataframe"] }
|
|
78
|
+
pytest = "^8.3.4"
|
|
79
|
+
xdoctest = "^1.2.0"
|
|
80
|
+
coverage = "^7.6.10"
|
|
81
|
+
pytest-cov = "^6.0.0"
|
|
82
|
+
|
|
83
|
+
scikit-learn = "^1.6.1"
|
|
84
|
+
dask = { version = "^2025.1.0", extras = ["dataframe"] }
|
|
85
85
|
|
|
86
86
|
[tool.poetry.group.devops.dependencies]
|
|
87
|
-
invoke = "
|
|
88
|
-
mypy = "
|
|
89
|
-
ruff = "
|
|
87
|
+
invoke = "~2.2.0"
|
|
88
|
+
mypy = "~1.17.0"
|
|
89
|
+
ruff = "~0.12.3"
|
|
90
90
|
|
|
91
|
-
safety = "
|
|
92
|
-
codespell = "
|
|
93
|
-
bump2version = "
|
|
91
|
+
safety = "~3.5.1"
|
|
92
|
+
codespell = "~2.4.1"
|
|
93
|
+
bump2version = "~1.0.1"
|
|
94
94
|
|
|
95
95
|
[tool.ruff]
|
|
96
96
|
line-length = 120
|
|
@@ -135,8 +135,6 @@ select = [
|
|
|
135
135
|
"TRY",
|
|
136
136
|
]
|
|
137
137
|
ignore = [
|
|
138
|
-
"ANN101", # Missing type annotation for `self` in method
|
|
139
|
-
"ANN102", # Missing type annotation for `cls` in method
|
|
140
138
|
"D105", # Missing docstring in magic method
|
|
141
139
|
"D107", # Missing docstring in `__init__`
|
|
142
140
|
"ANN401",
|
|
@@ -147,8 +145,9 @@ ignore = [
|
|
|
147
145
|
"PLR0913", #
|
|
148
146
|
"SIM108", # Use ternary operator - coverage issue, see https://github.com/nedbat/coveragepy/issues/509
|
|
149
147
|
"TD003", # Missing issue link
|
|
148
|
+
"TC006", # Add quotes to type expression in `typing.cast()`
|
|
149
|
+
"UP038", # https://github.com/astral-sh/ruff/issues/7871
|
|
150
150
|
]
|
|
151
|
-
allowed-confusables = ["ℹ"]
|
|
152
151
|
|
|
153
152
|
[tool.ruff.lint.per-file-ignores]
|
|
154
153
|
"tests/*" = [
|
|
@@ -169,6 +168,10 @@ allowed-confusables = ["ℹ"]
|
|
|
169
168
|
"ARG002", # Unused method argument: `groups`
|
|
170
169
|
]
|
|
171
170
|
|
|
171
|
+
"**/time_split/app/*" = [
|
|
172
|
+
"S101" # Allow 'assert' keyword
|
|
173
|
+
]
|
|
174
|
+
|
|
172
175
|
[tool.ruff.lint.pydocstyle]
|
|
173
176
|
convention = "google"
|
|
174
177
|
[tool.ruff.lint.pycodestyle]
|
|
@@ -179,13 +182,10 @@ max-line-length = 150
|
|
|
179
182
|
filterwarnings = [
|
|
180
183
|
"error",
|
|
181
184
|
"ignore:Matplotlib is currently using agg:UserWarning",
|
|
182
|
-
"ignore:.*
|
|
185
|
+
"ignore:.*'mode' parameter.*Pillow 13.*:DeprecationWarning",
|
|
183
186
|
]
|
|
184
187
|
log_file_level = 10
|
|
185
188
|
log_file = "pytest.log"
|
|
186
|
-
norecursedirs = [
|
|
187
|
-
"**/cli_modules/*",
|
|
188
|
-
]
|
|
189
189
|
|
|
190
190
|
[build-system]
|
|
191
191
|
requires = ["poetry-core>=1.0.8"]
|
|
@@ -5,12 +5,12 @@ import logging as _logging
|
|
|
5
5
|
from ._frontend import log_split_progress, plot, split
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
|
+
"__version__", # Make MyPy happy
|
|
8
9
|
"log_split_progress",
|
|
9
10
|
"plot",
|
|
10
11
|
"split",
|
|
11
|
-
"__version__", # Make MyPy happy
|
|
12
12
|
]
|
|
13
13
|
|
|
14
|
-
__version__ = "0.
|
|
14
|
+
__version__ = "0.7.0"
|
|
15
15
|
|
|
16
16
|
_logging.getLogger(__name__).addHandler(_logging.NullHandler())
|
|
@@ -4,7 +4,7 @@ from typing import NamedTuple
|
|
|
4
4
|
|
|
5
5
|
from pandas import Timedelta, Timestamp
|
|
6
6
|
|
|
7
|
-
from ..settings import auto_expand_limits
|
|
7
|
+
from ..settings import auto_expand_limits, misc
|
|
8
8
|
from ..types import ExpandLimits, TimedeltaTypes
|
|
9
9
|
|
|
10
10
|
LimitsTuple = tuple[Timestamp, Timestamp]
|
|
@@ -23,6 +23,8 @@ def expand_limits(
|
|
|
23
23
|
) -> LimitsTuple:
|
|
24
24
|
"""Derive the `"real"` bounds of `limits`.
|
|
25
25
|
|
|
26
|
+
Use :attr:`time_split.settings.misc.round_limits` to allow inward "expansion".
|
|
27
|
+
|
|
26
28
|
Args:
|
|
27
29
|
limits: A tuple ``(min, max)`` of timestamps.
|
|
28
30
|
spec: Expansion spec as described in the :ref:`User guide`. Also supports level-tuples
|
|
@@ -61,6 +63,7 @@ def expand_limits(
|
|
|
61
63
|
>>> expand_limits(limits, "d<14h")
|
|
62
64
|
(Timestamp('2019-05-11 00:00:00'), Timestamp('2019-05-11 11:05:30'))
|
|
63
65
|
|
|
66
|
+
To disable this restriction, set :attr:`round_limits=True <.misc.round_limits>` in the settings.
|
|
64
67
|
"""
|
|
65
68
|
if limits[0] >= limits[1]:
|
|
66
69
|
msg = f"Bad limits. Expected limits[1] > limits[0], but got {limits=}."
|
|
@@ -102,19 +105,24 @@ def _from_levels(limits: LimitsTuple, *, levels: Iterable[_TimedeltaTuple] | Non
|
|
|
102
105
|
def _apply(limits: LimitsTuple, *, level: _TimedeltaTuple) -> LimitsTuple:
|
|
103
106
|
lo, hi = limits
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
round = misc.round_limits
|
|
109
|
+
lo_floor = lo.round(level.round_to) if round else lo.floor(level.round_to)
|
|
106
110
|
if abs(lo_floor - lo) > level.tolerance:
|
|
107
111
|
return limits
|
|
108
112
|
|
|
109
|
-
hi_ceil = hi.ceil(level.round_to)
|
|
113
|
+
hi_ceil = hi.round(level.round_to) if round else hi.ceil(level.round_to)
|
|
110
114
|
if abs(hi_ceil - hi) > level.tolerance:
|
|
111
115
|
return limits
|
|
112
116
|
|
|
113
117
|
if auto_expand_limits.SANITY_CHECK:
|
|
114
|
-
if
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
if round:
|
|
119
|
+
if lo_floor == hi_ceil:
|
|
120
|
+
return limits
|
|
121
|
+
else:
|
|
122
|
+
if lo.round(level.round_to) != lo_floor:
|
|
123
|
+
return limits
|
|
124
|
+
if hi.round(level.round_to) != hi_ceil:
|
|
125
|
+
return limits
|
|
118
126
|
|
|
119
127
|
return lo_floor, hi_ceil
|
|
120
128
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import NamedTuple,
|
|
1
|
+
from typing import NamedTuple, get_args
|
|
2
2
|
|
|
3
3
|
from pandas import DatetimeIndex, NaT, Timedelta, Timestamp, date_range
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ from ..types import DatetimeIterable, ExpandLimits, Schedule, TimedeltaTypes
|
|
|
6
6
|
from ._limits import LimitsTuple
|
|
7
7
|
from ._process_available import ProcessAvailableResult, process_available
|
|
8
8
|
|
|
9
|
-
NO_LIMITS =
|
|
9
|
+
NO_LIMITS: LimitsTuple = NaT, NaT
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class MaterializedSchedule(NamedTuple):
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
from dataclasses import asdict, dataclass
|
|
3
|
-
from functools import lru_cache
|
|
4
2
|
from typing import cast, get_args
|
|
5
3
|
|
|
6
4
|
from pandas import Timedelta
|
|
@@ -17,12 +15,9 @@ from ..types import (
|
|
|
17
15
|
Span,
|
|
18
16
|
TimedeltaTypes,
|
|
19
17
|
)
|
|
20
|
-
from ._limits import LimitsTuple
|
|
21
18
|
from ._schedule import MaterializedSchedule, materialize_schedule
|
|
22
19
|
from ._span import OffsetCalculator, to_strict_span
|
|
23
20
|
|
|
24
|
-
LOGGER = logging.getLogger("time_split")
|
|
25
|
-
|
|
26
21
|
|
|
27
22
|
@dataclass(frozen=True)
|
|
28
23
|
class DatetimeIndexSplitter:
|
|
@@ -39,7 +34,6 @@ class DatetimeIndexSplitter:
|
|
|
39
34
|
def get_splits(self, available: DatetimeIterable | None = None) -> DatetimeSplits:
|
|
40
35
|
"""Compute a split of given user data."""
|
|
41
36
|
ms = self._materialize_schedule(available)
|
|
42
|
-
self._log_expansion(ms.available_metadata.limits, expanded=ms.available_metadata.expanded_limits)
|
|
43
37
|
return self._make_bounds_list(ms)
|
|
44
38
|
|
|
45
39
|
def get_plot_data(self, available: DatetimeIterable | None = None) -> tuple[DatetimeSplits, MaterializedSchedule]:
|
|
@@ -70,7 +64,7 @@ class DatetimeIndexSplitter:
|
|
|
70
64
|
return ms
|
|
71
65
|
|
|
72
66
|
from_end = ms.available_metadata.expanded_limits[1] - (ms.schedule[-1] + Timedelta(self.after))
|
|
73
|
-
from_end = from_end.floor(schedule_frequency.base)
|
|
67
|
+
from_end = from_end.floor(schedule_frequency.base) # TODO(settings): should this depend on misc.round_limits?
|
|
74
68
|
|
|
75
69
|
return ms._replace(schedule=ms.schedule + from_end)
|
|
76
70
|
|
|
@@ -109,7 +103,6 @@ class DatetimeIndexSplitter:
|
|
|
109
103
|
|
|
110
104
|
Returns:
|
|
111
105
|
Filtered splits.
|
|
112
|
-
|
|
113
106
|
"""
|
|
114
107
|
if self.step != 1:
|
|
115
108
|
step = abs(self.step)
|
|
@@ -127,21 +120,6 @@ class DatetimeIndexSplitter:
|
|
|
127
120
|
|
|
128
121
|
return [s for s in splits if settings.filter(*s)]
|
|
129
122
|
|
|
130
|
-
def _log_expansion(self, original: LimitsTuple, *, expanded: LimitsTuple) -> None:
|
|
131
|
-
from .._frontend import format_expanded_limits
|
|
132
|
-
|
|
133
|
-
if original == expanded or not LOGGER.isEnabledFor(logging.INFO):
|
|
134
|
-
return
|
|
135
|
-
|
|
136
|
-
message = format_expanded_limits(original, expanded=expanded, expand_limits=self.expand_limits)
|
|
137
|
-
self._emit_once(message)
|
|
138
|
-
|
|
139
|
-
@staticmethod
|
|
140
|
-
@lru_cache(8)
|
|
141
|
-
def _emit_once(message: str) -> None:
|
|
142
|
-
"""Emit if **not** in the eight most recent emission messages."""
|
|
143
|
-
LOGGER.info(message)
|
|
144
|
-
|
|
145
123
|
def __post_init__(self) -> None:
|
|
146
124
|
# Verify n_splits
|
|
147
125
|
if self.n_splits < 0:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from time_split._compat import deprecated_params
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import ParamSpec, TypeVar
|
|
4
3
|
|
|
5
4
|
P = ParamSpec("P")
|
|
6
5
|
T = TypeVar("T")
|
|
@@ -15,12 +14,6 @@ def docs(__func: Callable[P, T], /) -> Callable[P, T]:
|
|
|
15
14
|
"""Add info the docstring of a function."""
|
|
16
15
|
assert __func.__doc__ is not None # noqa: S101
|
|
17
16
|
__func.__doc__ = __func.__doc__.format(**_DOCSTRINGS)
|
|
18
|
-
|
|
19
|
-
if isinstance(__func, type):
|
|
20
|
-
__func.__init__ = deprecated_params(__func.__init__) # type: ignore[misc]
|
|
21
|
-
else:
|
|
22
|
-
__func = deprecated_params(__func)
|
|
23
|
-
|
|
24
17
|
return __func
|
|
25
18
|
|
|
26
19
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
from collections.abc import Sized
|
|
1
2
|
from dataclasses import asdict, dataclass, replace
|
|
2
|
-
from typing import TYPE_CHECKING, Any, Literal
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
3
4
|
|
|
4
5
|
import pandas as pd
|
|
5
6
|
from pandas import Timestamp
|
|
6
7
|
from rics.misc import format_kwargs, get_public_module
|
|
7
|
-
|
|
8
|
-
from time_split._compat import fmt_sec
|
|
8
|
+
from rics.strings import format_seconds as fmt_sec
|
|
9
9
|
|
|
10
10
|
from .._backend import DatetimeIndexSplitter
|
|
11
11
|
from .._backend._datetime_index_like import DatetimeIndexLike
|
|
@@ -65,7 +65,7 @@ def plot(
|
|
|
65
65
|
after: Span = 1,
|
|
66
66
|
step: int = 1,
|
|
67
67
|
n_splits: int = 0,
|
|
68
|
-
available: DatetimeIterable | None = None,
|
|
68
|
+
available: DatetimeIterable | pd.DataFrame | pd.Series | None = None,
|
|
69
69
|
expand_limits: ExpandLimits = "auto",
|
|
70
70
|
ignore_filters: bool = False,
|
|
71
71
|
# Split plot args
|
|
@@ -112,6 +112,15 @@ def plot(
|
|
|
112
112
|
"""
|
|
113
113
|
import matplotlib.pyplot as plt
|
|
114
114
|
|
|
115
|
+
if (
|
|
116
|
+
available is not None
|
|
117
|
+
and hasattr(available, "index")
|
|
118
|
+
and not callable(available.index)
|
|
119
|
+
and hasattr(available, "shape")
|
|
120
|
+
and len(available.shape) != 1
|
|
121
|
+
):
|
|
122
|
+
available = available.index
|
|
123
|
+
|
|
115
124
|
splitter = DatetimeIndexSplitter(
|
|
116
125
|
schedule,
|
|
117
126
|
before=before,
|
|
@@ -121,6 +130,7 @@ def plot(
|
|
|
121
130
|
expand_limits=expand_limits,
|
|
122
131
|
ignore_filters=ignore_filters,
|
|
123
132
|
)
|
|
133
|
+
|
|
124
134
|
plot_data = _get_plot_data(available, splitter, row_count_bin=row_count_bin, show_removed=show_removed)
|
|
125
135
|
|
|
126
136
|
if bar_labels is True:
|
|
@@ -249,7 +259,7 @@ def _add_bar_labels(
|
|
|
249
259
|
unit=unit_or_labels,
|
|
250
260
|
)
|
|
251
261
|
|
|
252
|
-
for bar, label in zip(ax.containers, labels):
|
|
262
|
+
for bar, label in zip(ax.containers, labels, strict=True):
|
|
253
263
|
ax.bar_label(bar, labels=[label], **kwargs) # type: ignore[arg-type]
|
|
254
264
|
|
|
255
265
|
|
|
@@ -6,8 +6,7 @@ from typing import Any, Generic
|
|
|
6
6
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
from rics.collections.dicts import flatten_dict
|
|
9
|
-
|
|
10
|
-
from time_split._compat import fmt_sec
|
|
9
|
+
from rics.strings import format_seconds as fmt_sec
|
|
11
10
|
|
|
12
11
|
from ..settings import log_split_progress as settings
|
|
13
12
|
from ..types import DatetimeSplitBounds, FormatMetrics, GetMetrics, LoggerArg, MetricsType, SplitProgressExtras
|
|
@@ -122,7 +121,7 @@ class _ProgressTracker(Generic[MetricsType]):
|
|
|
122
121
|
n_splits = len(splits)
|
|
123
122
|
|
|
124
123
|
for n, split in enumerate(splits, start=1):
|
|
125
|
-
default_extras = SplitProgressExtras(
|
|
124
|
+
default_extras = SplitProgressExtras[MetricsType](
|
|
126
125
|
n=n,
|
|
127
126
|
n_splits=n_splits,
|
|
128
127
|
start=split.start.isoformat(),
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
+
import datetime
|
|
1
2
|
from typing import Any, Literal, overload
|
|
2
3
|
|
|
3
4
|
from pandas import Timestamp
|
|
5
|
+
from rics.strings import format_seconds as fmt_sec
|
|
4
6
|
|
|
5
7
|
from .._backend._limits import LimitsTuple
|
|
6
|
-
from .._compat import fmt_sec
|
|
7
8
|
from ..settings import log_split_progress
|
|
8
9
|
from ..types import DatetimeSplitBounds, DatetimeTypes, ExpandLimits
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
@overload
|
|
12
13
|
def to_string(
|
|
13
|
-
bounds: DatetimeTypes,
|
|
14
|
-
__ignored0:
|
|
15
|
-
__ignored1:
|
|
14
|
+
bounds: DatetimeSplitBounds | tuple[DatetimeTypes, DatetimeTypes, DatetimeTypes],
|
|
15
|
+
__ignored0: Literal[None] = None,
|
|
16
|
+
__ignored1: Literal[None] = None,
|
|
16
17
|
/,
|
|
17
18
|
*,
|
|
18
19
|
format: str | None = None,
|
|
@@ -21,9 +22,9 @@ def to_string(
|
|
|
21
22
|
|
|
22
23
|
@overload
|
|
23
24
|
def to_string(
|
|
24
|
-
start:
|
|
25
|
-
mid:
|
|
26
|
-
end:
|
|
25
|
+
start: DatetimeTypes,
|
|
26
|
+
mid: DatetimeTypes,
|
|
27
|
+
end: DatetimeTypes,
|
|
27
28
|
/,
|
|
28
29
|
*,
|
|
29
30
|
format: str | None = None,
|
|
@@ -31,7 +32,7 @@ def to_string(
|
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def to_string(
|
|
34
|
-
|
|
35
|
+
start_or_bounds: DatetimeTypes | DatetimeSplitBounds | tuple[DatetimeTypes, DatetimeTypes, DatetimeTypes],
|
|
35
36
|
mid: DatetimeTypes | None = None,
|
|
36
37
|
end: DatetimeTypes | None = None,
|
|
37
38
|
/,
|
|
@@ -40,15 +41,10 @@ def to_string(
|
|
|
40
41
|
) -> str:
|
|
41
42
|
"""Pretty-print a fold.
|
|
42
43
|
|
|
43
|
-
.. code-block:: python
|
|
44
|
-
:caption: Sample output.
|
|
45
|
-
|
|
46
|
-
('2021-12-30' <= [schedule: '2022-01-04' (Tuesday)] < '2022-01-04 18:00:00')
|
|
47
|
-
|
|
48
44
|
Args:
|
|
49
|
-
|
|
50
|
-
mid: Datetime-like. Must be ``None`` when `
|
|
51
|
-
end: Datetime-like. Must be ``None`` when `
|
|
45
|
+
start_or_bounds: A fold tuple ``(start, mid, end)``, or just `start` (followed by `mid` and `end`).
|
|
46
|
+
mid: Datetime-like. Must be ``None`` when `start_or_bounds` is a tuple.
|
|
47
|
+
end: Datetime-like. Must be ``None`` when `start_or_bounds` is a tuple.
|
|
52
48
|
format: A custom format to use. Use :attr:`~.settings.log_split_progress.FOLD_FORMAT` if ``None``, but note that
|
|
53
49
|
only the `start`, `mid` and `end` keys are available to this function.
|
|
54
50
|
|
|
@@ -58,20 +54,41 @@ def to_string(
|
|
|
58
54
|
Raises:
|
|
59
55
|
TypeError: If an incorrect number of timestamps are given.
|
|
60
56
|
|
|
57
|
+
Examples:
|
|
58
|
+
Sample format output.
|
|
59
|
+
|
|
60
|
+
>>> to_string("2021-12-30", "2022-01-04", "2022-01-04 18:00:00")
|
|
61
|
+
"'2021-12-30' <= [schedule: '2022-01-04' (Tuesday)] < '2022-01-04 18:00:00'"
|
|
62
|
+
|
|
63
|
+
The :attr:`default format <.log_split_progress.FOLD_FORMAT>` was used above.
|
|
64
|
+
|
|
65
|
+
Using properties. The `delta` is the distance from `mid` formatted by
|
|
66
|
+
:func:`~rics.strings.format_seconds`. The `date` property returns a :class:`datetime.date` object.
|
|
67
|
+
|
|
68
|
+
>>> to_string(
|
|
69
|
+
... ("2021-12-30", "2022-01-04", "2022-01-04 18:00:00"),
|
|
70
|
+
... format="'{start.date}' [-{start.delta}] <= '{mid.iso}' < [+{end.delta}]",
|
|
71
|
+
... )
|
|
72
|
+
"'2021-12-30' [-5d] <= '2022-01-04T00:00:00' < [+18h]"
|
|
73
|
+
|
|
74
|
+
The delta is always positive; you must add the sign yourself.
|
|
61
75
|
"""
|
|
62
|
-
if isinstance(
|
|
76
|
+
if isinstance(start_or_bounds, tuple):
|
|
63
77
|
if not (mid is None and end is None):
|
|
64
|
-
raise TypeError(f"Too many arguments: {(
|
|
65
|
-
start, mid, end =
|
|
78
|
+
raise TypeError(f"Too many arguments: {(start_or_bounds, mid, end)}.")
|
|
79
|
+
start, mid, end = start_or_bounds
|
|
66
80
|
else:
|
|
67
81
|
if mid is None or end is None:
|
|
68
|
-
raise TypeError(f"Too few arguments: {(
|
|
69
|
-
start =
|
|
82
|
+
raise TypeError(f"Too few arguments: {(start_or_bounds, mid, end)}.")
|
|
83
|
+
start = start_or_bounds
|
|
84
|
+
|
|
85
|
+
if format is None:
|
|
86
|
+
format = log_split_progress.FOLD_FORMAT
|
|
70
87
|
|
|
71
|
-
return
|
|
72
|
-
start=_PrettyTimestamp(start),
|
|
73
|
-
mid=_PrettyTimestamp(mid),
|
|
74
|
-
end=_PrettyTimestamp(end),
|
|
88
|
+
return format.format(
|
|
89
|
+
start=_PrettyTimestamp(start, mid),
|
|
90
|
+
mid=_PrettyTimestamp(mid, mid),
|
|
91
|
+
end=_PrettyTimestamp(end, mid),
|
|
75
92
|
)
|
|
76
93
|
|
|
77
94
|
|
|
@@ -95,9 +112,11 @@ def stringify(old: Timestamp, *, new: Timestamp | None = None, diff_only: bool =
|
|
|
95
112
|
|
|
96
113
|
|
|
97
114
|
class _PrettyTimestamp:
|
|
98
|
-
def __init__(self, raw: DatetimeTypes) -> None:
|
|
115
|
+
def __init__(self, raw: DatetimeTypes, anchor: DatetimeTypes | None = None) -> None:
|
|
99
116
|
self.timestamp = Timestamp(raw)
|
|
117
|
+
self._anchor = None if anchor is None else Timestamp(anchor)
|
|
100
118
|
self._auto: str | None = None
|
|
119
|
+
self._delta: str | None = None
|
|
101
120
|
|
|
102
121
|
def __getattr__(self, name: str) -> Any:
|
|
103
122
|
return getattr(self.timestamp, name)
|
|
@@ -123,6 +142,25 @@ class _PrettyTimestamp:
|
|
|
123
142
|
self._auto = format(timestamp, format_spec)
|
|
124
143
|
return self._auto
|
|
125
144
|
|
|
145
|
+
@property
|
|
146
|
+
def delta(self) -> str:
|
|
147
|
+
if self._anchor is None:
|
|
148
|
+
raise ValueError("Delta requires an anchor.")
|
|
149
|
+
|
|
150
|
+
if self._delta is None:
|
|
151
|
+
seconds = (self.timestamp - self._anchor).round("s").total_seconds()
|
|
152
|
+
self._delta = fmt_sec(abs(seconds))
|
|
153
|
+
|
|
154
|
+
return self._delta
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def iso(self) -> str:
|
|
158
|
+
return self.timestamp.isoformat() # type: ignore[no-any-return]
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def date(self) -> datetime.date:
|
|
162
|
+
return self.timestamp.date() # type: ignore[no-any-return]
|
|
163
|
+
|
|
126
164
|
|
|
127
165
|
def format_expanded_limits(
|
|
128
166
|
original: LimitsTuple,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Supporting functions for the Streamlit companion app.
|
|
2
|
+
|
|
3
|
+
The app may be installed by running ``pip install time-split[app]``. The application API is *not* stable, but will be
|
|
4
|
+
available under the ``time_split_app`` namespace. Repo: https://github.com/rsundqvist/time-split-app/.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from ._create_explorer_link import create_explorer_link
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"create_explorer_link",
|
|
11
|
+
]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from base64 import b16encode as encode_binary_data
|
|
1
2
|
from datetime import datetime
|
|
2
3
|
from typing import Any, Unpack
|
|
3
4
|
from urllib.parse import ParseResult, urlencode, urlparse, urlunparse
|
|
@@ -10,20 +11,21 @@ from ..types import DatetimeIndexSplitterKwargs, DatetimeIterable
|
|
|
10
11
|
@docs
|
|
11
12
|
def create_explorer_link(
|
|
12
13
|
host: str,
|
|
13
|
-
data: DatetimeIterable | str | None = None,
|
|
14
|
-
available: DatetimeIterable | str | None = None,
|
|
14
|
+
data: DatetimeIterable | str | bytes | None = None,
|
|
15
|
+
available: DatetimeIterable | str | bytes | None = None,
|
|
15
16
|
*,
|
|
16
17
|
show_removed: bool = True,
|
|
17
18
|
skip_default: bool = False,
|
|
18
19
|
**kwargs: Unpack[DatetimeIndexSplitterKwargs],
|
|
19
20
|
) -> str:
|
|
20
|
-
"""Create
|
|
21
|
+
"""Create an application URL.
|
|
21
22
|
|
|
22
23
|
Args:
|
|
23
24
|
host: Base address where the application is hosted.
|
|
24
25
|
data: {available} Regular `available` arguments (as passed to e.g. :func:`time_split.split`) are encoded as
|
|
25
|
-
a date range to generate dummy data for.
|
|
26
|
-
|
|
26
|
+
a date range to generate dummy data for.
|
|
27
|
+
Pass a ``str`` to use a bundled dataset, or ``bytes`` to use a custom loader. Note that this will disable
|
|
28
|
+
verification of the `**kwargs`.
|
|
27
29
|
available: Alias of `data`.
|
|
28
30
|
skip_default: If ``True``, do not include default split params in the link.
|
|
29
31
|
show_removed: {show_removed}
|
|
@@ -54,19 +56,20 @@ def create_explorer_link(
|
|
|
54
56
|
:target: https://hub.docker.com/r/rsundqvist/time-split/
|
|
55
57
|
:alt: Docker Image Size (tag)
|
|
56
58
|
"""
|
|
57
|
-
if sum([data is None, available is None]) != 1:
|
|
58
|
-
raise ValueError("Exactly one of `data` and `available` must be given.")
|
|
59
59
|
if data is None:
|
|
60
|
+
if available is None:
|
|
61
|
+
raise ValueError("Exactly one of `data` and `available` must be given.")
|
|
60
62
|
data = available
|
|
63
|
+
elif data is not None and available is not None:
|
|
64
|
+
raise ValueError("Exactly one of `data` and `available` must be given.")
|
|
61
65
|
|
|
62
|
-
assert data is not None # noqa: S101
|
|
63
66
|
pr = _create_explorer_link(host, data, show_removed=show_removed, skip_default=skip_default, **kwargs)
|
|
64
67
|
return urlunparse(pr)
|
|
65
68
|
|
|
66
69
|
|
|
67
70
|
def _create_explorer_link(
|
|
68
71
|
host: str,
|
|
69
|
-
data: DatetimeIterable | str,
|
|
72
|
+
data: DatetimeIterable | str | bytes,
|
|
70
73
|
*,
|
|
71
74
|
show_removed: bool = True,
|
|
72
75
|
skip_default: bool = False,
|
|
@@ -81,7 +84,7 @@ def _create_explorer_link(
|
|
|
81
84
|
|
|
82
85
|
|
|
83
86
|
def _create_query(
|
|
84
|
-
data: DatetimeIterable | str,
|
|
87
|
+
data: DatetimeIterable | str | bytes,
|
|
85
88
|
*,
|
|
86
89
|
skip_default: bool,
|
|
87
90
|
show_removed: bool,
|
|
@@ -89,6 +92,9 @@ def _create_query(
|
|
|
89
92
|
) -> dict[str, Any]:
|
|
90
93
|
query: dict[str, Any] = {}
|
|
91
94
|
|
|
95
|
+
if isinstance(data, bytes):
|
|
96
|
+
data = "0x" + encode_binary_data(data).decode()
|
|
97
|
+
|
|
92
98
|
if isinstance(data, str):
|
|
93
99
|
query = {"data": data, **kwargs}
|
|
94
100
|
else:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""CLI entrypoint.
|
|
2
|
+
|
|
3
|
+
Use ``python -m time_split --help`` to get started.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import click as _c
|
|
8
|
+
from rics.click import _alias_command_group
|
|
9
|
+
from time_split_app.cli import main as _app_main
|
|
10
|
+
except ImportError as e:
|
|
11
|
+
name = "time-split[app]"
|
|
12
|
+
exc = ImportError(f"Install `{name}` to use.", name=name)
|
|
13
|
+
exc.add_note(f"Package `{e.name}` not installed. Run:\n `pip install {name}`\nto use the CLI entrypoint.")
|
|
14
|
+
raise exc from e
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@_c.group(
|
|
18
|
+
name="time-split",
|
|
19
|
+
epilog="Visit https://time-split.readthedocs.io/ for help.",
|
|
20
|
+
cls=_alias_command_group.AliasedCommandGroup,
|
|
21
|
+
)
|
|
22
|
+
def main() -> None:
|
|
23
|
+
"""See https://time-split.readthedocs.io/."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
main.add_command(_app_main)
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
main()
|
|
@@ -177,3 +177,33 @@ class misc: # noqa: N801
|
|
|
177
177
|
|
|
178
178
|
filter: _t.Callable[[_pd.Timestamp, _pd.Timestamp, _pd.Timestamp], bool] | None = None
|
|
179
179
|
"""A callable ``(start, mid, end) -> bool`` applied to all generated folds."""
|
|
180
|
+
|
|
181
|
+
round_limits: bool = False
|
|
182
|
+
"""If ``True``, use rounding instead of ``floor`` and ``ceil`` for limits expansion.
|
|
183
|
+
|
|
184
|
+
Normally, limits such as
|
|
185
|
+
|
|
186
|
+
>>> ('2019-04-10 23:55:00', '2019-05-12 01:00:00')
|
|
187
|
+
|
|
188
|
+
will never be rounded to
|
|
189
|
+
|
|
190
|
+
>>> ('2019-04-11', '2019-05-12')
|
|
191
|
+
|
|
192
|
+
since that wouldn't be an expansion of the limits. Setting ``round_limits=True`` will allow the
|
|
193
|
+
:func:`~time_split.support.expand_limits` function round these bounds inward to the nearest dates.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def no_init(self: object) -> None:
|
|
198
|
+
"""Prevents initialization of config classes."""
|
|
199
|
+
raise TypeError(
|
|
200
|
+
f"Class '{__package__}.{type(self).__name__}' is used as a public"
|
|
201
|
+
f" namespace. There is no need to instantiate this class."
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
for cls in auto_expand_limits, plot, log_split_progress, misc:
|
|
206
|
+
cls.__init__ = no_init # type: ignore[method-assign]
|
|
207
|
+
|
|
208
|
+
del cls
|
|
209
|
+
del no_init
|
|
@@ -13,15 +13,13 @@ dependencies if you need to use the ``support`` module.
|
|
|
13
13
|
|
|
14
14
|
from .._backend import DatetimeIndexSplitter, expand_limits, process_available
|
|
15
15
|
from .._frontend import default_metrics_formatter, fold_weight, format_expanded_limits, to_string
|
|
16
|
-
from ._create_explorer_link import create_explorer_link
|
|
17
16
|
|
|
18
17
|
__all__ = [
|
|
19
18
|
"DatetimeIndexSplitter",
|
|
20
|
-
"expand_limits",
|
|
21
|
-
"process_available",
|
|
22
19
|
"default_metrics_formatter",
|
|
20
|
+
"expand_limits",
|
|
23
21
|
"fold_weight",
|
|
24
|
-
"to_string",
|
|
25
22
|
"format_expanded_limits",
|
|
26
|
-
"
|
|
23
|
+
"process_available",
|
|
24
|
+
"to_string",
|
|
27
25
|
]
|
|
@@ -86,7 +86,7 @@ class LogSplitProgressKwargs(_t.TypedDict, _t.Generic[MetricsType], total=False)
|
|
|
86
86
|
get_metrics: GetMetrics[MetricsType]
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
class SplitProgressExtras(_t.TypedDict):
|
|
89
|
+
class SplitProgressExtras(_t.TypedDict, _t.Generic[MetricsType]):
|
|
90
90
|
"""Named `extras` used for messages logged by :func:`.log_split_progress`."""
|
|
91
91
|
|
|
92
92
|
n: int
|
|
@@ -101,3 +101,5 @@ class SplitProgressExtras(_t.TypedDict):
|
|
|
101
101
|
"""An :meth:`ISO-formatted <pandas.Timestamp.isoformat>` timestamp (see :attr:`.DatetimeSplitBounds.end`)."""
|
|
102
102
|
seconds: _t.NotRequired[float]
|
|
103
103
|
"""User time for the fold. Available only for the :attr:`fold-end message <.settings.log_split_progress.END_MESSAGE>`."""
|
|
104
|
+
metrics: _t.NotRequired[MetricsType]
|
|
105
|
+
"""Optional fold metrics. Typically appended to the :attr:`fold-end message <.settings.log_split_progress.END_MESSAGE>`."""
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""Vendored functions."""
|
|
2
|
-
|
|
3
|
-
from functools import wraps
|
|
4
|
-
from typing import Callable, ParamSpec, TypeVar
|
|
5
|
-
from warnings import warn
|
|
6
|
-
|
|
7
|
-
from rics.misc import tname
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
from rics import strings
|
|
11
|
-
|
|
12
|
-
fmt_perf = strings.format_perf_counter
|
|
13
|
-
fmt_sec = strings.format_seconds
|
|
14
|
-
except ImportError:
|
|
15
|
-
from rics import performance
|
|
16
|
-
|
|
17
|
-
# TODO(0.5.0): Drop this.
|
|
18
|
-
fmt_perf = performance.format_perf_counter
|
|
19
|
-
fmt_sec = performance.format_seconds
|
|
20
|
-
|
|
21
|
-
P = ParamSpec("P")
|
|
22
|
-
T = TypeVar("T")
|
|
23
|
-
|
|
24
|
-
PARAMS = (("expand_limits", "flex"),)
|
|
25
|
-
WARNED: set[str] = set() # Minimize the amount of warnings - once per param is enough.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def deprecated_params(__func: Callable[P, T], /) -> Callable[P, T]:
|
|
29
|
-
@wraps(__func)
|
|
30
|
-
def wrap(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
31
|
-
for new, old in PARAMS:
|
|
32
|
-
if old in kwargs:
|
|
33
|
-
if new in kwargs:
|
|
34
|
-
name = tname(__func, prefix_classname=True)
|
|
35
|
-
msg = f"Ambiguous call: {name}() does not accept both `{old}` and `{new}`."
|
|
36
|
-
raise TypeError(msg)
|
|
37
|
-
|
|
38
|
-
if old not in WARNED:
|
|
39
|
-
WARNED.add(old)
|
|
40
|
-
|
|
41
|
-
name = tname(__func, prefix_classname=True)
|
|
42
|
-
msg = f"{name}(): The `{old}` parameter is deprecated; use `{new}` instead."
|
|
43
|
-
warn(message=msg, category=DeprecationWarning, stacklevel=2)
|
|
44
|
-
|
|
45
|
-
kwargs[new] = kwargs.pop(old)
|
|
46
|
-
|
|
47
|
-
return __func(*args, **kwargs)
|
|
48
|
-
|
|
49
|
-
return wrap
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -5,11 +5,11 @@ from ._to_string import format_expanded_limits, to_string
|
|
|
5
5
|
from ._weight import fold_weight
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
|
+
"default_metrics_formatter",
|
|
8
9
|
"fold_weight",
|
|
10
|
+
"format_expanded_limits",
|
|
9
11
|
"log_split_progress",
|
|
10
|
-
"default_metrics_formatter",
|
|
11
12
|
"plot",
|
|
12
13
|
"split",
|
|
13
14
|
"to_string",
|
|
14
|
-
"format_expanded_limits",
|
|
15
15
|
]
|
|
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
|