aggmetrics 0.1.0.dev0__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.
- aggmetrics/__init__.py +74 -0
- aggmetrics/aggregation.py +210 -0
- aggmetrics/core.py +351 -0
- aggmetrics/diagnostics.py +312 -0
- aggmetrics/inference.py +339 -0
- aggmetrics/measures.py +329 -0
- aggmetrics/sklearn_compat.py +87 -0
- aggmetrics/tnorms.py +111 -0
- aggmetrics-0.1.0.dev0.dist-info/METADATA +203 -0
- aggmetrics-0.1.0.dev0.dist-info/RECORD +13 -0
- aggmetrics-0.1.0.dev0.dist-info/WHEEL +5 -0
- aggmetrics-0.1.0.dev0.dist-info/licenses/LICENSE +29 -0
- aggmetrics-0.1.0.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aggmetrics
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
|
+
Summary: Aggregating classification performance measures for machine learning: indicator matrices and aggregation operators
|
|
5
|
+
Author-email: "Ronaldo C. Prati" <ronaldo.prati@ufabc.edu.br>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/rcprati/aggmetrics
|
|
8
|
+
Project-URL: Repository, https://github.com/rcprati/aggmetrics
|
|
9
|
+
Project-URL: Documentation, https://aggmetrics.readthedocs.io
|
|
10
|
+
Project-URL: Issues, https://github.com/rcprati/aggmetrics/issues
|
|
11
|
+
Keywords: machine learning,classification,evaluation,performance measures,metrics,multilabel,multiclass,class imbalance,confusion matrix
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy>=1.20
|
|
21
|
+
Provides-Extra: sklearn
|
|
22
|
+
Requires-Dist: scikit-learn>=1.0; extra == "sklearn"
|
|
23
|
+
Provides-Extra: frame
|
|
24
|
+
Requires-Dist: pandas>=1.3; extra == "frame"
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
27
|
+
Requires-Dist: scikit-learn>=1.0; extra == "test"
|
|
28
|
+
Requires-Dist: pandas>=1.3; extra == "test"
|
|
29
|
+
Provides-Extra: docs
|
|
30
|
+
Requires-Dist: sphinx>=5.0; extra == "docs"
|
|
31
|
+
Requires-Dist: furo; extra == "docs"
|
|
32
|
+
Provides-Extra: all
|
|
33
|
+
Requires-Dist: scikit-learn>=1.0; extra == "all"
|
|
34
|
+
Requires-Dist: pandas>=1.3; extra == "all"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# aggmetrics
|
|
38
|
+
|
|
39
|
+
**Aggregating classification performance measures for machine learning: indicator matrices and aggregation operators.**
|
|
40
|
+
|
|
41
|
+
Any binary performance measure expressible in terms of the four confusion-matrix
|
|
42
|
+
counts is extended to multiclass, multilabel, ordinal, cost-sensitive and
|
|
43
|
+
soft-label settings by substituting one of a small set of aggregation operators.
|
|
44
|
+
Micro-, macro- and exemplar-averaging (and pooling across cross-validation folds)
|
|
45
|
+
become instances of a single construction rather than separate conventions.
|
|
46
|
+
|
|
47
|
+
The package also reports when a measure is being used outside the range where it
|
|
48
|
+
behaves as commonly assumed — which is the part that is hard to get from a
|
|
49
|
+
formula library.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import numpy as np
|
|
53
|
+
from aggmetrics import IndicatorMatrix, diagnose
|
|
54
|
+
|
|
55
|
+
y_true = np.array([0, 1, 2, 2, 1, 0])
|
|
56
|
+
y_pred = np.array([0, 2, 2, 1, 1, 1])
|
|
57
|
+
im = IndicatorMatrix(y_true, y_pred, task='multiclass')
|
|
58
|
+
|
|
59
|
+
im.compute('f1', 'macro') # 0.5222
|
|
60
|
+
im.compute('f1', 'micro') # 0.5 -- equals accuracy on single-label data
|
|
61
|
+
im.compute('mcc', 'macro') # macro-MCC: no standard implementation exists
|
|
62
|
+
im.compute('informedness', 'micro')
|
|
63
|
+
im.accuracy() # 0.5
|
|
64
|
+
|
|
65
|
+
im.report() # DataFrame: measures x operators
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install aggmetrics # core, requires only numpy
|
|
72
|
+
pip install "aggmetrics[all]" # adds scikit-learn and pandas support
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Why not just use `sklearn.metrics`?
|
|
76
|
+
|
|
77
|
+
`sklearn` exposes four `average` values for a fixed list of measures. The
|
|
78
|
+
correspondence is exact — `'micro'`, `'macro'`, `'weighted'` and `'samples'` are
|
|
79
|
+
Σ₁, Σₘ with uniform weights, Σₘ with prevalence weights and Σₙ respectively — but
|
|
80
|
+
the combinations available are a subset of those the construction generates, and
|
|
81
|
+
nothing in the library tells you when a combination is misleading.
|
|
82
|
+
|
|
83
|
+
Three things this package adds:
|
|
84
|
+
|
|
85
|
+
**Any measure under any operator.** Macro-MCC, exemplar-Jaccard, micro-informedness,
|
|
86
|
+
weighted G-mean. 21 binary measures × 4 operators, without special-casing.
|
|
87
|
+
|
|
88
|
+
**Structural properties as first-class data.** Each measure records whether it is
|
|
89
|
+
*aggregation-decomposable*, whether it is skew-invariant, and whether its value at
|
|
90
|
+
`tp = 0` depends on `tn`. These determine which identities apply.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from aggmetrics import get_measure, list_measures
|
|
94
|
+
|
|
95
|
+
list_measures(decomposable=True) # micro == denominator-weighted macro
|
|
96
|
+
get_measure('mcc').decomposable # False
|
|
97
|
+
get_measure('recall').bayes_threshold() # 0.0 -- maximised by predicting all positive
|
|
98
|
+
get_measure('mcc').attainable_floor(10) # -0.111, not -1
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**The generative claim, as an operation.** Supply a binary formula; the properties
|
|
102
|
+
that decide which identities apply are *derived*, not declared:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from aggmetrics import register_measure, infer_properties
|
|
106
|
+
|
|
107
|
+
def prevalence_threshold(tp, fp, fn, tn):
|
|
108
|
+
r, s = tp / (tp + fn), tn / (tn + fp)
|
|
109
|
+
return (np.sqrt(r * (1 - s)) + s - 1) / (r + s - 1)
|
|
110
|
+
|
|
111
|
+
m = register_measure('prevalence_threshold', prevalence_threshold)
|
|
112
|
+
m.decomposable # False -- inferred by fitting <a,v>/<b,v> and validating
|
|
113
|
+
m.skew_invariant # True -- a function of recall and specificity
|
|
114
|
+
m.bayes_threshold() # None -- no closed form outside the decomposable class
|
|
115
|
+
|
|
116
|
+
im.compute('prevalence_threshold', 'exemplar') # usable immediately
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Inference is checked against the 21 measures whose properties are known
|
|
120
|
+
analytically — `verify_inference()` — and all 21 agree, with recovered coefficients
|
|
121
|
+
accurate to 4.2e-15. The check paid for itself: it found two errors in our own hand
|
|
122
|
+
annotations (error rate and FPR both retain a `tn` dependence at `tp = 0`, so both
|
|
123
|
+
*flatter* a wholly wrong classifier, reaching 2/m and 1/(m-1) respectively).
|
|
124
|
+
|
|
125
|
+
**Diagnostics.** `diagnose()` flags the traps:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
y = np.arange(500) % 10
|
|
129
|
+
yhat = (y + 1) % 10 # every single prediction is wrong
|
|
130
|
+
im = IndicatorMatrix(y, yhat, task='multiclass')
|
|
131
|
+
|
|
132
|
+
im.compute('label_accuracy', 'micro') # 0.800 (!)
|
|
133
|
+
im.compute('mcc', 'micro') # -0.111 (!)
|
|
134
|
+
im.accuracy() # 0.000
|
|
135
|
+
|
|
136
|
+
for f in diagnose(im):
|
|
137
|
+
print(f)
|
|
138
|
+
# [warning] inflated_floor: 'label_accuracy' cannot fall below 0.8000 on this
|
|
139
|
+
# 10-class one-hot problem even if every prediction is wrong ...
|
|
140
|
+
# [note] micro_collapse: micro-precision, micro-recall and micro-F1 are all
|
|
141
|
+
# equal to accuracy ...
|
|
142
|
+
# [warning] degenerate_optimum: 'recall' is maximised at threshold 0.000 ...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Checks currently implemented: pooled value outside the per-class range;
|
|
146
|
+
`tn`-dependent measures on one-hot multiclass data; micro-collapse on single-label
|
|
147
|
+
data; undefined per-class entries and the size of the convention effect; measures
|
|
148
|
+
whose optimal decision rule is not the Bayes rule for 0/1 loss.
|
|
149
|
+
|
|
150
|
+
## Features
|
|
151
|
+
|
|
152
|
+
- **21 binary measures**, resolvable by name or alias (`iou` → `jaccard`, `dice` → `f1`),
|
|
153
|
+
plus `register_measure` for your own, with properties inferred.
|
|
154
|
+
- **Four operators**: `micro`, `macro`, `weighted`, `exemplar`.
|
|
155
|
+
- **Explicit undefined-value conventions**: `exclude` (default), `zero`
|
|
156
|
+
(`sklearn`'s behaviour), `one`, `nan`. The choice is a parameter, not a default
|
|
157
|
+
buried in the implementation.
|
|
158
|
+
- **Soft ground truth** via t-norms, with `marginal_defect()` showing that only the
|
|
159
|
+
product t-norm preserves the marginals.
|
|
160
|
+
- **Cost matrices**, recovering MAE and MSE as special cases.
|
|
161
|
+
- **Observation masks** for partial labels, semi-supervised evaluation and abstention.
|
|
162
|
+
- **scikit-learn scorers** via `make_scorer`, usable in `cross_val_score` and
|
|
163
|
+
`GridSearchCV`.
|
|
164
|
+
- **AI Agent Skills included** (`.agents/skills/aggmetrics-guide/`), providing machine-readable
|
|
165
|
+
runbooks, axiomatic invariants, and diagnostic protocols for AI coding assistants.
|
|
166
|
+
|
|
167
|
+
## The test suite verifies the theory
|
|
168
|
+
|
|
169
|
+
Each result of the companion paper is an executable test, so a change that breaks
|
|
170
|
+
a theorem breaks a test:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
python -m pytest tests/test_theory.py -v
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| Test | Result verified |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `test_theorem1_holds_for_decomposable` | micro = denominator-weighted macro, 15 measures × 3 samples |
|
|
179
|
+
| `test_theorem1_fails_for_non_decomposable` | the identity has no extension beyond that class |
|
|
180
|
+
| `test_theorem2_product_is_uniquely_marginally_consistent` | only the product t-norm preserves marginals |
|
|
181
|
+
| `test_theorem5_micro_collapse` | micro-P = micro-R = micro-F₁ = accuracy, single-label |
|
|
182
|
+
| `test_proposition5_thresholds` | Bayes threshold `(β+δ)/(α+β+γ+δ)` |
|
|
183
|
+
| `test_proposition7_floors` | attainable floors under one-hot encoding |
|
|
184
|
+
| `test_proposition2_recall_prior_free_specificity_not` | one-vs-rest skew behaviour |
|
|
185
|
+
| `test_corollary5_gap_is_normalised_covariance` | the micro–macro gap identity |
|
|
186
|
+
| `test_theorem3_mae_and_mse_as_cost_matrices` | ordinal losses as cost matrices |
|
|
187
|
+
| `test_inference_reproduces_every_annotation` | inference recovers all 21 analytic annotations |
|
|
188
|
+
|
|
189
|
+
138 tests, no skips.
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
python examples/quickstart.py
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Citing
|
|
198
|
+
|
|
199
|
+
Please cite both the software and the companion paper; see `CITATION.cff`.
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
BSD 3-Clause.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
aggmetrics/__init__.py,sha256=sNcuut_3lyhaGmPdfg8dBRCvpQFjqEWqz5cndq_ecoc,2768
|
|
2
|
+
aggmetrics/aggregation.py,sha256=lzDROGNlOgbl8ZEu0PgN8CyEM7-K_clxPUzcEhzwjCc,8620
|
|
3
|
+
aggmetrics/core.py,sha256=OO4p7CDfoP20w9JbFZxSyu51ONqrlSjvbmPcPdgBO5I,15691
|
|
4
|
+
aggmetrics/diagnostics.py,sha256=s65hgdXbPTRDJNi01JJHmI-fVIqVl5Mcjvp2mmZm6pc,13440
|
|
5
|
+
aggmetrics/inference.py,sha256=Q7pPTX-wOpZPnMEiiTGwMfD0pBkUH--r4_uAwLpzi8o,13615
|
|
6
|
+
aggmetrics/measures.py,sha256=s3WcPZ85aiq3w4wzzf94_mRmErDTNTjj5m8XKZ8eeT0,13586
|
|
7
|
+
aggmetrics/sklearn_compat.py,sha256=dHIoChd7OFHXR_J2GFQrMeW-sg8zN7UiygnNafhy7-k,3396
|
|
8
|
+
aggmetrics/tnorms.py,sha256=jN_ar6XfG55Thi9igypKsQDPB_hk3IcUCi3bIk4yR9c,3674
|
|
9
|
+
aggmetrics-0.1.0.dev0.dist-info/licenses/LICENSE,sha256=ZrA8zubLlbGMZUl3UeJbDFPD1u4mgs9MoMXRMQNIcg0,1524
|
|
10
|
+
aggmetrics-0.1.0.dev0.dist-info/METADATA,sha256=qX5Mrn8GTmTwdlYqIMnAa2nJXYroUub7JR9r-Wg-Qm8,8728
|
|
11
|
+
aggmetrics-0.1.0.dev0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
12
|
+
aggmetrics-0.1.0.dev0.dist-info/top_level.txt,sha256=CHCjg30FAscOgGgqmN7mu1MckrPVNphD6pG6BY_Di0s,11
|
|
13
|
+
aggmetrics-0.1.0.dev0.dist-info/RECORD,,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Ronaldo C. Prati
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
17
|
+
may be used to endorse or promote products derived from this software without
|
|
18
|
+
specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
21
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
22
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
aggmetrics
|