Pandas Dataframe-Spalten, die mit sklearn skaliert werden


137

Ich habe einen Pandas-Datenrahmen mit gemischten Spalten und möchte den min_max_scaler von sklearn auf einige der Spalten anwenden. Im Idealfall würde ich diese Transformationen gerne an Ort und Stelle durchführen, habe aber noch keinen Weg gefunden, dies zu tun. Ich habe den folgenden Code geschrieben, der funktioniert:

import pandas as pd
import numpy as np
from sklearn import preprocessing

scaler = preprocessing.MinMaxScaler()

dfTest = pd.DataFrame({'A':[14.00,90.20,90.95,96.27,91.21],'B':[103.02,107.26,110.35,114.23,114.68], 'C':['big','small','big','small','small']})
min_max_scaler = preprocessing.MinMaxScaler()

def scaleColumns(df, cols_to_scale):
    for col in cols_to_scale:
        df[col] = pd.DataFrame(min_max_scaler.fit_transform(pd.DataFrame(dfTest[col])),columns=[col])
    return df

dfTest

    A   B   C
0    14.00   103.02  big
1    90.20   107.26  small
2    90.95   110.35  big
3    96.27   114.23  small
4    91.21   114.68  small

scaled_df = scaleColumns(dfTest,['A','B'])
scaled_df

A   B   C
0    0.000000    0.000000    big
1    0.926219    0.363636    small
2    0.935335    0.628645    big
3    1.000000    0.961407    small
4    0.938495    1.000000    small

Ich bin gespannt, ob dies der bevorzugte / effizienteste Weg ist, um diese Transformation durchzuführen. Gibt es eine Möglichkeit, wie ich df.apply verwenden könnte, die besser wäre?

Ich bin auch überrascht, dass ich den folgenden Code nicht zum Laufen bringen kann:

bad_output = min_max_scaler.fit_transform(dfTest['A'])

Wenn ich einen gesamten Datenrahmen an den Skalierer übergebe, funktioniert dies:

dfTest2 = dfTest.drop('C', axis = 1) good_output = min_max_scaler.fit_transform(dfTest2) good_output

Ich bin verwirrt, warum das Übergeben einer Serie an den Scaler fehlschlägt. In meinem obigen vollständigen Arbeitscode hatte ich gehofft, nur eine Reihe an den Skalierer zu übergeben und dann die Datenrahmenspalte = auf die skalierte Reihe zu setzen. Ich habe gesehen, dass diese Frage an einigen anderen Stellen gestellt wurde, aber keine gute Antwort gefunden. Jede Hilfe, um zu verstehen, was hier vor sich geht, wäre sehr dankbar!


1
Funktioniert es, wenn Sie dies tun bad_output = min_max_scaler.fit_transform(dfTest['A'].values)? Der Zugriff auf das valuesAttribut gibt ein Numpy-Array zurück. Aus irgendeinem Grund ruft die Scikit-Lern-API manchmal die richtige Methode auf, mit der Pandas ein Numpy-Array zurückgibt, und manchmal nicht.
EdChum

Die Datenrahmen von Pandas sind ziemlich komplizierte Objekte mit Konventionen, die nicht mit den Konventionen von scikit-learn übereinstimmen. Wenn Sie alles in NumPy-Arrays konvertieren, ist das Arbeiten mit scikit-learn viel einfacher.
Fred Foo

@edChum - bad_output = in_max_scaler.fit_transform(dfTest['A'].values)hat auch nicht funktioniert. @larsmans - ja, ich hatte darüber nachgedacht, diesen Weg zu gehen, es scheint nur ein Ärger zu sein. Ich weiß nicht, ob es ein Fehler ist oder nicht, dass Pandas einen vollständigen Datenrahmen an eine sklearn-Funktion übergeben kann, aber keine Serie. Mein Verständnis eines Datenrahmens war, dass es sich um ein Diktat von Serien handelt. In dem Buch "Python for Data Analysis" heißt es, dass Pandas auf Numpy aufgebaut sind, um die Verwendung in NumPy-zentrierten Anwendungen zu vereinfachen.
FlyingMeatball

Antworten:


213

Ich bin nicht sicher, ob frühere Versionen dies pandasverhindert haben, aber jetzt funktioniert das folgende Snippet perfekt für mich und produziert genau das, was Sie wollen, ohne es verwenden zu müssenapply

>>> import pandas as pd
>>> from sklearn.preprocessing import MinMaxScaler


>>> scaler = MinMaxScaler()

>>> dfTest = pd.DataFrame({'A':[14.00,90.20,90.95,96.27,91.21],
                           'B':[103.02,107.26,110.35,114.23,114.68],
                           'C':['big','small','big','small','small']})

>>> dfTest[['A', 'B']] = scaler.fit_transform(dfTest[['A', 'B']])

>>> dfTest
          A         B      C
0  0.000000  0.000000    big
1  0.926219  0.363636  small
2  0.935335  0.628645    big
3  1.000000  0.961407  small
4  0.938495  1.000000  small

80
Ordentlich! Eine allgemeinere Versiondf[df.columns] = scaler.fit_transform(df[df.columns])
Citynorman

6
@RajeshThevar Die äußeren Klammern sind die typischen Auswahlklammern der Pandas und weisen die Pandas an, eine Spalte aus dem Datenrahmen auszuwählen. Die inneren Klammern geben eine Liste an. Sie übergeben eine Liste an den Pandas-Selektor. Wenn Sie nur einzelne Klammern verwenden - mit einem Spaltennamen gefolgt von einem anderen, durch ein Komma getrennt -, interpretiert pandas dies so, als würden Sie versuchen, eine Spalte aus einem Datenrahmen mit mehrstufigen Spalten (einem MultiIndex) auszuwählen und einen Schlüsselfehler auszulösen .
Ken

1
Um die Antwort von @ ken zu ergänzen, wenn Sie genau sehen möchten, wie Pandas diese Indizierungslogik implementiert und warum ein Tupel von Werten anders interpretiert wird als eine Liste, können Sie sich ansehen, wie DataFrames die __getitem__Methode implementiert . Insbesondere können Sie Ihr Ipython öffnen und tun pd.DataFrame.__getitem__??; nachdem du Pandas natürlich als pd importiert hast;)
LetsPlayYahtzee

4
Ein praktischer Hinweis: Wenn Sie Zug- / Testdatenaufteilungen verwenden, möchten Sie nur auf Ihre Trainingsdaten und nicht auf Ihre Testdaten passen.
David J.

1
Um alle außer der Zeitstempelspalte zu skalieren, kombinieren Sie mit columns =df.columns.drop('timestamps') df[df.columns] = scaler.fit_transform(df[df.columns]
intecho

19

So was?

dfTest = pd.DataFrame({
           'A':[14.00,90.20,90.95,96.27,91.21],
           'B':[103.02,107.26,110.35,114.23,114.68], 
           'C':['big','small','big','small','small']
         })
dfTest[['A','B']] = dfTest[['A','B']].apply(
                           lambda x: MinMaxScaler().fit_transform(x))
dfTest

    A           B           C
0   0.000000    0.000000    big
1   0.926219    0.363636    small
2   0.935335    0.628645    big
3   1.000000    0.961407    small
4   0.938495    1.000000    small

3
Ich erhalte eine Reihe von DeprecationWarnings, wenn ich dieses Skript ausführe. Wie soll es aktualisiert werden?
Pir

Siehe @ LetsPlayYahtzees Antwort unten
AJP

2
Eine einfachere Version: dfTest [['A', 'B']] = dfTest [['A', 'B']]. Apply (MinMaxScaler (). Fit_transform)
Alexandre V.

12

Wie in pirs Kommentar erwähnt, erzeugt die .apply(lambda el: scale.fit_transform(el))Methode die folgende Warnung:

DeprecationWarning: Das Übergeben von 1d-Arrays als Daten ist in 0.17 veraltet und erhöht ValueError in 0.19. Ändern Sie Ihre Daten entweder mit X.reshape (-1, 1), wenn Ihre Daten über ein einzelnes Feature verfügen, oder mit X.reshape (1, -1), wenn sie ein einzelnes Beispiel enthalten.

Das Konvertieren Ihrer Spalten in numpy-Arrays sollte den Job erledigen (ich bevorzuge StandardScaler):

from sklearn.preprocessing import StandardScaler
scale = StandardScaler()

dfTest[['A','B','C']] = scale.fit_transform(dfTest[['A','B','C']].as_matrix())

- Edit Nov 2018 (Getestet auf Pandas 0.23.4 ) -

Wie Rob Murray in den Kommentaren erwähnt, .as_matrix()kehrt Pandas in der aktuellen (v0.23.4) Version zurück FutureWarning. Daher sollte es ersetzt werden durch .values:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()

scaler.fit_transform(dfTest[['A','B']].values)

- Bearbeiten Mai 2019 (Getestet auf Pandas 0.24.2 ) -

Wie Joelostblom in den Kommentaren erwähnt: "Da 0.24.0wird empfohlen, .to_numpy()anstelle von zu verwenden .values."

Aktualisiertes Beispiel:

import pandas as pd
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
dfTest = pd.DataFrame({
               'A':[14.00,90.20,90.95,96.27,91.21],
               'B':[103.02,107.26,110.35,114.23,114.68],
               'C':['big','small','big','small','small']
             })
dfTest[['A', 'B']] = scaler.fit_transform(dfTest[['A','B']].to_numpy())
dfTest
      A         B      C
0 -1.995290 -1.571117    big
1  0.436356 -0.603995  small
2  0.460289  0.100818    big
3  0.630058  0.985826  small
4  0.468586  1.088469  small

1
Verwendung .valuesanstelle von .as_matrix()wie as_matrix()jetzt gibt ein FutureWarning.
Rob Murray


10
df = pd.DataFrame(scale.fit_transform(df.values), columns=df.columns, index=df.index)

Dies sollte ohne Abschreibungswarnungen funktionieren.


7

Sie können dies pandasnur tun mit :

In [235]:
dfTest = pd.DataFrame({'A':[14.00,90.20,90.95,96.27,91.21],'B':[103.02,107.26,110.35,114.23,114.68], 'C':['big','small','big','small','small']})
df = dfTest[['A', 'B']]
df_norm = (df - df.min()) / (df.max() - df.min())
print df_norm
print pd.concat((df_norm, dfTest.C),1)

          A         B
0  0.000000  0.000000
1  0.926219  0.363636
2  0.935335  0.628645
3  1.000000  0.961407
4  0.938495  1.000000
          A         B      C
0  0.000000  0.000000    big
1  0.926219  0.363636  small
2  0.935335  0.628645    big
3  1.000000  0.961407  small
4  0.938495  1.000000  small

6
Ich weiß, dass ich es nur in Pandas tun kann, aber ich möchte vielleicht irgendwann eine andere sklearn-Methode anwenden, die nicht so einfach zu schreiben ist. Ich bin mehr daran interessiert herauszufinden, warum die Bewerbung für eine Serie nicht wie erwartet funktioniert, als an einer streng einfacheren Lösung. Mein nächster Schritt wird darin bestehen, einen RandomForestRegressor auszuführen, und ich möchte sicherstellen, dass ich verstehe, wie Pandas und sklearn zusammenarbeiten.
FlyingMeatball

5
Diese Antwort ist gefährlich, da df.max() - df.min()sie 0 sein kann, was zu einer Ausnahme führt. Darüber hinaus df.min()wird zweimal berechnet, was ineffizient ist. Beachten Sie, dass dies df.ptp()äquivalent zu ist df.max() - df.min().
Acumenus

3

Ich weiß, dass es ein sehr alter Kommentar ist, aber trotzdem:

Verwenden Sie anstelle einer einfachen Klammer (dfTest['A'])doppelte Klammern (dfTest[['A']]).

dh : min_max_scaler.fit_transform(dfTest[['A']]).

Ich glaube, dies wird das gewünschte Ergebnis liefern.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.