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,5 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
@@ -0,0 +1,5 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
@@ -0,0 +1,106 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing_extensions import Any
10
+ from .._type_aliases import DaskKFoldType
11
+
12
+ from dask import compute
13
+
14
+ from ._fold_splitter import _fold_splitter as _dask_fold_splitter
15
+ from pybear.model_selection.GSTCV._GSTCV._fit._fold_splitter import \
16
+ _fold_splitter as _sk_fold_splitter
17
+
18
+
19
+
20
+ def _estimator_fit_params_helper(
21
+ _data_len: int,
22
+ _fit_params: dict[str, Any],
23
+ _KFOLD: DaskKFoldType
24
+ ) -> dict[int, dict[str, Any]]:
25
+ """This module customizes the estimator's fit params for each pass
26
+ of cv, to be passed at fit time for the respective fold.
27
+
28
+ This is being done via a dictionary keyed by fold index, whose values
29
+ are dictionaries holding the respective fit params for that fold. In
30
+ particular, this is designed to perform splitting on any fit param
31
+ whose length matches the number of examples in the data, so that the
32
+ contents of that fit param are matched correctly to the train fold
33
+ of data concurrently being passed to fit. Other params that are not
34
+ split are simply replicated into each dictionary inside the helper.
35
+
36
+ Parameters
37
+ ----------
38
+ _data_len : int
39
+ The number of examples in the full data set.
40
+ _fit_params : dict[str, Any]
41
+ All the fit params passed to GSTCVDask fit for the estimator.
42
+ _KFOLD : DaskKFoldType
43
+ The KFold indices that were used to create the train / test
44
+ splits of data.
45
+
46
+ Returns
47
+ -------
48
+ _fit_params_helper : dict[int, dict[str, Any]]
49
+ A dictionary of customized fit params for each pass of cv.
50
+
51
+ """
52
+
53
+ # validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
54
+ try:
55
+ float(_data_len)
56
+ if isinstance(_data_len, bool):
57
+ raise Exception
58
+ if not int(_data_len) == _data_len:
59
+ raise Exception
60
+ _data_len = int(_data_len)
61
+ if not _data_len > 0:
62
+ raise Exception
63
+ except:
64
+ raise TypeError(f"'data_len' must be an integer greater than 0")
65
+
66
+ assert isinstance(_fit_params, dict)
67
+ assert all(map(isinstance, list(_fit_params), (str for i in _fit_params)))
68
+
69
+ assert isinstance(_KFOLD, list), f"{type(_KFOLD)=}"
70
+ assert all(map(isinstance, _KFOLD, (tuple for _ in _KFOLD)))
71
+ # END validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
72
+
73
+
74
+ _fit_params_helper = {}
75
+
76
+ for f_idx, (train_idxs, test_idxs) in enumerate(_KFOLD):
77
+
78
+ _fit_params_helper[f_idx] = {}
79
+
80
+ for _fit_param_key, _fit_param_value in _fit_params.items():
81
+
82
+ try:
83
+ iter(_fit_param_value)
84
+ if isinstance(_fit_param_value, (dict, str)):
85
+ raise Exception
86
+ if [*compute(len(_fit_param_value))][0] != _data_len:
87
+ raise Exception
88
+ # remember we only care about the train fold.
89
+ # the fit_param may not be a dask object. try with
90
+ # dask _fold_splitter, if that fails, try sk _fold_splitter
91
+ try:
92
+ _fit_params_helper[f_idx][_fit_param_key] = \
93
+ _dask_fold_splitter(train_idxs, test_idxs, _fit_param_value)[0][0]
94
+ except:
95
+ _fit_params_helper[f_idx][_fit_param_key] = \
96
+ _sk_fold_splitter(train_idxs, test_idxs, _fit_param_value)[0][0]
97
+ except:
98
+ _fit_params_helper[f_idx][_fit_param_key] = _fit_param_value
99
+
100
+
101
+ return _fit_params_helper
102
+
103
+
104
+
105
+
106
+
@@ -0,0 +1,86 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing_extensions import Union
10
+
11
+ from .._type_aliases import (
12
+ DaskSlicerType,
13
+ DaskSplitType,
14
+ DaskXType,
15
+ DaskYType
16
+ )
17
+
18
+ import dask.array as da
19
+ import dask.dataframe as ddf
20
+
21
+
22
+
23
+ def _fold_splitter(
24
+ train_idxs: DaskSlicerType,
25
+ test_idxs: DaskSlicerType,
26
+ *data_objects: Union[DaskXType, DaskYType]
27
+ ) -> tuple[DaskSplitType, ...]:
28
+ """Split given data objects into train / test pairs using the given
29
+ train and test indices.
30
+
31
+ The train and test indices independently slice the given data objects;
32
+ the entire data object need not be consumed in a train / test split
33
+ and the splits can also possibly share indices. Standard indexing
34
+ rules apply. Returns a tuple whose length is equal to the number of
35
+ data objects passed, holding tuples of the train / test splits for
36
+ the respective data objects. `train_idxs` and `test_idxs` must be 1D
37
+ vectors of indices, not booleans.
38
+
39
+ Parameters
40
+ ----------
41
+ train_idxs : DaskSlicerType
42
+ 1D vector of row indices used to slice train sets out of every
43
+ given data object.
44
+ test_idxs : DaskSlicerType
45
+ 1D vector of row indices used to slice test sets out of every
46
+ given data object.
47
+ *data_objects : Union[DaskXType, DaskYType]
48
+ The data objects to slice. Need not be of equal size, and need
49
+ not be completely consumed in the train / test splits. However,
50
+ standard indexing rules apply when slicing by `train_idxs` and
51
+ `test_idxs`.
52
+
53
+ Returns
54
+ -------
55
+ SPLITS : tuple[DaskSplitType, ...]
56
+ Return the train / test splits for the given data objects in the
57
+ order passed in a tuple of tuples, each inner tuple containing a
58
+ train/test pair.
59
+
60
+ """
61
+
62
+
63
+ SPLITS = []
64
+ for _data in data_objects:
65
+
66
+ # the compute()s need to be here for ddf slicing to work
67
+ if isinstance(_data, da.core.Array):
68
+ _data_train = _data[train_idxs]
69
+ _data_test = _data[test_idxs]
70
+ elif isinstance(_data, ddf.DataFrame):
71
+ _data_train = _data.loc[train_idxs.compute(), :]
72
+ _data_test = _data.loc[test_idxs.compute(), :]
73
+ elif isinstance(_data, ddf.Series):
74
+ _data_train = _data[train_idxs.compute()]
75
+ _data_test = _data[test_idxs.compute()]
76
+ else:
77
+ raise TypeError(f"disallowed container '{type(_data)}'")
78
+
79
+ SPLITS.append(tuple((_data_train, _data_test)))
80
+
81
+
82
+ return tuple(SPLITS)
83
+
84
+
85
+
86
+
@@ -0,0 +1,117 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing import (
10
+ Iterator,
11
+ Optional
12
+ )
13
+ from .._type_aliases import (
14
+ DaskXType,
15
+ DaskYType,
16
+ DaskKFoldType
17
+ )
18
+
19
+ import time
20
+
21
+ from dask_ml.model_selection import KFold as dask_KFold
22
+
23
+
24
+
25
+ def _get_kfold(
26
+ _X: DaskXType,
27
+ _n_splits: int,
28
+ _iid: bool,
29
+ _verbose: int,
30
+ _y: Optional[DaskYType] = None
31
+ ) -> Iterator[DaskKFoldType]:
32
+ """Use dask_ml KFold to get train / test splits when cv is passed as
33
+ an integer.
34
+
35
+ KFold uses the number of rows in `_X` and `_n_splits` to determine
36
+ the indices in each train / test split. 'y' is optional in dask_ml
37
+ KFold. If passed, the number of rows in `_X` and `_y` must be equal.
38
+
39
+ *** IMPORTANT!!!
40
+ This function can be called multiple times within a single param grid
41
+ permutation, first to fit, again to get test score, then again if
42
+ `return_train_score`. Therefore, it must return the same indices for
43
+ each call. The only things that should cause indices to be different
44
+ are `_n_splits` and the number of rows in `_X`. Since this is dask
45
+ KFold, there is the wildcard of the `_iid` setting. If `_iid` is
46
+ False -- meaning the data is known to have some non-random grouping
47
+ along axis 0 -- via the 'shuffle' argument KFold will generate
48
+ indices that sample across chunks to randomize the data in the
49
+ splits. In that case, fix the 'random_state' parameter to make
50
+ selection repeatable. If `_iid` is True, 'shuffle' is False,
51
+ 'random_state' can be None, and the splits should be repeatable.
52
+
53
+ Parameters
54
+ ----------
55
+ _X : DaskXType
56
+ The data to be split.
57
+ _n_splits : int
58
+ The number of splits to produce; the number of split pairs
59
+ yielded by the returned generator object.
60
+ _iid : bool
61
+ True, the examples in X are distributed randomly; False,
62
+ there is some kind of non-random ordering of the examples in X.
63
+ _verbose : int
64
+ A number from 0 to 10 indicating the amount of information to
65
+ display to screen during the grid search trials. 0 means no
66
+ output, 10 means full output.
67
+ _y : Optional[DaskYType]
68
+ The target the data is being fit against, to be split in the
69
+ same way as the data.
70
+
71
+ Returns
72
+ -------
73
+ KFOLD : Iterator[DaskKFoldType]
74
+ A generator object yielding pairs of train test indices as
75
+ da.core.Array[int].
76
+
77
+ """
78
+
79
+ # validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
80
+ # 25_04_29 NOT VALIDATING X & y HERE ANYMORE. LET KFold RAISE.
81
+ assert isinstance(_n_splits, int)
82
+ assert _n_splits > 1
83
+ assert isinstance(_iid, bool)
84
+ try:
85
+ float(_verbose)
86
+ except:
87
+ raise AssertionError(f"'_verbose' must be numeric")
88
+ assert _verbose >= 0
89
+ # END validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
90
+
91
+
92
+ split_t0 = time.perf_counter()
93
+ # KFold keeps the same chunks ax X
94
+ # as of 25_04_29 dask_KFold only accepts da array for X
95
+ KFOLD = dask_KFold(
96
+ n_splits=_n_splits,
97
+ shuffle=not _iid,
98
+ random_state=7 if not _iid else None,
99
+ # shuffle is on if not iid. must use random_state so that later
100
+ # calls for train score get same splits.
101
+ ).split(_X, _y)
102
+
103
+
104
+ if _verbose >= 5:
105
+ print(f'split time = {time.perf_counter() - split_t0: ,.3g} s')
106
+
107
+ del split_t0
108
+
109
+ return KFOLD
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
@@ -0,0 +1,127 @@
1
+ # Author:
2
+ # Bill Sousa
3
+ #
4
+ # License: BSD 3 clause
5
+ #
6
+
7
+
8
+
9
+ from typing import Literal
10
+ from typing_extensions import (
11
+ Any,
12
+ Union
13
+ )
14
+ from .._type_aliases import (
15
+ DaskXType,
16
+ DaskYType
17
+ )
18
+
19
+ import numbers
20
+ import time
21
+ import warnings
22
+
23
+ from pybear.model_selection.GSTCV._type_aliases import ClassifierProtocol
24
+
25
+
26
+
27
+ def _parallelized_fit(
28
+ _f_idx: int,
29
+ _X_train: DaskXType,
30
+ _y_train: DaskYType,
31
+ _estimator: ClassifierProtocol,
32
+ _grid: dict[str, Any],
33
+ _error_score: Union[numbers.Real, Literal['raise']],
34
+ **_fit_params
35
+ ) -> tuple[ClassifierProtocol, float, bool]:
36
+ """Estimator fit method designed for dask parallelism.
37
+
38
+ Special exception handling on fit.
39
+
40
+ Parameters
41
+ ----------
42
+ _f_idx : int
43
+ The zero-based fold index of the train partition used in this
44
+ fit; parallelism occurs over the different folds.
45
+ _X_train : DaskXType
46
+ A train partition of the data being fit.
47
+ _y_train : DaskYType
48
+ The corresponding train partition of the target for the X train
49
+ partition.
50
+ _estimator : ClassifierProtocol
51
+ Any scikit-style classifier, having `fit`, `predict_proba`,
52
+ `get_params`, and `set_params` methods (the `score` method is
53
+ not necessary, as GSTCVDask never calls it.) This includes,
54
+ but is not limited to, dask_ml, XGBoost dask, and LGBM dask
55
+ classifiers.
56
+ _grid : dict[str, Any]
57
+ The hyperparameter values to be used during this fit. One
58
+ permutation of all the grid search permutations.
59
+ _error_score : Union[numbers.Real, Literal['raise']]
60
+ If a training fold excepts during fitting, the exception can be
61
+ allowed to raise by passing the 'raise' literal. Otherwise,
62
+ passing a number-like will cause the exception to be handled,
63
+ allowing the grid search to proceed, and the given number carries
64
+ through scoring tabulations in place of the missing score(s).
65
+ **_fit_params : dict[str, Any]
66
+ Dictionary of fit_param:value pairs to be passed to the
67
+ estimator's `fit` method. `fit_params` must have been processed
68
+ by :func:`_estimator_fit_params_helper` so that any `fit_param`
69
+ that has length == (n_samples in X and y) is split in the same
70
+ way as X and y.
71
+
72
+ Returns
73
+ -------
74
+ __ : tuple[_estimator, _fit_time, _fit_excepted]
75
+ _estimator : EstimatorProtocol
76
+ The fit estimator
77
+ _fit_time : float
78
+ The seconds elapsed when performing the fit
79
+ _fit_excepted : bool
80
+ True if the fit excepted and '_error_score' was not 'raise';
81
+ False if the fit ran successfully.
82
+
83
+ """
84
+
85
+
86
+ # validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
87
+
88
+ assert isinstance(_f_idx, int)
89
+ assert isinstance(_grid, dict)
90
+ assert isinstance(_error_score, (str, numbers.Real))
91
+
92
+ # END validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * *
93
+
94
+
95
+ _fit_excepted = False
96
+
97
+ _X_train = _X_train.persist()
98
+ _y_train = _y_train.persist()
99
+
100
+ t0_fit = time.perf_counter()
101
+
102
+ try:
103
+ _estimator.fit(_X_train, _y_train, **_fit_params)
104
+ except BrokenPipeError:
105
+ raise BrokenPipeError # FOR PYTEST ONLY
106
+ except Exception as f:
107
+ if _error_score == 'raise':
108
+ raise ValueError(
109
+ f"estimator excepted during fitting on {_grid}, cv fold "
110
+ f"index {_f_idx} --- {f}"
111
+ )
112
+ else:
113
+ _fit_excepted = True
114
+ warnings.warn(
115
+ f'\033[93mfit excepted on {_grid}, cv fold index {_f_idx}\033[0m'
116
+ )
117
+
118
+ _fit_time = time.perf_counter() - t0_fit
119
+
120
+ del t0_fit
121
+
122
+ return _estimator, _fit_time, _fit_excepted
123
+
124
+
125
+
126
+
127
+
@@ -0,0 +1,228 @@
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
+ ThresholdsWIPType,
14
+ MaskedHolderType
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 \
27
+ import _val_predict_proba
28
+
29
+
30
+
31
+ def _parallelized_scorer(
32
+ _X_test: DaskXType,
33
+ _y_test: DaskYType,
34
+ _FIT_OUTPUT_TUPLE: tuple[ClassifierProtocol, float, bool],
35
+ _f_idx: int,
36
+ _SCORER_DICT: ScorerWIPType,
37
+ _THRESHOLDS: ThresholdsWIPType,
38
+ _error_score: Union[numbers.Real, None],
39
+ _verbose: int
40
+ ) -> tuple[MaskedHolderType, MaskedHolderType]:
41
+
42
+ # dont adjust the spacing, is congruent with train scorer
43
+
44
+ """Using the estimators fit on each train fold, use `predict_proba`
45
+ and _X_tests to generate _y_preds and score against the corresponding
46
+ _y_tests using all of the scorers and thresholds.
47
+
48
+ Builds one fold layer of the TEST_FOLD_x_THRESHOLD_x_SCORER__SCORE
49
+ and TEST_FOLD_x_THRESHOLD_x_SCORER__SCORE_TIME cubes.
50
+
51
+ Parameters
52
+ ----------
53
+ _X_test : DaskXType
54
+ A test partition of the data, matched up with the estimator that
55
+ was trained on the complementary train set.
56
+ _y_test : DaskYType
57
+ The corresponding test partition of the target for the `_X_test`
58
+ partition.
59
+ _FIT_OUTPUT_TUPLE : tuple[ClassifierProtocol, float, bool]
60
+ A tuple holding the fitted estimator, the fit time (not needed
61
+ here), and the fit_excepted boolean (needed here.)
62
+ _f_idx : int
63
+ The zero-based split index of the test partition used here;
64
+ parallelism occurs over the different splits.
65
+ _SCORER_DICT : ScorerWIPType
66
+ A dictionary with scorer name as keys and the scorer callables
67
+ as values. The scorer callables are scoring metrics (or similar),
68
+ not make_scorer.
69
+ _THRESHOLDS : ThresholdsWIPType
70
+ For the current search permutation, there was a mother param
71
+ grid that contained a 'thresholds' parameter, that was separated
72
+ from the mother before building cv_results. This is the vector
73
+ of thresholds from the mother that also mothered this search
74
+ permutation.
75
+ _error_score : Union[numbers.Real, Literal['raise']]
76
+ If the training fold complementing this test fold excepted during
77
+ fitting and `error_score` was set to the 'raise' literal, this
78
+ module cannot be reached. Otherwise, a number or number-like was
79
+ passed to `error_score`. If 'fit_excepted' is True, this module
80
+ puts the `error_score` value in every position of the
81
+ TEST_THRESHOLD_x_SCORER__SCORE_LAYER array. If `error_score` is
82
+ set to np.nan, that layer is also masked. Every value in
83
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER is set to np.nan and
84
+ masked.
85
+ _verbose : int
86
+ A number from 0 to 10 that indicates the amount of information
87
+ to display to the screen during the grid search process. 0 means
88
+ no output, 10 means maximum output.
89
+
90
+ Returns
91
+ -------
92
+ __ : tuple[np.ma.MaskedArray, np.ma.MaskedArray]
93
+ TEST_THRESHOLD_x_SCORER__SCORE_LAYER : MaskedHolderType
94
+ Masked array of shape (n_thresholds, n_scorers) holding the
95
+ scores for each scorer on each threshold for one fold of
96
+ test data.
97
+
98
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER : MaskedHolderType
99
+ Masked array of shape (n_thresholds, n_scorers) holding the
100
+ times to score each scorer on each threshold for one fold of
101
+ test data.
102
+
103
+ """
104
+
105
+ # validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** *
106
+ assert isinstance(_FIT_OUTPUT_TUPLE, tuple)
107
+ assert isinstance(_f_idx, int)
108
+ assert isinstance(_SCORER_DICT, dict)
109
+ assert all(map(callable, _SCORER_DICT.values()))
110
+ assert isinstance(_THRESHOLDS, list)
111
+ assert isinstance(_verbose, (int, float))
112
+ # END validation ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * *
113
+
114
+ if _verbose >= 5:
115
+ print(f"Start scoring fold {_f_idx + 1} test with different thresholds "
116
+ f"and scorers")
117
+
118
+ _estimator_, _fit_time, _fit_excepted = _FIT_OUTPUT_TUPLE
119
+
120
+ TEST_THRESHOLD_x_SCORER__SCORE_LAYER: MaskedHolderType = \
121
+ np.ma.zeros((len(_THRESHOLDS), len(_SCORER_DICT)), dtype=np.float64)
122
+ TEST_THRESHOLD_x_SCORER__SCORE_LAYER.mask = True
123
+
124
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER: MaskedHolderType = \
125
+ np.ma.zeros((len(_THRESHOLDS), len(_SCORER_DICT)), dtype=np.float64)
126
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER.mask = True
127
+
128
+ # IF A FOLD EXCEPTED DURING FIT, ALL THE THRESHOLDS AND SCORERS
129
+ # IN THAT FOLD LAYER GET SET TO error_score.
130
+ # SCORE TIME CANT BE TAKEN SINCE SCORING WONT BE DONE SO ALSO MASK THAT
131
+ if _fit_excepted:
132
+ TEST_THRESHOLD_x_SCORER__SCORE_LAYER[:, :] = _error_score
133
+ if _error_score is np.nan:
134
+ TEST_THRESHOLD_x_SCORER__SCORE_LAYER[:, :] = np.ma.masked
135
+
136
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER[:, :] = np.nan
137
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER[:, :] = np.ma.masked
138
+
139
+ if _verbose >= 5:
140
+ print(f'fold {_f_idx + 1} excepted during fit, unable to score')
141
+
142
+ return (TEST_THRESHOLD_x_SCORER__SCORE_LAYER,
143
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER)
144
+
145
+ # v v v only accessible if fit() did not except v v v
146
+
147
+ _X_test = _X_test.persist()
148
+ _y_test = _y_test.persist()
149
+
150
+ pp0_time = time.perf_counter()
151
+ _predict_proba = _estimator_.predict_proba(_X_test)[:, -1].ravel()
152
+ _val_predict_proba(
153
+ _predict_proba,
154
+ _X_test.shape[0] if hasattr(_X_test, 'shape') else len(_X_test)
155
+ )
156
+ pp_time = time.perf_counter() - pp0_time
157
+ del pp0_time
158
+
159
+ if _verbose >= 5:
160
+ print(f'fold {_f_idx + 1} test predict_proba time = {pp_time: ,.3g} s')
161
+ del pp_time
162
+
163
+ # GET SCORES FOR ALL SCORERS & THRESHOLDS ##########################
164
+
165
+ _test_fold_score_t0 = time.perf_counter()
166
+ for thresh_idx, _threshold in enumerate(_THRESHOLDS):
167
+
168
+ _y_pred_t0 = time.perf_counter()
169
+ _y_test_pred = (_predict_proba >= _threshold).astype(np.uint8)
170
+ _ypt = time.perf_counter() - _y_pred_t0
171
+ del _y_pred_t0
172
+ if _verbose == 10:
173
+ print(f"fold {_f_idx+1} test thresholding time = {_ypt: ,.3g} s")
174
+ del _ypt
175
+
176
+ for s_idx, scorer_key in enumerate(_SCORER_DICT):
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
185
+
186
+ test_scorer_t0 = time.perf_counter()
187
+ _score = _SCORER_DICT[scorer_key](_y_test, _y_test_pred)
188
+ test_scorer_score_time = time.perf_counter() - test_scorer_t0
189
+ del test_scorer_t0
190
+ TEST_THRESHOLD_x_SCORER__SCORE_LAYER[thresh_idx, s_idx] = _score
191
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER[thresh_idx, s_idx] = \
192
+ test_scorer_score_time
193
+
194
+ if _verbose >= 8:
195
+ print(f"fold {_f_idx + 1} '{scorer_key}' test score time = "
196
+ f"{test_scorer_score_time: ,.3g} s")
197
+
198
+ del _score, test_scorer_score_time
199
+
200
+
201
+ tfst = time.perf_counter() - _test_fold_score_t0
202
+ del _test_fold_score_t0
203
+
204
+ # END GET SCORE FOR ALL SCORERS & THRESHOLDS #######################
205
+
206
+ if _verbose >= 5:
207
+ print(f'End scoring fold {_f_idx + 1} test with different thresholds and '
208
+ f'scorers')
209
+ _ = _f_idx + 1
210
+
211
+ print(f'fold {_} total test thresh & score wall time = {tfst: ,.3g} s')
212
+ _ast = tfst / len(_SCORER_DICT) / len(_THRESHOLDS)
213
+ print(f'fold {_} avg test thresh & score wall time = {_ast: ,.3g} s')
214
+ del _ast
215
+
216
+ __ = TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER
217
+ print(f'fold {_} total test actual scoring time = {__.sum(): ,.3g} s')
218
+ print(f'fold {_} avg test actual scoring time = {__.mean(): ,.3g} s')
219
+ del _, __
220
+
221
+ del _X_test, _y_test, _predict_proba, _y_test_pred, tfst
222
+
223
+
224
+ return (TEST_THRESHOLD_x_SCORER__SCORE_LAYER,
225
+ TEST_THRESHOLD_x_SCORER__SCORE_TIME_LAYER)
226
+
227
+
228
+