classifier-toolkit 0.1.0__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.
- classifier_toolkit/eda/__init__.py +19 -0
- classifier_toolkit/eda/bivariate_analysis.py +413 -0
- classifier_toolkit/eda/eda_toolkit.py +549 -0
- classifier_toolkit/eda/feature_engineering.py +1310 -0
- classifier_toolkit/eda/first_glance.py +253 -0
- classifier_toolkit/eda/univariate_analysis.py +778 -0
- classifier_toolkit/eda/visualizations.py +1248 -0
- classifier_toolkit/eda/warnings/__init__.py +4 -0
- classifier_toolkit/eda/warnings/automated_warnings.py +20 -0
- classifier_toolkit/eda/warnings/default_warnings.py +286 -0
- classifier_toolkit/feature_selection/__init__.py +33 -0
- classifier_toolkit/feature_selection/base.py +182 -0
- classifier_toolkit/feature_selection/embedded_methods/__init__.py +7 -0
- classifier_toolkit/feature_selection/embedded_methods/elastic_net.py +178 -0
- classifier_toolkit/feature_selection/meta_selector.py +329 -0
- classifier_toolkit/feature_selection/utils/__init__.py +17 -0
- classifier_toolkit/feature_selection/utils/plottings.py +65 -0
- classifier_toolkit/feature_selection/utils/scoring.py +86 -0
- classifier_toolkit/feature_selection/wrapper_methods/__init__.py +11 -0
- classifier_toolkit/feature_selection/wrapper_methods/rfe.py +345 -0
- classifier_toolkit/feature_selection/wrapper_methods/sequential_selection.py +566 -0
- classifier_toolkit/model_fitting/__init__.py +0 -0
- classifier_toolkit/model_fitting/model_search.py +3 -0
- classifier_toolkit-0.1.0.dist-info/METADATA +99 -0
- classifier_toolkit-0.1.0.dist-info/RECORD +26 -0
- classifier_toolkit-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from colorama import Fore, Style, init
|
|
5
|
+
from tabulate import tabulate
|
|
6
|
+
|
|
7
|
+
init(autoreset=True) # Initialize colorama
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FirstGlance:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
dataframe: pd.DataFrame,
|
|
14
|
+
numerical_columns: Optional[List[str]],
|
|
15
|
+
categorical_columns: Optional[List[str]],
|
|
16
|
+
exclude_columns: Optional[List[str]] = None,
|
|
17
|
+
id_column: Optional[str] = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Initialize the FirstGlance class with data and configuration.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
dataframe : pd.DataFrame
|
|
25
|
+
The data to be analyzed.
|
|
26
|
+
numerical_columns : Optional[List[str]]
|
|
27
|
+
List of numerical columns.
|
|
28
|
+
categorical_columns : Optional[List[str]]
|
|
29
|
+
List of categorical columns.
|
|
30
|
+
exclude_columns : Optional[List[str]], optional
|
|
31
|
+
List of columns to exclude from analysis, by default None.
|
|
32
|
+
id_column : Optional[str], optional
|
|
33
|
+
Column to identify duplicates, by default None.
|
|
34
|
+
"""
|
|
35
|
+
self.data = dataframe
|
|
36
|
+
self.numerical_columns = numerical_columns or []
|
|
37
|
+
self.categorical_columns = categorical_columns or []
|
|
38
|
+
self.exclude_columns = exclude_columns or []
|
|
39
|
+
self.id_column = id_column
|
|
40
|
+
|
|
41
|
+
# Define the numerical and categorical columns using the _get_analysis_columns method
|
|
42
|
+
self.numerical_columns = self._get_analysis_columns(numerical_columns)
|
|
43
|
+
self.categorical_columns = self._get_analysis_columns(categorical_columns)
|
|
44
|
+
self.columns = self._get_analysis_columns()
|
|
45
|
+
|
|
46
|
+
def _get_analysis_columns(self, column_list: Optional[List] = None) -> List[str]:
|
|
47
|
+
"""
|
|
48
|
+
Get the columns to analyze based on the provided column list.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
column_list : Optional[List], optional
|
|
53
|
+
List of columns to analyze, by default None.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
List[str]
|
|
58
|
+
List of columns to analyze.
|
|
59
|
+
"""
|
|
60
|
+
if column_list is None:
|
|
61
|
+
return [col for col in self.data.columns if col not in self.exclude_columns]
|
|
62
|
+
else:
|
|
63
|
+
return [col for col in column_list if col not in self.exclude_columns]
|
|
64
|
+
|
|
65
|
+
def generate_first_glance(self) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Display the first few rows and summary statistics of the data.
|
|
68
|
+
"""
|
|
69
|
+
print(Fore.GREEN + "First few rows:")
|
|
70
|
+
print(self.data.head().to_string())
|
|
71
|
+
print("-" * 60)
|
|
72
|
+
print(Fore.GREEN + "Summary statistics:")
|
|
73
|
+
print(self.data.describe().to_string())
|
|
74
|
+
|
|
75
|
+
def check_duplicates(self) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Check for duplicates based on the provided id column.
|
|
78
|
+
"""
|
|
79
|
+
dup_count = self.data[self.id_column].duplicated().sum()
|
|
80
|
+
total_rows = len(self.data)
|
|
81
|
+
print(
|
|
82
|
+
Fore.RED
|
|
83
|
+
+ f"""
|
|
84
|
+
WARNING! There are {dup_count:,} duplicates based on the provided id.
|
|
85
|
+
For context, there are {total_rows:,} rows in this dataframe.
|
|
86
|
+
Duplicate percentage: {(dup_count / total_rows) * 100:.2f}% To see the duplicate
|
|
87
|
+
values in more detail, please call the see_duplicates method
|
|
88
|
+
"""
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def check_data_types(self) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Check the data types of each column in the dataframe.
|
|
94
|
+
"""
|
|
95
|
+
for col, dtype in self.data.dtypes.items():
|
|
96
|
+
print(f"{col}: {dtype}")
|
|
97
|
+
|
|
98
|
+
def check_unique_values(self) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Display the number of unique values in each column.
|
|
101
|
+
"""
|
|
102
|
+
for col in self.columns:
|
|
103
|
+
unique_count = self.data[col].nunique()
|
|
104
|
+
print(f"{col}: {unique_count:,}")
|
|
105
|
+
|
|
106
|
+
def check_value_counts(self, top_n: int = 10) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Display the value counts of the top n values in each column.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
top_n : int, optional
|
|
113
|
+
Number of top values to display, by default 10.
|
|
114
|
+
"""
|
|
115
|
+
for col in self.columns:
|
|
116
|
+
print(Fore.GREEN + f"\nColumn: {col}")
|
|
117
|
+
value_counts = self.data[col].value_counts().head(top_n)
|
|
118
|
+
for value, count in value_counts.items():
|
|
119
|
+
print(f"{value}: {count:,}")
|
|
120
|
+
if len(self.data[col].unique()) > top_n:
|
|
121
|
+
print("...")
|
|
122
|
+
print("-" * 60)
|
|
123
|
+
|
|
124
|
+
def see_duplicates(self) -> None:
|
|
125
|
+
"""
|
|
126
|
+
Display the duplicate rows in the dataframe.
|
|
127
|
+
"""
|
|
128
|
+
# Count occurrences of each ID
|
|
129
|
+
id_counts = self.data[self.id_column].value_counts()
|
|
130
|
+
|
|
131
|
+
# Filter for IDs that appear more than once
|
|
132
|
+
duplicate_counts = id_counts[id_counts > 1]
|
|
133
|
+
|
|
134
|
+
if duplicate_counts.empty:
|
|
135
|
+
print("No duplicates found.")
|
|
136
|
+
else:
|
|
137
|
+
print(
|
|
138
|
+
f"Duplicates found. Number of duplicates grouped by {self.id_column}:\n"
|
|
139
|
+
)
|
|
140
|
+
print(duplicate_counts)
|
|
141
|
+
|
|
142
|
+
def super_vision(self) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Perform a complete analysis, including all checks and prints.
|
|
145
|
+
"""
|
|
146
|
+
print(Fore.CYAN + Style.BRIGHT + "=== Data Overview ===")
|
|
147
|
+
print(f"Total rows: {len(self.data):,}")
|
|
148
|
+
print(f"Total columns: {len(self.data.columns):,}")
|
|
149
|
+
print(f"Memory usage: {self.data.memory_usage(deep=True).sum() / 1e6:.2f} MB")
|
|
150
|
+
print("\n" + "=" * 50 + "\n")
|
|
151
|
+
|
|
152
|
+
print(Fore.CYAN + Style.BRIGHT + "=== First Glance ===")
|
|
153
|
+
self.generate_first_glance()
|
|
154
|
+
print("\n" + "=" * 50 + "\n")
|
|
155
|
+
|
|
156
|
+
print(
|
|
157
|
+
Fore.CYAN
|
|
158
|
+
+ Style.BRIGHT
|
|
159
|
+
+ f"=== Duplicate Checks Based on {self.id_column} ==="
|
|
160
|
+
)
|
|
161
|
+
self.check_duplicates()
|
|
162
|
+
print("\n" + "=" * 50 + "\n")
|
|
163
|
+
|
|
164
|
+
print(Fore.CYAN + Style.BRIGHT + "=== Quality Checks ===")
|
|
165
|
+
self.get_column_statistics()
|
|
166
|
+
print("\n" + "=" * 50 + "\n")
|
|
167
|
+
|
|
168
|
+
print(Fore.CYAN + Style.BRIGHT + "=== Value Counts (Top 10) ===")
|
|
169
|
+
self.check_value_counts(top_n=10)
|
|
170
|
+
|
|
171
|
+
def get_column_statistics(self) -> None:
|
|
172
|
+
stats: List[List[str]] = []
|
|
173
|
+
max_col_length = max(len(col) for col in self.columns)
|
|
174
|
+
|
|
175
|
+
for col in self.columns:
|
|
176
|
+
null_count = self.data[col].isnull().sum()
|
|
177
|
+
dtype = self.data[col].dtype
|
|
178
|
+
unique_count = self.data[col].nunique()
|
|
179
|
+
negative_count: Optional[int] = None
|
|
180
|
+
|
|
181
|
+
if col in self.numerical_columns:
|
|
182
|
+
negative_count = (self.data[col] < 0).sum()
|
|
183
|
+
|
|
184
|
+
# Format numbers with thousand separators
|
|
185
|
+
null_count_str = f"{null_count:,}".rjust(10)
|
|
186
|
+
unique_count_str = f"{unique_count:,}".rjust(15)
|
|
187
|
+
|
|
188
|
+
# Handle numpy.int64 and other numeric types
|
|
189
|
+
if negative_count is not None:
|
|
190
|
+
negative_count_str = f"{negative_count:,}".rjust(15)
|
|
191
|
+
else:
|
|
192
|
+
negative_count_str = "N/A".rjust(15)
|
|
193
|
+
|
|
194
|
+
stats.append(
|
|
195
|
+
[
|
|
196
|
+
col.ljust(max_col_length),
|
|
197
|
+
null_count_str,
|
|
198
|
+
str(dtype).ljust(10),
|
|
199
|
+
unique_count_str,
|
|
200
|
+
negative_count_str,
|
|
201
|
+
]
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
headers = [
|
|
205
|
+
"Column",
|
|
206
|
+
"Null Count",
|
|
207
|
+
"Data Type",
|
|
208
|
+
"Unique Values",
|
|
209
|
+
"Negative Values",
|
|
210
|
+
]
|
|
211
|
+
table = tabulate(stats, headers=headers, tablefmt="grid")
|
|
212
|
+
|
|
213
|
+
lines = table.split("\n")
|
|
214
|
+
for i, line in enumerate(lines):
|
|
215
|
+
if i <= 2: # Header lines including the top border
|
|
216
|
+
print(Fore.CYAN + line)
|
|
217
|
+
else:
|
|
218
|
+
cells = line.split("|")
|
|
219
|
+
if len(cells) > 1:
|
|
220
|
+
null_count_str = cells[2].strip()
|
|
221
|
+
negative_count_str = cells[5].strip()
|
|
222
|
+
|
|
223
|
+
if null_count_str != "0":
|
|
224
|
+
cells[2] = (
|
|
225
|
+
Fore.RED
|
|
226
|
+
+ Style.BRIGHT
|
|
227
|
+
+ null_count_str.center(14)
|
|
228
|
+
+ Style.RESET_ALL
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if negative_count_str not in ["0", "N/A"]:
|
|
232
|
+
cells[5] = (
|
|
233
|
+
Fore.RED
|
|
234
|
+
+ Style.BRIGHT
|
|
235
|
+
+ negative_count_str.center(14)
|
|
236
|
+
+ Style.RESET_ALL
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
formatted_line = f"| {cells[1].strip().ljust(max_col_length)} | {cells[2].center(10)} | {cells[3].strip().ljust(10)} | {cells[4].strip().rjust(15)} | {cells[5].strip().rjust(15)} |"
|
|
240
|
+
print(Fore.WHITE + formatted_line)
|
|
241
|
+
|
|
242
|
+
print(Fore.WHITE + lines[-1]) # Bottom border
|
|
243
|
+
|
|
244
|
+
def __str__(self) -> str:
|
|
245
|
+
"""
|
|
246
|
+
Return a string representation of the FirstGlance object.
|
|
247
|
+
|
|
248
|
+
Returns
|
|
249
|
+
-------
|
|
250
|
+
str
|
|
251
|
+
String representation of the FirstGlance object.
|
|
252
|
+
"""
|
|
253
|
+
return f"FirstGlance object with shape: {self.data.shape}"
|