tabaudit 0.1.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.
tabaudit-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sadia Samia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: tabaudit
3
+ Version: 0.1.0
4
+ Summary: Audit tabular ML datasets for leakage, duplicates, label noise and imbalance before you train.
5
+ Author: Sadia Samia
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/sadiasamia121912/tabaudit
8
+ Project-URL: Issues, https://github.com/sadiasamia121912/tabaudit/issues
9
+ Keywords: machine-learning,data-quality,data-leakage,label-noise,cleanlab,audit
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: pandas>=2.0
23
+ Requires-Dist: numpy>=1.24
24
+ Requires-Dist: scikit-learn>=1.3
25
+ Requires-Dist: cleanlab>=2.5
26
+ Requires-Dist: typer>=0.12
27
+ Requires-Dist: rich>=13.0
28
+ Requires-Dist: jinja2>=3.1
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8; extra == "dev"
31
+ Requires-Dist: ruff>=0.5; extra == "dev"
32
+ Requires-Dist: build; extra == "dev"
33
+ Requires-Dist: twine; extra == "dev"
34
+ Requires-Dist: pyarrow; extra == "dev"
35
+ Dynamic: license-file
36
+
37
+ # tabaudit
38
+
39
+ **Find the problems in your dataset before your model does.**
40
+
41
+ `tabaudit` is a command-line tool that audits a tabular ML dataset for the defects that
42
+ quietly inflate metrics and break models in production — target leakage, train/test
43
+ contamination, duplicates, label noise, class imbalance and schema problems — and gives it
44
+ a single **Data Health Score** with concrete, prioritised fixes.
45
+
46
+ ```
47
+ pip install tabaudit
48
+ tabaudit audit train.csv --target churn --test test.csv --html report.html
49
+ ```
50
+
51
+ ![tabaudit demo](https://raw.githubusercontent.com/sadiasamia121912/tabaudit/main/docs/demo.gif)
52
+
53
+ ---
54
+
55
+ ## Why
56
+
57
+ A model that scores 99% on a leaked dataset is worse than useless: it looks finished and
58
+ fails silently in production. Most of these defects take one line of pandas to fix — the
59
+ hard part is *noticing* them. `tabaudit` makes the check automatic, fast, and repeatable
60
+ (it runs in CI with `--fail-under`).
61
+
62
+ ## What it checks
63
+
64
+ | check | finds | severity |
65
+ | ------------- | --------------------------------------------------------------------------------------------------------- | -------- |
66
+ | `leakage` | single features that predict the target almost perfectly on their own (incl. *missingness* leaks), identifier columns, columns named after the target | CRITICAL / HIGH / MEDIUM |
67
+ | `duplicates` | exact duplicate rows, feature-identical rows with conflicting labels, **test rows that also appear in train** | CRITICAL → LOW |
68
+ | `label_noise` | probably-mislabeled rows via confident learning ([cleanlab](https://github.com/cleanlab/cleanlab)), ranked and tiered | HIGH → INFO |
69
+ | `imbalance` | class ratio, classes with < 10 examples | HIGH → LOW |
70
+ | `schema` | ≥ 50 % missing columns, missing labels, constant / near-constant columns, numbers stored as text, leftover index columns | HIGH → INFO |
71
+
72
+ Every finding carries a plain-English explanation of *why it matters* and *what to do*.
73
+ Findings are weighted into a 0–100 score and an A–F grade.
74
+
75
+ ## Quick start
76
+
77
+ ```bash
78
+ pip install tabaudit
79
+
80
+ # See every check fire on a synthetic dataset with planted defects
81
+ tabaudit demo
82
+
83
+ # Audit your own data
84
+ tabaudit audit data.csv --target label
85
+ tabaudit audit train.parquet -t label --test test.parquet --html report.html --json report.json
86
+
87
+ # Only some checks, sampled for speed, gate a CI pipeline
88
+ tabaudit audit data.csv -t label -c leakage,duplicates --max-rows 20000 --fail-under 75
89
+ ```
90
+
91
+ Supported inputs: CSV, TSV, Parquet, Feather, JSON-lines. Classification and regression
92
+ targets are inferred automatically; omit `--target` to run only the unsupervised checks.
93
+
94
+ `--html` writes a self-contained report you can send to whoever owns the data:
95
+
96
+ ![tabaudit HTML report](https://raw.githubusercontent.com/sadiasamia121912/tabaudit/main/docs/report_screenshot.png)
97
+
98
+ ### Python API
99
+
100
+ ```python
101
+ from tabaudit import run_audit
102
+
103
+ report = run_audit("train.csv", target="churn", test="test.csv")
104
+ print(report.score, report.grade) # 6 'F'
105
+ for f in report.sorted_findings():
106
+ print(f.severity.value, f.title, f.columns)
107
+ report.to_dict() # JSON-serialisable
108
+ ```
109
+
110
+ ## Example output
111
+
112
+ ```
113
+ ┌────────────────────────────────── Verdict ──────────────────────────────────┐
114
+ │ Data Health Score 6/100 F │
115
+ │ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
116
+ │ Do not train on this dataset as-is │
117
+ │ CRITICAL 2 MEDIUM 4 LOW 2 │
118
+ └─────────────────────────────────────────────────────────────────────────────┘
119
+ ┌─ CRITICAL 105 test rows (8.2%) also appear in the training set ──────────┐
120
+ │ The model has already seen these rows. Any test-set score is partly │
121
+ │ memorisation, not generalisation. │
122
+ │ → Remove overlapping rows from the test set, or re-split with a │
123
+ │ group-aware splitter if rows belong to entities. │
124
+ └──────────────────────────────────────────────────────────────── duplicates ─┘
125
+ ┌─ CRITICAL 1 feature(s) predict the target almost perfectly on their own ─┐
126
+ │ churn_reason (AUC=1.000) - its *missingness alone* has AUC 1.00 │
127
+ │ → This is target leakage: the column encodes the answer (recorded after │
128
+ │ the outcome, or derived from it). Remove it - any model trained with it │
129
+ │ will look excellent and fail in production. │
130
+ └─────────────────────────────────────────────────────────────────── leakage ─┘
131
+ ```
132
+
133
+ ## Results on real datasets
134
+
135
+ Ten well-known public datasets, loaded straight from OpenML and audited with **default
136
+ settings** — no tuning, no column dropping. Full write-up, including what the tool got
137
+ wrong on the first run and how it was fixed: [`docs/benchmarks.md`](https://github.com/sadiasamia121912/tabaudit/blob/main/docs/benchmarks.md).
138
+
139
+ | dataset | rows | score | headline finding |
140
+ |---|---:|:-:|---|
141
+ | titanic | 1 309 | 64 C | **HIGH** `boat` predicts survival alone (AUC 0.97 vs next-best 0.74) — target leakage |
142
+ | spambase | 4 601 | 75 B | **HIGH** 391 exact duplicate rows (8.5 %) |
143
+ | creditcard | 284 807 | 78 B | **HIGH** 578 : 1 class imbalance; 9 144 duplicate rows |
144
+ | telco-customer-churn | 7 043 | 80 B | `TotalCharges` is numeric but stored as text; 18 conflicting-label groups |
145
+ | breast-w | 699 | 82 B | **HIGH** 236 exact duplicate rows (34 %) |
146
+ | bank-marketing | 45 211 | 83 B | `duration` stands far above every other feature (AUC 0.81 vs 0.65) — a documented leak |
147
+ | adult | 48 842 | 84 B | 5 groups of rows with identical features but different labels |
148
+ | credit-g | 1 000 | 93 A | ~6 % of rows likely mislabeled |
149
+ | heart-statlog | 270 | 93 A | ~6 % of rows likely mislabeled |
150
+ | diabetes | 768 | 93 A | ~5 % of rows likely mislabeled |
151
+
152
+ - **4 of 10 have a CRITICAL/HIGH finding; 10 of 10 have at least one MEDIUM.** Every HIGH
153
+ is a documented property of the dataset (Titanic's lifeboat column, spambase and
154
+ breast-w duplicates, creditcard's 0.17 % fraud rate).
155
+ - The leakage check catches both the near-perfect leak (`boat`) and the *soft* one
156
+ (bank-marketing `duration`, which the dataset's own documentation says to drop), while
157
+ correctly reporting breast-w's six strong-but-honest features as "easy task", not leakage.
158
+ - Label-noise suspects were checked by hand on two datasets: of the 10 top-ranked rows,
159
+ 5 look genuinely mislabeled, 5 are ambiguous, 0 look like false alarms
160
+ ([review sheet](https://github.com/sadiasamia121912/tabaudit/blob/main/docs/label_noise_review.md)).
161
+
162
+ Reproduce with `python benchmarks/run_benchmarks.py` (~6 min, downloads ~50 MB).
163
+
164
+ ## How the hard checks work
165
+
166
+ _Short version. Every threshold, and the reason for it, is in [`docs/checks.md`](https://github.com/sadiasamia121912/tabaudit/blob/main/docs/checks.md)._
167
+
168
+ **Leakage.** For each feature *alone*, a shallow decision tree is cross-validated against
169
+ the target. A single column with out-of-fold AUC ≥ 0.98 (or R² ≥ 0.98 for regression) is
170
+ almost never a legitimate signal — it is the answer written down after the fact. Below
171
+ that, a leak has to be an *outlier*: the sorted single-feature scores are split at their
172
+ largest gap, and only a feature that sits ≥ 0.15 above every other one is flagged (HIGH if
173
+ it scores ≥ 0.90, MEDIUM "soft leak" if ≥ 0.75). Several strong features bunched together
174
+ mean the task is easy, not leaky — that case is reported at INFO. Missing values are
175
+ encoded so the tree can split on *missingness itself*, which catches the common "this field
176
+ is only filled in for positives" leak.
177
+
178
+ **Label noise.** An out-of-fold gradient-boosting model produces class probabilities for
179
+ every row; cleanlab's confident-learning filter flags rows whose given label it confidently
180
+ contradicts. The model is deliberately regularised because an over-confident model makes
181
+ cleanlab over-flag. Leaky and identifier columns found by the `leakage` check are excluded
182
+ first — otherwise the leak makes the model agree with every wrong label and the noise is
183
+ invisible.
184
+
185
+ Suspects are reported in two tiers: **likely** (model gives the given label < 20 %
186
+ probability) and **suspected** (any confident-learning flag), ranked most-confident first.
187
+
188
+ ### Measured on known ground truth
189
+
190
+ The demo generator knows exactly which labels it flipped, so the detector can be scored
191
+ honestly (`python examples/validate_label_noise.py <noise_rate>`; 6 000 rows, clean-data
192
+ AUC ≈ 0.88):
193
+
194
+ | planted noise | flipped rows | "likely" flagged | likely precision / recall | top-25 precision |
195
+ | ------------- | ------------ | ---------------- | ------------------------- | ---------------- |
196
+ | 2.2 % | 110 | 187 | 32 % / 55 % | 56 % |
197
+ | 6.4 % | 317 | 313 | 60 % / 59 % | 72 % |
198
+ | 10.3 % | 514 | 420 | 73 % / 60 % | 72 % |
199
+
200
+ Random label flips on rows the model is genuinely unsure about are indistinguishable from
201
+ correct labels, so no detector can reach high precision *and* recall here. The point is
202
+ the ranked review list, not the raw count — and the count is a useful estimate at realistic
203
+ noise rates.
204
+
205
+ ## Development
206
+
207
+ ```bash
208
+ git clone https://github.com/sadiasamia121912/tabaudit && cd tabaudit
209
+ python -m venv .venv && .venv/Scripts/activate # or source .venv/bin/activate
210
+ pip install -e ".[dev]"
211
+ pytest
212
+ ruff check src tests examples && ruff format --check src tests examples
213
+ ```
214
+
215
+ Adding a check: drop a module in `src/tabaudit/checks/` exposing
216
+ `run(ctx: AuditContext) -> list[Finding]` and register it in `checks/__init__.py`.
217
+ Checks run in registry order and may communicate through `ctx.excluded_features`.
218
+
219
+ ## Roadmap
220
+
221
+ - [ ] Group / time leakage: entity IDs shared across splits, features that peek into the future
222
+ - [ ] Near-duplicate detection (fuzzy text, numeric tolerance)
223
+ - [ ] Label-noise support for regression targets
224
+ - [x] Audit results for popular public benchmark datasets — see [Results on real datasets](#results-on-real-datasets)
225
+ - [ ] `pre-commit` hook and GitHub Action
226
+
227
+ ## License
228
+
229
+ MIT
@@ -0,0 +1,193 @@
1
+ # tabaudit
2
+
3
+ **Find the problems in your dataset before your model does.**
4
+
5
+ `tabaudit` is a command-line tool that audits a tabular ML dataset for the defects that
6
+ quietly inflate metrics and break models in production — target leakage, train/test
7
+ contamination, duplicates, label noise, class imbalance and schema problems — and gives it
8
+ a single **Data Health Score** with concrete, prioritised fixes.
9
+
10
+ ```
11
+ pip install tabaudit
12
+ tabaudit audit train.csv --target churn --test test.csv --html report.html
13
+ ```
14
+
15
+ ![tabaudit demo](https://raw.githubusercontent.com/sadiasamia121912/tabaudit/main/docs/demo.gif)
16
+
17
+ ---
18
+
19
+ ## Why
20
+
21
+ A model that scores 99% on a leaked dataset is worse than useless: it looks finished and
22
+ fails silently in production. Most of these defects take one line of pandas to fix — the
23
+ hard part is *noticing* them. `tabaudit` makes the check automatic, fast, and repeatable
24
+ (it runs in CI with `--fail-under`).
25
+
26
+ ## What it checks
27
+
28
+ | check | finds | severity |
29
+ | ------------- | --------------------------------------------------------------------------------------------------------- | -------- |
30
+ | `leakage` | single features that predict the target almost perfectly on their own (incl. *missingness* leaks), identifier columns, columns named after the target | CRITICAL / HIGH / MEDIUM |
31
+ | `duplicates` | exact duplicate rows, feature-identical rows with conflicting labels, **test rows that also appear in train** | CRITICAL → LOW |
32
+ | `label_noise` | probably-mislabeled rows via confident learning ([cleanlab](https://github.com/cleanlab/cleanlab)), ranked and tiered | HIGH → INFO |
33
+ | `imbalance` | class ratio, classes with < 10 examples | HIGH → LOW |
34
+ | `schema` | ≥ 50 % missing columns, missing labels, constant / near-constant columns, numbers stored as text, leftover index columns | HIGH → INFO |
35
+
36
+ Every finding carries a plain-English explanation of *why it matters* and *what to do*.
37
+ Findings are weighted into a 0–100 score and an A–F grade.
38
+
39
+ ## Quick start
40
+
41
+ ```bash
42
+ pip install tabaudit
43
+
44
+ # See every check fire on a synthetic dataset with planted defects
45
+ tabaudit demo
46
+
47
+ # Audit your own data
48
+ tabaudit audit data.csv --target label
49
+ tabaudit audit train.parquet -t label --test test.parquet --html report.html --json report.json
50
+
51
+ # Only some checks, sampled for speed, gate a CI pipeline
52
+ tabaudit audit data.csv -t label -c leakage,duplicates --max-rows 20000 --fail-under 75
53
+ ```
54
+
55
+ Supported inputs: CSV, TSV, Parquet, Feather, JSON-lines. Classification and regression
56
+ targets are inferred automatically; omit `--target` to run only the unsupervised checks.
57
+
58
+ `--html` writes a self-contained report you can send to whoever owns the data:
59
+
60
+ ![tabaudit HTML report](https://raw.githubusercontent.com/sadiasamia121912/tabaudit/main/docs/report_screenshot.png)
61
+
62
+ ### Python API
63
+
64
+ ```python
65
+ from tabaudit import run_audit
66
+
67
+ report = run_audit("train.csv", target="churn", test="test.csv")
68
+ print(report.score, report.grade) # 6 'F'
69
+ for f in report.sorted_findings():
70
+ print(f.severity.value, f.title, f.columns)
71
+ report.to_dict() # JSON-serialisable
72
+ ```
73
+
74
+ ## Example output
75
+
76
+ ```
77
+ ┌────────────────────────────────── Verdict ──────────────────────────────────┐
78
+ │ Data Health Score 6/100 F │
79
+ │ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
80
+ │ Do not train on this dataset as-is │
81
+ │ CRITICAL 2 MEDIUM 4 LOW 2 │
82
+ └─────────────────────────────────────────────────────────────────────────────┘
83
+ ┌─ CRITICAL 105 test rows (8.2%) also appear in the training set ──────────┐
84
+ │ The model has already seen these rows. Any test-set score is partly │
85
+ │ memorisation, not generalisation. │
86
+ │ → Remove overlapping rows from the test set, or re-split with a │
87
+ │ group-aware splitter if rows belong to entities. │
88
+ └──────────────────────────────────────────────────────────────── duplicates ─┘
89
+ ┌─ CRITICAL 1 feature(s) predict the target almost perfectly on their own ─┐
90
+ │ churn_reason (AUC=1.000) - its *missingness alone* has AUC 1.00 │
91
+ │ → This is target leakage: the column encodes the answer (recorded after │
92
+ │ the outcome, or derived from it). Remove it - any model trained with it │
93
+ │ will look excellent and fail in production. │
94
+ └─────────────────────────────────────────────────────────────────── leakage ─┘
95
+ ```
96
+
97
+ ## Results on real datasets
98
+
99
+ Ten well-known public datasets, loaded straight from OpenML and audited with **default
100
+ settings** — no tuning, no column dropping. Full write-up, including what the tool got
101
+ wrong on the first run and how it was fixed: [`docs/benchmarks.md`](https://github.com/sadiasamia121912/tabaudit/blob/main/docs/benchmarks.md).
102
+
103
+ | dataset | rows | score | headline finding |
104
+ |---|---:|:-:|---|
105
+ | titanic | 1 309 | 64 C | **HIGH** `boat` predicts survival alone (AUC 0.97 vs next-best 0.74) — target leakage |
106
+ | spambase | 4 601 | 75 B | **HIGH** 391 exact duplicate rows (8.5 %) |
107
+ | creditcard | 284 807 | 78 B | **HIGH** 578 : 1 class imbalance; 9 144 duplicate rows |
108
+ | telco-customer-churn | 7 043 | 80 B | `TotalCharges` is numeric but stored as text; 18 conflicting-label groups |
109
+ | breast-w | 699 | 82 B | **HIGH** 236 exact duplicate rows (34 %) |
110
+ | bank-marketing | 45 211 | 83 B | `duration` stands far above every other feature (AUC 0.81 vs 0.65) — a documented leak |
111
+ | adult | 48 842 | 84 B | 5 groups of rows with identical features but different labels |
112
+ | credit-g | 1 000 | 93 A | ~6 % of rows likely mislabeled |
113
+ | heart-statlog | 270 | 93 A | ~6 % of rows likely mislabeled |
114
+ | diabetes | 768 | 93 A | ~5 % of rows likely mislabeled |
115
+
116
+ - **4 of 10 have a CRITICAL/HIGH finding; 10 of 10 have at least one MEDIUM.** Every HIGH
117
+ is a documented property of the dataset (Titanic's lifeboat column, spambase and
118
+ breast-w duplicates, creditcard's 0.17 % fraud rate).
119
+ - The leakage check catches both the near-perfect leak (`boat`) and the *soft* one
120
+ (bank-marketing `duration`, which the dataset's own documentation says to drop), while
121
+ correctly reporting breast-w's six strong-but-honest features as "easy task", not leakage.
122
+ - Label-noise suspects were checked by hand on two datasets: of the 10 top-ranked rows,
123
+ 5 look genuinely mislabeled, 5 are ambiguous, 0 look like false alarms
124
+ ([review sheet](https://github.com/sadiasamia121912/tabaudit/blob/main/docs/label_noise_review.md)).
125
+
126
+ Reproduce with `python benchmarks/run_benchmarks.py` (~6 min, downloads ~50 MB).
127
+
128
+ ## How the hard checks work
129
+
130
+ _Short version. Every threshold, and the reason for it, is in [`docs/checks.md`](https://github.com/sadiasamia121912/tabaudit/blob/main/docs/checks.md)._
131
+
132
+ **Leakage.** For each feature *alone*, a shallow decision tree is cross-validated against
133
+ the target. A single column with out-of-fold AUC ≥ 0.98 (or R² ≥ 0.98 for regression) is
134
+ almost never a legitimate signal — it is the answer written down after the fact. Below
135
+ that, a leak has to be an *outlier*: the sorted single-feature scores are split at their
136
+ largest gap, and only a feature that sits ≥ 0.15 above every other one is flagged (HIGH if
137
+ it scores ≥ 0.90, MEDIUM "soft leak" if ≥ 0.75). Several strong features bunched together
138
+ mean the task is easy, not leaky — that case is reported at INFO. Missing values are
139
+ encoded so the tree can split on *missingness itself*, which catches the common "this field
140
+ is only filled in for positives" leak.
141
+
142
+ **Label noise.** An out-of-fold gradient-boosting model produces class probabilities for
143
+ every row; cleanlab's confident-learning filter flags rows whose given label it confidently
144
+ contradicts. The model is deliberately regularised because an over-confident model makes
145
+ cleanlab over-flag. Leaky and identifier columns found by the `leakage` check are excluded
146
+ first — otherwise the leak makes the model agree with every wrong label and the noise is
147
+ invisible.
148
+
149
+ Suspects are reported in two tiers: **likely** (model gives the given label < 20 %
150
+ probability) and **suspected** (any confident-learning flag), ranked most-confident first.
151
+
152
+ ### Measured on known ground truth
153
+
154
+ The demo generator knows exactly which labels it flipped, so the detector can be scored
155
+ honestly (`python examples/validate_label_noise.py <noise_rate>`; 6 000 rows, clean-data
156
+ AUC ≈ 0.88):
157
+
158
+ | planted noise | flipped rows | "likely" flagged | likely precision / recall | top-25 precision |
159
+ | ------------- | ------------ | ---------------- | ------------------------- | ---------------- |
160
+ | 2.2 % | 110 | 187 | 32 % / 55 % | 56 % |
161
+ | 6.4 % | 317 | 313 | 60 % / 59 % | 72 % |
162
+ | 10.3 % | 514 | 420 | 73 % / 60 % | 72 % |
163
+
164
+ Random label flips on rows the model is genuinely unsure about are indistinguishable from
165
+ correct labels, so no detector can reach high precision *and* recall here. The point is
166
+ the ranked review list, not the raw count — and the count is a useful estimate at realistic
167
+ noise rates.
168
+
169
+ ## Development
170
+
171
+ ```bash
172
+ git clone https://github.com/sadiasamia121912/tabaudit && cd tabaudit
173
+ python -m venv .venv && .venv/Scripts/activate # or source .venv/bin/activate
174
+ pip install -e ".[dev]"
175
+ pytest
176
+ ruff check src tests examples && ruff format --check src tests examples
177
+ ```
178
+
179
+ Adding a check: drop a module in `src/tabaudit/checks/` exposing
180
+ `run(ctx: AuditContext) -> list[Finding]` and register it in `checks/__init__.py`.
181
+ Checks run in registry order and may communicate through `ctx.excluded_features`.
182
+
183
+ ## Roadmap
184
+
185
+ - [ ] Group / time leakage: entity IDs shared across splits, features that peek into the future
186
+ - [ ] Near-duplicate detection (fuzzy text, numeric tolerance)
187
+ - [ ] Label-noise support for regression targets
188
+ - [x] Audit results for popular public benchmark datasets — see [Results on real datasets](#results-on-real-datasets)
189
+ - [ ] `pre-commit` hook and GitHub Action
190
+
191
+ ## License
192
+
193
+ MIT
@@ -0,0 +1,65 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tabaudit"
7
+ version = "0.1.0"
8
+ description = "Audit tabular ML datasets for leakage, duplicates, label noise and imbalance before you train."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [{ name = "Sadia Samia" }]
14
+ keywords = ["machine-learning", "data-quality", "data-leakage", "label-noise", "cleanlab", "audit"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: Science/Research",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
25
+ ]
26
+ dependencies = [
27
+ "pandas>=2.0",
28
+ "numpy>=1.24",
29
+ "scikit-learn>=1.3",
30
+ "cleanlab>=2.5",
31
+ "typer>=0.12",
32
+ "rich>=13.0",
33
+ "jinja2>=3.1",
34
+ ]
35
+
36
+ [project.optional-dependencies]
37
+ dev = ["pytest>=8", "ruff>=0.5", "build", "twine", "pyarrow"]
38
+
39
+ [project.scripts]
40
+ tabaudit = "tabaudit.cli:app"
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/sadiasamia121912/tabaudit"
44
+ Issues = "https://github.com/sadiasamia121912/tabaudit/issues"
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
48
+
49
+ [tool.setuptools.package-data]
50
+ "tabaudit.report" = ["*.html"]
51
+
52
+ [tool.ruff]
53
+ line-length = 100
54
+ target-version = "py310"
55
+
56
+ [tool.ruff.lint]
57
+ select = ["E", "F", "I", "UP", "B", "RUF", "SIM"]
58
+ ignore = [
59
+ "B008", # typer.Option() in defaults is the documented Typer idiom
60
+ "E501", # formatter owns line length
61
+ "RUF001", "RUF002", "RUF003", # we print unicode glyphs (×, →) on purpose
62
+ ]
63
+
64
+ [tool.pytest.ini_options]
65
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """tabaudit — audit tabular ML datasets before you train."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from tabaudit.audit import run_audit
6
+ from tabaudit.findings import AuditReport, Finding, Severity
7
+
8
+ __all__ = ["AuditReport", "Finding", "Severity", "__version__", "run_audit"]
@@ -0,0 +1,102 @@
1
+ """Orchestrates loading, task inference and running every registered check."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from collections.abc import Callable, Iterable
7
+ from pathlib import Path
8
+
9
+ import pandas as pd
10
+
11
+ from tabaudit import __version__
12
+ from tabaudit.checks import REGISTRY
13
+ from tabaudit.context import AuditContext
14
+ from tabaudit.findings import AuditReport, CheckRun, DatasetSummary, Finding
15
+ from tabaudit.loader import dtype_kinds, infer_task, load_table
16
+
17
+ ProgressFn = Callable[[str, str], None] # (check_name, status) -> None
18
+
19
+
20
+ def run_audit(
21
+ data: str | Path | pd.DataFrame,
22
+ target: str | None = None,
23
+ test: str | Path | pd.DataFrame | None = None,
24
+ checks: Iterable[str] | None = None,
25
+ max_rows: int = 50_000,
26
+ random_state: int = 42,
27
+ on_progress: ProgressFn | None = None,
28
+ ) -> AuditReport:
29
+ """Audit a dataset and return an :class:`AuditReport`.
30
+
31
+ Parameters
32
+ ----------
33
+ data: path to CSV/Parquet/… or a DataFrame.
34
+ target: name of the label column (omit for unsupervised checks only).
35
+ test: optional held-out set, used to detect train/test contamination.
36
+ checks: subset of check names to run (default: all).
37
+ """
38
+ df, path = (
39
+ (data, "<DataFrame>") if isinstance(data, pd.DataFrame) else (load_table(data), str(data))
40
+ )
41
+ test_df, test_path = (None, None)
42
+ if test is not None:
43
+ test_df, test_path = (
44
+ (test, "<DataFrame>")
45
+ if isinstance(test, pd.DataFrame)
46
+ else (load_table(test), str(test))
47
+ )
48
+
49
+ if target is not None and target not in df.columns:
50
+ close = [c for c in df.columns if c.lower() == target.lower()]
51
+ hint = f" Did you mean '{close[0]}'?" if close else ""
52
+ raise KeyError(f"Target column '{target}' not found.{hint}")
53
+
54
+ task = infer_task(df[target]) if target else "unsupervised"
55
+ n_classes = (
56
+ int(df[target].nunique(dropna=True)) if target and task == "classification" else None
57
+ )
58
+
59
+ ctx = AuditContext(
60
+ df=df,
61
+ target=target,
62
+ task=task,
63
+ test_df=test_df,
64
+ max_rows=max_rows,
65
+ random_state=random_state,
66
+ )
67
+ selected = list(checks) if checks else list(REGISTRY)
68
+ unknown = [c for c in selected if c not in REGISTRY]
69
+ if unknown:
70
+ raise ValueError(f"Unknown check(s): {unknown}. Available: {list(REGISTRY)}")
71
+
72
+ findings: list[Finding] = []
73
+ runs: list[CheckRun] = []
74
+ for name in selected:
75
+ fn = REGISTRY[name]
76
+ if on_progress:
77
+ on_progress(name, "running")
78
+ t0 = time.perf_counter()
79
+ try:
80
+ out = fn(ctx)
81
+ findings.extend(out)
82
+ runs.append(CheckRun(name, "ok", time.perf_counter() - t0, len(out)))
83
+ except Exception as exc:
84
+ runs.append(
85
+ CheckRun(name, "error", time.perf_counter() - t0, 0, f"{type(exc).__name__}: {exc}")
86
+ )
87
+ if on_progress:
88
+ on_progress(name, runs[-1].status)
89
+
90
+ summary = DatasetSummary(
91
+ path=path,
92
+ n_rows=len(df),
93
+ n_cols=int(df.shape[1]),
94
+ target=target,
95
+ task=task,
96
+ n_classes=n_classes,
97
+ test_path=test_path,
98
+ n_test_rows=len(test_df) if test_df is not None else None,
99
+ memory_mb=round(float(df.memory_usage(deep=True).sum()) / 1e6, 2),
100
+ dtypes=dtype_kinds(df),
101
+ )
102
+ return AuditReport(summary=summary, findings=findings, checks=runs, version=__version__)
@@ -0,0 +1,22 @@
1
+ """Check registry. Each check is a callable (AuditContext) -> list[Finding]."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+
7
+ from tabaudit.checks import duplicates, imbalance, label_noise, leakage, schema
8
+ from tabaudit.context import AuditContext
9
+ from tabaudit.findings import Finding
10
+
11
+ CheckFn = Callable[[AuditContext], list[Finding]]
12
+
13
+ # Ordered: cheap structural checks first, model-based checks last.
14
+ REGISTRY: dict[str, CheckFn] = {
15
+ "schema": schema.run,
16
+ "duplicates": duplicates.run,
17
+ "imbalance": imbalance.run,
18
+ "leakage": leakage.run,
19
+ "label_noise": label_noise.run,
20
+ }
21
+
22
+ __all__ = ["REGISTRY", "CheckFn"]