• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

INGEOTEC / CompStats / 13462896555

21 Feb 2025 06:25PM UTC coverage: 98.031%. First build
13462896555

Pull #20

github

mgraffg
docs (multiple)
Pull Request #20: Multiple performance measures

231 of 235 new or added lines in 6 files covered. (98.3%)

1344 of 1371 relevant lines covered (98.03%)

2.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.21
/CompStats/interface.py
1
# Copyright 2025 Sergio Nava Muñoz and Mario Graff Guerrero
2

3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6

7
#     http://www.apache.org/licenses/LICENSE-2.0
8

9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14
from dataclasses import dataclass
3✔
15
from sklearn.metrics import balanced_accuracy_score
3✔
16
from sklearn.base import clone
3✔
17
import pandas as pd
3✔
18
import numpy as np
3✔
19
from CompStats.bootstrap import StatisticSamples
3✔
20
from CompStats.utils import progress_bar
3✔
21
from CompStats import measurements
3✔
22
from CompStats.measurements import SE
3✔
23
from CompStats.performance import plot_performance, plot_difference
3✔
24
from CompStats.utils import dataframe
3✔
25

26

27
class Perf(object):
3✔
28
    """Perf is an entry point to CompStats
29

30
    :param y_true: True measurement or could be a pandas.DataFrame where column label 'y' corresponds to the true measurement.
31
    :type y_true: numpy.ndarray or pandas.DataFrame
32
    :param score_func: Function to measure the performance, it is assumed that the best algorithm has the highest value.
33
    :type score_func: Function where the first argument is :math:`y` and the second is :math:`\\hat{y}.`
34
    :param error_func: Function to measure the performance where the best algorithm has the lowest value.
35
    :type error_func: Function where the first argument is :math:`y` and the second is :math:`\\hat{y}.` 
36
    :param y_pred: Predictions, the algorithms will be identified with alg-k where k=1 is the first argument included in :py:attr:`args.`
37
    :type y_pred: numpy.ndarray
38
    :param kwargs: Predictions, the algorithms will be identified using the keyword
39
    :type kwargs: numpy.ndarray
40
    :param n_jobs: Number of jobs to compute the statistic, default=-1 corresponding to use all threads.
41
    :type n_jobs: int
42
    :param num_samples: Number of bootstrap samples, default=500.
43
    :type num_samples: int
44
    :param use_tqdm: Whether to use tqdm.tqdm to visualize the progress, default=True.
45
    :type use_tqdm: bool
46

47

48
    >>> from sklearn.svm import LinearSVC
49
    >>> from sklearn.linear_model import LogisticRegression
50
    >>> from sklearn.ensemble import RandomForestClassifier
51
    >>> from sklearn.datasets import load_iris
52
    >>> from sklearn.model_selection import train_test_split
53
    >>> from sklearn.base import clone
54
    >>> from CompStats.interface import Perf
55
    >>> X, y = load_iris(return_X_y=True)
56
    >>> _ = train_test_split(X, y, test_size=0.3)
57
    >>> X_train, X_val, y_train, y_val = _
58
    >>> m = LinearSVC().fit(X_train, y_train)
59
    >>> hy = m.predict(X_val)
60
    >>> ens = RandomForestClassifier().fit(X_train, y_train)
61
    >>> perf = Perf(y_val, hy, forest=ens.predict(X_val))
62
    >>> perf
63
    <Perf>
64
    Statistic with its standard error (se)
65
    statistic (se)
66
    0.9792 (0.0221) <= alg-1
67
    0.9744 (0.0246) <= forest
68
    
69
    If an algorithm's prediction is missing, this can be included by calling the instance, as can be seen in the following instruction. Note that the algorithm's name can also be given with the keyword :py:attr:`name.`
70

71
    >>> lr = LogisticRegression().fit(X_train, y_train)
72
    >>> perf(lr.predict(X_val), name='Log. Reg.')
73
    <Perf>
74
    Statistic with its standard error (se)
75
    statistic (se)
76
    1.0000 (0.0000) <= Log. Reg.
77
    0.9792 (0.0221) <= alg-1
78
    0.9744 (0.0246) <= forest
79
    
80
    The performance function used to compare the algorithms can be changed, and the same bootstrap samples would be used if the instance were cloned. Consequently, the values are computed using the same samples, as can be seen in the following example.
81

82
    >>> perf_error = clone(perf)
83
    >>> perf_error.error_func = lambda y, hy: (y != hy).mean()
84
    >>> perf_error
85
    <Perf>
86
    Statistic with its standard error (se)
87
    statistic (se)
88
    0.0000 (0.0000) <= Log. Reg.
89
    0.0222 (0.0237) <= alg-1
90
    0.0222 (0.0215) <= forest
91

92
    """
93
    def __init__(self, y_true, *y_pred,
3✔
94
                 score_func=balanced_accuracy_score,
95
                 error_func=None,
96
                 num_samples: int=500,
97
                 n_jobs: int=-1,
98
                 use_tqdm=True,
99
                 **kwargs):
100
        assert (score_func is None) ^ (error_func is None)
3✔
101
        self.score_func = score_func
3✔
102
        self.error_func = error_func
3✔
103
        algs = {}
3✔
104
        for k, v in enumerate(y_pred):
3✔
105
            algs[f'alg-{k+1}'] = np.asanyarray(v)
3✔
106
        algs.update(**kwargs)
3✔
107
        self.predictions = algs
3✔
108
        self.y_true = y_true
3✔
109
        self.num_samples = num_samples
3✔
110
        self.n_jobs = n_jobs
3✔
111
        self.use_tqdm = use_tqdm
3✔
112
        self.sorting_func = np.linalg.norm
3✔
113
        self._init()
3✔
114

115
    def _init(self):
3✔
116
        """Compute the bootstrap statistic"""
117

118
        bib = True if self.score_func is not None else False
3✔
119
        if hasattr(self, '_statistic_samples'):
3✔
120
            _ = self.statistic_samples
×
121
            _.BiB = bib
×
122
        else:
123
            _ = StatisticSamples(statistic=self.statistic_func,
3✔
124
                                 n_jobs=self.n_jobs,
125
                                 num_samples=self.num_samples,
126
                                 BiB=bib)
127
            _.samples(N=self.y_true.shape[0])
3✔
128
        self.statistic_samples = _
3✔
129

130
    def get_params(self):
3✔
131
        """Parameters"""
132

133
        return dict(y_true=self.y_true,
3✔
134
                    score_func=self.score_func,
135
                    error_func=self.error_func,
136
                    num_samples=self.num_samples,
137
                    n_jobs=self.n_jobs)
138

139
    def __sklearn_clone__(self):
3✔
140
        klass = self.__class__
3✔
141
        params = self.get_params()
3✔
142
        ins = klass(**params)
3✔
143
        ins.predictions = dict(self.predictions)
3✔
144
        ins._statistic_samples._samples = self.statistic_samples._samples
3✔
145
        ins.sorting_func = self.sorting_func
3✔
146
        return ins
3✔
147

148
    def __repr__(self):
3✔
149
        """Prediction statistics with standard error in parenthesis"""
150
        arg = 'score_func' if self.error_func is None else 'error_func'
3✔
151
        func_name = self.statistic_func.__name__
3✔
152
        statistic = self.statistic
3✔
153
        if isinstance(statistic, dict):
3✔
NEW
154
            return f"<{self.__class__.__name__}({arg}={func_name})>\n{self}"
×
155
        elif isinstance(statistic, float):
3✔
156
            return f"<{self.__class__.__name__}({arg}={func_name}, statistic={statistic:0.4f}, se={self.se:0.4f})>"
3✔
157
        desc = [f'{k:0.4f}' for k in statistic]
3✔
158
        desc = ', '.join(desc)
3✔
159
        desc_se = [f'{k:0.4f}' for k in self.se]
3✔
160
        desc_se = ', '.join(desc_se)
3✔
161
        return f"<{self.__class__.__name__}({arg}={func_name}, statistic=[{desc}], se=[{desc_se}])>"
3✔
162
        
163
    def __str__(self):
3✔
164
        """Prediction statistics with standard error in parenthesis"""
165
        if not isinstance(self.statistic, dict):
3✔
166
            return self.__repr__()
3✔
167

168
        se = self.se
3✔
169
        output = ["Statistic with its standard error (se)"]
3✔
170
        output.append("statistic (se)")
3✔
171
        for key, value in self.statistic.items():
3✔
172
            if isinstance(value, float):
3✔
173
                desc = f'{value:0.4f} ({se[key]:0.4f}) <= {key}'
3✔
174
            else:
175
                desc = [f'{v:0.4f} ({k:0.4f})'
3✔
176
                        for v, k in zip(value, se[key])]
177
                desc = ', '.join(desc)
3✔
178
                desc = f'{desc} <= {key}'
3✔
179
            output.append(desc)
3✔
180
        return "\n".join(output)
3✔
181

182
    def __call__(self, y_pred, name=None):
3✔
183
        """Add predictions"""
184
        if name is None:
3✔
185
            k = len(self.predictions) + 1
3✔
186
            if k == 0:
3✔
187
                k = 1
×
188
            name = f'alg-{k}'
3✔
189
        self.best = None
3✔
190
        self.predictions[name] = np.asanyarray(y_pred)
3✔
191
        samples = self._statistic_samples
3✔
192
        calls = samples.calls
3✔
193
        if name in calls:
3✔
194
            del calls[name]
3✔
195
        return self
3✔
196

197
    def difference(self, wrt: str=None):
3✔
198
        """Compute the difference w.r.t any algorithm by default is the best
199

200
        >>> from sklearn.svm import LinearSVC
201
        >>> from sklearn.ensemble import RandomForestClassifier
202
        >>> from sklearn.datasets import load_iris
203
        >>> from sklearn.model_selection import train_test_split
204
        >>> from sklearn.base import clone
205
        >>> from CompStats.interface import Perf
206
        >>> X, y = load_iris(return_X_y=True)
207
        >>> _ = train_test_split(X, y, test_size=0.3)
208
        >>> X_train, X_val, y_train, y_val = _
209
        >>> m = LinearSVC().fit(X_train, y_train)
210
        >>> hy = m.predict(X_val)
211
        >>> ens = RandomForestClassifier().fit(X_train, y_train)
212
        >>> perf = Perf(y_val, hy, forest=ens.predict(X_val))
213
        >>> perf.difference()
214
        <Difference>
215
        difference p-values w.r.t alg-1
216
        forest 0.06        
217
        """
218
        if wrt is None:
3✔
219
            wrt = self.best
3✔
220
        if isinstance(wrt, str):
3✔
221
            base = self.statistic_samples.calls[wrt]
3✔
222
        else:
223
            base = np.array([self.statistic_samples.calls[key][:, col]
3✔
224
                            for col, key in enumerate(wrt)]).T       
225
        sign = 1 if self.statistic_samples.BiB else -1
3✔
226
        diff = dict()
3✔
227
        for k, v in self.statistic_samples.calls.items():
3✔
228
            if base.ndim == 1 and k == wrt:
3✔
229
                continue
3✔
230
            diff[k] = sign * (base - v)
3✔
231
        diff_ins = Difference(statistic_samples=clone(self.statistic_samples),
3✔
232
                              statistic=self.statistic)
233
        diff_ins.sorting_func = self.sorting_func
3✔
234
        diff_ins.statistic_samples.calls = diff
3✔
235
        diff_ins.statistic_samples.info['best'] = self.best
3✔
236
        diff_ins.best = self.best
3✔
237
        return diff_ins
3✔
238

239
    @property
3✔
240
    def best(self):
3✔
241
        """System with best performance"""
242
        if hasattr(self, '_best') and self._best is not None:
3✔
243
            return self._best
3✔
244
        if not isinstance(self.statistic, dict):
3✔
245
            key, value = list(self.statistic_samples.calls.items())[0]
3✔
246
            if value.ndim == 1:
3✔
247
                self._best = key
3✔
248
            else:
249
                self._best = np.array([key] * value.shape[1])
3✔
250
            return self._best
3✔
251
        BiB = True if self.statistic_samples.BiB else False
3✔
252
        keys = np.array(list(self.statistic.keys()))
3✔
253
        data = np.asanyarray([self.statistic[k]
3✔
254
                              for k in keys])        
255
        if isinstance(self.statistic[keys[0]], np.ndarray):
3✔
256
            if BiB:
3✔
257
                best = data.argmax(axis=0)
3✔
258
            else:
NEW
259
                best = data.argmin(axis=0)
×
260
        else:
261
            if BiB:
3✔
262
                best = data.argmax()
3✔
263
            else:
NEW
264
                best = data.argmin()
×
265
        self._best = keys[best]
3✔
266
        return self._best
3✔
267
    
268
    @best.setter
3✔
269
    def best(self, value):
3✔
270
        self._best = value
3✔
271

272
    @property
3✔
273
    def sorting_func(self):
3✔
274
        """Rank systems when multiple performances are used"""
275
        return self._sorting_func
3✔
276
    
277
    @sorting_func.setter
3✔
278
    def sorting_func(self, value):
3✔
279
        self._sorting_func = value
3✔
280

281
    @property
3✔
282
    def statistic(self):
3✔
283
        """Statistic
284

285
        >>> from sklearn.svm import LinearSVC
286
        >>> from sklearn.ensemble import RandomForestClassifier
287
        >>> from sklearn.datasets import load_iris
288
        >>> from sklearn.model_selection import train_test_split
289
        >>> from CompStats.interface import Perf
290
        >>> X, y = load_iris(return_X_y=True)
291
        >>> _ = train_test_split(X, y, test_size=0.3)
292
        >>> X_train, X_val, y_train, y_val = _
293
        >>> m = LinearSVC().fit(X_train, y_train)
294
        >>> hy = m.predict(X_val)
295
        >>> ens = RandomForestClassifier().fit(X_train, y_train)
296
        >>> perf = Perf(y_val, hy, forest=ens.predict(X_val))
297
        >>> perf.statistic
298
        {'alg-1': 1.0, 'forest': 0.9500891265597148}     
299
        """
300

301
        data = sorted([(k, self.statistic_func(self.y_true, v))
3✔
302
                       for k, v in self.predictions.items()],
303
                      key=lambda x: self.sorting_func(x[1]), 
304
                      reverse=self.statistic_samples.BiB)
305
        if len(data) == 1:
3✔
306
            return data[0][1]
3✔
307
        return dict(data)
3✔
308

309
    @property
3✔
310
    def se(self):
3✔
311
        """Standard Error
312
    
313
        >>> from sklearn.svm import LinearSVC
314
        >>> from sklearn.ensemble import RandomForestClassifier
315
        >>> from sklearn.datasets import load_iris
316
        >>> from sklearn.model_selection import train_test_split
317
        >>> from CompStats.interface import Perf
318
        >>> X, y = load_iris(return_X_y=True)
319
        >>> _ = train_test_split(X, y, test_size=0.3)
320
        >>> X_train, X_val, y_train, y_val = _
321
        >>> m = LinearSVC().fit(X_train, y_train)
322
        >>> hy = m.predict(X_val)
323
        >>> ens = RandomForestClassifier().fit(X_train, y_train)
324
        >>> perf = Perf(y_val, hy, forest=ens.predict(X_val))
325
        >>> perf.se
326
        {'alg-1': 0.0, 'forest': 0.026945730782184187}
327
        """
328

329
        output = SE(self.statistic_samples)
3✔
330
        if len(output) == 1:
3✔
331
            return list(output.values())[0]
3✔
332
        return output
3✔
333

334
    def plot(self, value_name:str=None,
3✔
335
             var_name:str='Performance',
336
             alg_legend:str='Algorithm',
337
             perf_names:list=None,
338
             CI:float=0.05,
339
             kind:str='point', linestyle:str='none',
340
             col_wrap:int=3, capsize:float=0.2,
341
             **kwargs):
342
        """plot with seaborn
343

344
        >>> from sklearn.svm import LinearSVC
345
        >>> from sklearn.ensemble import RandomForestClassifier
346
        >>> from sklearn.datasets import load_iris
347
        >>> from sklearn.model_selection import train_test_split
348
        >>> from CompStats.interface import Perf
349
        >>> X, y = load_iris(return_X_y=True)
350
        >>> _ = train_test_split(X, y, test_size=0.3)
351
        >>> X_train, X_val, y_train, y_val = _
352
        >>> m = LinearSVC().fit(X_train, y_train)
353
        >>> hy = m.predict(X_val)
354
        >>> ens = RandomForestClassifier().fit(X_train, y_train)
355
        >>> perf = Perf(y_val, hy, score_func=None,
356
                        error_func=lambda y, hy: (y != hy).mean(),
357
                        forest=ens.predict(X_val))
358
        >>> perf.plot()
359
        """
360
        import seaborn as sns
3✔
361
        if self.score_func is not None:
3✔
362
            value_name = 'Score'
3✔
363
        else:
364
            value_name = 'Error'
×
365
        df = self.dataframe(value_name=value_name, var_name=var_name,
3✔
366
                            alg_legend=alg_legend, perf_names=perf_names)
367
        if var_name not in df.columns:
3✔
368
            var_name = None
3✔
369
            col_wrap = None
3✔
370
        ci = lambda x: measurements.CI(x, alpha=CI)
3✔
371
        f_grid = sns.catplot(df, x=value_name,
3✔
372
                             errorbar=ci,
373
                             y=alg_legend,
374
                             col=var_name,
375
                             kind=kind,
376
                             linestyle=linestyle,
377
                             col_wrap=col_wrap,
378
                             capsize=capsize)
379
        return f_grid
3✔
380

381
    
382
    def dataframe(self, value_name:str='Score',
3✔
383
                  var_name:str='Performance',
384
                  alg_legend:str='Algorithm',
385
                  perf_names:str=None):
386
        """Dataframe"""
387
        if perf_names is None and isinstance(self.best, np.ndarray):
3✔
388
            func_name = self.statistic_func.__name__
3✔
389
            perf_names = [f'{func_name}({i})'
3✔
390
                          for i, k in enumerate(self.best)]
391
        return dataframe(self, value_name=value_name,
3✔
392
                         var_name=var_name,
393
                         alg_legend=alg_legend,
394
                         perf_names=perf_names)
395

396
    @property
3✔
397
    def n_jobs(self):
3✔
398
        """Number of jobs to compute the statistics"""
399
        return self._n_jobs
3✔
400

401
    @n_jobs.setter
3✔
402
    def n_jobs(self, value):
3✔
403
        self._n_jobs = value
3✔
404

405
    @property
3✔
406
    def statistic_func(self):
3✔
407
        """Statistic function"""
408
        if self.score_func is not None:
3✔
409
            return self.score_func
3✔
410
        return self.error_func
3✔
411

412
    @property
3✔
413
    def statistic_samples(self):
3✔
414
        """Statistic Samples"""
415

416
        samples = self._statistic_samples
3✔
417
        algs = set(samples.calls.keys())
3✔
418
        algs = set(self.predictions.keys()) - algs
3✔
419
        if len(algs):
3✔
420
            for key in progress_bar(algs, use_tqdm=self.use_tqdm):
3✔
421
                samples(self.y_true, self.predictions[key], name=key)
3✔
422
        return self._statistic_samples
3✔
423

424
    @statistic_samples.setter
3✔
425
    def statistic_samples(self, value):
3✔
426
        self._statistic_samples = value
3✔
427

428
    @property
3✔
429
    def num_samples(self):
3✔
430
        """Number of bootstrap samples"""
431
        return self._num_samples
3✔
432

433
    @num_samples.setter
3✔
434
    def num_samples(self, value):
3✔
435
        self._num_samples = value
3✔
436

437
    @property
3✔
438
    def predictions(self):
3✔
439
        """Predictions"""
440
        return self._predictions
3✔
441

442
    @predictions.setter
3✔
443
    def predictions(self, value):
3✔
444
        self._predictions = value
3✔
445

446
    @property
3✔
447
    def y_true(self):
3✔
448
        """True output, gold standard o :math:`y`"""
449

450
        return self._y_true
3✔
451

452
    @y_true.setter
3✔
453
    def y_true(self, value):
3✔
454
        if isinstance(value, pd.DataFrame):
3✔
455
            self._y_true = value['y'].to_numpy()
3✔
456
            algs = {}
3✔
457
            for c in value.columns:
3✔
458
                if c == 'y':
3✔
459
                    continue
3✔
460
                algs[c] = value[c].to_numpy()
3✔
461
            self.predictions.update(algs)
3✔
462
            return
3✔
463
        self._y_true = value
3✔
464

465
    @property
3✔
466
    def score_func(self):
3✔
467
        """Score function"""
468
        return self._score_func
3✔
469

470
    @score_func.setter
3✔
471
    def score_func(self, value):
3✔
472
        self._score_func = value
3✔
473
        if value is not None:
3✔
474
            self.error_func = None
3✔
475
            if hasattr(self, '_statistic_samples'):
3✔
476
                self._statistic_samples.statistic = value
×
477
                self._statistic_samples.BiB = True
×
478

479
    @property
3✔
480
    def error_func(self):
3✔
481
        """Error function"""
482
        return self._error_func
3✔
483

484
    @error_func.setter
3✔
485
    def error_func(self, value):
3✔
486
        self._error_func = value
3✔
487
        if value is not None:
3✔
488
            self.score_func = None
3✔
489
            if hasattr(self, '_statistic_samples'):
3✔
490
                self._statistic_samples.statistic = value
3✔
491
                self._statistic_samples.BiB = False
3✔
492

493

494
@dataclass
3✔
495
class Difference:
3✔
496
    """Difference
497
    
498
    >>> from sklearn.svm import LinearSVC
499
    >>> from sklearn.ensemble import RandomForestClassifier
500
    >>> from sklearn.datasets import load_iris
501
    >>> from sklearn.model_selection import train_test_split
502
    >>> from sklearn.base import clone
503
    >>> from CompStats.interface import Perf
504
    >>> X, y = load_iris(return_X_y=True)
505
    >>> _ = train_test_split(X, y, test_size=0.3)
506
    >>> X_train, X_val, y_train, y_val = _
507
    >>> m = LinearSVC().fit(X_train, y_train)
508
    >>> hy = m.predict(X_val)
509
    >>> ens = RandomForestClassifier().fit(X_train, y_train)
510
    >>> perf = Perf(y_val, hy, forest=ens.predict(X_val))
511
    >>> diff = perf.difference()
512
    >>> diff
513
    <Difference>
514
    difference p-values w.r.t alg-1
515
    0.0780 <= forest
516
    """
517

518
    statistic_samples:StatisticSamples=None
3✔
519
    statistic:dict=None
3✔
520
    best:str=None
3✔
521

522
    @property
3✔
523
    def sorting_func(self):
3✔
524
        """Rank systems when multiple performances are used"""
525
        return self._sorting_func
3✔
526
    
527
    @sorting_func.setter
3✔
528
    def sorting_func(self, value):
3✔
529
        self._sorting_func = value    
3✔
530

531
    def __repr__(self):
3✔
532
        """p-value"""
NEW
533
        return f"<{self.__class__.__name__}>\n{self}"
×
534

535
    def __str__(self):
3✔
536
        """p-value"""
537
        if isinstance(self.best, str):
3✔
538
            best = f' w.r.t {self.best}'
3✔
539
        else:
540
            best = ''
3✔
541
        output = [f"difference p-values {best}"]
3✔
542
        best = self.best
3✔
543
        if isinstance(best, np.ndarray):
3✔
544
            desc = ', '.join(best)
3✔
545
            output.append(f'{desc} <= Best')
3✔
546
        for key, value in self.p_value().items():
3✔
547
            if isinstance(value, float):
3✔
548
                output.append(f'{value:0.4f} <= {key}')
3✔
549
            else:
550
                desc = [f'{v:0.4f}' for v in value]
3✔
551
                desc = ', '.join(desc)
3✔
552
                desc = f'{desc} <= {key}'
3✔
553
                output.append(desc)
3✔
554
        return "\n".join(output)
3✔
555

556
    def _delta_best(self):
3✔
557
        """Compute multiple delta"""
558
        if isinstance(self.best, str):
3✔
559
            return self.statistic[self.best]
3✔
560
        keys = np.unique(self.best)
3✔
561
        statistic = np.array([self.statistic[k]
3✔
562
                              for k in keys])
563
        m = {v: k for k, v in enumerate(keys)}
3✔
564
        best = np.array([m[x] for x in self.best])
3✔
565
        return statistic[best, np.arange(best.shape[0])]
3✔
566

567
    def p_value(self, right:bool=True):
3✔
568
        """Compute p_value of the differences
569

570
        :param right: Estimate the p-value using :math:`\\text{sample} \\geq 2\\delta`
571
        :type right: bool  
572
        
573
        >>> from sklearn.svm import LinearSVC
574
        >>> from sklearn.ensemble import RandomForestClassifier
575
        >>> from sklearn.datasets import load_iris
576
        >>> from sklearn.model_selection import train_test_split
577
        >>> from sklearn.base import clone
578
        >>> from CompStats.interface import Perf
579
        >>> X, y = load_iris(return_X_y=True)
580
        >>> _ = train_test_split(X, y, test_size=0.3)
581
        >>> X_train, X_val, y_train, y_val = _
582
        >>> m = LinearSVC().fit(X_train, y_train)
583
        >>> hy = m.predict(X_val)
584
        >>> ens = RandomForestClassifier().fit(X_train, y_train)
585
        >>> perf = Perf(y_val, hy, forest=ens.predict(X_val))
586
        >>> diff = perf.difference()
587
        >>> diff.p_value()
588
        {'forest': np.float64(0.3)}
589
        """
590
        values = []
3✔
591
        sign = 1 if self.statistic_samples.BiB else -1
3✔
592
        delta_best = self._delta_best()
3✔
593
        for k, v in self.statistic_samples.calls.items():
3✔
594
            delta = 2 * sign * (delta_best - self.statistic[k])
3✔
595
            if not isinstance(delta_best, np.ndarray):
3✔
596
                if right:
3✔
597
                    values.append((k, (v >= delta).mean()))
3✔
598
                else:
599
                    values.append((k, (v <= 0).mean()))
×
600
            else:
601
                if right:
3✔
602
                    values.append((k, (v >= delta).mean(axis=0)))
3✔
603
                else:
604
                    values.append((k, (v <= 0).mean(axis=0)))
3✔
605
        values.sort(key=lambda x: self.sorting_func(x[1]))
3✔
606
        return dict(values)
3✔
607

608
    def plot(self, **kwargs):
3✔
609
        """Plot
610

611
        >>> from sklearn.svm import LinearSVC
612
        >>> from sklearn.ensemble import RandomForestClassifier
613
        >>> from sklearn.datasets import load_iris
614
        >>> from sklearn.model_selection import train_test_split
615
        >>> from sklearn.base import clone
616
        >>> from CompStats.interface import Perf
617
        >>> X, y = load_iris(return_X_y=True)
618
        >>> _ = train_test_split(X, y, test_size=0.3)
619
        >>> X_train, X_val, y_train, y_val = _
620
        >>> m = LinearSVC().fit(X_train, y_train)
621
        >>> hy = m.predict(X_val)
622
        >>> ens = RandomForestClassifier().fit(X_train, y_train)
623
        >>> perf = Perf(y_val, hy, forest=ens.predict(X_val))
624
        >>> diff = perf.difference()
625
        >>> diff.plot()
626
        """
627

628
        return plot_difference(self.statistic_samples, **kwargs)
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc