pybear-dask 0.2.0__py3-none-any.whl

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 (33) hide show
  1. pybear_dask/__init__.py +19 -0
  2. pybear_dask/_version.py +38 -0
  3. pybear_dask/base/__init__.py +24 -0
  4. pybear_dask/base/_is_classifier.py +186 -0
  5. pybear_dask/model_selection/GSTCV/_GSTCVDask/GSTCVDask.py +918 -0
  6. pybear_dask/model_selection/GSTCV/_GSTCVDask/__init__.py +5 -0
  7. pybear_dask/model_selection/GSTCV/_GSTCVDask/_fit/__init__.py +5 -0
  8. pybear_dask/model_selection/GSTCV/_GSTCVDask/_fit/_estimator_fit_params_helper.py +106 -0
  9. pybear_dask/model_selection/GSTCV/_GSTCVDask/_fit/_fold_splitter.py +86 -0
  10. pybear_dask/model_selection/GSTCV/_GSTCVDask/_fit/_get_kfold.py +117 -0
  11. pybear_dask/model_selection/GSTCV/_GSTCVDask/_fit/_parallelized_fit.py +127 -0
  12. pybear_dask/model_selection/GSTCV/_GSTCVDask/_fit/_parallelized_scorer.py +228 -0
  13. pybear_dask/model_selection/GSTCV/_GSTCVDask/_fit/_parallelized_train_scorer.py +226 -0
  14. pybear_dask/model_selection/GSTCV/_GSTCVDask/_param_conditioning/__init__.py +5 -0
  15. pybear_dask/model_selection/GSTCV/_GSTCVDask/_param_conditioning/_scheduler.py +88 -0
  16. pybear_dask/model_selection/GSTCV/_GSTCVDask/_type_aliases.py +43 -0
  17. pybear_dask/model_selection/GSTCV/_GSTCVDask/_validation/__init__.py +5 -0
  18. pybear_dask/model_selection/GSTCV/_GSTCVDask/_validation/_cache_cv.py +37 -0
  19. pybear_dask/model_selection/GSTCV/_GSTCVDask/_validation/_dask_estimator.py +84 -0
  20. pybear_dask/model_selection/GSTCV/_GSTCVDask/_validation/_iid.py +38 -0
  21. pybear_dask/model_selection/GSTCV/_GSTCVDask/_validation/_validation.py +54 -0
  22. pybear_dask/model_selection/GSTCV/_GSTCVDask/_validation/_y.py +91 -0
  23. pybear_dask/model_selection/GSTCV/__init__.py +5 -0
  24. pybear_dask/model_selection/__init__.py +28 -0
  25. pybear_dask/model_selection/autogridsearch/AutoGSTCVDask.py +71 -0
  26. pybear_dask/model_selection/autogridsearch/AutoGridSearchCVDask.py +70 -0
  27. pybear_dask/model_selection/autogridsearch/__init__.py +19 -0
  28. pybear_dask/model_selection/autogridsearch/_autogridsearch_wrapper/__init__.py +5 -0
  29. pybear_dask/model_selection/autogridsearch/_autogridsearch_wrapper/_refit_can_be_skipped.py +100 -0
  30. pybear_dask-0.2.0.dist-info/LICENSE +28 -0
  31. pybear_dask-0.2.0.dist-info/METADATA +253 -0
  32. pybear_dask-0.2.0.dist-info/RECORD +33 -0
  33. pybear_dask-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,226 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing_extensions import Union
10
+ from pybear.model_selection.GSTCV._type_aliases import (
11
+ ScorerWIPType,
12
+ ClassifierProtocol,
13
+ MaskedHolderType,
14
+ NDArrayHolderType
15
+ )
16
+ from .._type_aliases import (
17
+ DaskXType,
18
+ DaskYType
19
+ )
20
+
21
+ import numbers
22
+ import time
23
+
24
+ import numpy as np
25
+
26
+ from pybear.model_selection.GSTCV._GSTCVMixin._validation._predict_proba import _val_predict_proba
27
+
28
+
29
+
30
+ def _parallelized_train_scorer(
31
+ _X_train: DaskXType,
32
+ _y_train: DaskYType,
33
+ _FIT_OUTPUT_TUPLE: tuple[ClassifierProtocol, float, bool],
34
+ _f_idx: int,
35
+ _SCORER_DICT: ScorerWIPType,
36
+ _BEST_THRESHOLDS_BY_SCORER: NDArrayHolderType,
37
+ _error_score: Union[numbers.Real, None],
38
+ _verbose: int
39
+ ) -> MaskedHolderType:
40
+
41
+ # dont adjust the spacing, is congruent with test scorer
42
+
43
+ """Using the estimators fit on each train fold, use `predict_proba`
44
+ and _X_trains to generate _y_preds and score against the corresponding
45
+ _y_trains using all of the scorers.
46
+
47
+ Fill one layer of the TRAIN_FOLD_x_SCORER__SCORE.
48
+
49
+ Parameters
50
+ ----------
51
+ _X_train : DaskXType
52
+ A train partition of the data that was fit.
53
+ _y_train : DaskYType
54
+ The corresponding train partition of the target for the X train
55
+ partition.
56
+ _FIT_OUTPUT_TUPLE : tuple[ClassifierProtocol, float, bool]
57
+ A tuple holding the fitted estimator, the fit time (not needed
58
+ here), and the fit_excepted boolean (needed here.)
59
+ _f_idx : int
60
+ The zero-based split index of the train partition used here;
61
+ parallelism occurs over the different splits.
62
+ _SCORER_DICT : ScorerWIPType
63
+ A dictionary with scorer name as keys and the scorer callables
64
+ as values. The scorer callables are scoring metrics (or similar),
65
+ not make_scorer.
66
+ _BEST_THRESHOLDS_BY_SCORER : NDArrayHolderType:
67
+ After all of the fold / threshold / scorer combinations are
68
+ scored, the folds are averaged and the threshold with the maximum
69
+ score for each scorer is found. This vector has length n_scorers
70
+ and in each position holds a float indicating the threshold
71
+ value that is the best threshold for that scorer.
72
+ _error_score : Union[numbers.Real, Literal['raise']]
73
+ If this training fold excepted during fitting and `error_score`
74
+ was set to the 'raise' literal, this module cannot be reached.
75
+ Otherwise, a number or number-like was passed to `error_score`.
76
+ If 'fit_excepted' is True, this module puts the `error_score`
77
+ value in every position of the TRAIN_SCORER__SCORE_LAYER vector.
78
+ If `error_score` is set to np.nan, that layer is also masked.
79
+ _verbose : int
80
+ A number from 0 to 10 that indicates the amount of information
81
+ to display to the screen during the grid search process. 0 means
82
+ no output, 10 means maximum output.
83
+
84
+ Returns
85
+ -------
86
+ TRAIN_SCORER__SCORE_LAYER : MaskedHolderType
87
+ Masked array of shape (n_scorers, ). The score for this fold of
88
+ train data using every scorer and the best threshold associated
89
+ with that scorer.
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ """
101
+
102
+ # validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
103
+ assert isinstance(_FIT_OUTPUT_TUPLE, tuple)
104
+ assert isinstance(_f_idx, int)
105
+ assert isinstance(_SCORER_DICT, dict)
106
+ assert all(map(callable, _SCORER_DICT.values()))
107
+ assert isinstance(_BEST_THRESHOLDS_BY_SCORER, np.ndarray)
108
+ assert isinstance(_verbose, (int, float))
109
+ # END validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * *
110
+
111
+ if _verbose >= 5:
112
+ print(f"Start scoring fold {_f_idx + 1} train with different scorers")
113
+
114
+
115
+ _estimator_, _fit_time, _fit_excepted = _FIT_OUTPUT_TUPLE
116
+
117
+ TRAIN_SCORER__SCORE_LAYER: MaskedHolderType = \
118
+ np.ma.zeros(len(_SCORER_DICT), dtype=np.float64)
119
+ TRAIN_SCORER__SCORE_LAYER.mask = True
120
+
121
+
122
+
123
+
124
+
125
+
126
+ # IF A FOLD EXCEPTED DURING FIT, PUT IN error_score.
127
+ # IF error_score==np.nan, ALSO MASK THIS LAYER
128
+ if _fit_excepted:
129
+ TRAIN_SCORER__SCORE_LAYER[:] = _error_score
130
+ if _error_score is np.nan:
131
+ TRAIN_SCORER__SCORE_LAYER[:] = np.ma.masked
132
+
133
+
134
+
135
+
136
+ if _verbose >= 5:
137
+ print(f'fold {_f_idx + 1} excepted during fit, unable to score')
138
+
139
+ return TRAIN_SCORER__SCORE_LAYER
140
+
141
+
142
+ # v v v only accessible if fit() did not except v v v
143
+
144
+ _X_train = _X_train.persist()
145
+ _y_train = _y_train.persist()
146
+
147
+ pp0_time = time.perf_counter()
148
+ _predict_proba = _estimator_.predict_proba(_X_train)[:, -1].ravel()
149
+ _val_predict_proba(
150
+ _predict_proba,
151
+ _X_train.shape[0] if hasattr(_X_train, 'shape') else len(_X_train)
152
+ )
153
+ pp_time = time.perf_counter() - pp0_time
154
+ del pp0_time
155
+
156
+ if _verbose >= 5:
157
+ print(f'fold {_f_idx + 1} train predict_proba time = {pp_time: ,.3g} s')
158
+ del pp_time
159
+
160
+ # GET SCORES FOR ALL SCORERS #######################################
161
+
162
+ _train_fold_score_t0 = time.perf_counter()
163
+
164
+
165
+
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+ for s_idx, scorer_key in enumerate(_SCORER_DICT):
174
+
175
+ _y_pred_t0 = time.perf_counter()
176
+ _y_train_pred = (_predict_proba >= _BEST_THRESHOLDS_BY_SCORER[s_idx])
177
+ _ypt = time.perf_counter() - _y_pred_t0
178
+ del _y_pred_t0
179
+ if _verbose == 10:
180
+ print(f"fold {_f_idx+1} train thresholding time = {_ypt: ,.3g} s")
181
+ del _ypt
182
+
183
+ train_scorer_t0 = time.perf_counter()
184
+ _score = _SCORER_DICT[scorer_key](_y_train, _y_train_pred)
185
+ train_scorer_score_time = time.perf_counter() - train_scorer_t0
186
+ del train_scorer_t0
187
+
188
+
189
+ TRAIN_SCORER__SCORE_LAYER[s_idx] = _score
190
+
191
+ if _verbose >= 8:
192
+ print(f"fold {_f_idx+1} '{scorer_key}' train score time = "
193
+ f"{train_scorer_score_time: ,.3g} s")
194
+
195
+ del _score, train_scorer_score_time
196
+
197
+
198
+ tfst = time.perf_counter() - _train_fold_score_t0
199
+ del _train_fold_score_t0
200
+
201
+ # END GET SCORE FOR ALL SCORERS ####################################
202
+
203
+ if _verbose >= 5:
204
+ print(f'End scoring fold {_f_idx + 1} train with different scorers')
205
+
206
+ _ = _f_idx + 1
207
+
208
+ print(f'fold {_} total train thresh & score wall time = {tfst: ,.3g} s')
209
+ _ast = tfst / len(_SCORER_DICT)
210
+ print(f'fold {_} avg train thresh & score wall time = {_ast: ,.3g} s')
211
+ del _ast
212
+
213
+
214
+
215
+
216
+
217
+
218
+ del _X_train, _y_train, _predict_proba, _y_train_pred, tfst
219
+
220
+
221
+ return TRAIN_SCORER__SCORE_LAYER
222
+
223
+
224
+
225
+
226
+
@@ -0,0 +1,5 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
@@ -0,0 +1,88 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing_extensions import Union
10
+ from .._type_aliases import DaskSchedulerType
11
+
12
+ import contextlib
13
+ import numbers
14
+
15
+ from distributed import (
16
+ Client,
17
+ get_client
18
+ )
19
+
20
+
21
+
22
+ def _cond_scheduler(
23
+ _scheduler: Union[DaskSchedulerType, None],
24
+ _n_jobs: Union[numbers.Integral, None]
25
+ ) -> DaskSchedulerType:
26
+ """Set the dask scheduler.
27
+
28
+ The passed scheduler supersedes all other external schedulers. If
29
+ "None" was passed to the scheduler kwarg of GSTCVDask (the default),
30
+ look for an external context manager or global scheduler using
31
+ `get_client`. If one exists, use nullcontext as an internal context
32
+ manager to not interfere with the external scheduler. If "None" was
33
+ passed to the scheduler kwarg of `GSTCVDask` and there is no external
34
+ scheduler, instantiate `distributed.Client`, which defaults to
35
+ `LocalCluster`, with `n_workers`=`n_jobs` and 1 thread per worker.
36
+ If `n_jobs` is None, uses the default `distributed.Client` behavior
37
+ when `n_workers` is set to None.
38
+
39
+ If a scheduler is passed, this module does not perform any validation
40
+ but allows that to be handled by dask at compute time.
41
+
42
+ This module intentionally deviates from the dask_ml API, and
43
+ disallows any shorthand methods for setting up a scheduler (such
44
+ as strings like 'threading' and 'multiprocessing', which are
45
+ ultimately passed to dask.base.get_scheduler.) All of these types
46
+ of configurations should be handled by the user external to the
47
+ `GSTCVDask` module. As much as possible, dask and distributed
48
+ objects are allowed to flow through without any hard-coded input.
49
+
50
+ Parameters
51
+ ----------
52
+ _scheduler : Union[SchedulerType, None]
53
+ _scheduler to be validated and used for compute
54
+
55
+ Returns
56
+ -------
57
+ _scheduler : DaskSchedulerType
58
+ Validated, instantiated scheduler
59
+
60
+ """
61
+
62
+
63
+ if _scheduler is None:
64
+ # if there is no hard scheduler...
65
+ try:
66
+ # ...try to get an existing client...
67
+ get_client()
68
+ # if a client is available (either an external context manager
69
+ # or a default scheduler in an outer scope) let that scheduler
70
+ # take precedence. set _scheduler to nullcontext for empty
71
+ # internal context managers.
72
+ _scheduler = contextlib.nullcontext()
73
+ except ValueError:
74
+ # ...if no external client and no hard scheduler (client),
75
+ # create a new one
76
+ _scheduler = Client(n_workers=_n_jobs, threads_per_worker=1)
77
+
78
+ else:
79
+ # if there is a hard scheduler, that supersedes all.
80
+ pass
81
+
82
+
83
+ return _scheduler
84
+
85
+
86
+
87
+
88
+
@@ -0,0 +1,43 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing import (
10
+ ContextManager,
11
+ Iterable,
12
+ Sequence
13
+ )
14
+ from typing_extensions import (
15
+ TypeAlias,
16
+ Union
17
+ )
18
+
19
+ import numbers
20
+
21
+ import dask
22
+ import distributed
23
+
24
+
25
+
26
+ DaskXType: TypeAlias = Iterable
27
+ DaskYType: TypeAlias = Union[Sequence[numbers.Integral], None]
28
+
29
+ DaskSlicerType: TypeAlias = dask.array.core.Array
30
+
31
+ DaskKFoldType: TypeAlias = tuple[DaskSlicerType, DaskSlicerType]
32
+
33
+ DaskSplitType: TypeAlias = tuple[DaskXType, DaskYType]
34
+
35
+ DaskSchedulerType: TypeAlias = Union[
36
+ distributed.scheduler.Scheduler,
37
+ distributed.client.Client,
38
+ ContextManager # nullcontext
39
+ ]
40
+
41
+
42
+
43
+
@@ -0,0 +1,5 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
@@ -0,0 +1,37 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ def _val_cache_cv(
10
+ _cache_cv: bool
11
+ ) -> None:
12
+ """Validate `cache_cv`.
13
+
14
+ `cache_cv` can only be boolean. Indicates if the train/test folds
15
+ are to be stored once first generated, or if the folds are generated
16
+ from X and y with the KFold indices at each point of use.
17
+
18
+ Parameters
19
+ ----------
20
+ _cache_cv : bool
21
+ To be validated
22
+
23
+ Returns
24
+ -------
25
+ None
26
+
27
+ """
28
+
29
+
30
+ if not isinstance(_cache_cv, bool):
31
+ raise TypeError(f"'cache_cv' must be a bool")
32
+
33
+
34
+
35
+
36
+
37
+
@@ -0,0 +1,84 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from pybear.model_selection.GSTCV._type_aliases import ClassifierProtocol
10
+
11
+ import sys
12
+ import warnings
13
+
14
+ from sklearn.pipeline import Pipeline
15
+
16
+
17
+
18
+ def _val_dask_estimator(
19
+ _estimator: ClassifierProtocol
20
+ ) -> None:
21
+ """Warn if the estimator is not a dask classifier, either from dask
22
+ itself, or from XGBoost or LightGBM.
23
+
24
+ The `GSTCVDask` module is expected to most likely encounter dask_ml,
25
+ xgboost, and lightgbm dask estimators. The estimator must be passed
26
+ as an instance, not the class itself.
27
+
28
+ Parameters
29
+ ----------
30
+ _estimator : ClassifierProtocol
31
+ the estimator to be validated
32
+
33
+ Returns
34
+ -------
35
+ None
36
+
37
+ """
38
+
39
+
40
+ # validate estimator ** * ** * ** * ** * ** * ** * ** * ** * ** * **
41
+
42
+ def get_inner_most_estimator(__estimator):
43
+
44
+ try:
45
+ if isinstance(__estimator, Pipeline):
46
+ return get_inner_most_estimator(__estimator.steps[-1][-1])
47
+ else:
48
+ return get_inner_most_estimator(__estimator.estimator)
49
+ except:
50
+ return __estimator
51
+
52
+
53
+ __estimator = get_inner_most_estimator(_estimator)
54
+
55
+ try:
56
+ _module = sys.modules[__estimator.__class__.__module__].__file__
57
+ except:
58
+ raise AttributeError(f"'{__estimator.__class__.__name__}' is not "
59
+ f"a valid classifier")
60
+
61
+ # 24_08_04 change raise to warn
62
+ # to allow XGBClassifier, reference errors associated with
63
+ # DaskXGBClassifier and dask GridSearch CV
64
+ # 25_06_28 no longer checking for non-dask estimator
65
+ # __ = str(_module).lower()
66
+ # if 'dask_ml' not in __:
67
+ # warnings.warn(f"'{__estimator.__class__.__name__}' does not "
68
+ # f"appear to be a dask classifier.")
69
+ # if 'dask' not in __ and 'conftest' not in __: # allow pytest with
70
+ # mock clf
71
+ # raise TypeError(f"'{__estimator.__class__.__name__}' is not a
72
+ # f"dask classifier. GSTCVDask can only accept dask classifiers. "
73
+ # f"\nTo use non-dask classifiers, use the GSTCV package.")
74
+
75
+ del get_inner_most_estimator, __estimator, _module
76
+
77
+ # END validate estimator ** * ** * ** * ** * ** * ** * ** * ** * **
78
+
79
+
80
+
81
+
82
+
83
+
84
+
@@ -0,0 +1,38 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ def _val_iid(
10
+ _iid: bool
11
+ ) -> None:
12
+ """Validate `iid`.
13
+
14
+ `iid` can only be boolean. Indicates whether the data is believed to
15
+ have random distribution of examples (True) or if the data is
16
+ organized non-randomly in some way (False).
17
+
18
+ Parameters
19
+ ----------
20
+ _iid : bool
21
+ To be validated.
22
+
23
+ Returns
24
+ -------
25
+ None
26
+
27
+ """
28
+
29
+
30
+ if not isinstance(_iid, bool):
31
+ raise TypeError(f"'iid' must be a bool")
32
+
33
+
34
+
35
+
36
+
37
+
38
+
@@ -0,0 +1,54 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from pybear.model_selection.GSTCV._type_aliases import ClassifierProtocol
10
+
11
+ from ....GSTCV._GSTCVDask._validation._dask_estimator import _val_dask_estimator
12
+ from ....GSTCV._GSTCVDask._validation._cache_cv import _val_cache_cv
13
+ from ....GSTCV._GSTCVDask._validation._iid import _val_iid
14
+
15
+
16
+
17
+ def _validation(
18
+ _estimator: ClassifierProtocol,
19
+ _iid: bool,
20
+ _cache_cv: bool
21
+ ) -> None:
22
+ """Centralized hub for `GSTCVDask` parameter validation.
23
+
24
+ See the submodules for more information.
25
+
26
+ Parameters
27
+ ----------
28
+ _estimator : ClassifierProtocol
29
+ The estimator passed to `GSTCVDask`.
30
+ _iid : bool
31
+ Whether the data is randomly distributed along the example axis
32
+ or is organized in some non-random way.
33
+ _cache_cv : bool
34
+ Indicates if the train/test folds of the data are to be stored
35
+ when first generated, or if the folds are generated from X, y
36
+ and the KFold indices at each point of need. Caching all the
37
+ folds for each split of the data is memory-intensive.
38
+
39
+ Returns
40
+ -------
41
+ None
42
+
43
+ """
44
+
45
+
46
+ _val_dask_estimator(_estimator)
47
+
48
+ _val_iid(_iid)
49
+
50
+ _val_cache_cv(_cache_cv)
51
+
52
+
53
+
54
+
@@ -0,0 +1,91 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing import Iterable
10
+
11
+ import numbers
12
+
13
+ import numpy as np
14
+ from dask import compute
15
+ import dask.array as da
16
+ import dask.dataframe as ddf
17
+
18
+
19
+
20
+ def _val_y(
21
+ _y: Iterable[numbers.Integral] # not DaskYType... see the notes.
22
+ ) -> None:
23
+ """Validate `_y`.
24
+
25
+ `_y` must be single label and binary in [0, 1].
26
+
27
+ The validation is considerably looser than what would be for
28
+ DaskYType. This allows *any* 1D container holding 0's and 1's.
29
+ Let the estimator raise an error if there is a problem with the
30
+ container.
31
+
32
+ Parameters
33
+ ----------
34
+ _y : vector-like of shape (n_samples,) or (n_samples, 1)
35
+ The target for the data.
36
+
37
+ Returns
38
+ -------
39
+ None
40
+
41
+ """
42
+
43
+
44
+ # y ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
45
+
46
+ if hasattr(_y, 'shape'):
47
+ if hasattr(_y, 'compute'):
48
+ y_shape = compute(*_y.shape)
49
+ else:
50
+ y_shape = _y.shape
51
+ else:
52
+ try:
53
+ y_shape = np.array(_y).shape
54
+ if isinstance(_y, set):
55
+ y_shape = np.array(list(_y)).shape
56
+ except:
57
+ raise TypeError(f"'y' must have a 'shape' attribute.")
58
+
59
+
60
+ _err_msg = (
61
+ f"GSTCVDask can only perform thresholding on vector-like binary "
62
+ f"targets with values in [0,1]. \nPass 'y' as a vector of 0's and 1's."
63
+ )
64
+
65
+ if len(y_shape) == 1:
66
+ pass
67
+ elif len(y_shape) == 2:
68
+ if y_shape[1] != 1:
69
+ raise ValueError(_err_msg)
70
+ else:
71
+ raise ValueError(_err_msg)
72
+
73
+
74
+ if isinstance(_y, da.core.Array):
75
+ _unique = set(da.unique(_y).compute())
76
+ elif isinstance(_y, (ddf.DataFrame, ddf.Series)):
77
+ _unique = set(da.unique(_y.to_dask_array(lengths=True)).compute())
78
+ elif hasattr(_y, 'shape'):
79
+ _unique = set(np.unique(_y))
80
+ else:
81
+ _unique = set(np.unique(list(_y)))
82
+
83
+
84
+ if not _unique.issubset({0, 1}):
85
+ raise ValueError(_err_msg)
86
+
87
+ del _unique
88
+
89
+
90
+
91
+
@@ -0,0 +1,5 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #