datapilot-kit 0.3.0rc1__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.
- datapilot/__init__.py +11 -0
- datapilot/analysis/__init__.py +3 -0
- datapilot/analysis/correlation.py +58 -0
- datapilot/analysis/datatype.py +48 -0
- datapilot/analysis/duplicate.py +40 -0
- datapilot/analysis/health.py +163 -0
- datapilot/analysis/insights.py +101 -0
- datapilot/analysis/missing.py +56 -0
- datapilot/analysis/models.py +108 -0
- datapilot/analysis/outliers.py +85 -0
- datapilot/analysis/statistics.py +46 -0
- datapilot/analysis/summary.py +36 -0
- datapilot/api/__init__.py +7 -0
- datapilot/api/analyze.py +16 -0
- datapilot/assets/style.css +1081 -0
- datapilot/cli/__init__.py +3 -0
- datapilot/core/__init__.py +3 -0
- datapilot/core/loader.py +72 -0
- datapilot/core/report.py +104 -0
- datapilot/interpretation/__init__.py +3 -0
- datapilot/llm/__init__.py +0 -0
- datapilot/recommendation/__init__.py +0 -0
- datapilot/reporting/__init__.py +3 -0
- datapilot/reporting/fragments.py +329 -0
- datapilot/reporting/html.py +51 -0
- datapilot/reporting/placeholders.py +133 -0
- datapilot/reporting/renderer.py +57 -0
- datapilot/templates/report.html +528 -0
- datapilot/ui/__init__.py +0 -0
- datapilot/ui/cards.py +70 -0
- datapilot/ui/console.py +12 -0
- datapilot/ui/dashboard.py +90 -0
- datapilot/ui/panels.py +16 -0
- datapilot/ui/renderer.py +21 -0
- datapilot/ui/sections.py +89 -0
- datapilot/ui/tables.py +21 -0
- datapilot/ui/theme.py +20 -0
- datapilot/utils/__init__.py +3 -0
- datapilot/visualization/__init__.py +0 -0
- datapilot_kit-0.3.0rc1.dist-info/METADATA +103 -0
- datapilot_kit-0.3.0rc1.dist-info/RECORD +44 -0
- datapilot_kit-0.3.0rc1.dist-info/WHEEL +5 -0
- datapilot_kit-0.3.0rc1.dist-info/licenses/LICENSE +21 -0
- datapilot_kit-0.3.0rc1.dist-info/top_level.txt +1 -0
datapilot/__init__.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Correlation analysis for Datapilot.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import SupportsFloat, cast
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
from .models import CorrelationSummary
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def generate_correlation_summary(
|
|
13
|
+
dataframe: pd.DataFrame,
|
|
14
|
+
) -> CorrelationSummary:
|
|
15
|
+
"""
|
|
16
|
+
Generate Pearson correlation analysis for numeric columns.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
numeric_dataframe = dataframe.select_dtypes(
|
|
20
|
+
include="number",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
correlation_matrix = numeric_dataframe.corr()
|
|
24
|
+
|
|
25
|
+
strong_positive_pairs: dict[str, float] = {}
|
|
26
|
+
strong_negative_pairs: dict[str, float] = {}
|
|
27
|
+
|
|
28
|
+
columns = list(correlation_matrix.columns)
|
|
29
|
+
|
|
30
|
+
for i, left in enumerate(columns):
|
|
31
|
+
for right in columns[i + 1:]:
|
|
32
|
+
|
|
33
|
+
correlation = cast(
|
|
34
|
+
SupportsFloat,
|
|
35
|
+
correlation_matrix.loc[left, right],
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
value = float(correlation)
|
|
39
|
+
|
|
40
|
+
pair = f"{left} ↔ {right}"
|
|
41
|
+
|
|
42
|
+
if value >= 0.70:
|
|
43
|
+
strong_positive_pairs[pair] = round(
|
|
44
|
+
value,
|
|
45
|
+
3,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
elif value <= -0.70:
|
|
49
|
+
strong_negative_pairs[pair] = round(
|
|
50
|
+
value,
|
|
51
|
+
3,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return CorrelationSummary(
|
|
55
|
+
correlation_matrix=correlation_matrix,
|
|
56
|
+
strong_positive_pairs=strong_positive_pairs,
|
|
57
|
+
strong_negative_pairs=strong_negative_pairs,
|
|
58
|
+
)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data type analysis for Datapilot.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .models import DataTypeSummary
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_data_type_summary(
|
|
11
|
+
dataframe: pd.DataFrame,
|
|
12
|
+
) -> DataTypeSummary:
|
|
13
|
+
"""
|
|
14
|
+
Generate data type statistics for a dataset.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
dataframe : pandas.DataFrame
|
|
19
|
+
Dataset to analyze.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
DataTypeSummary
|
|
24
|
+
Summary of dataset column types.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
numeric_columns = dataframe.select_dtypes(
|
|
28
|
+
include=["number"]
|
|
29
|
+
).columns.tolist()
|
|
30
|
+
|
|
31
|
+
categorical_columns = dataframe.select_dtypes(
|
|
32
|
+
include=["object", "string", "category"]
|
|
33
|
+
).columns.tolist()
|
|
34
|
+
|
|
35
|
+
boolean_columns = dataframe.select_dtypes(
|
|
36
|
+
include=["bool"]
|
|
37
|
+
).columns.tolist()
|
|
38
|
+
|
|
39
|
+
datetime_columns = dataframe.select_dtypes(
|
|
40
|
+
include=["datetime", "datetimetz"]
|
|
41
|
+
).columns.tolist()
|
|
42
|
+
|
|
43
|
+
return DataTypeSummary(
|
|
44
|
+
numeric_columns=numeric_columns,
|
|
45
|
+
categorical_columns=categorical_columns,
|
|
46
|
+
boolean_columns=boolean_columns,
|
|
47
|
+
datetime_columns=datetime_columns,
|
|
48
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Duplicate row analysis for Datapilot.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .models import DuplicateSummary
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_duplicate_summary(
|
|
11
|
+
dataframe: pd.DataFrame,
|
|
12
|
+
) -> DuplicateSummary:
|
|
13
|
+
"""
|
|
14
|
+
Generate duplicate row statistics for a dataset.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
dataframe : pandas.DataFrame
|
|
19
|
+
Dataset to analyze.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
DuplicateSummary
|
|
24
|
+
Summary of duplicate rows in the dataset.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
total_duplicates = int(dataframe.duplicated().sum())
|
|
28
|
+
|
|
29
|
+
total_rows = len(dataframe)
|
|
30
|
+
|
|
31
|
+
duplicate_percentage = (
|
|
32
|
+
round((total_duplicates / total_rows) * 100, 2)
|
|
33
|
+
if total_rows > 0
|
|
34
|
+
else 0.0
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return DuplicateSummary(
|
|
38
|
+
total_duplicates=total_duplicates,
|
|
39
|
+
duplicate_percentage=duplicate_percentage,
|
|
40
|
+
)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dataset health assessment for Datapilot.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .datatype import generate_data_type_summary
|
|
8
|
+
from .duplicate import generate_duplicate_summary
|
|
9
|
+
from .missing import generate_missing_value_summary
|
|
10
|
+
from .models import DatasetHealth
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def generate_dataset_health(
|
|
14
|
+
dataframe: pd.DataFrame,
|
|
15
|
+
) -> DatasetHealth:
|
|
16
|
+
"""
|
|
17
|
+
Generate an overall health assessment for a dataset.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
missing = generate_missing_value_summary(dataframe)
|
|
21
|
+
duplicates = generate_duplicate_summary(dataframe)
|
|
22
|
+
data_types = generate_data_type_summary(dataframe)
|
|
23
|
+
|
|
24
|
+
score = 100.0
|
|
25
|
+
|
|
26
|
+
# -----------------------------
|
|
27
|
+
# Missing Value Penalty (40 pts)
|
|
28
|
+
# -----------------------------
|
|
29
|
+
score -= (missing.missing_percentage / 100) * 40
|
|
30
|
+
|
|
31
|
+
# -----------------------------
|
|
32
|
+
# Duplicate Penalty (35 pts)
|
|
33
|
+
# -----------------------------
|
|
34
|
+
score -= (duplicates.duplicate_percentage / 100) * 35
|
|
35
|
+
|
|
36
|
+
# -----------------------------
|
|
37
|
+
# Structure Penalty (25 pts)
|
|
38
|
+
# -----------------------------
|
|
39
|
+
total_columns = len(dataframe.columns)
|
|
40
|
+
|
|
41
|
+
recognized_columns = (
|
|
42
|
+
len(data_types.numeric_columns)
|
|
43
|
+
+ len(data_types.categorical_columns)
|
|
44
|
+
+ len(data_types.boolean_columns)
|
|
45
|
+
+ len(data_types.datetime_columns)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
unrecognized_columns = max(
|
|
49
|
+
total_columns - recognized_columns,
|
|
50
|
+
0,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if total_columns > 0:
|
|
54
|
+
score -= (
|
|
55
|
+
unrecognized_columns / total_columns
|
|
56
|
+
) * 25
|
|
57
|
+
|
|
58
|
+
score = max(0, min(100, round(score)))
|
|
59
|
+
|
|
60
|
+
# -----------------------------
|
|
61
|
+
# Grade
|
|
62
|
+
# -----------------------------
|
|
63
|
+
if score >= 95:
|
|
64
|
+
grade = "A+"
|
|
65
|
+
status = "Excellent"
|
|
66
|
+
|
|
67
|
+
elif score >= 90:
|
|
68
|
+
grade = "A"
|
|
69
|
+
status = "Healthy"
|
|
70
|
+
|
|
71
|
+
elif score >= 80:
|
|
72
|
+
grade = "B"
|
|
73
|
+
status = "Good"
|
|
74
|
+
|
|
75
|
+
elif score >= 70:
|
|
76
|
+
grade = "C"
|
|
77
|
+
status = "Fair"
|
|
78
|
+
|
|
79
|
+
elif score >= 60:
|
|
80
|
+
grade = "D"
|
|
81
|
+
status = "Poor"
|
|
82
|
+
|
|
83
|
+
else:
|
|
84
|
+
grade = "F"
|
|
85
|
+
status = "Critical"
|
|
86
|
+
|
|
87
|
+
# -----------------------------
|
|
88
|
+
# ML Readiness
|
|
89
|
+
# -----------------------------
|
|
90
|
+
ml_ready = (
|
|
91
|
+
score >= 85
|
|
92
|
+
and missing.missing_percentage <= 20
|
|
93
|
+
and duplicates.duplicate_percentage <= 5
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# -----------------------------
|
|
97
|
+
# Strengths
|
|
98
|
+
# -----------------------------
|
|
99
|
+
strengths = []
|
|
100
|
+
|
|
101
|
+
if missing.total_missing == 0:
|
|
102
|
+
strengths.append("No missing values detected.")
|
|
103
|
+
|
|
104
|
+
if duplicates.total_duplicates == 0:
|
|
105
|
+
strengths.append("No duplicate rows detected.")
|
|
106
|
+
|
|
107
|
+
if unrecognized_columns == 0:
|
|
108
|
+
strengths.append("All columns have recognized data types.")
|
|
109
|
+
|
|
110
|
+
# -----------------------------
|
|
111
|
+
# Weaknesses
|
|
112
|
+
# -----------------------------
|
|
113
|
+
weaknesses = []
|
|
114
|
+
|
|
115
|
+
if missing.total_missing > 0:
|
|
116
|
+
weaknesses.append(
|
|
117
|
+
f"{missing.total_missing} missing values detected."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if duplicates.total_duplicates > 0:
|
|
121
|
+
weaknesses.append(
|
|
122
|
+
f"{duplicates.total_duplicates} duplicate rows detected."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if unrecognized_columns > 0:
|
|
126
|
+
weaknesses.append(
|
|
127
|
+
f"{unrecognized_columns} columns have unsupported data types."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# -----------------------------
|
|
131
|
+
# Recommendations
|
|
132
|
+
# -----------------------------
|
|
133
|
+
recommendations = []
|
|
134
|
+
|
|
135
|
+
if missing.missing_percentage > 0:
|
|
136
|
+
recommendations.append(
|
|
137
|
+
"Handle missing values before further analysis."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if duplicates.total_duplicates > 0:
|
|
141
|
+
recommendations.append(
|
|
142
|
+
"Remove duplicate rows."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if unrecognized_columns > 0:
|
|
146
|
+
recommendations.append(
|
|
147
|
+
"Review unsupported column data types."
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if not recommendations:
|
|
151
|
+
recommendations.append(
|
|
152
|
+
"Dataset is ready for analysis."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return DatasetHealth(
|
|
156
|
+
score=score,
|
|
157
|
+
grade=grade,
|
|
158
|
+
status=status,
|
|
159
|
+
ml_ready=ml_ready,
|
|
160
|
+
strengths=strengths,
|
|
161
|
+
weaknesses=weaknesses,
|
|
162
|
+
recommendations=recommendations,
|
|
163
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Insight generation for Datapilot.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .correlation import generate_correlation_summary
|
|
8
|
+
from .duplicate import generate_duplicate_summary
|
|
9
|
+
from .missing import generate_missing_value_summary
|
|
10
|
+
from .models import InsightSummary
|
|
11
|
+
from .outliers import generate_outlier_summary
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def generate_insight_summary(
|
|
15
|
+
dataframe: pd.DataFrame,
|
|
16
|
+
) -> InsightSummary:
|
|
17
|
+
"""
|
|
18
|
+
Generate actionable dataset insights.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
insights: list[str] = []
|
|
22
|
+
|
|
23
|
+
recommendations: list[str] = []
|
|
24
|
+
|
|
25
|
+
missing = generate_missing_value_summary(
|
|
26
|
+
dataframe
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
duplicates = generate_duplicate_summary(
|
|
30
|
+
dataframe
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
outliers = generate_outlier_summary(
|
|
34
|
+
dataframe
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
correlation = generate_correlation_summary(
|
|
38
|
+
dataframe
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if missing.total_missing > 0:
|
|
42
|
+
|
|
43
|
+
insights.append(
|
|
44
|
+
f"Dataset contains "
|
|
45
|
+
f"{missing.total_missing} missing values."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
recommendations.append(
|
|
49
|
+
"Handle missing values before "
|
|
50
|
+
"training machine learning models."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if duplicates.total_duplicates > 0:
|
|
54
|
+
|
|
55
|
+
insights.append(
|
|
56
|
+
f"Dataset contains "
|
|
57
|
+
f"{duplicates.total_duplicates} duplicate rows."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
recommendations.append(
|
|
61
|
+
"Remove duplicate rows."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if outliers.total_outliers > 0:
|
|
65
|
+
|
|
66
|
+
insights.append(
|
|
67
|
+
f"Detected "
|
|
68
|
+
f"{outliers.total_outliers} statistical outliers."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
recommendations.append(
|
|
72
|
+
"Review outliers before modelling."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if correlation.strong_positive_pairs:
|
|
76
|
+
|
|
77
|
+
insights.append(
|
|
78
|
+
"Highly correlated numeric features detected."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
recommendations.append(
|
|
82
|
+
"Review correlated features to "
|
|
83
|
+
"reduce multicollinearity."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if correlation.strong_negative_pairs:
|
|
87
|
+
|
|
88
|
+
insights.append(
|
|
89
|
+
"Strong negative correlations detected."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if not insights:
|
|
93
|
+
|
|
94
|
+
insights.append(
|
|
95
|
+
"No significant data quality issues detected."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return InsightSummary(
|
|
99
|
+
insights=insights,
|
|
100
|
+
recommendations=recommendations,
|
|
101
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Missing value analysis for Datapilot.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .models import MissingValueSummary
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_missing_value_summary(
|
|
11
|
+
dataframe: pd.DataFrame,
|
|
12
|
+
) -> MissingValueSummary:
|
|
13
|
+
"""
|
|
14
|
+
Generate missing value statistics for a dataset.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
dataframe : pandas.DataFrame
|
|
19
|
+
Dataset to analyze.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
MissingValueSummary
|
|
24
|
+
Summary of missing values in the dataset.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
total_missing = int(dataframe.isna().sum().sum())
|
|
28
|
+
|
|
29
|
+
total_cells = dataframe.shape[0] * dataframe.shape[1]
|
|
30
|
+
|
|
31
|
+
missing_percentage = (
|
|
32
|
+
round((total_missing / total_cells) * 100, 2)
|
|
33
|
+
if total_cells > 0
|
|
34
|
+
else 0.0
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
missing_per_column = dataframe.isna().sum()
|
|
38
|
+
|
|
39
|
+
columns_with_missing = {
|
|
40
|
+
str(column): int(count)
|
|
41
|
+
for column, count in missing_per_column.items()
|
|
42
|
+
if count > 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
columns_without_missing = [
|
|
46
|
+
str(column)
|
|
47
|
+
for column, count in missing_per_column.items()
|
|
48
|
+
if count == 0
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
return MissingValueSummary(
|
|
52
|
+
total_missing=total_missing,
|
|
53
|
+
missing_percentage=missing_percentage,
|
|
54
|
+
columns_with_missing=columns_with_missing,
|
|
55
|
+
columns_without_missing=columns_without_missing,
|
|
56
|
+
)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models used by Datapilot analysis modules.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(slots=True)
|
|
11
|
+
class DatasetSummary:
|
|
12
|
+
"""
|
|
13
|
+
Summary information about a dataset.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
rows: int
|
|
17
|
+
columns: int
|
|
18
|
+
memory_usage_mb: float
|
|
19
|
+
column_names: list[str]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(slots=True)
|
|
23
|
+
class MissingValueSummary:
|
|
24
|
+
"""
|
|
25
|
+
Summary information about missing values in a dataset.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
total_missing: int
|
|
29
|
+
missing_percentage: float
|
|
30
|
+
columns_with_missing: dict[str, int]
|
|
31
|
+
columns_without_missing: list[str]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(slots=True)
|
|
35
|
+
class DuplicateSummary:
|
|
36
|
+
"""
|
|
37
|
+
Summary information about duplicate rows in a dataset.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
total_duplicates: int
|
|
41
|
+
duplicate_percentage: float
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(slots=True)
|
|
45
|
+
class DataTypeSummary:
|
|
46
|
+
"""
|
|
47
|
+
Summary information about dataset column types.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
numeric_columns: list[str]
|
|
51
|
+
categorical_columns: list[str]
|
|
52
|
+
boolean_columns: list[str]
|
|
53
|
+
datetime_columns: list[str]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(slots=True)
|
|
57
|
+
class DatasetHealth:
|
|
58
|
+
"""
|
|
59
|
+
Overall health assessment of a dataset.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
score: int
|
|
63
|
+
grade: str
|
|
64
|
+
status: str
|
|
65
|
+
ml_ready: bool
|
|
66
|
+
strengths: list[str]
|
|
67
|
+
weaknesses: list[str]
|
|
68
|
+
recommendations: list[str]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(slots=True)
|
|
72
|
+
class StatisticsSummary:
|
|
73
|
+
"""
|
|
74
|
+
Statistical summary of numeric columns.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
column_statistics: dict[str, dict[str, float]]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(slots=True)
|
|
81
|
+
class OutlierSummary:
|
|
82
|
+
"""
|
|
83
|
+
Summary of outlier detection.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
total_outliers: int
|
|
87
|
+
outlier_percentage: float
|
|
88
|
+
columns_with_outliers: dict[str, int]
|
|
89
|
+
columns_without_outliers: list[str]
|
|
90
|
+
|
|
91
|
+
@dataclass(slots=True)
|
|
92
|
+
class CorrelationSummary:
|
|
93
|
+
"""
|
|
94
|
+
Summary of correlation analysis.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
correlation_matrix: pd.DataFrame
|
|
98
|
+
strong_positive_pairs: dict[str, float]
|
|
99
|
+
strong_negative_pairs: dict[str, float]
|
|
100
|
+
|
|
101
|
+
@dataclass(slots=True)
|
|
102
|
+
class InsightSummary:
|
|
103
|
+
"""
|
|
104
|
+
Summary of generated dataset insights.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
insights: list[str]
|
|
108
|
+
recommendations: list[str]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Outlier detection for Datapilot.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .models import OutlierSummary
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_outlier_summary(
|
|
11
|
+
dataframe: pd.DataFrame,
|
|
12
|
+
) -> OutlierSummary:
|
|
13
|
+
"""
|
|
14
|
+
Detect outliers using the IQR method.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
numeric_dataframe = dataframe.select_dtypes(
|
|
18
|
+
include="number",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
total_outliers = 0
|
|
22
|
+
|
|
23
|
+
columns_with_outliers: dict[str, int] = {}
|
|
24
|
+
|
|
25
|
+
columns_without_outliers: list[str] = []
|
|
26
|
+
|
|
27
|
+
for column in numeric_dataframe.columns:
|
|
28
|
+
|
|
29
|
+
series = numeric_dataframe[column].dropna()
|
|
30
|
+
|
|
31
|
+
if series.empty:
|
|
32
|
+
columns_without_outliers.append(
|
|
33
|
+
str(column)
|
|
34
|
+
)
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
q1 = series.quantile(0.25)
|
|
38
|
+
q3 = series.quantile(0.75)
|
|
39
|
+
|
|
40
|
+
iqr = q3 - q1
|
|
41
|
+
|
|
42
|
+
lower_bound = q1 - (1.5 * iqr)
|
|
43
|
+
upper_bound = q3 + (1.5 * iqr)
|
|
44
|
+
|
|
45
|
+
outlier_count = int(
|
|
46
|
+
(
|
|
47
|
+
(series < lower_bound)
|
|
48
|
+
| (series > upper_bound)
|
|
49
|
+
).sum()
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if outlier_count > 0:
|
|
53
|
+
|
|
54
|
+
columns_with_outliers[
|
|
55
|
+
str(column)
|
|
56
|
+
] = outlier_count
|
|
57
|
+
|
|
58
|
+
total_outliers += outlier_count
|
|
59
|
+
|
|
60
|
+
else:
|
|
61
|
+
|
|
62
|
+
columns_without_outliers.append(
|
|
63
|
+
str(column)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
total_numeric_values = int(
|
|
67
|
+
numeric_dataframe.count().sum()
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
outlier_percentage = (
|
|
71
|
+
round(
|
|
72
|
+
(total_outliers / total_numeric_values)
|
|
73
|
+
* 100,
|
|
74
|
+
2,
|
|
75
|
+
)
|
|
76
|
+
if total_numeric_values > 0
|
|
77
|
+
else 0.0
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return OutlierSummary(
|
|
81
|
+
total_outliers=total_outliers,
|
|
82
|
+
outlier_percentage=outlier_percentage,
|
|
83
|
+
columns_with_outliers=columns_with_outliers,
|
|
84
|
+
columns_without_outliers=columns_without_outliers,
|
|
85
|
+
)
|