time-split 0.1.0__tar.gz → 0.2.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.
Files changed (35) hide show
  1. {time_split-0.1.0 → time_split-0.2.0}/PKG-INFO +6 -6
  2. {time_split-0.1.0 → time_split-0.2.0}/README.md +4 -4
  3. {time_split-0.1.0 → time_split-0.2.0}/pyproject.toml +2 -2
  4. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/__init__.py +2 -2
  5. time_split-0.2.0/src/time_split/_backend/__init__.py +12 -0
  6. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_backend/_datetime_index_like.py +1 -1
  7. time_split-0.1.0/src/time_split/_backend/_available.py → time_split-0.2.0/src/time_split/_backend/_process_available.py +4 -4
  8. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_backend/_schedule.py +3 -3
  9. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_backend/_splitter.py +6 -6
  10. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_docstrings.py +1 -1
  11. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_frontend/_plot.py +3 -6
  12. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_frontend/_split.py +1 -1
  13. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/sklearn/_impl.py +1 -1
  14. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/split_data.py +21 -6
  15. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/support/__init__.py +3 -1
  16. time_split-0.2.0/src/time_split/support/types.py +11 -0
  17. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/types.py +6 -3
  18. time_split-0.1.0/src/time_split/_backend/__init__.py +0 -4
  19. {time_split-0.1.0 → time_split-0.2.0}/LICENSE.md +0 -0
  20. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_backend/_limits.py +0 -0
  21. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_backend/_span.py +0 -0
  22. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_frontend/__init__.py +0 -0
  23. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_frontend/_progress.py +0 -0
  24. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_frontend/_to_string.py +0 -0
  25. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_frontend/_weight.py +0 -0
  26. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/_support.py +0 -0
  27. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/__init__.py +0 -0
  28. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/_log_progress.py +0 -0
  29. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/pandas/__init__.py +0 -0
  30. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/pandas/_impl.py +0 -0
  31. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/polars/__init__.py +0 -0
  32. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/polars/_impl.py +0 -0
  33. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/integration/sklearn/__init__.py +0 -0
  34. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/py.typed +0 -0
  35. {time_split-0.1.0 → time_split-0.2.0}/src/time_split/settings.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: time-split
3
- Version: 0.1.0
4
- Summary: My personal little ML engineering library.
3
+ Version: 0.2.0
4
+ Summary: Time-based k-fold validation splits for heterogeneous data.
5
5
  Home-page: https://github.com/rsundqvist/time-split
6
6
  Author: Richard Sundqvist
7
7
  Author-email: richard.sundqvist@live.se
@@ -30,7 +30,7 @@ Project-URL: Repository, https://github.com/rsundqvist/time-split
30
30
  Description-Content-Type: text/markdown
31
31
 
32
32
  # Time Split <!-- omit in toc -->
33
- Flexible k-fold validation splits for heterogeneous data.
33
+ Time-based k-fold validation splits for heterogeneous data.
34
34
 
35
35
  -----------------
36
36
  [![PyPI - Version](https://img.shields.io/pypi/v/time-split.svg)](https://pypi.python.org/pypi/time-split)
@@ -46,12 +46,12 @@ Flexible k-fold validation splits for heterogeneous data.
46
46
  src="https://raw.githubusercontent.com/rsundqvist/time-split/master/docs/2x2-examples.jpg"><br>
47
47
  </div>
48
48
 
49
- Plotted folds on a two-by-two grid. See the
49
+ Folds plotted on a two-by-two grid. See the
50
50
  [examples](https://time-split.readthedocs.io/en/stable/auto_examples/index.html) page for more.
51
51
 
52
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.
53
+ A library for creating time-based cross-validation splits of _heterogeneous_ data, such as raw transaction data with
54
+ strong non-stationary characteristics.
55
55
 
56
56
  ## Highlighted Features
57
57
  - [Splitting schedules](https://time-split.readthedocs.io/en/stable/guide/schedules.html) based on a fixed interval,
@@ -1,5 +1,5 @@
1
1
  # Time Split <!-- omit in toc -->
2
- Flexible k-fold validation splits for heterogeneous data.
2
+ Time-based k-fold validation splits for heterogeneous data.
3
3
 
4
4
  -----------------
5
5
  [![PyPI - Version](https://img.shields.io/pypi/v/time-split.svg)](https://pypi.python.org/pypi/time-split)
@@ -15,12 +15,12 @@ Flexible k-fold validation splits for heterogeneous data.
15
15
  src="https://raw.githubusercontent.com/rsundqvist/time-split/master/docs/2x2-examples.jpg"><br>
16
16
  </div>
17
17
 
18
- Plotted folds on a two-by-two grid. See the
18
+ Folds plotted on a two-by-two grid. See the
19
19
  [examples](https://time-split.readthedocs.io/en/stable/auto_examples/index.html) page for more.
20
20
 
21
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.
22
+ A library for creating time-based cross-validation splits of _heterogeneous_ data, such as raw transaction data with
23
+ strong non-stationary characteristics.
24
24
 
25
25
  ## Highlighted Features
26
26
  - [Splitting schedules](https://time-split.readthedocs.io/en/stable/guide/schedules.html) based on a fixed interval,
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "time-split"
3
- version = "0.1.0"
4
- description = "My personal little ML engineering library."
3
+ version = "0.2.0"
4
+ description = "Time-based k-fold validation splits for heterogeneous data."
5
5
  authors = ["Richard Sundqvist <richard.sundqvist@live.se>"]
6
6
 
7
7
  readme = "README.md"
@@ -1,4 +1,4 @@
1
- """Create temporal k-folds for cross-validation with heterogeneous data."""
1
+ """Time-based k-fold validation splits for heterogeneous data."""
2
2
 
3
3
  import logging as _logging
4
4
 
@@ -11,6 +11,6 @@ __all__ = [
11
11
  "__version__", # Make MyPy happy
12
12
  ]
13
13
 
14
- __version__ = "0.1.0"
14
+ __version__ = "0.2.0"
15
15
 
16
16
  _logging.getLogger(__name__).addHandler(_logging.NullHandler())
@@ -0,0 +1,12 @@
1
+ from ._datetime_index_like import DatetimeIndexLike
2
+ from ._limits import expand_limits
3
+ from ._process_available import ProcessAvailableResult, process_available
4
+ from ._splitter import DatetimeIndexSplitter
5
+
6
+ __all__ = [
7
+ "DatetimeIndexLike",
8
+ "DatetimeIndexSplitter",
9
+ "ProcessAvailableResult",
10
+ "process_available",
11
+ "expand_limits",
12
+ ]
@@ -8,7 +8,7 @@ from ..types import DatetimeTypes
8
8
 
9
9
  @runtime_checkable
10
10
  class DatetimeIndexLike(Protocol, Iterable[DatetimeTypes]):
11
- """A type that behaves (sort of) like a pandas DatetimeIndex."""
11
+ """A type that behaves (sort of) like a :class:`pandas.DatetimeIndex`."""
12
12
 
13
13
  def __iter__(self) -> Iterator[DatetimeTypes]: ...
14
14
 
@@ -8,15 +8,15 @@ from ._datetime_index_like import DatetimeIndexLike
8
8
  from ._limits import LimitsTuple, expand_limits
9
9
 
10
10
 
11
- class AvailableMetadata(NamedTuple):
12
- """Output of :func:`process_available`."""
11
+ class ProcessAvailableResult(NamedTuple):
12
+ """Output of :func:`.process_available`."""
13
13
 
14
14
  available_as_index: DatetimeIndexLike | None
15
15
  limits: LimitsTuple
16
16
  expanded_limits: LimitsTuple
17
17
 
18
18
 
19
- def process_available(available: DatetimeIterable, *, flex: Flex) -> AvailableMetadata:
19
+ def process_available(available: DatetimeIterable, *, flex: Flex) -> ProcessAvailableResult:
20
20
  """Process a user-given `available` argument.
21
21
 
22
22
  Args:
@@ -50,7 +50,7 @@ def process_available(available: DatetimeIterable, *, flex: Flex) -> AvailableMe
50
50
  limits = Timestamp(min_dt), Timestamp(max_dt)
51
51
 
52
52
  expanded_limits = expand_limits(limits, flex=flex)
53
- return AvailableMetadata(available_retval, limits=limits, expanded_limits=expanded_limits)
53
+ return ProcessAvailableResult(available_retval, limits=limits, expanded_limits=expanded_limits)
54
54
 
55
55
 
56
56
  def _compute_data_limits(available: DatetimeIndexLike) -> LimitsTuple:
@@ -3,8 +3,8 @@ from typing import NamedTuple, cast, get_args
3
3
  from pandas import DatetimeIndex, NaT, Timedelta, Timestamp, date_range
4
4
 
5
5
  from ..types import DatetimeIterable, Flex, Schedule, TimedeltaTypes
6
- from ._available import AvailableMetadata, process_available
7
6
  from ._limits import LimitsTuple
7
+ from ._process_available import ProcessAvailableResult, process_available
8
8
 
9
9
  NO_LIMITS = cast(LimitsTuple, (NaT, NaT))
10
10
 
@@ -13,7 +13,7 @@ class MaterializedSchedule(NamedTuple):
13
13
  """An explicit schedule."""
14
14
 
15
15
  schedule: DatetimeIndex
16
- available_metadata: AvailableMetadata
16
+ available_metadata: ProcessAvailableResult
17
17
 
18
18
 
19
19
  def materialize_schedule(
@@ -24,7 +24,7 @@ def materialize_schedule(
24
24
  try:
25
25
  return MaterializedSchedule(
26
26
  DatetimeIndex(schedule),
27
- available_metadata=AvailableMetadata(None, NO_LIMITS, NO_LIMITS),
27
+ available_metadata=ProcessAvailableResult(None, NO_LIMITS, NO_LIMITS),
28
28
  )
29
29
  except TypeError as e:
30
30
  raise ValueError("Schedule must be explicit when not bounded by an available range.") from e
@@ -26,13 +26,13 @@ LOGGER = logging.getLogger("time_split")
26
26
 
27
27
  @dataclass(frozen=True)
28
28
  class DatetimeIndexSplitter:
29
- """Backend interface for splitting user data."""
29
+ """Backend interface for splitting user data. See the :ref:`Parameter overview` page."""
30
30
 
31
31
  schedule: Schedule
32
32
  before: Span
33
33
  after: Span
34
34
  step: int
35
- n_splits: int | None
35
+ n_splits: int
36
36
  flex: Flex
37
37
 
38
38
  def get_splits(self, available: DatetimeIterable | None = None) -> DatetimeSplits:
@@ -115,10 +115,10 @@ class DatetimeIndexSplitter:
115
115
  splits = [s for i, s in enumerate(reversed(splits)) if i % step == 0]
116
116
  splits.reverse()
117
117
 
118
- if self.n_splits:
118
+ if self.n_splits > 0:
119
119
  splits = splits[-self.n_splits :]
120
120
 
121
- if self.step < 0:
121
+ if self.step < 0: # Poorly documented - might not work as expected?
122
122
  splits.reverse()
123
123
 
124
124
  return splits
@@ -147,8 +147,8 @@ class DatetimeIndexSplitter:
147
147
 
148
148
  def __post_init__(self) -> None:
149
149
  # Verify n_splits
150
- if self.n_splits is not None and self.n_splits < 1:
151
- raise ValueError(f"Expected n_splits >= 1, but got n_splits={self.n_splits!r}.")
150
+ if self.n_splits < 0:
151
+ raise ValueError(f"Expected n_splits >= 0, but got n_splits={self.n_splits!r}.")
152
152
 
153
153
  # Verify before/after
154
154
  to_strict_span(self.before, name="before")
@@ -21,7 +21,7 @@ _SPAN = (
21
21
  f"or `'all'` (requires `available` data)."
22
22
  )
23
23
  _DOCSTRINGS = {
24
- "schedule": f"A collection of timestamps, a {_OFFSET}, or a cron expression.",
24
+ "schedule": f"A :attr:`~time_split.types.DatetimeIterable`, {_OFFSET}, or `cron <https://pypi.org/project/croniter/>`_ expression.",
25
25
  "before": _SPAN.format(arg="before"),
26
26
  "after": _SPAN.format(arg="after"),
27
27
  "step": "Select a subset of folds, preferring folds later in the schedule.",
@@ -1,4 +1,4 @@
1
- from dataclasses import asdict, dataclass
1
+ from dataclasses import asdict, dataclass, replace
2
2
  from typing import TYPE_CHECKING, Any, Literal
3
3
 
4
4
  import pandas as pd
@@ -62,7 +62,7 @@ def plot(
62
62
  before: Span = "7d",
63
63
  after: Span = 1,
64
64
  step: int = 1,
65
- n_splits: int | None = None,
65
+ n_splits: int = 0,
66
66
  available: DatetimeIterable | None = None,
67
67
  flex: Flex = "auto",
68
68
  # Split plot args
@@ -283,10 +283,7 @@ def _get_plot_data(
283
283
 
284
284
  if show_removed:
285
285
  kept_splits = set(splits)
286
- kwargs = asdict(splitter) # Can't use dataclasses.replace: 3.10+ only
287
- kwargs["n_splits"] = None
288
- kwargs["step"] = 1
289
- splits = DatetimeIndexSplitter(**kwargs).get_plot_data(available)[0]
286
+ splits = replace(splitter, n_splits=0, step=1).get_plot_data(available)[0]
290
287
  if splitter.step < 0:
291
288
  splits.reverse()
292
289
  removed = set(splits) - set(kept_splits)
@@ -10,7 +10,7 @@ def split(
10
10
  before: Span = "7d",
11
11
  after: Span = 1,
12
12
  step: int = 1,
13
- n_splits: int | None = None,
13
+ n_splits: int = 0,
14
14
  available: DatetimeIterable | None = None,
15
15
  flex: Flex = "auto",
16
16
  ) -> DatetimeSplits:
@@ -57,7 +57,7 @@ class ScikitLearnSplitter(BaseCrossValidator): # type: ignore[misc]
57
57
  *,
58
58
  before: Span = "7d",
59
59
  after: Span = 1,
60
- n_splits: int | None = None,
60
+ n_splits: int = 0,
61
61
  flex: Flex = "auto",
62
62
  step: int = 1,
63
63
  log_progress: LogProgressArg = False,
@@ -6,14 +6,18 @@ Users may implement splitting of any data type by implementing suitable ``as_ava
6
6
  import typing as _t
7
7
  from datetime import datetime as _datetime
8
8
 
9
- from .. import _frontend, types
9
+ from .. import _frontend
10
+ from .. import types as _tst
10
11
  from .._docstrings import docs as _docs
11
12
  from . import _log_progress
12
13
 
14
+ if _t.TYPE_CHECKING:
15
+ import pandas
16
+
13
17
  DataT = _t.TypeVar("DataT")
14
18
  """Type of data to split."""
15
19
 
16
- DataAsAvailableFn = _t.Callable[[DataT], types.DatetimeIterable]
20
+ DataAsAvailableFn = _t.Callable[[DataT], _tst.DatetimeIterable]
17
21
  """A callable ``(data: DataT) -> DatetimeIterable``."""
18
22
  DataSelectFn = _t.Callable[[DataT, _datetime, _datetime], DataT]
19
23
  """A callable ``(data: DataT, left_inclusive: datetime, end_exclusive: datetime) -> DataT)``."""
@@ -23,12 +27,23 @@ class DatetimeSplit(_t.NamedTuple, _t.Generic[DataT]):
23
27
  """Time-based split of a generic data type."""
24
28
 
25
29
  data: DataT
26
- """Data before ``bounds.mid``."""
30
+ """Data before the simulated :attr:`training_date`.
31
+
32
+ Bounded by `bounds.start <= time(future_data) < bounds.mid`.
33
+ """
27
34
  future_data: DataT
28
- """Data after ``bounds.mid``."""
29
- bounds: types.DatetimeSplitBounds
35
+ """Data after the simulated :attr:`training_date`.
36
+
37
+ Bounded by `bounds.mid <= time(future_data) < bounds.end`.
38
+ """
39
+ bounds: _tst.DatetimeSplitBounds
30
40
  """The underlying bounds that produced this split."""
31
41
 
42
+ @property
43
+ def training_date(self) -> "pandas.Timestamp":
44
+ """Returns the simulated training date (alias of :attr:`self.bounds.mid <.DatetimeSplitBounds.mid>`)."""
45
+ return self.bounds.mid
46
+
32
47
 
33
48
  @_docs
34
49
  def split_data(
@@ -37,7 +52,7 @@ def split_data(
37
52
  log_progress: _log_progress.LogProgressArg = False,
38
53
  as_available: DataAsAvailableFn[DataT],
39
54
  select: DataSelectFn[DataT],
40
- **kwargs: _t.Unpack[types.DatetimeIndexSplitterKwargs],
55
+ **kwargs: _t.Unpack[_tst.DatetimeIndexSplitterKwargs],
41
56
  ) -> _t.Iterable[DatetimeSplit[DataT]]:
42
57
  """Base implementation for splitting integrated `data` types.
43
58
 
@@ -11,11 +11,13 @@ This module may change without notice. Stick to the top-level :mod:`time_split`-
11
11
  dependencies if you need to use the ``support`` module.
12
12
  """
13
13
 
14
- from .._backend import expand_limits
14
+ from .._backend import DatetimeIndexSplitter, expand_limits, process_available
15
15
  from .._frontend import fold_weight, to_string
16
16
 
17
17
  __all__ = [
18
+ "DatetimeIndexSplitter",
18
19
  "expand_limits",
20
+ "process_available",
19
21
  "fold_weight",
20
22
  "to_string",
21
23
  ]
@@ -0,0 +1,11 @@
1
+ """Internal types."""
2
+
3
+ from time_split._backend._schedule import MaterializedSchedule
4
+
5
+ from .._backend import DatetimeIndexLike, ProcessAvailableResult
6
+
7
+ __all__ = [
8
+ "DatetimeIndexLike",
9
+ "MaterializedSchedule",
10
+ "ProcessAvailableResult",
11
+ ]
@@ -30,6 +30,9 @@ class DatetimeSplitBounds(_t.NamedTuple):
30
30
  """Schedule timestamp.
31
31
 
32
32
  Right (exclusive) limit of the `data` range, left (inclusive) limit of the `future_data` range.
33
+
34
+ When using :mod:`.integration` functions, These are available as :attr:`.DatetimeSplit.data` and
35
+ :attr:`.DatetimeSplit.future_data`, respectively.
33
36
  """
34
37
  end: _pd.Timestamp
35
38
  """Right (exclusive) limit of the `future_data` range."""
@@ -49,13 +52,13 @@ class DatetimeSplitCounts(_t.NamedTuple):
49
52
  class DatetimeIndexSplitterKwargs(_t.TypedDict, total=False):
50
53
  """Keyword arguments for creating a ``DatetimeIndexSplitter``.
51
54
 
52
- The ``DatetimeIndexSplitter`` is the backing type for all splitting logic. It is not exposed directly to users. See
53
- :func:`.split` or one of the related functions for user-facing APIs.
55
+ The :class:`time_split.support.DatetimeIndexSplitter` is the backend implementation for the splitting logic. In most
56
+ cases, it should not be used directly. See func:`.split` or one of the related functions for user-facing APIs.
54
57
  """
55
58
 
56
59
  schedule: _t.Required[Schedule]
57
60
  before: Span
58
61
  after: Span
59
62
  step: int
60
- n_splits: int | None
63
+ n_splits: int
61
64
  flex: Flex
@@ -1,4 +0,0 @@
1
- from ._limits import expand_limits
2
- from ._splitter import DatetimeIndexSplitter
3
-
4
- __all__ = ["DatetimeIndexSplitter", "expand_limits"]
File without changes