tinyconformal 0.1.2__tar.gz → 0.2.0__tar.gz
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.
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/PKG-INFO +67 -27
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/README.md +61 -20
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/pyproject.toml +14 -4
- tinyconformal-0.2.0/tests/test_classifiers.py +200 -0
- tinyconformal-0.2.0/tests/test_regressors.py +249 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/classifier/class_conditional.py +3 -3
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal.egg-info/PKG-INFO +67 -27
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal.egg-info/SOURCES.txt +1 -2
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal.egg-info/requires.txt +5 -6
- tinyconformal-0.1.2/tests/test_classifiers.py +0 -144
- tinyconformal-0.1.2/tinyconformal/plot/__init__.py +0 -6
- tinyconformal-0.1.2/tinyconformal/plot/plot.py +0 -347
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/LICENSE +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/setup.cfg +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/classifier/__init__.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/classifier/base.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/classifier/marginal.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/regressor/__init__.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/regressor/base.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/regressor/cqr.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/regressor/exactness_bound.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal/regressor/icp.py +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal.egg-info/dependency_links.txt +0 -0
- {tinyconformal-0.1.2 → tinyconformal-0.2.0}/tinyconformal.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tinyconformal
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A small toolbox for conformal prediction
|
|
5
5
|
Author-email: Lucas Leão <heylucasleao@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -14,18 +14,21 @@ Requires-Dist: venn-abers>=1.4.6
|
|
|
14
14
|
Requires-Dist: scikit-learn>=1.3.0
|
|
15
15
|
Requires-Dist: quantile-forest>=1.4.2
|
|
16
16
|
Provides-Extra: plot
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist: kaleido<=0.2.1; extra == "plot"
|
|
17
|
+
Requires-Dist: tinyshift[plot]; extra == "plot"
|
|
19
18
|
Provides-Extra: notebook
|
|
20
19
|
Requires-Dist: nbformat>=5.10.4; extra == "notebook"
|
|
21
|
-
Requires-Dist: ipykernel
|
|
22
|
-
Provides-Extra:
|
|
23
|
-
Requires-Dist: plot; extra == "
|
|
24
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: ipykernel>6.7.0; extra == "notebook"
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: tinyconformal[notebook,plot]; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
25
24
|
Dynamic: license-file
|
|
26
25
|
|
|
27
26
|
# TinyConformal
|
|
28
|
-
|
|
27
|
+
|
|
28
|
+
Related project: [tinyshift](https://github.com/HeyLucasLeao/tinyshift)
|
|
29
|
+
|
|
30
|
+
TinyConformal is a Python library for conformal prediction in classification and regression.
|
|
31
|
+
It provides tools to build valid prediction sets and prediction intervals with a target significance level (`alpha`).
|
|
29
32
|
|
|
30
33
|
For more information on a previous project related to Out-of-Bag (OOB) solutions, visit [this link](https://github.com/HeyLucasLeao/cp-study).
|
|
31
34
|
|
|
@@ -40,36 +43,68 @@ Currently, TinyConformal supports Out-of-Bag (OOB) solutions for `RandomForestCl
|
|
|
40
43
|
|
|
41
44
|
## Installation
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
### Using pip
|
|
44
47
|
|
|
45
48
|
```bash
|
|
46
49
|
pip install tinyconformal
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
Optional extras:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install "tinyconformal[plot]"
|
|
56
|
+
pip install "tinyconformal[notebook]"
|
|
57
|
+
pip install "tinyconformal[dev]"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Using uv
|
|
61
|
+
|
|
62
|
+
Install in the current environment:
|
|
50
63
|
|
|
51
64
|
```bash
|
|
52
|
-
|
|
65
|
+
uv pip install tinyconformal
|
|
53
66
|
```
|
|
54
67
|
|
|
55
|
-
|
|
68
|
+
Add as a dependency in a project:
|
|
56
69
|
|
|
57
|
-
|
|
70
|
+
```bash
|
|
71
|
+
uv add tinyconformal
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Optional extras with uv:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uv pip install "tinyconformal[plot]"
|
|
78
|
+
uv pip install "tinyconformal[notebook]"
|
|
79
|
+
uv pip install "tinyconformal[dev]"
|
|
80
|
+
```
|
|
58
81
|
|
|
59
|
-
|
|
82
|
+
## Submodules and usage
|
|
83
|
+
|
|
84
|
+
TinyConformal is organized into two main submodules:
|
|
85
|
+
|
|
86
|
+
- `tinyconformal.classifier`: conformal classifiers for binary classification.
|
|
87
|
+
- `tinyconformal.regressor`: conformal regressors and exactness-bound utilities.
|
|
88
|
+
|
|
89
|
+
### Classifier submodule
|
|
90
|
+
|
|
91
|
+
Import from `tinyconformal.classifier`:
|
|
60
92
|
|
|
61
93
|
```python
|
|
62
|
-
from tinyconformal.classifier import BinaryClassConditionalConformalClassifier
|
|
63
94
|
from tinyconformal.classifier import BinaryMarginalConformalClassifier
|
|
95
|
+
from tinyconformal.classifier import BinaryClassConditionalConformalClassifier
|
|
64
96
|
```
|
|
65
|
-
### Importing Regressors
|
|
66
97
|
|
|
67
|
-
|
|
98
|
+
### Regressor submodule
|
|
99
|
+
|
|
100
|
+
Import from `tinyconformal.regressor`:
|
|
68
101
|
|
|
69
102
|
```python
|
|
70
103
|
from tinyconformal.regressor import ConformalizedRegressor
|
|
71
104
|
from tinyconformal.regressor import ConformalizedQuantileRegressor
|
|
105
|
+
from tinyconformal.regressor import ExactnessBound
|
|
72
106
|
```
|
|
107
|
+
|
|
73
108
|
### Example
|
|
74
109
|
|
|
75
110
|
Example usage of `BinaryClassConditionalConformalClassifier`:
|
|
@@ -85,7 +120,7 @@ learner.fit(X_train, y_train)
|
|
|
85
120
|
|
|
86
121
|
# Create and fit the conformal classifier
|
|
87
122
|
conformal_classifier = BinaryClassConditionalConformalClassifier(learner)
|
|
88
|
-
conformal_classifier.fit(
|
|
123
|
+
conformal_classifier.fit(y=y_train, oob=True)
|
|
89
124
|
|
|
90
125
|
# Make predictions
|
|
91
126
|
X_test = ... # your test data
|
|
@@ -113,13 +148,18 @@ For regressors, you can combine an exactness bound estimate with unlabeled calib
|
|
|
113
148
|
|
|
114
149
|
```python
|
|
115
150
|
from sklearn.ensemble import RandomForestRegressor
|
|
116
|
-
from tinyconformal import ConformalizedRegressor, ExactnessBound
|
|
151
|
+
from tinyconformal.regressor import ConformalizedRegressor, ExactnessBound
|
|
117
152
|
|
|
118
153
|
learner = RandomForestRegressor(random_state=42)
|
|
119
|
-
tilde_beta = ExactnessBound.estimate_icp_bound(
|
|
154
|
+
tilde_beta, beta = ExactnessBound.estimate_icp_bound(
|
|
155
|
+
learner, X_train, y_train, p=0.95, cv=5
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Fit learner before using conformal regressor
|
|
159
|
+
learner.fit(X_train, y_train)
|
|
120
160
|
|
|
121
161
|
regressor = ConformalizedRegressor(learner, alpha=0.05)
|
|
122
|
-
regressor.unlabeled_fit(X_unlabeled, tilde_beta=tilde_beta)
|
|
162
|
+
regressor.unlabeled_fit(X_unlabeled, tilde_beta=tilde_beta, beta=beta)
|
|
123
163
|
|
|
124
164
|
intervals = regressor.predict_interval(X_test)
|
|
125
165
|
```
|
|
@@ -140,16 +180,16 @@ print(results)
|
|
|
140
180
|
`BinaryMarginalConformalClassifier` is a marginal-coverage conformal classifier that uses a classifier as the underlying learner.
|
|
141
181
|
|
|
142
182
|
- Training via labeled calibration: `fit(X, y)`
|
|
143
|
-
- Training via OOB calibration: `fit(
|
|
144
|
-
- Training via unlabeled calibration: `unlabeled_fit(X)`
|
|
183
|
+
- Training via OOB calibration: `fit(y=y_train, oob=True)`
|
|
184
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, beta=...)`
|
|
145
185
|
|
|
146
186
|
### BinaryClassConditionalConformalClassifier
|
|
147
187
|
|
|
148
188
|
`BinaryClassConditionalConformalClassifier` is a class-conditional conformal classifier that uses a classifier as the underlying learner.
|
|
149
189
|
|
|
150
190
|
- Training via labeled calibration: `fit(X, y)`
|
|
151
|
-
- Training via OOB calibration: `fit(
|
|
152
|
-
- Training via unlabeled calibration: `unlabeled_fit(X)` using pseudo-labels derived from the model probabilities
|
|
191
|
+
- Training via OOB calibration: `fit(y=y_train, oob=True)`
|
|
192
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, beta=...)` using pseudo-labels derived from the model probabilities
|
|
153
193
|
|
|
154
194
|
### ConformalizedRegressor
|
|
155
195
|
|
|
@@ -157,7 +197,7 @@ print(results)
|
|
|
157
197
|
|
|
158
198
|
- Training via labeled calibration: `fit(X, y)`
|
|
159
199
|
- Training via OOB calibration: `fit(X, y, oob=True)`
|
|
160
|
-
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=...)` using an exactness bound
|
|
200
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=..., beta=...)` using an exactness bound
|
|
161
201
|
|
|
162
202
|
### ConformalizedQuantileRegressor
|
|
163
203
|
|
|
@@ -165,7 +205,7 @@ print(results)
|
|
|
165
205
|
|
|
166
206
|
- Training via labeled calibration: `fit(X, y)`
|
|
167
207
|
- Training via OOB calibration: `fit(X, y, oob=True)`
|
|
168
|
-
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=...)` using an exactness bound
|
|
208
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=..., beta=...)` using an exactness bound
|
|
169
209
|
|
|
170
210
|
### ExactnessBound
|
|
171
211
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# TinyConformal
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
Related project: [tinyshift](https://github.com/HeyLucasLeao/tinyshift)
|
|
4
|
+
|
|
5
|
+
TinyConformal is a Python library for conformal prediction in classification and regression.
|
|
6
|
+
It provides tools to build valid prediction sets and prediction intervals with a target significance level (`alpha`).
|
|
3
7
|
|
|
4
8
|
For more information on a previous project related to Out-of-Bag (OOB) solutions, visit [this link](https://github.com/HeyLucasLeao/cp-study).
|
|
5
9
|
|
|
@@ -14,36 +18,68 @@ Currently, TinyConformal supports Out-of-Bag (OOB) solutions for `RandomForestCl
|
|
|
14
18
|
|
|
15
19
|
## Installation
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
### Using pip
|
|
18
22
|
|
|
19
23
|
```bash
|
|
20
24
|
pip install tinyconformal
|
|
21
25
|
```
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
Optional extras:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install "tinyconformal[plot]"
|
|
31
|
+
pip install "tinyconformal[notebook]"
|
|
32
|
+
pip install "tinyconformal[dev]"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Using uv
|
|
36
|
+
|
|
37
|
+
Install in the current environment:
|
|
24
38
|
|
|
25
39
|
```bash
|
|
26
|
-
|
|
40
|
+
uv pip install tinyconformal
|
|
27
41
|
```
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
Add as a dependency in a project:
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
```bash
|
|
46
|
+
uv add tinyconformal
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Optional extras with uv:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv pip install "tinyconformal[plot]"
|
|
53
|
+
uv pip install "tinyconformal[notebook]"
|
|
54
|
+
uv pip install "tinyconformal[dev]"
|
|
55
|
+
```
|
|
32
56
|
|
|
33
|
-
|
|
57
|
+
## Submodules and usage
|
|
58
|
+
|
|
59
|
+
TinyConformal is organized into two main submodules:
|
|
60
|
+
|
|
61
|
+
- `tinyconformal.classifier`: conformal classifiers for binary classification.
|
|
62
|
+
- `tinyconformal.regressor`: conformal regressors and exactness-bound utilities.
|
|
63
|
+
|
|
64
|
+
### Classifier submodule
|
|
65
|
+
|
|
66
|
+
Import from `tinyconformal.classifier`:
|
|
34
67
|
|
|
35
68
|
```python
|
|
36
|
-
from tinyconformal.classifier import BinaryClassConditionalConformalClassifier
|
|
37
69
|
from tinyconformal.classifier import BinaryMarginalConformalClassifier
|
|
70
|
+
from tinyconformal.classifier import BinaryClassConditionalConformalClassifier
|
|
38
71
|
```
|
|
39
|
-
### Importing Regressors
|
|
40
72
|
|
|
41
|
-
|
|
73
|
+
### Regressor submodule
|
|
74
|
+
|
|
75
|
+
Import from `tinyconformal.regressor`:
|
|
42
76
|
|
|
43
77
|
```python
|
|
44
78
|
from tinyconformal.regressor import ConformalizedRegressor
|
|
45
79
|
from tinyconformal.regressor import ConformalizedQuantileRegressor
|
|
80
|
+
from tinyconformal.regressor import ExactnessBound
|
|
46
81
|
```
|
|
82
|
+
|
|
47
83
|
### Example
|
|
48
84
|
|
|
49
85
|
Example usage of `BinaryClassConditionalConformalClassifier`:
|
|
@@ -59,7 +95,7 @@ learner.fit(X_train, y_train)
|
|
|
59
95
|
|
|
60
96
|
# Create and fit the conformal classifier
|
|
61
97
|
conformal_classifier = BinaryClassConditionalConformalClassifier(learner)
|
|
62
|
-
conformal_classifier.fit(
|
|
98
|
+
conformal_classifier.fit(y=y_train, oob=True)
|
|
63
99
|
|
|
64
100
|
# Make predictions
|
|
65
101
|
X_test = ... # your test data
|
|
@@ -87,13 +123,18 @@ For regressors, you can combine an exactness bound estimate with unlabeled calib
|
|
|
87
123
|
|
|
88
124
|
```python
|
|
89
125
|
from sklearn.ensemble import RandomForestRegressor
|
|
90
|
-
from tinyconformal import ConformalizedRegressor, ExactnessBound
|
|
126
|
+
from tinyconformal.regressor import ConformalizedRegressor, ExactnessBound
|
|
91
127
|
|
|
92
128
|
learner = RandomForestRegressor(random_state=42)
|
|
93
|
-
tilde_beta = ExactnessBound.estimate_icp_bound(
|
|
129
|
+
tilde_beta, beta = ExactnessBound.estimate_icp_bound(
|
|
130
|
+
learner, X_train, y_train, p=0.95, cv=5
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Fit learner before using conformal regressor
|
|
134
|
+
learner.fit(X_train, y_train)
|
|
94
135
|
|
|
95
136
|
regressor = ConformalizedRegressor(learner, alpha=0.05)
|
|
96
|
-
regressor.unlabeled_fit(X_unlabeled, tilde_beta=tilde_beta)
|
|
137
|
+
regressor.unlabeled_fit(X_unlabeled, tilde_beta=tilde_beta, beta=beta)
|
|
97
138
|
|
|
98
139
|
intervals = regressor.predict_interval(X_test)
|
|
99
140
|
```
|
|
@@ -114,16 +155,16 @@ print(results)
|
|
|
114
155
|
`BinaryMarginalConformalClassifier` is a marginal-coverage conformal classifier that uses a classifier as the underlying learner.
|
|
115
156
|
|
|
116
157
|
- Training via labeled calibration: `fit(X, y)`
|
|
117
|
-
- Training via OOB calibration: `fit(
|
|
118
|
-
- Training via unlabeled calibration: `unlabeled_fit(X)`
|
|
158
|
+
- Training via OOB calibration: `fit(y=y_train, oob=True)`
|
|
159
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, beta=...)`
|
|
119
160
|
|
|
120
161
|
### BinaryClassConditionalConformalClassifier
|
|
121
162
|
|
|
122
163
|
`BinaryClassConditionalConformalClassifier` is a class-conditional conformal classifier that uses a classifier as the underlying learner.
|
|
123
164
|
|
|
124
165
|
- Training via labeled calibration: `fit(X, y)`
|
|
125
|
-
- Training via OOB calibration: `fit(
|
|
126
|
-
- Training via unlabeled calibration: `unlabeled_fit(X)` using pseudo-labels derived from the model probabilities
|
|
166
|
+
- Training via OOB calibration: `fit(y=y_train, oob=True)`
|
|
167
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, beta=...)` using pseudo-labels derived from the model probabilities
|
|
127
168
|
|
|
128
169
|
### ConformalizedRegressor
|
|
129
170
|
|
|
@@ -131,7 +172,7 @@ print(results)
|
|
|
131
172
|
|
|
132
173
|
- Training via labeled calibration: `fit(X, y)`
|
|
133
174
|
- Training via OOB calibration: `fit(X, y, oob=True)`
|
|
134
|
-
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=...)` using an exactness bound
|
|
175
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=..., beta=...)` using an exactness bound
|
|
135
176
|
|
|
136
177
|
### ConformalizedQuantileRegressor
|
|
137
178
|
|
|
@@ -139,7 +180,7 @@ print(results)
|
|
|
139
180
|
|
|
140
181
|
- Training via labeled calibration: `fit(X, y)`
|
|
141
182
|
- Training via OOB calibration: `fit(X, y, oob=True)`
|
|
142
|
-
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=...)` using an exactness bound
|
|
183
|
+
- Training via unlabeled calibration: `unlabeled_fit(X, tilde_beta=..., beta=...)` using an exactness bound
|
|
143
184
|
|
|
144
185
|
### ExactnessBound
|
|
145
186
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tinyconformal"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A small toolbox for conformal prediction"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
authors = [{name = "Lucas Leão", email = "heylucasleao@gmail.com"}]
|
|
@@ -18,6 +18,16 @@ dependencies = [
|
|
|
18
18
|
requires-python = ">=3.10"
|
|
19
19
|
|
|
20
20
|
[project.optional-dependencies]
|
|
21
|
-
plot = [
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
plot = [
|
|
22
|
+
"tinyshift[plot]",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
notebook = [
|
|
26
|
+
"nbformat>=5.10.4",
|
|
27
|
+
"ipykernel>6.7.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
dev = [
|
|
31
|
+
"tinyconformal[plot,notebook]",
|
|
32
|
+
"pytest>=7.0.0",
|
|
33
|
+
]
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026 Lucas Leão
|
|
2
|
+
# TinyConformal - A small toolbox for conformal prediction
|
|
3
|
+
# Licensed under the MIT License
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
import numpy as np
|
|
8
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
9
|
+
from sklearn.model_selection import train_test_split
|
|
10
|
+
from sklearn.datasets import make_classification
|
|
11
|
+
from tinyconformal.classifier.marginal import BinaryMarginalConformalClassifier
|
|
12
|
+
from tinyconformal.classifier.class_conditional import (
|
|
13
|
+
BinaryClassConditionalConformalClassifier,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def dataset():
|
|
19
|
+
weights = [0.4, 0.6]
|
|
20
|
+
seed = 42
|
|
21
|
+
|
|
22
|
+
X, y = make_classification(
|
|
23
|
+
n_samples=1500,
|
|
24
|
+
n_features=20,
|
|
25
|
+
n_informative=2,
|
|
26
|
+
weights=weights,
|
|
27
|
+
random_state=seed,
|
|
28
|
+
n_redundant=2,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
X_train, X_test, y_train, y_test = train_test_split(
|
|
32
|
+
X, y, test_size=0.2, random_state=seed, stratify=y
|
|
33
|
+
)
|
|
34
|
+
X_train, X_calib, y_train, y_calib = train_test_split(
|
|
35
|
+
X_train, y_train, test_size=0.25, random_state=seed, stratify=y_train
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
"X_train": X_train,
|
|
40
|
+
"y_train": y_train,
|
|
41
|
+
"X_calib": X_calib,
|
|
42
|
+
"y_calib": y_calib,
|
|
43
|
+
"X_test": X_test,
|
|
44
|
+
"y_test": y_test,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.fixture
|
|
49
|
+
def learner(dataset):
|
|
50
|
+
model = RandomForestClassifier(oob_score=True, n_estimators=10)
|
|
51
|
+
model.fit(dataset["X_train"], dataset["y_train"])
|
|
52
|
+
return model
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _assert_classifier_outputs(classifier, dataset):
|
|
56
|
+
classifier.calibrate(dataset["X_calib"], dataset["y_calib"])
|
|
57
|
+
assert 0 < classifier.alpha <= 0.2
|
|
58
|
+
|
|
59
|
+
y_proba = classifier.predict_proba(dataset["X_test"])
|
|
60
|
+
assert y_proba.shape == (dataset["X_test"].shape[0], 2)
|
|
61
|
+
|
|
62
|
+
prediction_set = classifier.predict_set(dataset["X_test"])
|
|
63
|
+
assert prediction_set.shape == (dataset["X_test"].shape[0], 2)
|
|
64
|
+
|
|
65
|
+
p_values = classifier.predict_p(dataset["X_test"])
|
|
66
|
+
assert p_values.shape == (dataset["X_test"].shape[0], 2)
|
|
67
|
+
|
|
68
|
+
y_pred = classifier.predict(dataset["X_test"])
|
|
69
|
+
assert y_pred.shape == (dataset["X_test"].shape[0],)
|
|
70
|
+
|
|
71
|
+
eval_dict = classifier.evaluate(dataset["X_test"], dataset["y_test"])
|
|
72
|
+
assert isinstance(eval_dict, dict)
|
|
73
|
+
expected_keys = {
|
|
74
|
+
"total",
|
|
75
|
+
"alpha",
|
|
76
|
+
"beta",
|
|
77
|
+
"coverage_rate",
|
|
78
|
+
"one_c",
|
|
79
|
+
"avg_c",
|
|
80
|
+
"empty",
|
|
81
|
+
"error",
|
|
82
|
+
"log_loss",
|
|
83
|
+
"ece",
|
|
84
|
+
"bm",
|
|
85
|
+
"mcc",
|
|
86
|
+
"f1",
|
|
87
|
+
"fpr",
|
|
88
|
+
}
|
|
89
|
+
assert set(eval_dict.keys()) == expected_keys
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_marginal_classifier(dataset, learner):
|
|
93
|
+
classifier = BinaryMarginalConformalClassifier(learner)
|
|
94
|
+
classifier.fit(dataset["X_calib"], dataset["y_calib"], oob=False)
|
|
95
|
+
_assert_classifier_outputs(classifier, dataset)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_class_cond_classifier(dataset, learner):
|
|
99
|
+
classifier = BinaryClassConditionalConformalClassifier(learner)
|
|
100
|
+
classifier.fit(dataset["X_calib"], dataset["y_calib"], oob=False)
|
|
101
|
+
_assert_classifier_outputs(classifier, dataset)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_oob_marginal_classifier(dataset, learner):
|
|
105
|
+
classifier = BinaryMarginalConformalClassifier(learner)
|
|
106
|
+
classifier.fit(y=dataset["y_train"], oob=True)
|
|
107
|
+
_assert_classifier_outputs(classifier, dataset)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_oob_class_conditional_classifier(dataset, learner):
|
|
111
|
+
classifier = BinaryClassConditionalConformalClassifier(learner)
|
|
112
|
+
classifier.fit(y=dataset["y_train"], oob=True)
|
|
113
|
+
_assert_classifier_outputs(classifier, dataset)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.mark.parametrize(
|
|
117
|
+
"classifier_cls",
|
|
118
|
+
[BinaryMarginalConformalClassifier, BinaryClassConditionalConformalClassifier],
|
|
119
|
+
)
|
|
120
|
+
def test_unlabeled_fit_requires_X(classifier_cls, learner):
|
|
121
|
+
classifier = classifier_cls(learner)
|
|
122
|
+
|
|
123
|
+
with pytest.raises(ValueError, match="Unlabeled calibration data"):
|
|
124
|
+
classifier.unlabeled_fit(X=None)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@pytest.mark.parametrize(
|
|
128
|
+
"classifier_cls",
|
|
129
|
+
[BinaryMarginalConformalClassifier, BinaryClassConditionalConformalClassifier],
|
|
130
|
+
)
|
|
131
|
+
def test_unlabeled_fit_warns_when_beta_missing(classifier_cls, learner, dataset):
|
|
132
|
+
classifier = classifier_cls(learner)
|
|
133
|
+
|
|
134
|
+
with pytest.warns(UserWarning, match="beta"):
|
|
135
|
+
classifier.unlabeled_fit(dataset["X_calib"])
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@pytest.mark.parametrize(
|
|
139
|
+
"classifier_cls",
|
|
140
|
+
[BinaryMarginalConformalClassifier, BinaryClassConditionalConformalClassifier],
|
|
141
|
+
)
|
|
142
|
+
def test_unlabeled_fit_disables_calibration(classifier_cls, learner, dataset):
|
|
143
|
+
classifier = classifier_cls(learner)
|
|
144
|
+
classifier.unlabeled_fit(dataset["X_calib"], beta=0.1)
|
|
145
|
+
|
|
146
|
+
with pytest.raises(ValueError, match="Calibration is not applicable"):
|
|
147
|
+
classifier.calibrate(dataset["X_test"], dataset["y_test"])
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_unlabeled_fit_marginal_predict_p_properties(dataset, learner):
|
|
151
|
+
classifier = BinaryMarginalConformalClassifier(learner)
|
|
152
|
+
classifier.unlabeled_fit(dataset["X_calib"], beta=0.1)
|
|
153
|
+
|
|
154
|
+
assert classifier.is_unlabeled is True
|
|
155
|
+
assert classifier.beta == 0.1
|
|
156
|
+
assert classifier.hinge.shape == (dataset["X_calib"].shape[0],)
|
|
157
|
+
assert classifier.n == dataset["X_calib"].shape[0]
|
|
158
|
+
|
|
159
|
+
p_values = classifier.predict_p(dataset["X_test"])
|
|
160
|
+
assert p_values.shape == (dataset["X_test"].shape[0], 2)
|
|
161
|
+
assert np.all(p_values >= 0)
|
|
162
|
+
assert np.all(p_values <= 1)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_unlabeled_fit_class_conditional_predict_p_properties(dataset, learner):
|
|
166
|
+
classifier = BinaryClassConditionalConformalClassifier(learner)
|
|
167
|
+
classifier.unlabeled_fit(dataset["X_calib"], beta=0.1)
|
|
168
|
+
|
|
169
|
+
assert classifier.is_unlabeled is True
|
|
170
|
+
assert classifier.beta == 0.1
|
|
171
|
+
assert len(classifier.hinge) == 2
|
|
172
|
+
assert len(classifier.n) == 2
|
|
173
|
+
assert sum(classifier.n) == dataset["X_calib"].shape[0]
|
|
174
|
+
|
|
175
|
+
p_values = classifier.predict_p(dataset["X_test"])
|
|
176
|
+
assert p_values.shape == (dataset["X_test"].shape[0], 2)
|
|
177
|
+
assert np.all(p_values >= 0)
|
|
178
|
+
assert np.all(p_values <= 1)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@pytest.mark.parametrize(
|
|
182
|
+
"classifier_cls",
|
|
183
|
+
[BinaryMarginalConformalClassifier, BinaryClassConditionalConformalClassifier],
|
|
184
|
+
)
|
|
185
|
+
def test_fit_requires_y(classifier_cls, learner, dataset):
|
|
186
|
+
classifier = classifier_cls(learner)
|
|
187
|
+
|
|
188
|
+
with pytest.raises(ValueError, match="true labels"):
|
|
189
|
+
classifier.fit(dataset["X_calib"], y=None, oob=False)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@pytest.mark.parametrize(
|
|
193
|
+
"classifier_cls",
|
|
194
|
+
[BinaryMarginalConformalClassifier, BinaryClassConditionalConformalClassifier],
|
|
195
|
+
)
|
|
196
|
+
def test_fit_oob_rejects_X(classifier_cls, learner, dataset):
|
|
197
|
+
classifier = classifier_cls(learner)
|
|
198
|
+
|
|
199
|
+
with pytest.raises(ValueError, match="should not be provided"):
|
|
200
|
+
classifier.fit(dataset["X_calib"], y=dataset["y_train"], oob=True)
|