scikit-extensions 0.2.0__tar.gz → 0.2.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.
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/PKG-INFO +1 -1
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/pyproject.toml +1 -1
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/pyproject.toml.orig +1 -1
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/skext/linear_model.py +8 -4
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/skext/multicolinerity.py +28 -6
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/skext/multilabels.py +46 -23
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/README.md +0 -0
- {scikit_extensions-0.2.0 → scikit_extensions-0.2.2}/skext/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scikit-extensions
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: A collection of Scikit-learn-compatible transformers for feature engineering, preprocessing, and pipeline utilities.
|
|
5
5
|
Keywords: scikit-learn,transformers,preprocessing,pipeline
|
|
6
6
|
Author: sh99
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "scikit-extensions"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "A collection of Scikit-learn-compatible transformers for feature engineering, preprocessing, and pipeline utilities."
|
|
5
5
|
authors = [{ name = "sh99", email = "shriramh99@gmail.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -39,12 +39,15 @@ class OLSRegressor(BaseEstimator, RegressorMixin):
|
|
|
39
39
|
self : object
|
|
40
40
|
Returns self.
|
|
41
41
|
"""
|
|
42
|
+
input_feature_names = X.columns.tolist() if isinstance(X, pd.DataFrame) else None
|
|
43
|
+
|
|
42
44
|
# Check input data
|
|
43
45
|
X, y = check_X_y(X, y, y_numeric=True)
|
|
44
46
|
|
|
45
47
|
# Convert to DataFrame to preserve feature names
|
|
46
|
-
if
|
|
47
|
-
self.feature_names_ =
|
|
48
|
+
if input_feature_names is not None:
|
|
49
|
+
self.feature_names_ = input_feature_names
|
|
50
|
+
X = pd.DataFrame(X, columns=self.feature_names_)
|
|
48
51
|
else:
|
|
49
52
|
self.feature_names_ = [f'x{i}' for i in range(X.shape[1])]
|
|
50
53
|
X = pd.DataFrame(X, columns=self.feature_names_)
|
|
@@ -98,14 +101,15 @@ class OLSRegressor(BaseEstimator, RegressorMixin):
|
|
|
98
101
|
"""Return model coefficients (excluding intercept)."""
|
|
99
102
|
if self.results_ is None:
|
|
100
103
|
raise ValueError("Model not fitted. Call 'fit' first.")
|
|
101
|
-
|
|
104
|
+
params = np.asarray(self.results_.params)
|
|
105
|
+
return params[1:] if self.fit_intercept else params
|
|
102
106
|
|
|
103
107
|
@property
|
|
104
108
|
def intercept_(self):
|
|
105
109
|
"""Return intercept value."""
|
|
106
110
|
if self.results_ is None:
|
|
107
111
|
raise ValueError("Model not fitted. Call 'fit' first.")
|
|
108
|
-
return self.results_.params[0] if self.fit_intercept else 0.0
|
|
112
|
+
return np.asarray(self.results_.params)[0] if self.fit_intercept else 0.0
|
|
109
113
|
|
|
110
114
|
def summary(self):
|
|
111
115
|
"""Return statsmodels summary object."""
|
|
@@ -31,8 +31,6 @@ class MultiCollinearityReducer(BaseEstimator, TransformerMixin):
|
|
|
31
31
|
self.correlation_threshold = correlation_threshold
|
|
32
32
|
self.strategy = strategy
|
|
33
33
|
self.random_state = random_state
|
|
34
|
-
self.selected_features_ = None
|
|
35
|
-
self.feature_correlations_ = None
|
|
36
34
|
|
|
37
35
|
def _select_by_variance(self, X, features):
|
|
38
36
|
"""Select feature with highest variance from group"""
|
|
@@ -74,6 +72,18 @@ class MultiCollinearityReducer(BaseEstimator, TransformerMixin):
|
|
|
74
72
|
"""
|
|
75
73
|
# Convert to DataFrame if needed
|
|
76
74
|
X_ = X.copy() if isinstance(X, pd.DataFrame) else pd.DataFrame(X)
|
|
75
|
+
self.n_features_in_ = X_.shape[1]
|
|
76
|
+
if isinstance(X, pd.DataFrame):
|
|
77
|
+
self.input_columns_ = list(X.columns)
|
|
78
|
+
if all(isinstance(column, str) for column in X.columns):
|
|
79
|
+
self.feature_names_in_ = np.asarray(X.columns, dtype=object)
|
|
80
|
+
elif hasattr(self, "feature_names_in_"):
|
|
81
|
+
del self.feature_names_in_
|
|
82
|
+
else:
|
|
83
|
+
self.input_columns_ = [f"x{i}" for i in range(X_.shape[1])]
|
|
84
|
+
X_.columns = self.input_columns_
|
|
85
|
+
if hasattr(self, "feature_names_in_"):
|
|
86
|
+
del self.feature_names_in_
|
|
77
87
|
|
|
78
88
|
# Calculate correlation matrix
|
|
79
89
|
corr_matrix = X_.corr().abs()
|
|
@@ -129,12 +139,24 @@ class MultiCollinearityReducer(BaseEstimator, TransformerMixin):
|
|
|
129
139
|
pandas.DataFrame
|
|
130
140
|
Transformed data with selected features only
|
|
131
141
|
"""
|
|
132
|
-
if self
|
|
142
|
+
if not hasattr(self, "selected_features_"):
|
|
133
143
|
raise ValueError("Transformer has not been fitted yet.")
|
|
134
|
-
|
|
135
|
-
X_ = X.copy() if isinstance(X, pd.DataFrame) else pd.DataFrame(X)
|
|
144
|
+
|
|
145
|
+
X_ = X.copy() if isinstance(X, pd.DataFrame) else pd.DataFrame(X, columns=self.input_columns_)
|
|
146
|
+
if X_.shape[1] != self.n_features_in_:
|
|
147
|
+
raise ValueError("X has a different number of features than during fit.")
|
|
148
|
+
if isinstance(X, pd.DataFrame) and list(X.columns) != self.input_columns_:
|
|
149
|
+
raise ValueError("Input columns must match the columns seen during fit.")
|
|
136
150
|
return X_[self.selected_features_]
|
|
137
151
|
|
|
138
152
|
def get_feature_names_out(self, input_features=None):
|
|
139
153
|
"""Return feature names for output features."""
|
|
140
|
-
|
|
154
|
+
if not hasattr(self, "selected_features_"):
|
|
155
|
+
raise ValueError("Transformer has not been fitted yet.")
|
|
156
|
+
if input_features is not None and hasattr(self, "feature_names_in_"):
|
|
157
|
+
if not np.array_equal(input_features, self.feature_names_in_):
|
|
158
|
+
raise ValueError("input_features must match feature_names_in_ when feature names are available.")
|
|
159
|
+
return np.asarray(self.selected_features_, dtype=object)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
MulticollinearityRemover = MultiCollinearityReducer
|
|
@@ -35,8 +35,6 @@ class MultiLabelBinarizerTransformer(BaseEstimator, TransformerMixin):
|
|
|
35
35
|
If True, the output will be a sparse matrix in Compressed Sparse Row (CSR) format. Otherwise, a dense NumPy array is returned.
|
|
36
36
|
"""
|
|
37
37
|
self.sparse_output = sparse_output
|
|
38
|
-
self.mlbs = {}
|
|
39
|
-
self.feature_names_in_ = None
|
|
40
38
|
|
|
41
39
|
def fit(self, X, y=None):
|
|
42
40
|
"""
|
|
@@ -57,10 +55,15 @@ class MultiLabelBinarizerTransformer(BaseEstimator, TransformerMixin):
|
|
|
57
55
|
"""
|
|
58
56
|
if not isinstance(X, pd.DataFrame):
|
|
59
57
|
raise ValueError("Input must be a pandas DataFrame")
|
|
60
|
-
self.
|
|
61
|
-
self.
|
|
58
|
+
self.input_columns_ = list(X.columns)
|
|
59
|
+
self.n_features_in_ = X.shape[1]
|
|
60
|
+
if all(isinstance(column, str) for column in X.columns):
|
|
61
|
+
self.feature_names_in_ = np.asarray(X.columns, dtype=object)
|
|
62
|
+
elif hasattr(self, "feature_names_in_"):
|
|
63
|
+
del self.feature_names_in_
|
|
64
|
+
self.mlbs_ = {
|
|
62
65
|
col: MultiLabelBinarizer(sparse_output=self.sparse_output).fit(X[col])
|
|
63
|
-
for col in self.
|
|
66
|
+
for col in self.input_columns_
|
|
64
67
|
}
|
|
65
68
|
return self
|
|
66
69
|
|
|
@@ -80,9 +83,13 @@ class MultiLabelBinarizerTransformer(BaseEstimator, TransformerMixin):
|
|
|
80
83
|
"""
|
|
81
84
|
if not isinstance(X, pd.DataFrame):
|
|
82
85
|
raise ValueError("Input must be a pandas DataFrame")
|
|
86
|
+
if not hasattr(self, "mlbs_"):
|
|
87
|
+
raise ValueError("Transformer has not been fitted yet.")
|
|
88
|
+
if list(X.columns) != self.input_columns_:
|
|
89
|
+
raise ValueError("Input columns must match the columns seen during fit.")
|
|
83
90
|
transformed_parts = []
|
|
84
|
-
for col in self.
|
|
85
|
-
mlb = self.
|
|
91
|
+
for col in self.input_columns_:
|
|
92
|
+
mlb = self.mlbs_[col]
|
|
86
93
|
part = mlb.transform(X[col])
|
|
87
94
|
if not self.sparse_output:
|
|
88
95
|
part = pd.DataFrame(
|
|
@@ -91,7 +98,7 @@ class MultiLabelBinarizerTransformer(BaseEstimator, TransformerMixin):
|
|
|
91
98
|
index=X.index
|
|
92
99
|
)
|
|
93
100
|
transformed_parts.append(part)
|
|
94
|
-
return hstack(transformed_parts) if self.sparse_output else pd.concat(transformed_parts, axis=1)
|
|
101
|
+
return hstack(transformed_parts, format="csr") if self.sparse_output else pd.concat(transformed_parts, axis=1)
|
|
95
102
|
|
|
96
103
|
def inverse_transform(self, X):
|
|
97
104
|
"""
|
|
@@ -107,24 +114,31 @@ class MultiLabelBinarizerTransformer(BaseEstimator, TransformerMixin):
|
|
|
107
114
|
inverse_transformed : pandas DataFrame of shape (n_samples, n_features)
|
|
108
115
|
A DataFrame where each column contains the original label sets for each sample.
|
|
109
116
|
"""
|
|
117
|
+
if not hasattr(self, "mlbs_"):
|
|
118
|
+
raise ValueError("Transformer has not been fitted yet.")
|
|
110
119
|
if self.sparse_output and not issparse(X):
|
|
111
120
|
raise ValueError("Expected sparse input for inverse_transform but got dense.")
|
|
112
|
-
if not self.sparse_output
|
|
113
|
-
X =
|
|
121
|
+
if not self.sparse_output:
|
|
122
|
+
X = np.asarray(X)
|
|
114
123
|
result = {}
|
|
115
124
|
if self.sparse_output:
|
|
116
125
|
current_col = 0
|
|
117
|
-
for col in self.
|
|
118
|
-
mlb = self.
|
|
126
|
+
for col in self.input_columns_:
|
|
127
|
+
mlb = self.mlbs_[col]
|
|
119
128
|
n_classes = len(mlb.classes_)
|
|
120
129
|
col_slice = X[:, current_col:current_col + n_classes]
|
|
121
|
-
result[col] = mlb.inverse_transform(col_slice)
|
|
130
|
+
result[col] = [list(labels) for labels in mlb.inverse_transform(col_slice)]
|
|
122
131
|
current_col += n_classes
|
|
123
132
|
else:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
current_col = 0
|
|
134
|
+
for col in self.input_columns_:
|
|
135
|
+
mlb = self.mlbs_[col]
|
|
136
|
+
n_classes = len(mlb.classes_)
|
|
137
|
+
result[col] = [
|
|
138
|
+
list(labels)
|
|
139
|
+
for labels in mlb.inverse_transform(X[:, current_col:current_col + n_classes])
|
|
140
|
+
]
|
|
141
|
+
current_col += n_classes
|
|
128
142
|
return pd.DataFrame(result)
|
|
129
143
|
|
|
130
144
|
def get_feature_names_out(self, input_features=None):
|
|
@@ -141,11 +155,20 @@ class MultiLabelBinarizerTransformer(BaseEstimator, TransformerMixin):
|
|
|
141
155
|
feature_names_out : list of str
|
|
142
156
|
The output feature names.
|
|
143
157
|
"""
|
|
158
|
+
if not hasattr(self, "mlbs_"):
|
|
159
|
+
raise ValueError("Transformer has not been fitted yet.")
|
|
144
160
|
if input_features is None:
|
|
145
|
-
input_features =
|
|
161
|
+
input_features = getattr(
|
|
162
|
+
self,
|
|
163
|
+
"feature_names_in_",
|
|
164
|
+
np.asarray([f"x{i}" for i in range(self.n_features_in_)], dtype=object),
|
|
165
|
+
)
|
|
166
|
+
elif hasattr(self, "feature_names_in_") and not np.array_equal(
|
|
167
|
+
input_features, self.feature_names_in_
|
|
168
|
+
):
|
|
169
|
+
raise ValueError("input_features must match feature_names_in_ when feature names are available.")
|
|
146
170
|
out = []
|
|
147
|
-
for col in input_features:
|
|
148
|
-
mlb = self.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return out
|
|
171
|
+
for col, fitted_col in zip(input_features, self.input_columns_):
|
|
172
|
+
mlb = self.mlbs_[fitted_col]
|
|
173
|
+
out.extend([f"{col}__{cls}" for cls in mlb.classes_])
|
|
174
|
+
return np.asarray(out, dtype=object)
|
|
File without changes
|
|
File without changes
|