unifiedbooster 0.4.2__py3-none-any.whl → 0.6.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.
@@ -0,0 +1,486 @@
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ Evaluation of conformal predictors.
5
+ """
6
+
7
+ # Authors: Henrik Linusson
8
+
9
+ # TODO: cross_val_score/run_experiment should possibly allow multiple to be evaluated on identical folding
10
+
11
+ from __future__ import division
12
+
13
+ from nonconformist.base import RegressorMixin, ClassifierMixin
14
+
15
+ import sys
16
+ import numpy as np
17
+ import pandas as pd
18
+
19
+ from sklearn.cross_validation import StratifiedShuffleSplit
20
+ from sklearn.cross_validation import KFold
21
+ from sklearn.cross_validation import train_test_split
22
+ from sklearn.base import clone, BaseEstimator
23
+
24
+
25
+ class BaseIcpCvHelper(BaseEstimator):
26
+ """Base class for cross validation helpers."""
27
+
28
+ def __init__(self, icp, calibration_portion):
29
+ super(BaseIcpCvHelper, self).__init__()
30
+ self.icp = icp
31
+ self.calibration_portion = calibration_portion
32
+
33
+ def predict(self, x, significance=None):
34
+ return self.icp.predict(x, significance)
35
+
36
+
37
+ class ClassIcpCvHelper(BaseIcpCvHelper, ClassifierMixin):
38
+ """Helper class for running the ``cross_val_score`` evaluation
39
+ method on IcpClassifiers.
40
+
41
+ See also
42
+ --------
43
+ IcpRegCrossValHelper
44
+
45
+ Examples
46
+ --------
47
+ >>> from sklearn.datasets import load_iris
48
+ >>> from sklearn.ensemble import RandomForestClassifier
49
+ >>> from nonconformist.icp import IcpClassifier
50
+ >>> from nonconformist.nc import ClassifierNc, MarginErrFunc
51
+ >>> from nonconformist.evaluation import ClassIcpCvHelper
52
+ >>> from nonconformist.evaluation import class_mean_errors
53
+ >>> from nonconformist.evaluation import cross_val_score
54
+ >>> data = load_iris()
55
+ >>> nc = ProbEstClassifierNc(RandomForestClassifier(), MarginErrFunc())
56
+ >>> icp = IcpClassifier(nc)
57
+ >>> icp_cv = ClassIcpCvHelper(icp)
58
+ >>> cross_val_score(icp_cv,
59
+ ... data.data,
60
+ ... data.target,
61
+ ... iterations=2,
62
+ ... folds=2,
63
+ ... scoring_funcs=[class_mean_errors],
64
+ ... significance_levels=[0.1])
65
+ ... # doctest: +SKIP
66
+ class_mean_errors fold iter significance
67
+ 0 0.013333 0 0 0.1
68
+ 1 0.080000 1 0 0.1
69
+ 2 0.053333 0 1 0.1
70
+ 3 0.080000 1 1 0.1
71
+ """
72
+
73
+ def __init__(self, icp, calibration_portion=0.25):
74
+ super(ClassIcpCvHelper, self).__init__(icp, calibration_portion)
75
+
76
+ def fit(self, x, y):
77
+ split = StratifiedShuffleSplit(
78
+ y, n_iter=1, test_size=self.calibration_portion
79
+ )
80
+ for train, cal in split:
81
+ self.icp.fit(x[train, :], y[train])
82
+ self.icp.calibrate(x[cal, :], y[cal])
83
+
84
+
85
+ class RegIcpCvHelper(BaseIcpCvHelper, RegressorMixin):
86
+ """Helper class for running the ``cross_val_score`` evaluation
87
+ method on IcpRegressors.
88
+
89
+ See also
90
+ --------
91
+ IcpClassCrossValHelper
92
+
93
+ Examples
94
+ --------
95
+ >>> from sklearn.datasets import load_boston
96
+ >>> from sklearn.ensemble import RandomForestRegressor
97
+ >>> from nonconformist.icp import IcpRegressor
98
+ >>> from nonconformist.nc import RegressorNc, AbsErrorErrFunc
99
+ >>> from nonconformist.evaluation import RegIcpCvHelper
100
+ >>> from nonconformist.evaluation import reg_mean_errors
101
+ >>> from nonconformist.evaluation import cross_val_score
102
+ >>> data = load_boston()
103
+ >>> nc = RegressorNc(RandomForestRegressor(), AbsErrorErrFunc())
104
+ >>> icp = IcpRegressor(nc)
105
+ >>> icp_cv = RegIcpCvHelper(icp)
106
+ >>> cross_val_score(icp_cv,
107
+ ... data.data,
108
+ ... data.target,
109
+ ... iterations=2,
110
+ ... folds=2,
111
+ ... scoring_funcs=[reg_mean_errors],
112
+ ... significance_levels=[0.1])
113
+ ... # doctest: +SKIP
114
+ fold iter reg_mean_errors significance
115
+ 0 0 0 0.185771 0.1
116
+ 1 1 0 0.138340 0.1
117
+ 2 0 1 0.071146 0.1
118
+ 3 1 1 0.043478 0.1
119
+ """
120
+
121
+ def __init__(self, icp, calibration_portion=0.25):
122
+ super(RegIcpCvHelper, self).__init__(icp, calibration_portion)
123
+
124
+ def fit(self, x, y):
125
+ split = train_test_split(x, y, test_size=self.calibration_portion)
126
+ x_tr, x_cal, y_tr, y_cal = split[0], split[1], split[2], split[3]
127
+ self.icp.fit(x_tr, y_tr)
128
+ self.icp.calibrate(x_cal, y_cal)
129
+
130
+
131
+ # -----------------------------------------------------------------------------
132
+ #
133
+ # -----------------------------------------------------------------------------
134
+ def cross_val_score(
135
+ model,
136
+ x,
137
+ y,
138
+ iterations=10,
139
+ folds=10,
140
+ fit_params=None,
141
+ scoring_funcs=None,
142
+ significance_levels=None,
143
+ verbose=False,
144
+ ):
145
+ """Evaluates a conformal predictor using cross-validation.
146
+
147
+ Parameters
148
+ ----------
149
+ model : object
150
+ Conformal predictor to evaluate.
151
+
152
+ x : numpy array of shape [n_samples, n_features]
153
+ Inputs of data to use for evaluation.
154
+
155
+ y : numpy array of shape [n_samples]
156
+ Outputs of data to use for evaluation.
157
+
158
+ iterations : int
159
+ Number of iterations to use for evaluation. The data set is randomly
160
+ shuffled before each iteration.
161
+
162
+ folds : int
163
+ Number of folds to use for evaluation.
164
+
165
+ fit_params : dictionary
166
+ Parameters to supply to the conformal prediction object on training.
167
+
168
+ scoring_funcs : iterable
169
+ List of evaluation functions to apply to the conformal predictor in each
170
+ fold. Each evaluation function should have a signature
171
+ ``scorer(prediction, y, significance)``.
172
+
173
+ significance_levels : iterable
174
+ List of significance levels at which to evaluate the conformal
175
+ predictor.
176
+
177
+ verbose : boolean
178
+ Indicates whether to output progress information during evaluation.
179
+
180
+ Returns
181
+ -------
182
+ scores : pandas DataFrame
183
+ Tabulated results for each iteration, fold and evaluation function.
184
+ """
185
+
186
+ fit_params = fit_params if fit_params else {}
187
+ significance_levels = (
188
+ significance_levels
189
+ if significance_levels is not None
190
+ else np.arange(0.01, 1.0, 0.01)
191
+ )
192
+
193
+ df = pd.DataFrame()
194
+
195
+ columns = [
196
+ "iter",
197
+ "fold",
198
+ "significance",
199
+ ] + [f.__name__ for f in scoring_funcs]
200
+ for i in range(iterations):
201
+ idx = np.random.permutation(y.size)
202
+ x, y = x[idx, :], y[idx]
203
+ cv = KFold(y.size, folds)
204
+ for j, (train, test) in enumerate(cv):
205
+ if verbose:
206
+ sys.stdout.write(
207
+ "\riter {}/{} fold {}/{}".format(
208
+ i + 1, iterations, j + 1, folds
209
+ )
210
+ )
211
+ m = clone(model)
212
+ m.fit(x[train, :], y[train], **fit_params)
213
+ prediction = m.predict(x[test, :], significance=None)
214
+ for k, s in enumerate(significance_levels):
215
+ scores = [
216
+ scoring_func(prediction, y[test], s)
217
+ for scoring_func in scoring_funcs
218
+ ]
219
+ df_score = pd.DataFrame([[i, j, s] + scores], columns=columns)
220
+ df = df.append(df_score, ignore_index=True)
221
+
222
+ return df
223
+
224
+
225
+ def run_experiment(
226
+ models,
227
+ csv_files,
228
+ iterations=10,
229
+ folds=10,
230
+ fit_params=None,
231
+ scoring_funcs=None,
232
+ significance_levels=None,
233
+ normalize=False,
234
+ verbose=False,
235
+ header=0,
236
+ ):
237
+ """Performs a cross-validation evaluation of one or several conformal
238
+ predictors on a collection of data sets in csv format.
239
+
240
+ Parameters
241
+ ----------
242
+ models : object or iterable
243
+ Conformal predictor(s) to evaluate.
244
+
245
+ csv_files : iterable
246
+ List of file names (with absolute paths) containing csv-data, used to
247
+ evaluate the conformal predictor.
248
+
249
+ iterations : int
250
+ Number of iterations to use for evaluation. The data set is randomly
251
+ shuffled before each iteration.
252
+
253
+ folds : int
254
+ Number of folds to use for evaluation.
255
+
256
+ fit_params : dictionary
257
+ Parameters to supply to the conformal prediction object on training.
258
+
259
+ scoring_funcs : iterable
260
+ List of evaluation functions to apply to the conformal predictor in each
261
+ fold. Each evaluation function should have a signature
262
+ ``scorer(prediction, y, significance)``.
263
+
264
+ significance_levels : iterable
265
+ List of significance levels at which to evaluate the conformal
266
+ predictor.
267
+
268
+ verbose : boolean
269
+ Indicates whether to output progress information during evaluation.
270
+
271
+ Returns
272
+ -------
273
+ scores : pandas DataFrame
274
+ Tabulated results for each data set, iteration, fold and
275
+ evaluation function.
276
+ """
277
+ df = pd.DataFrame()
278
+ if not hasattr(models, "__iter__"):
279
+ models = [models]
280
+
281
+ for model in models:
282
+ is_regression = model.get_problem_type() == "regression"
283
+
284
+ n_data_sets = len(csv_files)
285
+ for i, csv_file in enumerate(csv_files):
286
+ if verbose:
287
+ print("\n{} ({} / {})".format(csv_file, i + 1, n_data_sets))
288
+ data = pd.read_csv(csv_file, header=header)
289
+ x, y = data.values[:, :-1], data.values[:, -1]
290
+ x = np.array(x, dtype=np.float64)
291
+ if normalize:
292
+ if is_regression:
293
+ y = y - y.min() / (y.max() - y.min())
294
+ else:
295
+ for j, y_ in enumerate(np.unique(y)):
296
+ y[y == y_] = j
297
+
298
+ scores = cross_val_score(
299
+ model,
300
+ x,
301
+ y,
302
+ iterations,
303
+ folds,
304
+ fit_params,
305
+ scoring_funcs,
306
+ significance_levels,
307
+ verbose,
308
+ )
309
+
310
+ ds_df = pd.DataFrame(scores)
311
+ ds_df["model"] = model.__class__.__name__
312
+ try:
313
+ ds_df["data_set"] = csv_file.split("/")[-1]
314
+ except:
315
+ ds_df["data_set"] = csv_file
316
+
317
+ df = df.append(ds_df)
318
+
319
+ return df
320
+
321
+
322
+ # -----------------------------------------------------------------------------
323
+ # Validity measures
324
+ # -----------------------------------------------------------------------------
325
+ def reg_n_correct(prediction, y, significance=None):
326
+ """Calculates the number of correct predictions made by a conformal
327
+ regression model.
328
+ """
329
+ if significance is not None:
330
+ idx = int(significance * 100 - 1)
331
+ prediction = prediction[:, :, idx]
332
+
333
+ low = y >= prediction[:, 0]
334
+ high = y <= prediction[:, 1]
335
+ correct = low * high
336
+
337
+ return y[correct].size
338
+
339
+
340
+ def reg_mean_errors(prediction, y, significance):
341
+ """Calculates the average error rate of a conformal regression model."""
342
+ return 1 - reg_n_correct(prediction, y, significance) / y.size
343
+
344
+
345
+ def class_n_correct(prediction, y, significance):
346
+ """Calculates the number of correct predictions made by a conformal
347
+ classification model.
348
+ """
349
+ labels, y = np.unique(y, return_inverse=True)
350
+ prediction = prediction > significance
351
+ correct = np.zeros((y.size,), dtype=bool)
352
+ for i, y_ in enumerate(y):
353
+ correct[i] = prediction[i, int(y_)]
354
+ return np.sum(correct)
355
+
356
+
357
+ def class_mean_errors(prediction, y, significance=None):
358
+ """Calculates the average error rate of a conformal classification model."""
359
+ return 1 - (class_n_correct(prediction, y, significance) / y.size)
360
+
361
+
362
+ def class_one_err(prediction, y, significance=None):
363
+ """Calculates the error rate of conformal classifier predictions containing
364
+ only a single output label.
365
+ """
366
+ labels, y = np.unique(y, return_inverse=True)
367
+ prediction = prediction > significance
368
+ idx = np.arange(0, y.size, 1)
369
+ idx = filter(lambda x: np.sum(prediction[x, :]) == 1, idx)
370
+ errors = filter(lambda x: not prediction[x, int(y[x])], idx)
371
+
372
+ if len(idx) > 0:
373
+ return np.size(errors) / np.size(idx)
374
+ else:
375
+ return 0
376
+
377
+
378
+ def class_mean_errors_one_class(prediction, y, significance, c=0):
379
+ """Calculates the average error rate of a conformal classification model,
380
+ considering only test examples belonging to class ``c``. Use
381
+ ``functools.partial`` in order to test other classes.
382
+ """
383
+ labels, y = np.unique(y, return_inverse=True)
384
+ prediction = prediction > significance
385
+ idx = np.arange(0, y.size, 1)[y == c]
386
+ errs = np.sum(1 for _ in filter(lambda x: not prediction[x, c], idx))
387
+
388
+ if idx.size > 0:
389
+ return errs / idx.size
390
+ else:
391
+ return 0
392
+
393
+
394
+ def class_one_err_one_class(prediction, y, significance, c=0):
395
+ """Calculates the error rate of conformal classifier predictions containing
396
+ only a single output label. Considers only test examples belonging to
397
+ class ``c``. Use ``functools.partial`` in order to test other classes.
398
+ """
399
+ labels, y = np.unique(y, return_inverse=True)
400
+ prediction = prediction > significance
401
+ idx = np.arange(0, y.size, 1)
402
+ idx = filter(lambda x: prediction[x, c], idx)
403
+ idx = filter(lambda x: np.sum(prediction[x, :]) == 1, idx)
404
+ errors = filter(lambda x: int(y[x]) != c, idx)
405
+
406
+ if len(idx) > 0:
407
+ return np.size(errors) / np.size(idx)
408
+ else:
409
+ return 0
410
+
411
+
412
+ # -----------------------------------------------------------------------------
413
+ # Efficiency measures
414
+ # -----------------------------------------------------------------------------
415
+ def _reg_interval_size(prediction, y, significance):
416
+ idx = int(significance * 100 - 1)
417
+ prediction = prediction[:, :, idx]
418
+
419
+ return prediction[:, 1] - prediction[:, 0]
420
+
421
+
422
+ def reg_min_size(prediction, y, significance):
423
+ return np.min(_reg_interval_size(prediction, y, significance))
424
+
425
+
426
+ def reg_q1_size(prediction, y, significance):
427
+ return np.percentile(_reg_interval_size(prediction, y, significance), 25)
428
+
429
+
430
+ def reg_median_size(prediction, y, significance):
431
+ return np.median(_reg_interval_size(prediction, y, significance))
432
+
433
+
434
+ def reg_q3_size(prediction, y, significance):
435
+ return np.percentile(_reg_interval_size(prediction, y, significance), 75)
436
+
437
+
438
+ def reg_max_size(prediction, y, significance):
439
+ return np.max(_reg_interval_size(prediction, y, significance))
440
+
441
+
442
+ def reg_mean_size(prediction, y, significance):
443
+ """Calculates the average prediction interval size of a conformal
444
+ regression model.
445
+ """
446
+ return np.mean(_reg_interval_size(prediction, y, significance))
447
+
448
+
449
+ def class_avg_c(prediction, y, significance):
450
+ """Calculates the average number of classes per prediction of a conformal
451
+ classification model.
452
+ """
453
+ prediction = prediction > significance
454
+ return np.sum(prediction) / prediction.shape[0]
455
+
456
+
457
+ def class_mean_p_val(prediction, y, significance):
458
+ """Calculates the mean of the p-values output by a conformal classification
459
+ model.
460
+ """
461
+ return np.mean(prediction)
462
+
463
+
464
+ def class_one_c(prediction, y, significance):
465
+ """Calculates the rate of singleton predictions (prediction sets containing
466
+ only a single class label) of a conformal classification model.
467
+ """
468
+ prediction = prediction > significance
469
+ n_singletons = np.sum(
470
+ 1 for _ in filter(lambda x: np.sum(x) == 1, prediction)
471
+ )
472
+ return n_singletons / y.size
473
+
474
+
475
+ def class_empty(prediction, y, significance):
476
+ """Calculates the rate of singleton predictions (prediction sets containing
477
+ only a single class label) of a conformal classification model.
478
+ """
479
+ prediction = prediction > significance
480
+ n_empty = np.sum(1 for _ in filter(lambda x: np.sum(x) == 0, prediction))
481
+ return n_empty / y.size
482
+
483
+
484
+ def n_test(prediction, y, significance):
485
+ """Provides the number of test patters used in the evaluation."""
486
+ return y.size