pandy dataframe kolumny skalowanie za pomocą sklearn

148

Mam ramkę danych pandy z kolumnami typu mieszanego i chciałbym zastosować min_max_scaler sklearn do niektórych kolumn. Idealnie, chciałbym dokonać tych transformacji na miejscu, ale jeszcze nie wymyśliłem sposobu, aby to zrobić. Napisałem następujący kod, który działa:

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

Jestem ciekawy, czy jest to preferowany / najbardziej efektywny sposób przeprowadzenia tej transformacji. Czy jest sposób, w jaki mógłbym użyć df.apply, który byłby lepszy?

Dziwię się też, że nie mogę uruchomić następującego kodu:

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

Jeśli przekażę całą ramkę danych do skalera, to działa:

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

Nie wiem, dlaczego przekazanie serii do skalera zawodzi. W moim pełnym kodzie roboczym powyżej miałem nadzieję, że przekażę serię do skalera, a następnie ustawię kolumnę dataframe = na skalowaną serię. Widziałem to pytanie zadane w kilku innych miejscach, ale nie znalazłem dobrej odpowiedzi. Każda pomoc w zrozumieniu tego, co się tutaj dzieje, byłaby bardzo mile widziana!

latająca kulka mięsna
źródło
1
Czy to działa, jeśli to zrobisz bad_output = min_max_scaler.fit_transform(dfTest['A'].values)? dostęp do valuesatrybutu zwraca tablicę numpy, z jakiegoś powodu czasami scikit learn api poprawnie wywoła właściwą metodę, która sprawia, że ​​pandy zwracają tablicę numpy, a czasami nie.
EdChum
Ramki danych Pandy są dość skomplikowanymi obiektami, których konwencje nie pasują do konwencji scikit-learn. Jeśli przekonwertujesz wszystko na tablice NumPy, praca z scikit-learn jest znacznie łatwiejsza.
Fred Foo,
@edChum - bad_output = in_max_scaler.fit_transform(dfTest['A'].values)też nie działa. @larsmans - tak, myślałem o zjechaniu tą trasą, wydaje się to po prostu kłopotliwe. Nie wiem, czy jest to błąd, czy nie, że Pandy mogą przekazać pełną ramkę danych do funkcji sklearn, ale nie serię. Moje rozumienie ramki danych było takie, że jest ona dyktatem serii. Czytając książkę „Python for Data Analysis”, stwierdza się, że pandy są zbudowane na bazie numpy, aby ułatwić korzystanie z aplikacji opartych na NumPy.
flyingmeatball

Odpowiedzi:

230

Nie jestem pewien, czy poprzednie wersje pandastemu zapobiegały, ale teraz następujący fragment działa idealnie dla mnie i produkuje dokładnie to, czego chcesz, bez konieczności użyciaapply

>>> 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
LetsPlayYahtzee
źródło
87
Schludny! Bardziej uogólniona wersjadf[df.columns] = scaler.fit_transform(df[df.columns])
citynorman
6
@RajeshThevar Nawiasy zewnętrzne są typowymi nawiasami selektora pand, informującymi pandy, aby wybrały kolumnę z ramki danych. Nawiasy wewnętrzne wskazują listę. Przekazujesz listę do selektora pand. Jeśli użyjesz tylko pojedynczych nawiasów - z jedną nazwą kolumny, po której następuje druga, oddzielona przecinkiem - pandy interpretują to tak, jakbyś próbował wybrać kolumnę z ramki danych z kolumnami wielopoziomowymi (MultiIndex) i wyrzuci błąd klucza .
ken
1
aby dodać do odpowiedzi @ ken, jeśli chcesz dokładnie zobaczyć, w jaki sposób pandy implementują tę logikę indeksowania i dlaczego krotka wartości byłaby interpretowana inaczej niż lista, możesz przyjrzeć się, jak DataFrames implementuje tę __getitem__metodę. W szczególności możesz otworzyć ipython i zrobić pd.DataFrame.__getitem__??; po zaimportowaniu pandy jako pd oczywiście;)
LetsPlayYahtzee
4
Uwaga praktyczna: dla tych, którzy używają podziałów danych pociąg / test, będziesz chciał dopasować tylko dane treningowe, a nie dane testowe.
David J.
1
Aby przeskalować wszystko oprócz kolumny ze znacznikami czasu, połącz z columns =df.columns.drop('timestamps') df[df.columns] = scaler.fit_transform(df[df.columns]
intotecho
19

Lubię to?

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
Eric Czech
źródło
3
Po uruchomieniu tego skryptu pojawia się kilka ostrzeżeń o wycofaniu. Jak należy to zaktualizować?
pir
Zobacz odpowiedź @ LetsPlayYahtzee poniżej
AJP
2
Prostsza wersja: dfTest [['A', 'B']] = dfTest [['A', 'B']]. Apply (MinMaxScaler (). Fit_transform)
Alexandre V.
12

Jak wspomniano w komentarzu pir - .apply(lambda el: scale.fit_transform(el))metoda zwróci następujące ostrzeżenie:

PrzestarzałeOstrzeżenie: przekazywanie tablic 1d, ponieważ dane są przestarzałe w wersji 0.17 i spowoduje podniesienie wartości ValueError w wersji 0.19. Zmień kształt danych za pomocą X.reshape (-1, 1), jeśli dane mają jedną funkcję lub X.reshape (1, -1), jeśli zawierają jedną próbkę.

Konwersja kolumn do tablic numpy powinna wystarczyć (wolę StandardScaler):

from sklearn.preprocessing import StandardScaler
scale = StandardScaler()

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

- Edycja listopad 2018 (testowane dla pand 0.23.4 ) -

Jak Rob Murray wspomina w komentarzach, w obecnej (v0.23.4) wersji pandy .as_matrix()powraca FutureWarning. Dlatego należy go zastąpić .values:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()

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

- Edycja maj 2019 (testowane dla pand 0.24.2 ) -

Jak wspomina joelostblom w komentarzach: „Ponieważ 0.24.0zaleca się używanie .to_numpy()zamiast.values ”.

Zaktualizowany przykład:

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
Obligacja o niskiej rentowności
źródło
1
użyj .valueszamiast, .as_matrix()jak as_matrix()teraz daje FutureWarning.
Rob Murray
1
Ponieważ 0.24.0, to zaleca się stosowanie .to_numpy()zamiast.values .
joelostblom
11
df = pd.DataFrame(scale.fit_transform(df.values), columns=df.columns, index=df.index)

Powinno to działać bez ostrzeżeń o amortyzacji.

athlonshi
źródło
7

Możesz to zrobić używając pandastylko:

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
CT Zhu
źródło
6
Wiem, że mogę to zrobić tylko w pandach, ale może zechcę w końcu zastosować inną metodę sklearn, która sama nie jest tak łatwa do napisania. Bardziej interesuje mnie ustalenie, dlaczego aplikowanie do serii nie działa tak, jak się spodziewałem, niż wymyślanie prostszego rozwiązania. Następnym krokiem będzie uruchomienie RandomForestRegressor i chcę się upewnić, że rozumiem, jak współpracują ze sobą pandy i sklearn.
flyingmeatball
5
Ta odpowiedź jest niebezpieczna, ponieważ df.max() - df.min()może wynosić 0, co prowadzi do wyjątku. Ponadto df.min()jest obliczany dwukrotnie, co jest nieefektywne. Zauważ, że df.ptp()jest to równoważne z df.max() - df.min().
Acumenus
3

Wiem, że to bardzo stary komentarz, ale nadal:

Zamiast używać pojedynczego nawiasu (dfTest['A']), użyj podwójnych (dfTest[['A']]).

tj min_max_scaler.fit_transform(dfTest[['A']]).

Wierzę, że da to pożądany efekt.

BLADY
źródło
2

(Testowane dla pand w wersji 1.0.5 )
Na podstawie odpowiedzi @athlonshi (wystąpił błąd ValueError: nie można przekonwertować ciągu na zmiennoprzecinkowy: 'duży' , w kolumnie C), pełny działający przykład bez ostrzeżenia:

import pandas as pd
from sklearn.preprocessing import MinMaxScaler
scale = preprocessing.MinMaxScaler()

df = 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']
         })
print(df)
df[["A","B"]] = pd.DataFrame(scale.fit_transform(df[["A","B"]].values), columns=["A","B"], index=df.index)
print(df)

       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
          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
Ricardo Rendich
źródło