ordboost 0.2.1__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.
- ordboost/__init__.py +46 -0
- ordboost/distributions.py +398 -0
- ordboost/mappers.py +1087 -0
- ordboost/metrics.py +256 -0
- ordboost/models.py +732 -0
- ordboost-0.2.1.dist-info/METADATA +173 -0
- ordboost-0.2.1.dist-info/RECORD +10 -0
- ordboost-0.2.1.dist-info/WHEEL +5 -0
- ordboost-0.2.1.dist-info/licenses/LICENSE +201 -0
- ordboost-0.2.1.dist-info/top_level.txt +1 -0
ordboost/__init__.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""OrdBoost: Non-parametric discrete ordinal-binning gradient boosting and continuous regression."""
|
|
2
|
+
|
|
3
|
+
from ordboost.distributions import (
|
|
4
|
+
ContinuousPredictiveDistribution,
|
|
5
|
+
DiscretePredictiveDistribution,
|
|
6
|
+
PredictiveDistribution,
|
|
7
|
+
)
|
|
8
|
+
from ordboost.mappers import (
|
|
9
|
+
BaseBinMapper,
|
|
10
|
+
ContinuousBinMapper,
|
|
11
|
+
EmpiricalMeanBinMapper,
|
|
12
|
+
EmpiricalMedianBinMapper,
|
|
13
|
+
QuantileBinMapper,
|
|
14
|
+
UniformBinMapper,
|
|
15
|
+
)
|
|
16
|
+
from ordboost.metrics import (
|
|
17
|
+
crps_score,
|
|
18
|
+
interval_coverage_rate,
|
|
19
|
+
pinball_loss,
|
|
20
|
+
winkler_score,
|
|
21
|
+
)
|
|
22
|
+
from ordboost.models import OrdBoostClassifier, OrdBoostRegressor
|
|
23
|
+
|
|
24
|
+
__version__ = "0.2.1"
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Models
|
|
28
|
+
"OrdBoostClassifier",
|
|
29
|
+
"OrdBoostRegressor",
|
|
30
|
+
# Mappers
|
|
31
|
+
"BaseBinMapper",
|
|
32
|
+
"ContinuousBinMapper",
|
|
33
|
+
"EmpiricalMeanBinMapper",
|
|
34
|
+
"EmpiricalMedianBinMapper",
|
|
35
|
+
"QuantileBinMapper",
|
|
36
|
+
"UniformBinMapper",
|
|
37
|
+
# Distributions
|
|
38
|
+
"PredictiveDistribution",
|
|
39
|
+
"DiscretePredictiveDistribution",
|
|
40
|
+
"ContinuousPredictiveDistribution",
|
|
41
|
+
# Metrics
|
|
42
|
+
"crps_score",
|
|
43
|
+
"interval_coverage_rate",
|
|
44
|
+
"pinball_loss",
|
|
45
|
+
"winkler_score",
|
|
46
|
+
]
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""Predictive probability distributions for discrete ordinal outcomes."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from numpy.typing import ArrayLike
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PredictiveDistribution(ABC):
|
|
11
|
+
"""Abstract base class for all predictive probability distributions.
|
|
12
|
+
|
|
13
|
+
Defines a unified interface for extracting point estimates, quantiles,
|
|
14
|
+
prediction intervals, and cumulative probabilities regardless of whether
|
|
15
|
+
the distribution is discrete or continuous.
|
|
16
|
+
|
|
17
|
+
Methods
|
|
18
|
+
-------
|
|
19
|
+
mean()
|
|
20
|
+
Calculate expected values across samples.
|
|
21
|
+
median()
|
|
22
|
+
Calculate 50th percentile predictions across samples.
|
|
23
|
+
ppf(q)
|
|
24
|
+
Calculate percent point function (inverse CDF / quantiles).
|
|
25
|
+
interval(alpha=0.10)
|
|
26
|
+
Calculate central prediction bounds for a given significance level.
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def mean(self) -> np.ndarray:
|
|
32
|
+
"""Calculate the expected value for each sample."""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def ppf(self, q: Union[float, np.ndarray]) -> np.ndarray:
|
|
37
|
+
"""Percent Point Function (inverse CDF / quantile calculation)."""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def median(self) -> np.ndarray:
|
|
41
|
+
"""Calculate the 50th percentile (median) prediction for each sample.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
np.ndarray
|
|
46
|
+
1D array of shape (n_samples,) containing median predictions.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
return self.ppf(0.5)
|
|
50
|
+
|
|
51
|
+
def interval(self, alpha: float = 0.10) -> tuple[np.ndarray, np.ndarray]:
|
|
52
|
+
"""Calculate central prediction bounds for a given significance level.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
alpha : float, default=0.10
|
|
57
|
+
Significance level (e.g., alpha=0.10 yields a 90% central interval).
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
tuple[np.ndarray, np.ndarray]
|
|
62
|
+
Tuple of (lower_bounds, upper_bounds), each as a 1D array.
|
|
63
|
+
|
|
64
|
+
Raises
|
|
65
|
+
------
|
|
66
|
+
ValueError
|
|
67
|
+
If `alpha` is not strictly within (0.0, 1.0).
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
if not 0.0 < alpha < 1.0:
|
|
71
|
+
raise ValueError("Significance level 'alpha' must be between 0.0 and 1.0.")
|
|
72
|
+
|
|
73
|
+
lower_q = alpha / 2.0
|
|
74
|
+
upper_q = 1.0 - (alpha / 2.0)
|
|
75
|
+
bounds = self.ppf(np.array([lower_q, upper_q]))
|
|
76
|
+
return bounds[:, 0], bounds[:, 1]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class DiscretePredictiveDistribution(PredictiveDistribution):
|
|
80
|
+
"""Encapsulates a discrete Probability Mass Function (PMF) matrix.
|
|
81
|
+
|
|
82
|
+
Provides vectorized utilities for computing cumulative distribution
|
|
83
|
+
functions (CDF), percent point functions (quantiles/PPF), expected values,
|
|
84
|
+
medians, and prediction intervals across samples.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
pmf : np.ndarray
|
|
89
|
+
A 2D float array of shape (n_samples, n_classes) representing the
|
|
90
|
+
predicted probability for each discrete target class. Values along
|
|
91
|
+
each row must sum to 1.0.
|
|
92
|
+
classes : np.ndarray
|
|
93
|
+
A 1D array of shape (n_classes,) representing the physical ordinal
|
|
94
|
+
class labels in strictly ascending order.
|
|
95
|
+
|
|
96
|
+
Attributes
|
|
97
|
+
----------
|
|
98
|
+
pmf : np.ndarray
|
|
99
|
+
A 2D float array of shape (n_samples, n_classes) containing predicted
|
|
100
|
+
class probabilities.
|
|
101
|
+
classes : np.ndarray
|
|
102
|
+
A 1D array of shape (n_classes,) containing the ordinal class labels.
|
|
103
|
+
cdf : np.ndarray
|
|
104
|
+
A 2D float array of shape (n_samples, n_classes) containing cumulative
|
|
105
|
+
probabilities computed from `pmf`.
|
|
106
|
+
|
|
107
|
+
Methods
|
|
108
|
+
-------
|
|
109
|
+
mean()
|
|
110
|
+
Calculate the expected value for each sample.
|
|
111
|
+
ppf(q)
|
|
112
|
+
Calculate the percent point function (inverse CDF / quantiles).
|
|
113
|
+
median()
|
|
114
|
+
Calculate the 50th percentile prediction for each sample.
|
|
115
|
+
interval(alpha=0.10)
|
|
116
|
+
Calculate central prediction bounds for a given significance level.
|
|
117
|
+
|
|
118
|
+
Raises
|
|
119
|
+
------
|
|
120
|
+
ValueError
|
|
121
|
+
If `pmf` is not a 2D array, `classes` is not a 1D array, or the
|
|
122
|
+
number of columns in `pmf` does not match the length of `classes`.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def __init__(self, pmf: np.ndarray, classes: np.ndarray) -> None:
|
|
127
|
+
pmf_arr = np.asarray(pmf, dtype=float)
|
|
128
|
+
classes_arr = np.asarray(classes)
|
|
129
|
+
|
|
130
|
+
if pmf_arr.ndim != 2:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
f"Expected 'pmf' to be a 2D array of shape (n_samples, n_classes), "
|
|
133
|
+
f"got shape {pmf_arr.shape}."
|
|
134
|
+
)
|
|
135
|
+
if classes_arr.ndim != 1:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f"Expected 'classes' to be a 1D array, got shape {classes_arr.shape}."
|
|
138
|
+
)
|
|
139
|
+
if pmf_arr.shape[1] != classes_arr.shape[0]:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
f"Mismatch between PMF class dimension ({pmf_arr.shape[1]}) "
|
|
142
|
+
f"and classes array length ({classes_arr.shape[0]})."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
self.pmf = pmf_arr
|
|
146
|
+
self.classes = classes_arr
|
|
147
|
+
self._cdf: np.ndarray | None = None
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def cdf(self) -> np.ndarray:
|
|
151
|
+
"""Compute the Cumulative Distribution Function (CDF) array.
|
|
152
|
+
|
|
153
|
+
Returns
|
|
154
|
+
-------
|
|
155
|
+
np.ndarray
|
|
156
|
+
2D array of shape (n_samples, n_classes) containing cumulative probabilities.
|
|
157
|
+
|
|
158
|
+
"""
|
|
159
|
+
if self._cdf is None:
|
|
160
|
+
self._cdf = np.clip(np.cumsum(self.pmf, axis=1), 0.0, 1.0)
|
|
161
|
+
return self._cdf
|
|
162
|
+
|
|
163
|
+
def mean(self) -> np.ndarray:
|
|
164
|
+
"""Calculate the expected value (mean) for each sample.
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
np.ndarray
|
|
169
|
+
1D array of shape (n_samples,) representing expected values in
|
|
170
|
+
physical class units.
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
return np.dot(self.pmf, self.classes)
|
|
174
|
+
|
|
175
|
+
def ppf(self, q: Union[float, np.ndarray]) -> np.ndarray:
|
|
176
|
+
"""Percent Point Function (inverse CDF / quantile calculation).
|
|
177
|
+
|
|
178
|
+
Maps quantile probabilities back to discrete physical class levels.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
q : float | np.ndarray
|
|
183
|
+
Quantile level(s) in the range [0.0, 1.0]. Can be a single scalar
|
|
184
|
+
or an array of quantiles.
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
np.ndarray
|
|
189
|
+
If `q` is a scalar, returns a 1D array of shape (n_samples,).
|
|
190
|
+
If `q` is 1D array of length `n_quantiles`, returns a 2D array of
|
|
191
|
+
shape (n_samples, n_quantiles).
|
|
192
|
+
|
|
193
|
+
Raises
|
|
194
|
+
------
|
|
195
|
+
ValueError
|
|
196
|
+
If any quantile in `q` lies outside [0.0, 1.0].
|
|
197
|
+
|
|
198
|
+
"""
|
|
199
|
+
q_arr = np.asarray(q, dtype=float)
|
|
200
|
+
if np.any((q_arr < 0.0) | (q_arr > 1.0)):
|
|
201
|
+
raise ValueError("All quantiles in 'q' must lie within [0.0, 1.0].")
|
|
202
|
+
|
|
203
|
+
cdf = self.cdf
|
|
204
|
+
if q_arr.ndim == 0:
|
|
205
|
+
indices = np.argmax(cdf >= q_arr, axis=1)
|
|
206
|
+
return self.classes[indices]
|
|
207
|
+
|
|
208
|
+
cdf_expanded = cdf[:, np.newaxis, :]
|
|
209
|
+
q_expanded = q_arr[np.newaxis, :, np.newaxis]
|
|
210
|
+
indices = np.argmax(cdf_expanded >= q_expanded, axis=2)
|
|
211
|
+
return self.classes[indices]
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class ContinuousPredictiveDistribution(PredictiveDistribution):
|
|
215
|
+
"""Encapsulates a continuous predictive Cumulative Distribution Function (CDF).
|
|
216
|
+
|
|
217
|
+
Provides vectorized utilities for computing expected continuous values,
|
|
218
|
+
medians, percent point functions (quantiles/PPF), central prediction
|
|
219
|
+
intervals, and continuous CDF probabilities across samples.
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
grid_y : np.ndarray
|
|
224
|
+
1D float array of shape (n_grid_points,) representing continuous physical
|
|
225
|
+
target grid values in strictly ascending order.
|
|
226
|
+
grid_cdf : np.ndarray
|
|
227
|
+
2D float array of shape (n_samples, n_grid_points) containing evaluated
|
|
228
|
+
cumulative probabilities across grid points.
|
|
229
|
+
|
|
230
|
+
Attributes
|
|
231
|
+
----------
|
|
232
|
+
grid_y : np.ndarray
|
|
233
|
+
1D float array containing grid values.
|
|
234
|
+
grid_cdf : np.ndarray
|
|
235
|
+
2D float array containing cumulative probabilities bounded in [0.0, 1.0].
|
|
236
|
+
|
|
237
|
+
Methods
|
|
238
|
+
-------
|
|
239
|
+
mean()
|
|
240
|
+
Calculate expected continuous values via numerical integration.
|
|
241
|
+
ppf(q)
|
|
242
|
+
Calculate percent point function (inverse CDF / quantiles).
|
|
243
|
+
cdf(y)
|
|
244
|
+
Evaluate continuous CDF probability P(Y <= y) at physical value y.
|
|
245
|
+
|
|
246
|
+
Raises
|
|
247
|
+
------
|
|
248
|
+
ValueError
|
|
249
|
+
If `grid_y` is not 1D, `grid_cdf` is not 2D, or shape dimensions mismatch.
|
|
250
|
+
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
def __init__(self, grid_y: np.ndarray, grid_cdf: np.ndarray) -> None:
|
|
254
|
+
y_arr = np.asarray(grid_y, dtype=float)
|
|
255
|
+
cdf_arr = np.asarray(grid_cdf, dtype=float)
|
|
256
|
+
|
|
257
|
+
if y_arr.ndim != 1 or cdf_arr.ndim != 2:
|
|
258
|
+
raise ValueError("Invalid array dimensions for grid_y or grid_cdf.")
|
|
259
|
+
if cdf_arr.shape[1] != y_arr.shape[0]:
|
|
260
|
+
raise ValueError("Grid CDF column dimension must match grid_y length.")
|
|
261
|
+
|
|
262
|
+
self.grid_y = y_arr
|
|
263
|
+
self.grid_cdf = np.clip(cdf_arr, 0.0, 1.0)
|
|
264
|
+
self._n_samples = cdf_arr.shape[0]
|
|
265
|
+
|
|
266
|
+
def mean(self) -> np.ndarray:
|
|
267
|
+
"""Calculate expected continuous values via numerical integration.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
np.ndarray
|
|
272
|
+
1D array of shape (n_samples,) containing expected physical values.
|
|
273
|
+
|
|
274
|
+
"""
|
|
275
|
+
dy = np.diff(self.grid_y)
|
|
276
|
+
avg_prob = 1.0 - 0.5 * (self.grid_cdf[:, :-1] + self.grid_cdf[:, 1:])
|
|
277
|
+
return np.sum(avg_prob * dy, axis=1) + self.grid_y[0]
|
|
278
|
+
|
|
279
|
+
def ppf(self, q: Union[float, ArrayLike]) -> np.ndarray:
|
|
280
|
+
"""Calculate continuous interpolated values at quantile level `q`.
|
|
281
|
+
|
|
282
|
+
Parameters
|
|
283
|
+
----------
|
|
284
|
+
q : float | ArrayLike
|
|
285
|
+
Quantile level(s) strictly in the range [0.0, 1.0].
|
|
286
|
+
|
|
287
|
+
Returns
|
|
288
|
+
-------
|
|
289
|
+
np.ndarray
|
|
290
|
+
If `q` is a scalar, returns a 1D array of shape (n_samples,).
|
|
291
|
+
If `q` is a 1D array of length `n_quantiles`, returns a 2D array
|
|
292
|
+
of shape (n_samples, n_quantiles).
|
|
293
|
+
|
|
294
|
+
Raises
|
|
295
|
+
------
|
|
296
|
+
ValueError
|
|
297
|
+
If any quantile in `q` lies outside [0.0, 1.0].
|
|
298
|
+
If `q` is not a 1D array or a float.
|
|
299
|
+
|
|
300
|
+
"""
|
|
301
|
+
q_arr = np.asarray(q, dtype=float)
|
|
302
|
+
if np.any((q_arr < 0.0) | (q_arr > 1.0)):
|
|
303
|
+
raise ValueError("All quantiles in 'q' must lie within [0.0, 1.0].")
|
|
304
|
+
|
|
305
|
+
n_samples, n_grid = self.grid_cdf.shape
|
|
306
|
+
|
|
307
|
+
# 1. Scalar quantile query -> returns shape (n_samples,)
|
|
308
|
+
if q_arr.ndim == 0:
|
|
309
|
+
q_val = q_arr.item()
|
|
310
|
+
idx = np.clip(
|
|
311
|
+
np.count_nonzero(self.grid_cdf <= q_val, axis=1) - 1,
|
|
312
|
+
0,
|
|
313
|
+
n_grid - 2,
|
|
314
|
+
)
|
|
315
|
+
rows = np.arange(n_samples)
|
|
316
|
+
q0, q1 = self.grid_cdf[rows, idx], self.grid_cdf[rows, idx + 1]
|
|
317
|
+
t = np.clip((q_val - q0) / (q1 - q0), 0.0, 1.0)
|
|
318
|
+
return (1.0 - t) * self.grid_y[idx] + t * self.grid_y[idx + 1]
|
|
319
|
+
|
|
320
|
+
# 2. Array quantile query -> returns shape (n_samples, n_quantiles)
|
|
321
|
+
if q_arr.ndim == 1:
|
|
322
|
+
grid_cdf_ = self.grid_cdf[:, np.newaxis, :]
|
|
323
|
+
grid_q_arr = q_arr[np.newaxis, :, np.newaxis]
|
|
324
|
+
|
|
325
|
+
idx = np.clip(
|
|
326
|
+
np.count_nonzero(grid_cdf_ <= grid_q_arr, axis=2) - 1,
|
|
327
|
+
0,
|
|
328
|
+
n_grid - 2,
|
|
329
|
+
)
|
|
330
|
+
q0 = np.take_along_axis(self.grid_cdf, idx, axis=1)
|
|
331
|
+
q1 = np.take_along_axis(self.grid_cdf, idx + 1, axis=1)
|
|
332
|
+
t = np.clip((q_arr[np.newaxis, :] - q0) / (q1 - q0), 0.0, 1.0)
|
|
333
|
+
return (1.0 - t) * self.grid_y[idx] + t * self.grid_y[idx + 1]
|
|
334
|
+
|
|
335
|
+
raise ValueError("Quantile 'q' must be a scalar float or a 1D array.")
|
|
336
|
+
|
|
337
|
+
def cdf(self, y: Union[float, ArrayLike]) -> np.ndarray:
|
|
338
|
+
"""Evaluate continuous CDF probability P(Y <= y) at physical value(s) y.
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
y : float | ArrayLike
|
|
343
|
+
If a scalar float, evaluates P(Y <= y) at y for all samples.
|
|
344
|
+
If a 1D array of shape (n_samples,), evaluates P(Y_i <= y_i)
|
|
345
|
+
sample-wise for each corresponding sample i.
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
np.ndarray
|
|
350
|
+
1D array of shape (n_samples,) containing evaluated probabilities.
|
|
351
|
+
|
|
352
|
+
Raises
|
|
353
|
+
------
|
|
354
|
+
ValueError
|
|
355
|
+
If y is an array and not of shape (n_samples,).
|
|
356
|
+
If y is not a scalar or a 1D array.
|
|
357
|
+
|
|
358
|
+
"""
|
|
359
|
+
y_arr = np.asarray(y, dtype=float)
|
|
360
|
+
n_grid = len(self.grid_y)
|
|
361
|
+
|
|
362
|
+
# 1. Scalar query (same y for all samples)
|
|
363
|
+
if y_arr.ndim == 0:
|
|
364
|
+
idx = int(
|
|
365
|
+
np.clip(
|
|
366
|
+
np.searchsorted(self.grid_y, y_arr.item(), side="right") - 1,
|
|
367
|
+
0,
|
|
368
|
+
n_grid - 2,
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
t = np.clip(
|
|
372
|
+
(y_arr.item() - self.grid_y[idx])
|
|
373
|
+
/ (self.grid_y[idx + 1] - self.grid_y[idx]),
|
|
374
|
+
0.0,
|
|
375
|
+
1.0,
|
|
376
|
+
)
|
|
377
|
+
return (1.0 - t) * self.grid_cdf[:, idx] + t * self.grid_cdf[:, idx + 1]
|
|
378
|
+
|
|
379
|
+
# 2. Vectorized 1D query (sample-wise y_i)
|
|
380
|
+
if y_arr.ndim == 1:
|
|
381
|
+
if len(y_arr) != self._n_samples:
|
|
382
|
+
raise ValueError(
|
|
383
|
+
f"Expected 1D 'y' array of length {self._n_samples}, "
|
|
384
|
+
f"got {len(y_arr)}."
|
|
385
|
+
)
|
|
386
|
+
idx = np.clip(
|
|
387
|
+
np.searchsorted(self.grid_y, y_arr, side="right") - 1,
|
|
388
|
+
0,
|
|
389
|
+
n_grid - 2,
|
|
390
|
+
)
|
|
391
|
+
y0, y1 = self.grid_y[idx], self.grid_y[idx + 1]
|
|
392
|
+
t = np.clip((y_arr - y0) / (y1 - y0), 0.0, 1.0)
|
|
393
|
+
rows = np.arange(self._n_samples)
|
|
394
|
+
return (1.0 - t) * self.grid_cdf[rows, idx] + t * self.grid_cdf[
|
|
395
|
+
rows, idx + 1
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
raise ValueError("Parameter 'y' must be a scalar float or a 1D array.")
|