datasetdna 0.1.2__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.
- datasetdna-0.1.2/LICENSE +21 -0
- datasetdna-0.1.2/PKG-INFO +176 -0
- datasetdna-0.1.2/README.md +145 -0
- datasetdna-0.1.2/datasetdna/__init__.py +1 -0
- datasetdna-0.1.2/datasetdna/cli.py +307 -0
- datasetdna-0.1.2/datasetdna/profiler/__init__.py +0 -0
- datasetdna-0.1.2/datasetdna/profiler/cardinality.py +63 -0
- datasetdna-0.1.2/datasetdna/profiler/categorical.py +117 -0
- datasetdna-0.1.2/datasetdna/profiler/correlations.py +313 -0
- datasetdna-0.1.2/datasetdna/profiler/duplicates.py +188 -0
- datasetdna-0.1.2/datasetdna/profiler/missing.py +25 -0
- datasetdna-0.1.2/datasetdna/profiler/numerical.py +88 -0
- datasetdna-0.1.2/datasetdna/profiler/outliers.py +200 -0
- datasetdna-0.1.2/datasetdna/profiler/overview.py +16 -0
- datasetdna-0.1.2/datasetdna/profiler/schema.py +12 -0
- datasetdna-0.1.2/datasetdna/profiler/target.py +273 -0
- datasetdna-0.1.2/datasetdna/profiler/types.py +204 -0
- datasetdna-0.1.2/datasetdna/recommendations/recommendations.py +838 -0
- datasetdna-0.1.2/datasetdna/reporting/__init__.py +0 -0
- datasetdna-0.1.2/datasetdna/reporting/console.py +1040 -0
- datasetdna-0.1.2/datasetdna/reporting/html.py +2068 -0
- datasetdna-0.1.2/datasetdna/scoring/health_score.py +785 -0
- datasetdna-0.1.2/datasetdna/utils/__init__.py +0 -0
- datasetdna-0.1.2/datasetdna/utils/helpers.py +387 -0
- datasetdna-0.1.2/datasetdna.egg-info/PKG-INFO +176 -0
- datasetdna-0.1.2/datasetdna.egg-info/SOURCES.txt +34 -0
- datasetdna-0.1.2/datasetdna.egg-info/dependency_links.txt +1 -0
- datasetdna-0.1.2/datasetdna.egg-info/entry_points.txt +2 -0
- datasetdna-0.1.2/datasetdna.egg-info/requires.txt +6 -0
- datasetdna-0.1.2/datasetdna.egg-info/top_level.txt +1 -0
- datasetdna-0.1.2/pyproject.toml +59 -0
- datasetdna-0.1.2/setup.cfg +4 -0
- datasetdna-0.1.2/tests/test_health_score.py +623 -0
- datasetdna-0.1.2/tests/test_profilers.py +909 -0
- datasetdna-0.1.2/tests/test_recommendations.py +252 -0
- datasetdna-0.1.2/tests/test_validation.py +788 -0
datasetdna-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kavya Rajput
|
|
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,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datasetdna
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: One-command dataset profiling and health analysis
|
|
5
|
+
Author: Kavya Rajput
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/KAVYA-29-ai/DatasetDNA
|
|
8
|
+
Project-URL: Repository, https://github.com/KAVYA-29-ai/DatasetDNA
|
|
9
|
+
Project-URL: Issues, https://github.com/KAVYA-29-ai/DatasetDNA/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: pandas
|
|
25
|
+
Requires-Dist: numpy
|
|
26
|
+
Requires-Dist: scipy
|
|
27
|
+
Requires-Dist: rich
|
|
28
|
+
Requires-Dist: typer
|
|
29
|
+
Requires-Dist: plotly
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# 🧬 DatasetDNA
|
|
33
|
+
|
|
34
|
+
**One command. Know if your dataset is safe to train on.**
|
|
35
|
+
|
|
36
|
+
DatasetDNA profiles any CSV and produces a clear, actionable health report — catching real data-quality problems (missing values, duplicates, invalid ranges) while separating them from statistical signals that need human judgment, not automatic penalties (correlation, skew, cardinality).
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install datasetdna
|
|
40
|
+
datasetdna your_data.csv
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That's it. No config, no notebook, no cloud upload — your data never leaves your machine.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## The problem with most profiling tools
|
|
48
|
+
|
|
49
|
+
Most auto-EDA tools treat every statistical pattern as a "problem" and dock your data a few points for it. That sounds fine — until you run it on the Iris dataset, one of the most famous, cleanest datasets in all of ML, and it tells you `petal_length ↔ petal_width` correlation of `0.96` is a health issue.
|
|
50
|
+
|
|
51
|
+
It isn't. That correlation is **expected botany**, not a data-quality defect. A tool that can't tell the difference between "this is broken" and "this is just how the world works" isn't trustworthy enough to run before training a real model.
|
|
52
|
+
|
|
53
|
+
DatasetDNA draws a hard line between the two:
|
|
54
|
+
|
|
55
|
+
| 🧬 Data Quality | 📊 Statistical Signals |
|
|
56
|
+
|---|---|
|
|
57
|
+
| **Affects your Health Score.** Objectively bad, in any context. | **Never affects your score.** Informational — could be fine, could be worth a look. |
|
|
58
|
+
| Missing values | Strong correlations |
|
|
59
|
+
| Duplicate rows | Skewness |
|
|
60
|
+
| Target class imbalance | High cardinality |
|
|
61
|
+
| Impossible values (age = 150, age = -5) | Outliers |
|
|
62
|
+
|
|
63
|
+
This distinction is the core design decision behind the whole tool — not an afterthought.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## What it checks
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
CSV
|
|
71
|
+
↓
|
|
72
|
+
Loader + Cleaning → handles $1,200 / "1,234" / N/A / null / "?" / "-" as one CSV, cleanly
|
|
73
|
+
↓
|
|
74
|
+
Semantic Type Detection → tells an ID column from a real feature, a date from a category
|
|
75
|
+
↓
|
|
76
|
+
┌─────────────────────────────┐
|
|
77
|
+
│ Overview Numerical │
|
|
78
|
+
│ Schema Categorical │
|
|
79
|
+
│ Missingness Outliers │
|
|
80
|
+
│ Duplicates Correlations│
|
|
81
|
+
│ Cardinality Target │
|
|
82
|
+
└─────────────────────────────┘
|
|
83
|
+
↓
|
|
84
|
+
Health Scoring → 0–100, driven only by real quality issues
|
|
85
|
+
↓
|
|
86
|
+
Console Report → color-coded, readable, no PhD required
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Semantic Type Detection** is what separates this from a plain `df.describe()` script — it automatically:
|
|
90
|
+
- Cleans numbers written as text (`"$55,000"`, `"1,200"`) into real numeric columns
|
|
91
|
+
- Recognizes disguised missing values (`N/A`, `null`, `?`, `-`, `unknown`) as actual missing data, not valid strings
|
|
92
|
+
- Flags ID-like columns (customer IDs, emails) and excludes them from correlation/association analysis, where they'd otherwise create meaningless "100% correlated" noise
|
|
93
|
+
- Validates domain-plausible ranges (e.g., an `age` column with values of `-5` or `150` gets flagged as a real error, not just an "outlier")
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Example output
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
$ datasetdna examples/real.csv
|
|
101
|
+
|
|
102
|
+
╭────────────── 🧬 Dataset Health ──────────────╮
|
|
103
|
+
│ 🟠 Health Score │
|
|
104
|
+
│ │
|
|
105
|
+
│ 70 / 100 │
|
|
106
|
+
│ Fair │
|
|
107
|
+
│ │
|
|
108
|
+
│ 6 quality issue(s) · 14 statistical signal(s) │
|
|
109
|
+
╰───────────────────────────────────────────────╯
|
|
110
|
+
|
|
111
|
+
🧬 DATA QUALITY
|
|
112
|
+
MEDIUM Column 'age' contains 10.0% missing values.
|
|
113
|
+
MEDIUM Dataset contains 5.0% duplicate rows.
|
|
114
|
+
HIGH Age-like column 'age' contains values outside 0-120. (range: -5.0 to 150.0)
|
|
115
|
+
|
|
116
|
+
📊 STATISTICAL SIGNALS
|
|
117
|
+
🔎 HIGH Strong correlation detected between age and salary. (Pearson: 0.949)
|
|
118
|
+
🔎 MEDIUM Column 'customer_id' has very high cardinality. (100.0% unique)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Full breakdown tables for schema, missingness, cardinality, numerical stats, outliers, correlations, and categorical associations follow below the summary.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Installation
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install datasetdna
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Or from source:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git clone https://github.com/KAVYA-29-ai/DatasetDNA
|
|
135
|
+
cd DatasetDNA
|
|
136
|
+
pip install -e .
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Requires Python 3.10+.
|
|
140
|
+
|
|
141
|
+
## Usage
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Basic profile
|
|
145
|
+
datasetdna data.csv
|
|
146
|
+
|
|
147
|
+
# Include target-column analysis (class imbalance, target relationships)
|
|
148
|
+
datasetdna data.csv --target churn
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Why this exists
|
|
154
|
+
|
|
155
|
+
Most ML failures aren't model failures — they're data failures nobody caught before training started: a leaked ID column, silent duplicates, a numeric column secretly stored as `"$1,200"` strings, a target with 95/5 imbalance nobody noticed until the model just predicted the majority class every time.
|
|
156
|
+
|
|
157
|
+
DatasetDNA is meant to be the first command you run against any new CSV, before you write a single line of `train_test_split`.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Roadmap
|
|
162
|
+
|
|
163
|
+
- [x] **Phase 1 — Core profiler**: semantic types, missingness, duplicates, cardinality, numerical/categorical analysis, outliers, correlations, target analysis, two-tier health scoring
|
|
164
|
+
- [x] **Phase 2 — Better UX**: actionable recommendations per issue, expanded validation, deeper test coverage
|
|
165
|
+
- [x] **Phase 3 — HTML dashboard**: interactive Plotly charts, exportable shareable reports
|
|
166
|
+
- [ ] **Phase 4 — PyPI**: stable public release
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Contributing
|
|
171
|
+
|
|
172
|
+
Issues and PRs welcome. If you've found a dataset that breaks DatasetDNA (crashes, wrong classification, missed issue) — that's exactly the kind of bug report that helps most. Open an issue with the CSV shape (not necessarily the data itself) and what you expected to see.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT © Kavya Rajput
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# 🧬 DatasetDNA
|
|
2
|
+
|
|
3
|
+
**One command. Know if your dataset is safe to train on.**
|
|
4
|
+
|
|
5
|
+
DatasetDNA profiles any CSV and produces a clear, actionable health report — catching real data-quality problems (missing values, duplicates, invalid ranges) while separating them from statistical signals that need human judgment, not automatic penalties (correlation, skew, cardinality).
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install datasetdna
|
|
9
|
+
datasetdna your_data.csv
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
That's it. No config, no notebook, no cloud upload — your data never leaves your machine.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The problem with most profiling tools
|
|
17
|
+
|
|
18
|
+
Most auto-EDA tools treat every statistical pattern as a "problem" and dock your data a few points for it. That sounds fine — until you run it on the Iris dataset, one of the most famous, cleanest datasets in all of ML, and it tells you `petal_length ↔ petal_width` correlation of `0.96` is a health issue.
|
|
19
|
+
|
|
20
|
+
It isn't. That correlation is **expected botany**, not a data-quality defect. A tool that can't tell the difference between "this is broken" and "this is just how the world works" isn't trustworthy enough to run before training a real model.
|
|
21
|
+
|
|
22
|
+
DatasetDNA draws a hard line between the two:
|
|
23
|
+
|
|
24
|
+
| 🧬 Data Quality | 📊 Statistical Signals |
|
|
25
|
+
|---|---|
|
|
26
|
+
| **Affects your Health Score.** Objectively bad, in any context. | **Never affects your score.** Informational — could be fine, could be worth a look. |
|
|
27
|
+
| Missing values | Strong correlations |
|
|
28
|
+
| Duplicate rows | Skewness |
|
|
29
|
+
| Target class imbalance | High cardinality |
|
|
30
|
+
| Impossible values (age = 150, age = -5) | Outliers |
|
|
31
|
+
|
|
32
|
+
This distinction is the core design decision behind the whole tool — not an afterthought.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## What it checks
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
CSV
|
|
40
|
+
↓
|
|
41
|
+
Loader + Cleaning → handles $1,200 / "1,234" / N/A / null / "?" / "-" as one CSV, cleanly
|
|
42
|
+
↓
|
|
43
|
+
Semantic Type Detection → tells an ID column from a real feature, a date from a category
|
|
44
|
+
↓
|
|
45
|
+
┌─────────────────────────────┐
|
|
46
|
+
│ Overview Numerical │
|
|
47
|
+
│ Schema Categorical │
|
|
48
|
+
│ Missingness Outliers │
|
|
49
|
+
│ Duplicates Correlations│
|
|
50
|
+
│ Cardinality Target │
|
|
51
|
+
└─────────────────────────────┘
|
|
52
|
+
↓
|
|
53
|
+
Health Scoring → 0–100, driven only by real quality issues
|
|
54
|
+
↓
|
|
55
|
+
Console Report → color-coded, readable, no PhD required
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Semantic Type Detection** is what separates this from a plain `df.describe()` script — it automatically:
|
|
59
|
+
- Cleans numbers written as text (`"$55,000"`, `"1,200"`) into real numeric columns
|
|
60
|
+
- Recognizes disguised missing values (`N/A`, `null`, `?`, `-`, `unknown`) as actual missing data, not valid strings
|
|
61
|
+
- Flags ID-like columns (customer IDs, emails) and excludes them from correlation/association analysis, where they'd otherwise create meaningless "100% correlated" noise
|
|
62
|
+
- Validates domain-plausible ranges (e.g., an `age` column with values of `-5` or `150` gets flagged as a real error, not just an "outlier")
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Example output
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
$ datasetdna examples/real.csv
|
|
70
|
+
|
|
71
|
+
╭────────────── 🧬 Dataset Health ──────────────╮
|
|
72
|
+
│ 🟠 Health Score │
|
|
73
|
+
│ │
|
|
74
|
+
│ 70 / 100 │
|
|
75
|
+
│ Fair │
|
|
76
|
+
│ │
|
|
77
|
+
│ 6 quality issue(s) · 14 statistical signal(s) │
|
|
78
|
+
╰───────────────────────────────────────────────╯
|
|
79
|
+
|
|
80
|
+
🧬 DATA QUALITY
|
|
81
|
+
MEDIUM Column 'age' contains 10.0% missing values.
|
|
82
|
+
MEDIUM Dataset contains 5.0% duplicate rows.
|
|
83
|
+
HIGH Age-like column 'age' contains values outside 0-120. (range: -5.0 to 150.0)
|
|
84
|
+
|
|
85
|
+
📊 STATISTICAL SIGNALS
|
|
86
|
+
🔎 HIGH Strong correlation detected between age and salary. (Pearson: 0.949)
|
|
87
|
+
🔎 MEDIUM Column 'customer_id' has very high cardinality. (100.0% unique)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Full breakdown tables for schema, missingness, cardinality, numerical stats, outliers, correlations, and categorical associations follow below the summary.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Installation
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install datasetdna
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or from source:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git clone https://github.com/KAVYA-29-ai/DatasetDNA
|
|
104
|
+
cd DatasetDNA
|
|
105
|
+
pip install -e .
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Requires Python 3.10+.
|
|
109
|
+
|
|
110
|
+
## Usage
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Basic profile
|
|
114
|
+
datasetdna data.csv
|
|
115
|
+
|
|
116
|
+
# Include target-column analysis (class imbalance, target relationships)
|
|
117
|
+
datasetdna data.csv --target churn
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Why this exists
|
|
123
|
+
|
|
124
|
+
Most ML failures aren't model failures — they're data failures nobody caught before training started: a leaked ID column, silent duplicates, a numeric column secretly stored as `"$1,200"` strings, a target with 95/5 imbalance nobody noticed until the model just predicted the majority class every time.
|
|
125
|
+
|
|
126
|
+
DatasetDNA is meant to be the first command you run against any new CSV, before you write a single line of `train_test_split`.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Roadmap
|
|
131
|
+
|
|
132
|
+
- [x] **Phase 1 — Core profiler**: semantic types, missingness, duplicates, cardinality, numerical/categorical analysis, outliers, correlations, target analysis, two-tier health scoring
|
|
133
|
+
- [x] **Phase 2 — Better UX**: actionable recommendations per issue, expanded validation, deeper test coverage
|
|
134
|
+
- [x] **Phase 3 — HTML dashboard**: interactive Plotly charts, exportable shareable reports
|
|
135
|
+
- [ ] **Phase 4 — PyPI**: stable public release
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Contributing
|
|
140
|
+
|
|
141
|
+
Issues and PRs welcome. If you've found a dataset that breaks DatasetDNA (crashes, wrong classification, missed issue) — that's exactly the kind of bug report that helps most. Open an issue with the CSV shape (not necessarily the data itself) and what you expected to see.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT © Kavya Rajput
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from datasetdna.profiler.overview import check_overview
|
|
8
|
+
from datasetdna.profiler.schema import check_schema
|
|
9
|
+
from datasetdna.profiler.missing import check_missing
|
|
10
|
+
from datasetdna.profiler.duplicates import check_duplicates
|
|
11
|
+
from datasetdna.profiler.cardinality import check_cardinality
|
|
12
|
+
from datasetdna.profiler.numerical import check_numerical
|
|
13
|
+
from datasetdna.profiler.categorical import check_categorical
|
|
14
|
+
from datasetdna.profiler.outliers import check_outliers
|
|
15
|
+
from datasetdna.profiler.correlations import check_correlations
|
|
16
|
+
from datasetdna.profiler.target import check_target
|
|
17
|
+
|
|
18
|
+
from datasetdna.scoring.health_score import (
|
|
19
|
+
calculate_health_score,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from datasetdna.recommendations.recommendations import (
|
|
23
|
+
generate_recommendations,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from datasetdna.reporting.console import (
|
|
27
|
+
console,
|
|
28
|
+
render_report,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from datasetdna.reporting.html import (
|
|
32
|
+
render_html_report,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
from datasetdna.utils.helpers import (
|
|
36
|
+
LARGE_FILE_SIZE_BYTES,
|
|
37
|
+
LARGE_FILE_SAMPLE_SIZE,
|
|
38
|
+
load_dataset,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
app = typer.Typer(
|
|
43
|
+
help="DatasetDNA - Automated Dataset Health Profiler",
|
|
44
|
+
add_completion=False,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# =============================================================
|
|
49
|
+
# TARGET INFERENCE
|
|
50
|
+
# =============================================================
|
|
51
|
+
|
|
52
|
+
TARGET_COLUMN_CANDIDATES = (
|
|
53
|
+
"target",
|
|
54
|
+
"label",
|
|
55
|
+
"churn",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def infer_target(
|
|
60
|
+
df,
|
|
61
|
+
) -> tuple[str | None, bool]:
|
|
62
|
+
"""
|
|
63
|
+
Infer a target column when the user does not provide one.
|
|
64
|
+
|
|
65
|
+
Priority:
|
|
66
|
+
target -> label -> churn
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
(column_name, inferred)
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
normalized_columns = {
|
|
73
|
+
column.strip().lower(): column
|
|
74
|
+
for column in df.columns
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for candidate in TARGET_COLUMN_CANDIDATES:
|
|
78
|
+
|
|
79
|
+
if candidate in normalized_columns:
|
|
80
|
+
return (
|
|
81
|
+
normalized_columns[candidate],
|
|
82
|
+
True,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return None, False
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# =============================================================
|
|
89
|
+
# LARGE FILE WARNING
|
|
90
|
+
# =============================================================
|
|
91
|
+
|
|
92
|
+
def warn_if_large_file(
|
|
93
|
+
path: str,
|
|
94
|
+
) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Warn the user when DatasetDNA will analyze a sample
|
|
97
|
+
instead of the complete dataset.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
if not os.path.exists(path):
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
if os.path.getsize(path) > LARGE_FILE_SIZE_BYTES:
|
|
104
|
+
typer.echo(
|
|
105
|
+
f"Large file detected — analyzing a "
|
|
106
|
+
f"{LARGE_FILE_SAMPLE_SIZE:,}-row sample."
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# =============================================================
|
|
111
|
+
# CLI COMMAND
|
|
112
|
+
# =============================================================
|
|
113
|
+
|
|
114
|
+
@app.command()
|
|
115
|
+
def profile(
|
|
116
|
+
file: str = typer.Argument(
|
|
117
|
+
...,
|
|
118
|
+
help="Path to the CSV file.",
|
|
119
|
+
),
|
|
120
|
+
target: str | None = typer.Option(
|
|
121
|
+
None,
|
|
122
|
+
"--target",
|
|
123
|
+
"-t",
|
|
124
|
+
help="Optional target column.",
|
|
125
|
+
),
|
|
126
|
+
html: bool = typer.Option(
|
|
127
|
+
False,
|
|
128
|
+
"--html",
|
|
129
|
+
help="Generate an HTML report.",
|
|
130
|
+
),
|
|
131
|
+
output: str = typer.Option(
|
|
132
|
+
"datasetdna_report.html",
|
|
133
|
+
"--output",
|
|
134
|
+
"-o",
|
|
135
|
+
help="HTML output file path.",
|
|
136
|
+
),
|
|
137
|
+
):
|
|
138
|
+
"""
|
|
139
|
+
Profile a CSV dataset and generate a health report.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
|
|
144
|
+
# ====================================================
|
|
145
|
+
# LARGE FILE WARNING
|
|
146
|
+
# ====================================================
|
|
147
|
+
|
|
148
|
+
warn_if_large_file(file)
|
|
149
|
+
|
|
150
|
+
# ====================================================
|
|
151
|
+
# LOAD DATASET
|
|
152
|
+
# ====================================================
|
|
153
|
+
|
|
154
|
+
df = load_dataset(file)
|
|
155
|
+
|
|
156
|
+
# ====================================================
|
|
157
|
+
# TARGET INFERENCE
|
|
158
|
+
# ====================================================
|
|
159
|
+
|
|
160
|
+
target_was_inferred = False
|
|
161
|
+
|
|
162
|
+
if target is None:
|
|
163
|
+
|
|
164
|
+
target, target_was_inferred = infer_target(
|
|
165
|
+
df
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# ====================================================
|
|
169
|
+
# RUN PROFILERS
|
|
170
|
+
# ====================================================
|
|
171
|
+
|
|
172
|
+
overview = check_overview(df)
|
|
173
|
+
|
|
174
|
+
schema = check_schema(df)
|
|
175
|
+
|
|
176
|
+
missing = check_missing(df)
|
|
177
|
+
|
|
178
|
+
duplicates = check_duplicates(df)
|
|
179
|
+
|
|
180
|
+
cardinality = check_cardinality(df)
|
|
181
|
+
|
|
182
|
+
numerical = check_numerical(df)
|
|
183
|
+
|
|
184
|
+
categorical = check_categorical(df)
|
|
185
|
+
|
|
186
|
+
outliers = check_outliers(df)
|
|
187
|
+
|
|
188
|
+
correlations = check_correlations(df)
|
|
189
|
+
|
|
190
|
+
target_result = check_target(
|
|
191
|
+
df,
|
|
192
|
+
target,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# ====================================================
|
|
196
|
+
# MARK INFERRED TARGET
|
|
197
|
+
# ====================================================
|
|
198
|
+
|
|
199
|
+
if target_was_inferred:
|
|
200
|
+
target_result["inferred"] = True
|
|
201
|
+
|
|
202
|
+
console.print(
|
|
203
|
+
f"[yellow]ℹ Auto-inferred '{target}' as the primary target variable.[/yellow]\n"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# ====================================================
|
|
207
|
+
# COLLECT RESULTS
|
|
208
|
+
# ====================================================
|
|
209
|
+
|
|
210
|
+
results = {
|
|
211
|
+
"overview": overview,
|
|
212
|
+
"schema": schema,
|
|
213
|
+
"missing": missing,
|
|
214
|
+
"duplicates": duplicates,
|
|
215
|
+
"cardinality": cardinality,
|
|
216
|
+
"numerical": numerical,
|
|
217
|
+
"categorical": categorical,
|
|
218
|
+
"outliers": outliers,
|
|
219
|
+
"correlations": correlations,
|
|
220
|
+
"target": target_result,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# ====================================================
|
|
224
|
+
# HEALTH SCORE
|
|
225
|
+
# ====================================================
|
|
226
|
+
|
|
227
|
+
health = calculate_health_score(
|
|
228
|
+
results
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
results["health"] = health
|
|
232
|
+
|
|
233
|
+
# ====================================================
|
|
234
|
+
# RECOMMENDATIONS
|
|
235
|
+
# ====================================================
|
|
236
|
+
|
|
237
|
+
recommendations = generate_recommendations(
|
|
238
|
+
results
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
results["recommendations"] = recommendations
|
|
242
|
+
|
|
243
|
+
# ====================================================
|
|
244
|
+
# CONSOLE REPORT
|
|
245
|
+
# ====================================================
|
|
246
|
+
|
|
247
|
+
render_report(
|
|
248
|
+
results,
|
|
249
|
+
health,
|
|
250
|
+
recommendations,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# ====================================================
|
|
254
|
+
# HTML REPORT
|
|
255
|
+
# ====================================================
|
|
256
|
+
|
|
257
|
+
if html:
|
|
258
|
+
|
|
259
|
+
output_path = render_html_report(
|
|
260
|
+
results,
|
|
261
|
+
output_path=output,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
typer.echo(
|
|
265
|
+
f"\nHTML report generated: {output_path}"
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
except FileNotFoundError as error:
|
|
269
|
+
|
|
270
|
+
typer.echo(
|
|
271
|
+
f"Error: {error}",
|
|
272
|
+
err=True,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
raise typer.Exit(
|
|
276
|
+
code=1
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
except ValueError as error:
|
|
280
|
+
|
|
281
|
+
typer.echo(
|
|
282
|
+
f"Error: {error}",
|
|
283
|
+
err=True,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
raise typer.Exit(
|
|
287
|
+
code=1
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
except Exception as error:
|
|
291
|
+
|
|
292
|
+
typer.echo(
|
|
293
|
+
f"Unexpected error: {error}",
|
|
294
|
+
err=True,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
raise typer.Exit(
|
|
298
|
+
code=1
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# =============================================================
|
|
303
|
+
# ENTRY POINT
|
|
304
|
+
# =============================================================
|
|
305
|
+
|
|
306
|
+
if __name__ == "__main__":
|
|
307
|
+
app()
|
|
File without changes
|