web-dev-qa-db-fra.com

pandas les colonnes de la structure de données évolutives avec sklearn

J'ai un pandas dataframe avec des colonnes de type mixte, et j'aimerais appliquer le min_max_scaler de sklearn à certaines des colonnes. Idéalement, j'aimerais effectuer ces transformations sur place, mais je n'ai pas encore trouvé le moyen de le faire. J'ai écrit le code suivant qui fonctionne:

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

Je suis curieux de savoir si c'est le moyen préféré/le plus efficace de faire cette transformation. Existe-t-il un moyen d'utiliser df.apply qui serait mieux?

Je suis également surpris de ne pas pouvoir utiliser le code suivant:

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

Si je passe un cadre de données entier au scaler, cela fonctionne:

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

Je ne comprends pas pourquoi passer une série au scaler échoue. Dans mon code de travail complet ci-dessus, j'avais espéré simplement passer une série au scaler, puis définir la colonne dataframe = sur la série mise à l'échelle. J'ai vu cette question posée à quelques autres endroits, mais je n'ai pas trouvé de bonne réponse. Toute aide pour comprendre ce qui se passe ici serait grandement appréciée!

91
flyingmeatball

Je ne suis pas sûr que les versions précédentes de pandas l'aient empêché, mais l'extrait suivant fonctionne parfaitement pour moi et produit exactement ce que vous voulez sans avoir à utiliser apply

>>> 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
136
LetsPlayYahtzee

Comme ça?

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
19
Eric Czech

Comme cela est mentionné dans le commentaire de pir - la méthode .apply(lambda el: scale.fit_transform(el)) produira l'avertissement suivant:

DeprecationWarning: le passage de tableaux 1d en tant que données est obsolète en 0.17 et augmentera ValueError en 0.19. Modifiez la forme de vos données en utilisant X.reshape (-1, 1) si vos données ont une seule fonctionnalité ou X.reshape (1, -1) si elles contiennent un seul échantillon.

La conversion de vos colonnes en tableaux numpy devrait faire l'affaire (je préfère StandardScaler):

from sklearn.preprocessing import StandardScaler
scale = StandardScaler()

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

Modifier Nov 2018 (testé pour pandas .23.4) -

Comme Rob Murray le mentionne dans les commentaires, dans la version actuelle (v0.23.4) de pandas .as_matrix() renvoie FutureWarning. Par conséquent, il devrait être remplacé par .values:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()

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

Modifier Mai 2019 (testé pour pandas .24.2) -

Comme joelostblom le mentionne dans les commentaires, "Depuis 0.24.0, il est recommandé d’utiliser .to_numpy() au lieu de .values."

Exemple mis à jour:

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
9
Low Yield Bond

Vous pouvez le faire en utilisant pandas uniquement:

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
CT Zhu
df = pd.DataFrame(scale.fit_transform(df.values), columns=df.columns, index=df.index)

Cela devrait fonctionner sans avertissements de dépréciation.

6
athlonshi