tpcp 3.2.0__tar.gz → 3.3.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.
- {tpcp-3.2.0 → tpcp-3.3.0}/PKG-INFO +1 -1
- {tpcp-3.2.0 → tpcp-3.3.0}/pyproject.toml +1 -1
- {tpcp-3.2.0 → tpcp-3.3.0}/pyproject.toml.orig +1 -1
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/__init__.py +1 -1
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/validate/__init__.py +2 -1
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/validate/_composed_splitters.py +86 -33
- {tpcp-3.2.0 → tpcp-3.3.0}/README.md +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/skills/tpcp/tpcp-basics/SKILL.md +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/skills/tpcp/tpcp-builder/SKILL.md +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/skills/tpcp/tpcp-datasets/SKILL.md +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/skills/tpcp/tpcp-multiprocessing/SKILL.md +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/skills/tpcp/tpcp-optimization/SKILL.md +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_algorithm.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_algorithm_utils.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_base.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_cli.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_dataset.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_hash.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_optimize.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_parameters.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_pipeline.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_utils/__init__.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_utils/_general.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/_utils/_score.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/caching.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/exceptions.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/misc/__init__.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/misc/_class_utils.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/misc/_typed_iterator.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/misc/_warning_error_context.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/optimize/__init__.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/optimize/_optimize.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/optimize/optuna.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/parallel.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/testing/__init__.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/testing/_algorithm_test_mixin.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/testing/_regression_utils.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/types.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/validate/_cross_val_helper.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/validate/_scorer.py +0 -0
- {tpcp-3.2.0 → tpcp-3.3.0}/src/tpcp/validate/_validate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: tpcp
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: Pipeline and Dataset helpers for complex algorithm evaluation.
|
|
5
5
|
Author: Arne Küderle, Robert Richer, Raul C. Sîmpetru, Björn Eskofier
|
|
6
6
|
Author-email: Arne Küderle <arne.kuederle@fau.de>, Robert Richer <robert.richer@fau.de>, Raul C. Sîmpetru <raul.simpetru@fau.de>, Björn Eskofier <bjoern.eskofier@fau.de>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Module for all helper methods to evaluate algorithms."""
|
|
2
2
|
|
|
3
|
-
from tpcp.validate._composed_splitters import CombinedSplitter, NoSplit
|
|
3
|
+
from tpcp.validate._composed_splitters import CombinedSplitter, NoSplit, SubsetSplitter
|
|
4
4
|
from tpcp.validate._cross_val_helper import BaseDatasetSplitter, DatasetSplitter
|
|
5
5
|
from tpcp.validate._scorer import (
|
|
6
6
|
Aggregator,
|
|
@@ -23,6 +23,7 @@ __all__ = [
|
|
|
23
23
|
"NoSplit",
|
|
24
24
|
"Scorer",
|
|
25
25
|
"ScorerTypes",
|
|
26
|
+
"SubsetSplitter",
|
|
26
27
|
"cross_validate",
|
|
27
28
|
"mean_agg",
|
|
28
29
|
"no_agg",
|
|
@@ -71,61 +71,115 @@ class NoSplit(BaseDatasetSplitter):
|
|
|
71
71
|
yield train.copy(), test.copy()
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
class SubsetSplitter(BaseDatasetSplitter):
|
|
75
|
+
"""Select a subset of the current dataset, then split it into train/test group labels.
|
|
76
|
+
|
|
77
|
+
This is a dataset splitter itself, usable directly as ``cv`` or as a named child of
|
|
78
|
+
:class:`CombinedSplitter`. The selector is evaluated for each ``get_n_splits`` or ``split``
|
|
79
|
+
call, so it should return the same subset for the same input when the calls are paired.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
selector
|
|
84
|
+
Callable receiving the current dataset and returning a subset whose group labels belong to it.
|
|
85
|
+
splitter
|
|
86
|
+
A native tpcp splitter or any input accepted by :class:`DatasetSplitter`, including raw sklearn
|
|
87
|
+
splitters and explicit lists of positional folds. Raw inputs are adapted to group labels
|
|
88
|
+
when used. Pass :class:`DatasetSplitter` explicitly when grouping or stratification by
|
|
89
|
+
dataset index columns is needed.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(self, selector: DatasetSelector, splitter: object) -> None:
|
|
93
|
+
self.selector = selector
|
|
94
|
+
self.splitter = splitter
|
|
95
|
+
|
|
96
|
+
def _prepare(self, dataset: Dataset) -> tuple[Dataset, BaseDatasetSplitter]:
|
|
97
|
+
selected = self.selector(dataset)
|
|
98
|
+
_validate_subset(dataset, selected)
|
|
99
|
+
return selected, _normalize_splitter(self.splitter)
|
|
100
|
+
|
|
101
|
+
def get_n_splits(self, dataset: Dataset) -> int | None:
|
|
102
|
+
"""Return the child splitter's fold count for the selected subset."""
|
|
103
|
+
selected, splitter = self._prepare(dataset)
|
|
104
|
+
return splitter.get_n_splits(selected)
|
|
105
|
+
|
|
106
|
+
def split(
|
|
107
|
+
self, dataset: Dataset, n_splits: int | None = None
|
|
108
|
+
) -> Iterator[tuple[list[GroupLabelT], list[GroupLabelT]]]:
|
|
109
|
+
"""Select the subset and yield its train/test group labels."""
|
|
110
|
+
selected, splitter = self._prepare(dataset)
|
|
111
|
+
if n_splits is None:
|
|
112
|
+
yield from splitter.split(selected)
|
|
113
|
+
else:
|
|
114
|
+
yield from splitter.split(selected, n_splits=n_splits)
|
|
115
|
+
|
|
116
|
+
|
|
74
117
|
class CombinedSplitter(BaseDatasetSplitter):
|
|
75
|
-
"""Combine corresponding folds of
|
|
118
|
+
"""Combine corresponding folds of named dataset splitters.
|
|
76
119
|
|
|
77
|
-
Pass a list of one or more ``(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
Pass a list of one or more ``(name, splitter)`` pairs as ``parts``, where each child implements
|
|
121
|
+
:class:`BaseDatasetSplitter`. Use :class:`SubsetSplitter` to apply a child to a selected subset.
|
|
122
|
+
Names are unique strings without ``__``, following tpcp's composite parameter convention.
|
|
123
|
+
All children receive the current input dataset. Raw sklearn splitters and positional fold lists
|
|
124
|
+
can be used inside :class:`SubsetSplitter` or :class:`DatasetSplitter`.
|
|
81
125
|
|
|
82
126
|
All specified fold counts must match, and at least one child must specify a count.
|
|
83
127
|
Children that report ``None`` receive that count through their ``split`` method.
|
|
84
128
|
All children must yield exactly that many folds. Train and test labels are
|
|
85
129
|
deduplicated separately in first-occurrence order, and overlapping assignments
|
|
86
|
-
raise ``ValueError``.
|
|
87
|
-
within nested splits, and may return any dataset whose group labels belong to
|
|
88
|
-
that input.
|
|
130
|
+
raise ``ValueError``. Nested compositions operate on the dataset supplied by their parent.
|
|
89
131
|
|
|
90
132
|
For example, cross-validate real recordings and always train on artificial recordings::
|
|
91
133
|
|
|
92
|
-
from tpcp.validate import
|
|
134
|
+
from tpcp.validate import (
|
|
135
|
+
CombinedSplitter,
|
|
136
|
+
DatasetSplitter,
|
|
137
|
+
NoSplit,
|
|
138
|
+
SubsetSplitter,
|
|
139
|
+
)
|
|
93
140
|
|
|
94
141
|
cv = CombinedSplitter(
|
|
95
142
|
parts=[
|
|
96
143
|
(
|
|
97
|
-
|
|
98
|
-
|
|
144
|
+
"real",
|
|
145
|
+
SubsetSplitter(
|
|
146
|
+
lambda ds: ds.get_subset(recording_type="real"),
|
|
147
|
+
DatasetSplitter(5, groupby="participant"),
|
|
148
|
+
),
|
|
99
149
|
),
|
|
100
150
|
(
|
|
101
|
-
|
|
102
|
-
NoSplit(
|
|
151
|
+
"artificial",
|
|
152
|
+
NoSplit(
|
|
153
|
+
None,
|
|
154
|
+
train=lambda ds: ds.get_subset(recording_type="artificial"),
|
|
155
|
+
),
|
|
103
156
|
),
|
|
104
157
|
],
|
|
105
158
|
)
|
|
106
159
|
|
|
160
|
+
cv.set_params(parts__real__splitter__base_splitter=3)
|
|
161
|
+
|
|
162
|
+
Replace a whole child with ``set_params(parts__real=new_splitter)``, or update its nested parameters
|
|
163
|
+
with paths such as ``parts__real__selector`` or ``parts__artificial__train``. Adding or removing parts
|
|
164
|
+
requires replacing the entire ``parts`` list.
|
|
165
|
+
|
|
107
166
|
Parameters
|
|
108
167
|
----------
|
|
109
168
|
parts
|
|
110
|
-
A list of
|
|
169
|
+
A composite parameter list of named :class:`BaseDatasetSplitter` objects, in the order their fold
|
|
170
|
+
contributions are combined. Migrate old ``(selector, splitter)`` entries to
|
|
171
|
+
``(name, SubsetSplitter(selector, splitter))``.
|
|
111
172
|
"""
|
|
112
173
|
|
|
113
|
-
|
|
174
|
+
_composite_params = ("parts",)
|
|
175
|
+
|
|
176
|
+
def __init__(self, parts: list[tuple[str, BaseDatasetSplitter]]) -> None:
|
|
114
177
|
self.parts = parts
|
|
115
178
|
|
|
116
|
-
def
|
|
179
|
+
def _fold_count(self, dataset: Dataset) -> tuple[int, list[int | None]]:
|
|
117
180
|
if not self.parts:
|
|
118
|
-
raise ValueError("CombinedSplitter requires at least one
|
|
119
|
-
|
|
120
|
-
for selector, splitter in self.parts:
|
|
121
|
-
selected = selector(dataset)
|
|
122
|
-
_validate_subset(dataset, selected)
|
|
123
|
-
prepared.append((selected, _normalize_splitter(splitter)))
|
|
124
|
-
return prepared
|
|
125
|
-
|
|
126
|
-
@staticmethod
|
|
127
|
-
def _fold_count(prepared: list[tuple[Dataset, BaseDatasetSplitter]]) -> tuple[int, list[int | None]]:
|
|
128
|
-
reported = [splitter.get_n_splits(dataset) for dataset, splitter in prepared]
|
|
181
|
+
raise ValueError("CombinedSplitter requires at least one named dataset splitter.")
|
|
182
|
+
reported = [splitter.get_n_splits(dataset) for _, splitter in self.parts]
|
|
129
183
|
counts = [count for count in reported if count is not None]
|
|
130
184
|
if not counts:
|
|
131
185
|
raise ValueError("CombinedSplitter requires at least one child to report an integer number of folds.")
|
|
@@ -134,19 +188,18 @@ class CombinedSplitter(BaseDatasetSplitter):
|
|
|
134
188
|
return counts[0], reported
|
|
135
189
|
|
|
136
190
|
def get_n_splits(self, dataset: Dataset) -> int:
|
|
137
|
-
"""Return the shared number of folds
|
|
138
|
-
return self._fold_count(
|
|
191
|
+
"""Return the shared number of folds reported by the children."""
|
|
192
|
+
return self._fold_count(dataset)[0]
|
|
139
193
|
|
|
140
194
|
def split(
|
|
141
195
|
self, dataset: Dataset, n_splits: int | None = None
|
|
142
196
|
) -> Iterator[tuple[list[GroupLabelT], list[GroupLabelT]]]:
|
|
143
197
|
"""Yield deduplicated, disjoint train and test labels for corresponding child folds."""
|
|
144
|
-
|
|
145
|
-
available, reported = self._fold_count(prepared)
|
|
198
|
+
available, reported = self._fold_count(dataset)
|
|
146
199
|
count = _requested_fold_count(n_splits, available)
|
|
147
200
|
iterators = [
|
|
148
|
-
iter(splitter.split(
|
|
149
|
-
for (
|
|
201
|
+
iter(splitter.split(dataset, n_splits=count)) if child_count is None else iter(splitter.split(dataset))
|
|
202
|
+
for (_, splitter), child_count in zip(self.parts, reported, strict=True)
|
|
150
203
|
]
|
|
151
204
|
for _ in range(count):
|
|
152
205
|
folds = []
|
|
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
|
|
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
|