webentwicklung-frage-antwort-db.com.de

Wie kann ich nur numerische Variablen in einer Sklearn-Pipeline standardisieren?

Ich versuche, eine Sklearn-Pipeline mit 2 Schritten zu erstellen:

  1. Standardisieren Sie die Daten
  2. Passen Sie die Daten mit KNN an

Meine Daten haben jedoch sowohl numerische als auch kategoriale Variablen, die ich mit pd.get_dummies in Dummies konvertiert habe. Ich möchte die numerischen Variablen standardisieren, aber die Dummys so lassen, wie sie sind. Ich habe das so gemacht:

X = dataframe containing both numeric and categorical columns
numeric = [list of numeric column names]
categorical = [list of categorical column names]
scaler = StandardScaler()
X_numeric_std = pd.DataFrame(data=scaler.fit_transform(X[numeric]), columns=numeric)
X_std = pd.merge(X_numeric_std, X[categorical], left_index=True, right_index=True)

Wenn ich jedoch eine Pipeline erstellen würde:

pipe = sklearn.pipeline.make_pipeline(StandardScaler(), KNeighborsClassifier())

Dies würde alle Spalten in meinem DataFrame standardisieren. Gibt es eine Möglichkeit, dies zu tun, während nur die numerischen Spalten standardisiert werden?

7
Nate Hutchinson

Angenommen, Sie haben das folgende DF:

In [163]: df
Out[163]:
     a     b    c    d
0  aaa  1.01  xxx  111
1  bbb  2.02  yyy  222
2  ccc  3.03  zzz  333

In [164]: df.dtypes
Out[164]:
a     object
b    float64
c     object
d      int64
dtype: object

sie können alle numerischen Spalten finden:

In [165]: num_cols = df.columns[df.dtypes.apply(lambda c: np.issubdtype(c, np.number))]

In [166]: num_cols
Out[166]: Index(['b', 'd'], dtype='object')

In [167]: df[num_cols]
Out[167]:
      b    d
0  1.01  111
1  2.02  222
2  3.03  333

und wende StandardScaler nur auf diese numerischen Spalten an:

In [168]: scaler = StandardScaler()

In [169]: df[num_cols] = scaler.fit_transform(df[num_cols])

In [170]: df
Out[170]:
     a         b    c         d
0  aaa -1.224745  xxx -1.224745
1  bbb  0.000000  yyy  0.000000
2  ccc  1.224745  zzz  1.224745

jetzt können Sie "eine heiße Kodierung" kategoriale (nicht numerische) Spalten erstellen ...

10
MaxU

Ich würde FeatureUnion verwenden. Ich mache dann normalerweise so etwas, vorausgesetzt, Sie verschlüsseln Ihre kategorialen Variablen auch innerhalb der Pipeline anstatt zuvor mit Pandas:

from sklearn.pipeline import Pipeline, FeatureUnion, make_pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.neighbors import KNeighborsClassifier

class Columns(BaseEstimator, TransformerMixin):
    def __init__(self, names=None):
        self.names = names

    def fit(self, X, y=None, **fit_params):
        return self

    def transform(self, X):
        return X[self.names]

numeric = [list of numeric column names]
categorical = [list of categorical column names]

pipe = Pipeline([
    ("features", FeatureUnion([
        ('numeric', make_pipeline(Columns(names=numeric),StandardScaler())),
        ('categorical', make_pipeline(Columns(names=categorical),OneHotEncoder(sparse=False)))
    ])),
    ('model', KNeighborsClassifier())
])

Sie können auch weiter nach Sklearn Pandas suchen, was auch interessant ist. 

5
Marcus V.

Da Sie Ihre kategorialen Funktionen mit pd.get_dummies in Dummies konvertiert haben, müssen Sie OneHotEncoder nicht verwenden. Als Ergebnis sollte Ihre Pipeline sein:

from sklearn.preprocessing import StandardScaler,FunctionTransformer
from sklearn.pipeline import Pipeline,FeatureUnion

knn=KNeighborsClassifier()

pipeline=Pipeline(steps= [
    ('feature_processing', FeatureUnion(transformer_list = [
            ('categorical', FunctionTransformer(lambda data: data[:, cat_indices])),

            #numeric
            ('numeric', Pipeline(steps = [
                ('select', FunctionTransformer(lambda data: data[:, num_indices])),
                ('scale', StandardScaler())
                        ]))
        ])),
    ('clf', knn)
    ]
)
1
ebrahimi