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,19 @@
|
|
|
1
|
+
from .bivariate_analysis import BivariateAnalysis
|
|
2
|
+
from .eda_toolkit import EDAToolkit
|
|
3
|
+
from .feature_engineering import FeatureEngineering
|
|
4
|
+
from .first_glance import FirstGlance
|
|
5
|
+
from .univariate_analysis import UnivariateAnalysis
|
|
6
|
+
from .visualizations import Visualizations
|
|
7
|
+
from .warnings import EDAWarning, WarningSystem, get_default_warnings
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"BivariateAnalysis",
|
|
11
|
+
"EDAToolkit",
|
|
12
|
+
"FeatureEngineering",
|
|
13
|
+
"FirstGlance",
|
|
14
|
+
"UnivariateAnalysis",
|
|
15
|
+
"Visualizations",
|
|
16
|
+
"EDAWarning",
|
|
17
|
+
"WarningSystem",
|
|
18
|
+
"get_default_warnings",
|
|
19
|
+
]
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import List, Optional, Tuple, Union
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import plotly.express as px
|
|
7
|
+
import plotly.graph_objects as go
|
|
8
|
+
import statsmodels.api as sm
|
|
9
|
+
from pandas.core.indexes.base import Index as PandasIndex
|
|
10
|
+
from sklearn.preprocessing import StandardScaler
|
|
11
|
+
from statsmodels.formula.api import ols
|
|
12
|
+
from tqdm import tqdm
|
|
13
|
+
|
|
14
|
+
from .univariate_analysis import UnivariateAnalysis
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BivariateAnalysis:
|
|
18
|
+
def __init__(
|
|
19
|
+
self, data: pd.DataFrame, numerical_columns: Optional[List[str]] = None
|
|
20
|
+
) -> None:
|
|
21
|
+
"""
|
|
22
|
+
Initialize the BivariateAnalysis class with data and optional numerical columns.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
data : pd.DataFrame
|
|
27
|
+
The data to be analyzed.
|
|
28
|
+
numerical_columns : Optional[List[str]], optional
|
|
29
|
+
List of numerical columns to consider. If None, all numerical columns in the data will be used.
|
|
30
|
+
"""
|
|
31
|
+
if not isinstance(data, pd.DataFrame):
|
|
32
|
+
raise ValueError("Data should be a pandas DataFrame")
|
|
33
|
+
self.data = data.copy()
|
|
34
|
+
if numerical_columns is None:
|
|
35
|
+
self.numerical_columns = self.data.select_dtypes(
|
|
36
|
+
include=[np.number]
|
|
37
|
+
).columns.tolist()
|
|
38
|
+
else:
|
|
39
|
+
self.numerical_columns = numerical_columns
|
|
40
|
+
|
|
41
|
+
def generate_correlation_heatmap(self, columns: List[str]) -> go.Figure:
|
|
42
|
+
"""
|
|
43
|
+
Generate an interactive correlation heatmap for the specified columns using Plotly.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
columns : list of str
|
|
48
|
+
List of column names to include in the correlation analysis.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
go.Figure
|
|
53
|
+
The Plotly figure object of the plot.
|
|
54
|
+
"""
|
|
55
|
+
if len(columns) == 0:
|
|
56
|
+
columns = self.numerical_columns
|
|
57
|
+
|
|
58
|
+
# Calculate the correlation matrix
|
|
59
|
+
corr_matrix = self.data[columns].corr(method="pearson")
|
|
60
|
+
|
|
61
|
+
# Create a mask for the upper triangle
|
|
62
|
+
mask = np.triu(np.ones_like(corr_matrix, dtype=bool))
|
|
63
|
+
corr_matrix_masked = corr_matrix.mask(mask)
|
|
64
|
+
|
|
65
|
+
# Calculate dynamic figure size and font size
|
|
66
|
+
n_features = len(columns)
|
|
67
|
+
base_size = 500 # Base size for the heatmap
|
|
68
|
+
fig_size = min(
|
|
69
|
+
1000, max(500, base_size + 20 * n_features)
|
|
70
|
+
) # Adjust figure size based on number of features
|
|
71
|
+
font_size = max(
|
|
72
|
+
8, min(12, 20 - n_features // 10)
|
|
73
|
+
) # Adjust font size based on number of features
|
|
74
|
+
|
|
75
|
+
# Create a Plotly heatmap
|
|
76
|
+
fig = go.Figure(
|
|
77
|
+
data=go.Heatmap(
|
|
78
|
+
z=corr_matrix_masked.values[
|
|
79
|
+
::-1
|
|
80
|
+
], # Reverse the order of rows to flip the triangle
|
|
81
|
+
x=corr_matrix_masked.columns,
|
|
82
|
+
y=corr_matrix_masked.index[
|
|
83
|
+
::-1
|
|
84
|
+
], # Reverse the order of labels to match the flipped triangle
|
|
85
|
+
colorscale="plasma",
|
|
86
|
+
zmin=-1,
|
|
87
|
+
zmax=1,
|
|
88
|
+
colorbar={"title": "Correlation"},
|
|
89
|
+
hovertemplate="x: %{x}<br>y: %{y}<br>Correlation: %{z:.2f}<extra></extra>",
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Update layout
|
|
94
|
+
fig.update_layout(
|
|
95
|
+
title=f"Correlation Heatmap ({'Pearson'.capitalize()} method)",
|
|
96
|
+
xaxis={
|
|
97
|
+
"title": "Features",
|
|
98
|
+
"side": "bottom",
|
|
99
|
+
"tickangle": 45, # Always set to 45 degrees
|
|
100
|
+
"tickfont": {"size": font_size},
|
|
101
|
+
"constrain": "domain",
|
|
102
|
+
},
|
|
103
|
+
yaxis={
|
|
104
|
+
"title": "Features",
|
|
105
|
+
"tickfont": {"size": font_size},
|
|
106
|
+
"scaleanchor": "x",
|
|
107
|
+
"scaleratio": 1,
|
|
108
|
+
"constrain": "domain",
|
|
109
|
+
},
|
|
110
|
+
plot_bgcolor="rgba(0,0,0,0)",
|
|
111
|
+
xaxis_showgrid=False,
|
|
112
|
+
yaxis_showgrid=False,
|
|
113
|
+
xaxis_zeroline=False,
|
|
114
|
+
yaxis_zeroline=False,
|
|
115
|
+
width=fig_size,
|
|
116
|
+
height=fig_size,
|
|
117
|
+
margin={"l": 100, "r": 100, "t": 100, "b": 100},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Adjust the size of the heatmap to leave space for labels
|
|
121
|
+
heatmap_size = 0.8 # 80% of the figure size
|
|
122
|
+
fig.update_layout(
|
|
123
|
+
xaxis={"domain": [0, heatmap_size]}, yaxis={"domain": [0, heatmap_size]}
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
fig.show()
|
|
127
|
+
|
|
128
|
+
return fig
|
|
129
|
+
|
|
130
|
+
def _clean_column_name(self, name: str) -> str:
|
|
131
|
+
"""
|
|
132
|
+
Clean column names to be compatible with formula notation.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
name : str
|
|
137
|
+
The column name to be cleaned.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
str
|
|
142
|
+
The cleaned column name.
|
|
143
|
+
"""
|
|
144
|
+
return re.sub(r"\W+", "_", name)
|
|
145
|
+
|
|
146
|
+
def perform_anova_numeric_categorical(
|
|
147
|
+
self, cat_cols: Union[str, List[str]]
|
|
148
|
+
) -> Tuple[go.Figure, pd.DataFrame]:
|
|
149
|
+
"""
|
|
150
|
+
Perform ANOVA between numerical and categorical variables using Fisher's F-statistic.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
cat_cols : Union[str, List[str]]
|
|
155
|
+
List of categorical column names to include in the ANOVA analysis.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
Tuple[go.Figure, pd.DataFrame]
|
|
160
|
+
The Plotly figure object of the ANOVA heatmap and the DataFrame containing ANOVA results.
|
|
161
|
+
"""
|
|
162
|
+
if isinstance(cat_cols, str):
|
|
163
|
+
cat_cols = [cat_cols]
|
|
164
|
+
|
|
165
|
+
# Check if the columns exist in the DataFrame
|
|
166
|
+
missing_columns = [col for col in cat_cols if col not in self.data.columns]
|
|
167
|
+
if missing_columns:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"The following columns are not in the DataFrame: {missing_columns}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Create a copy of the data to avoid modifying the original
|
|
173
|
+
data_copy = self.data.copy()
|
|
174
|
+
|
|
175
|
+
# Clean column names
|
|
176
|
+
data_copy.columns = PandasIndex(
|
|
177
|
+
[self._clean_column_name(col) for col in data_copy.columns]
|
|
178
|
+
)
|
|
179
|
+
cat_cols = [self._clean_column_name(col) for col in cat_cols]
|
|
180
|
+
numerical_columns = [
|
|
181
|
+
self._clean_column_name(col) for col in self.numerical_columns
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
# Standardize numerical data
|
|
185
|
+
std_scaler = StandardScaler()
|
|
186
|
+
numerical_feature_list_std = []
|
|
187
|
+
for num in numerical_columns:
|
|
188
|
+
data_copy[num + "_std"] = std_scaler.fit_transform(
|
|
189
|
+
data_copy[num].to_numpy().reshape(-1, 1)
|
|
190
|
+
)
|
|
191
|
+
numerical_feature_list_std.append(num + "_std")
|
|
192
|
+
|
|
193
|
+
# Perform ANOVA for each combination of numerical and categorical variables
|
|
194
|
+
rows = []
|
|
195
|
+
total_combinations = len(cat_cols) * len(numerical_feature_list_std)
|
|
196
|
+
|
|
197
|
+
with tqdm(total=total_combinations, desc="Performing ANOVA") as pbar:
|
|
198
|
+
for cat in cat_cols:
|
|
199
|
+
col = []
|
|
200
|
+
for num in numerical_feature_list_std:
|
|
201
|
+
try:
|
|
202
|
+
equation = f"{num} ~ C({cat})"
|
|
203
|
+
model = ols(equation, data=data_copy).fit()
|
|
204
|
+
anova_table = sm.stats.anova_lm(model, typ=1)
|
|
205
|
+
col.append(anova_table.loc[f"C({cat})"]["F"])
|
|
206
|
+
except Exception as e:
|
|
207
|
+
print(f"Error in ANOVA for {num} ~ {cat}: {e!s}")
|
|
208
|
+
col.append(np.nan)
|
|
209
|
+
pbar.update(1)
|
|
210
|
+
rows.append(col)
|
|
211
|
+
|
|
212
|
+
# Store the results in a DataFrame
|
|
213
|
+
anova_result = np.array(rows)
|
|
214
|
+
anova_result_df = pd.DataFrame(
|
|
215
|
+
anova_result, columns=self.numerical_columns, index=cat_cols
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Create a Plotly heatmap
|
|
219
|
+
fig = go.Figure(
|
|
220
|
+
data=go.Heatmap(
|
|
221
|
+
z=anova_result_df.values,
|
|
222
|
+
x=anova_result_df.columns,
|
|
223
|
+
y=anova_result_df.index,
|
|
224
|
+
colorscale="plasma",
|
|
225
|
+
zmin=anova_result_df.values.min(),
|
|
226
|
+
zmax=anova_result_df.values.max(),
|
|
227
|
+
colorbar={"title": "Fisher's F-statistic"},
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Update layout
|
|
232
|
+
fig.update_layout(
|
|
233
|
+
title="Fisher's Statistic Heatmap",
|
|
234
|
+
xaxis={"title": "Numerical Features"},
|
|
235
|
+
yaxis={"title": "Categorical Features"},
|
|
236
|
+
plot_bgcolor="rgba(0,0,0,0)", # Remove background grid
|
|
237
|
+
xaxis_showgrid=False,
|
|
238
|
+
yaxis_showgrid=False,
|
|
239
|
+
xaxis_zeroline=False,
|
|
240
|
+
yaxis_zeroline=False,
|
|
241
|
+
width=800,
|
|
242
|
+
height=800,
|
|
243
|
+
margin={
|
|
244
|
+
"l": 100,
|
|
245
|
+
"r": 100,
|
|
246
|
+
"t": 100,
|
|
247
|
+
"b": 100,
|
|
248
|
+
}, # Adjust margins to center the plot
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return fig, anova_result_df
|
|
252
|
+
|
|
253
|
+
def compute_pairwise_cramers_v(
|
|
254
|
+
self, categorical_features: List[str]
|
|
255
|
+
) -> pd.DataFrame:
|
|
256
|
+
"""
|
|
257
|
+
Compute pairwise Cramer's V for categorical features.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
categorical_features : List[str]
|
|
262
|
+
List of categorical feature names.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
pd.DataFrame
|
|
267
|
+
DataFrame with pairwise Cramer's V values.
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
n = len(categorical_features)
|
|
271
|
+
cramers_matrix = np.zeros((n, n))
|
|
272
|
+
|
|
273
|
+
for i in range(n):
|
|
274
|
+
for j in range(n): # Changed to calculate for all pairs
|
|
275
|
+
if i == j:
|
|
276
|
+
cramers_matrix[i, j] = 1.0 # Cramer's V with itself is 1
|
|
277
|
+
else:
|
|
278
|
+
cramers_v = UnivariateAnalysis._get_cramers_v( # noqa: SLF001
|
|
279
|
+
self.data, categorical_features[i], categorical_features[j]
|
|
280
|
+
)
|
|
281
|
+
cramers_matrix[i, j] = cramers_v
|
|
282
|
+
cramers_matrix[j, i] = cramers_v # Mirror the value
|
|
283
|
+
|
|
284
|
+
return pd.DataFrame(
|
|
285
|
+
cramers_matrix, index=categorical_features, columns=categorical_features
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def plot_pairwise_cramers_v(
|
|
289
|
+
self,
|
|
290
|
+
categorical_features: List[str],
|
|
291
|
+
fig_width: int = 800,
|
|
292
|
+
fig_height: int = 800,
|
|
293
|
+
) -> None:
|
|
294
|
+
"""
|
|
295
|
+
Plot pairwise Cramer's V for categorical variables using Plotly Express.
|
|
296
|
+
|
|
297
|
+
Parameters
|
|
298
|
+
----------
|
|
299
|
+
categorical_features : List[str]
|
|
300
|
+
List of categorical feature names. If None, all object and category columns will be used.
|
|
301
|
+
fig_width : int, optional
|
|
302
|
+
Width of the figure in pixels.
|
|
303
|
+
fig_height : int, optional
|
|
304
|
+
Height of the figure in pixels.
|
|
305
|
+
"""
|
|
306
|
+
# Compute pairwise Cramer's V
|
|
307
|
+
cramers_df = self.compute_pairwise_cramers_v(categorical_features)
|
|
308
|
+
|
|
309
|
+
# Mask the upper triangle
|
|
310
|
+
mask = np.triu(np.ones_like(cramers_df, dtype=bool))
|
|
311
|
+
|
|
312
|
+
# Create the heatmap using Plotly Express
|
|
313
|
+
fig = px.imshow(
|
|
314
|
+
cramers_df.where(~mask, np.nan), # Mask the upper triangle
|
|
315
|
+
labels={
|
|
316
|
+
"x": "Categorical Features",
|
|
317
|
+
"y": "Categorical Features",
|
|
318
|
+
"color": "Cramer's V",
|
|
319
|
+
},
|
|
320
|
+
x=cramers_df.columns,
|
|
321
|
+
y=cramers_df.index,
|
|
322
|
+
color_continuous_scale="Plasma",
|
|
323
|
+
text_auto=True,
|
|
324
|
+
aspect="auto",
|
|
325
|
+
width=fig_width,
|
|
326
|
+
height=fig_height,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# Update layout to remove gridlines
|
|
330
|
+
fig.update_layout(
|
|
331
|
+
title="Pairwise Cramer's V for Categorical Variables (Lower Triangle)",
|
|
332
|
+
xaxis={"showgrid": False}, # Remove x-axis gridlines
|
|
333
|
+
yaxis={"showgrid": False}, # Remove y-axis gridlines
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Display the figure
|
|
337
|
+
fig.show()
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
'''
|
|
341
|
+
def generate_correlation_heatmap(self, columns: List[str]) -> go.Figure:
|
|
342
|
+
"""
|
|
343
|
+
Generate an interactive correlation heatmap for the specified columns using Plotly.
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
columns : list of str
|
|
348
|
+
List of column names to include in the correlation analysis.
|
|
349
|
+
|
|
350
|
+
Returns
|
|
351
|
+
-------
|
|
352
|
+
go.Figure
|
|
353
|
+
The Plotly figure object of the plot.
|
|
354
|
+
"""
|
|
355
|
+
if len(columns) == 0:
|
|
356
|
+
columns = self.numerical_columns
|
|
357
|
+
|
|
358
|
+
# Calculate the correlation matrix
|
|
359
|
+
corr_matrix = self.data[columns].corr(method="pearson")
|
|
360
|
+
|
|
361
|
+
# Create a mask for the upper triangle
|
|
362
|
+
mask = np.triu(np.ones_like(corr_matrix, dtype=bool))
|
|
363
|
+
corr_matrix_masked = corr_matrix.mask(mask)
|
|
364
|
+
|
|
365
|
+
# Create a Plotly heatmap
|
|
366
|
+
fig = go.Figure(
|
|
367
|
+
data=go.Heatmap(
|
|
368
|
+
z=corr_matrix_masked.values[
|
|
369
|
+
::-1
|
|
370
|
+
], # Reverse the order of rows to flip the triangle
|
|
371
|
+
x=corr_matrix_masked.columns,
|
|
372
|
+
y=corr_matrix_masked.index[
|
|
373
|
+
::-1
|
|
374
|
+
], # Reverse the order of labels to match the flipped triangle
|
|
375
|
+
colorscale="plasma",
|
|
376
|
+
zmin=0,
|
|
377
|
+
zmax=1,
|
|
378
|
+
colorbar={"title": "Correlation"},
|
|
379
|
+
)
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# Update layout with square aspect ratio and remove excess margins
|
|
383
|
+
fig.update_layout(
|
|
384
|
+
title=f"Correlation Heatmap ({'Pearson'.capitalize()} method)",
|
|
385
|
+
xaxis={
|
|
386
|
+
"title": "Features",
|
|
387
|
+
"side": "bottom",
|
|
388
|
+
"tickangle": 45,
|
|
389
|
+
"constrain": "domain",
|
|
390
|
+
}, # Ensure square cells
|
|
391
|
+
yaxis={
|
|
392
|
+
"title": "Features",
|
|
393
|
+
"scaleanchor": "x",
|
|
394
|
+
"scaleratio": 1,
|
|
395
|
+
"constrain": "domain",
|
|
396
|
+
}, # Ensure square cells
|
|
397
|
+
plot_bgcolor="rgba(0,0,0,0)", # Remove background grid
|
|
398
|
+
xaxis_showgrid=False,
|
|
399
|
+
yaxis_showgrid=False,
|
|
400
|
+
xaxis_zeroline=False,
|
|
401
|
+
yaxis_zeroline=False,
|
|
402
|
+
margin={
|
|
403
|
+
"l": 40,
|
|
404
|
+
"r": 40,
|
|
405
|
+
"t": 40,
|
|
406
|
+
"b": 40,
|
|
407
|
+
}, # Adjust margins to center the plot
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
fig.show()
|
|
411
|
+
|
|
412
|
+
return fig
|
|
413
|
+
'''
|