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.
Files changed (36) hide show
  1. datasetdna-0.1.2/LICENSE +21 -0
  2. datasetdna-0.1.2/PKG-INFO +176 -0
  3. datasetdna-0.1.2/README.md +145 -0
  4. datasetdna-0.1.2/datasetdna/__init__.py +1 -0
  5. datasetdna-0.1.2/datasetdna/cli.py +307 -0
  6. datasetdna-0.1.2/datasetdna/profiler/__init__.py +0 -0
  7. datasetdna-0.1.2/datasetdna/profiler/cardinality.py +63 -0
  8. datasetdna-0.1.2/datasetdna/profiler/categorical.py +117 -0
  9. datasetdna-0.1.2/datasetdna/profiler/correlations.py +313 -0
  10. datasetdna-0.1.2/datasetdna/profiler/duplicates.py +188 -0
  11. datasetdna-0.1.2/datasetdna/profiler/missing.py +25 -0
  12. datasetdna-0.1.2/datasetdna/profiler/numerical.py +88 -0
  13. datasetdna-0.1.2/datasetdna/profiler/outliers.py +200 -0
  14. datasetdna-0.1.2/datasetdna/profiler/overview.py +16 -0
  15. datasetdna-0.1.2/datasetdna/profiler/schema.py +12 -0
  16. datasetdna-0.1.2/datasetdna/profiler/target.py +273 -0
  17. datasetdna-0.1.2/datasetdna/profiler/types.py +204 -0
  18. datasetdna-0.1.2/datasetdna/recommendations/recommendations.py +838 -0
  19. datasetdna-0.1.2/datasetdna/reporting/__init__.py +0 -0
  20. datasetdna-0.1.2/datasetdna/reporting/console.py +1040 -0
  21. datasetdna-0.1.2/datasetdna/reporting/html.py +2068 -0
  22. datasetdna-0.1.2/datasetdna/scoring/health_score.py +785 -0
  23. datasetdna-0.1.2/datasetdna/utils/__init__.py +0 -0
  24. datasetdna-0.1.2/datasetdna/utils/helpers.py +387 -0
  25. datasetdna-0.1.2/datasetdna.egg-info/PKG-INFO +176 -0
  26. datasetdna-0.1.2/datasetdna.egg-info/SOURCES.txt +34 -0
  27. datasetdna-0.1.2/datasetdna.egg-info/dependency_links.txt +1 -0
  28. datasetdna-0.1.2/datasetdna.egg-info/entry_points.txt +2 -0
  29. datasetdna-0.1.2/datasetdna.egg-info/requires.txt +6 -0
  30. datasetdna-0.1.2/datasetdna.egg-info/top_level.txt +1 -0
  31. datasetdna-0.1.2/pyproject.toml +59 -0
  32. datasetdna-0.1.2/setup.cfg +4 -0
  33. datasetdna-0.1.2/tests/test_health_score.py +623 -0
  34. datasetdna-0.1.2/tests/test_profilers.py +909 -0
  35. datasetdna-0.1.2/tests/test_recommendations.py +252 -0
  36. datasetdna-0.1.2/tests/test_validation.py +788 -0
@@ -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