Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add classes_ #1060

Closed
wants to merge 18 commits into from
9 changes: 5 additions & 4 deletions .github/workflows/python-package-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ jobs:
- name: Install dependencies
run: |
conda env update --file environment.yml --name base
conda install python=3.9 -y -q
- name: Test with pytest
run: |
conda update conda -y -q
conda config --add channels conda-forge
conda update conda -y -q
conda install scikit-learn=1.0.2 pandas=1.3.5 -y -q
conda install tensorflow joblib pytest -y -q
conda install imageio scikit-image -y -q
conda install libcblas -y -q
pip install scikit-learn==1.0.2 pandas==1.3.5
pip install tensorflow joblib pytest
pip install imageio scikit-image
conda install dlib -y -q
pip install markdown
pip install coverage
Expand Down
1 change: 1 addition & 0 deletions docs/sources/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The CHANGELOG for the current development version is available at

##### Changes

- Adds `classes_` attribute to `StackingClassifier`, `StackingCVClassifier` and `EnsembleVoteClassifier` to make these models compatible with scikit-learn 2.3.0 utility functions. ([#1060](https://github.com/rasbt/mlxtend/issues/1060))
- Address NumPy deprecations to make mlxtend compatible to NumPy 1.24
- Changed the signature of the `LinearRegression` model of sklearn in the test removing the `normalize` parameter as it is deprecated. ([#1036](https://github.com/rasbt/mlxtend/issues/1036))

Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dependencies:
- pandas>=1.3.4
- pip>=21.3.1
- pytest>=6.2.5
- scikit-learn>=1.0.1
- scikit-learn>=1.3.0
- scipy>=1.7.3
- setuptools>=59.4.0
- pip:
Expand Down
6 changes: 3 additions & 3 deletions mlxtend/classifier/ensemble_vote.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def fit(self, X, y, sample_weight=None):
)

self.le_ = LabelEncoder()
self.le_.fit(y)
y_transform = self.le_.fit(y)
self.classes_ = self.le_.classes_

if not self.fit_base_estimators and self.use_clones:
Expand Down Expand Up @@ -199,9 +199,9 @@ def fit(self, X, y, sample_weight=None):
print(_name_estimators((clf,))[0][1])

if sample_weight is None:
clf.fit(X, self.le_.transform(y))
clf.fit(X, y_transform)
else:
clf.fit(X, self.le_.transform(y), sample_weight=sample_weight)
clf.fit(X, y_transform, sample_weight=sample_weight)
return self

def predict(self, X):
Expand Down
14 changes: 10 additions & 4 deletions mlxtend/classifier/stacking_classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import numpy as np
from scipy import sparse
from sklearn.base import TransformerMixin, clone
from sklearn.preprocessing import LabelEncoder

from ..externals.estimator_checks import check_is_fitted
from ..externals.name_estimators import _name_estimators
Expand Down Expand Up @@ -163,6 +164,11 @@ def fit(self, X, y, sample_weight=None):
self : object

"""

self.le_ = LabelEncoder()
y_transform = self.le_.fit_transform(y)
self.classes_ = self.le_.classes_

if not self.fit_base_estimators:
warnings.warn(
"fit_base_estimators=False " "enforces use_clones to be `False`"
Expand Down Expand Up @@ -195,9 +201,9 @@ def fit(self, X, y, sample_weight=None):
if self.verbose > 1:
print(_name_estimators((clf,))[0][1])
if sample_weight is None:
clf.fit(X, y)
clf.fit(X, y_transform)
else:
clf.fit(X, y, sample_weight=sample_weight)
clf.fit(X, y_transform, sample_weight=sample_weight)

meta_features = self.predict_meta_features(X)

Expand All @@ -212,9 +218,9 @@ def fit(self, X, y, sample_weight=None):
meta_features = np.hstack((X, meta_features))

if sample_weight is None:
self.meta_clf_.fit(meta_features, y)
self.meta_clf_.fit(meta_features, y_transform)
else:
self.meta_clf_.fit(meta_features, y, sample_weight=sample_weight)
self.meta_clf_.fit(meta_features, y_transform, sample_weight=sample_weight)

return self

Expand Down
22 changes: 17 additions & 5 deletions mlxtend/classifier/stacking_cv_classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from sklearn.base import TransformerMixin, clone
from sklearn.model_selection import cross_val_predict
from sklearn.model_selection._split import check_cv
from sklearn.preprocessing import LabelEncoder

from ..externals.estimator_checks import check_is_fitted
from ..externals.name_estimators import _name_estimators
Expand Down Expand Up @@ -130,11 +131,17 @@ class StackingCVClassifier(
Fitted classifiers (clones of the original classifiers)
meta_clf_ : estimator
Fitted meta-classifier (clone of the original meta-estimator)
le_ : `sklearn.preprocessing.LabelEncoder`
Transformer used to encode the labels during fit and decode during
prediction.
classes_ : ndarray of shape (n_classes,)
The classes labels.
train_meta_features : numpy array, shape = [n_samples, n_classifiers]
meta-features for training data, where n_samples is the
number of samples
in training data and n_classifiers is the number of classfiers.


Examples
-----------
For usage examples, please see
Expand Down Expand Up @@ -212,6 +219,11 @@ def fit(self, X, y, groups=None, sample_weight=None):
self : object

"""

self.le_ = LabelEncoder()
y_transform = self.le_.fit_transform(y)
self.classes_ = self.le_.classes_

if self.use_clones:
self.clfs_ = clone(self.classifiers)
self.meta_clf_ = clone(self.meta_classifier)
Expand All @@ -221,7 +233,7 @@ def fit(self, X, y, groups=None, sample_weight=None):
if self.verbose > 0:
print("Fitting %d classifiers..." % (len(self.classifiers)))

final_cv = check_cv(self.cv, y, classifier=self.stratify)
final_cv = check_cv(self.cv, y_transform, classifier=self.stratify)
if isinstance(self.cv, int):
# Override shuffle parameter in case of self generated
# cross-validation strategy
Expand Down Expand Up @@ -287,18 +299,18 @@ def fit(self, X, y, groups=None, sample_weight=None):
# Fit the base models correctly this time using ALL the training set
for model in self.clfs_:
if sample_weight is None:
model.fit(X, y)
model.fit(X, y_transform)
else:
model.fit(X, y, sample_weight=sample_weight)
model.fit(X, y_transform, sample_weight=sample_weight)

# Fit the secondary model
if self.use_features_in_secondary:
meta_features = self._stack_first_level_features(X, meta_features)

if sample_weight is None:
self.meta_clf_.fit(meta_features, y)
self.meta_clf_.fit(meta_features, y_transform)
else:
self.meta_clf_.fit(meta_features, y, sample_weight=sample_weight)
self.meta_clf_.fit(meta_features, y_transform, sample_weight=sample_weight)

return self

Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
scipy>=1.2.1
numpy>=1.16.2
pandas>=0.24.2
scikit-learn>=1.0.2
scikit-learn>=1.3.0
matplotlib>=3.0.0
joblib>=0.13.2
joblib>=0.13.2
Loading