масштабування стовпців фреймів даних панди за допомогою sklearn


137

У мене є фрейм даних pandas зі стовпцями змішаного типу, і я хотів би застосувати min_max_scaler sklearn до деяких стовпців. В ідеалі я хотів би зробити ці перетворення на місці, але ще не знайшов способу зробити це. Я написав такий код, який працює:

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

Мені цікаво, якщо це кращий / найефективніший спосіб зробити цю трансформацію. Чи є спосіб, який я міг би використовувати df.apply, що було б краще?

Я також здивований, що не можу змусити наступний код працювати:

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

Якщо я передаю цілий кадр даних скалеру, він працює:

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

Мене бентежить, чому передача серії на скалер не вдається. У своєму повному робочому коді вище я сподівався просто передати серію на скалер, а потім встановити стовпчик фрейму даних = до масштабованого ряду. Я бачив це запитання у кількох інших місцях, але не знайшов гарної відповіді. Будь-яка допомога в розумінні того, що тут відбувається, буде дуже вдячна!


1
Це працює, якщо ви це зробите bad_output = min_max_scaler.fit_transform(dfTest['A'].values)? valuesДоступ до атрибута повертає масив numpy, чомусь іноді scikit learn api правильно викликає правильний метод, завдяки якому панди повертають numpy масив, а іноді - ні.
EdChum

Кадри даних Pandas - це досить складні об'єкти з умовами, які не відповідають умовам scikit-learn. Якщо ви конвертуєте все в масиви NumPy, працювати з scikit стає набагато простіше.
Фред Фоо

@edChum - bad_output = in_max_scaler.fit_transform(dfTest['A'].values)теж не працював. @larsmans - так, я думав про те, щоб піти цим маршрутом, це просто здається клопотом. Я не знаю, помилка чи ні, що Pandas може передавати повний кадр даних функції sklearn, але не серію. Моє розуміння фрейму даних полягало в тому, що це вислів серії. Читаючи в книзі "Python для аналізу даних", він зазначає, що панди побудовані на вершині нуме, щоб зробити їх легким у використанні в NumPy-орієнтованих додатках.
flyingmeatball

Відповіді:


214

Я не впевнений, чи pandasпопереджували це попередні версії, але тепер наступний фрагмент працює для мене ідеально і виробляє саме те, що ви хочете, не використовуючи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

80
Акуратно! Більш узагальнена версіяdf[df.columns] = scaler.fit_transform(df[df.columns])
citynorman

6
@RajeshThevar Зовнішні дужки є типовими селекторними дужками панди, що повідомляє пандам вибирати стовпчик із фрейму даних. Внутрішні дужки вказують список. Ви передаєте список селектору панд. Якщо ви просто використовуєте одинарні дужки - з одним ім'ям стовпця, а потім іншим, відокремленим комою - панди інтерпретує це так, ніби ви намагаєтесь вибрати стовпчик із фрейму даних з багаторівневими стовпцями (MultiIndex) і викине клавішну помилку .
кен

1
щоб додати відповідь @ ken, якщо ви хочете точно побачити, як панди реалізують цю логіку індексації і чому кордону значень інтерпретуватимуться інакше, ніж список, ви можете подивитися, як DataFrames реалізує __getitem__метод. Зокрема, ви можете відкрити вам ipython і робити pd.DataFrame.__getitem__??; після імпорту панд як pd звичайно;)
LetsPlayYahtzee

4
Практична примітка: для тих, хто використовує розбиття даних про поїзд / випробування, вам потрібно буде відповідати лише вашим навчальним даним, а не даним тестування.
Девід Дж.

1
Для масштабування всіх, крім часових позначок, поєднуйте з columns =df.columns.drop('timestamps') df[df.columns] = scaler.fit_transform(df[df.columns]
intotecho

19

Подобається це?

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
Коли я запускаю цей сценарій, я отримую купу DeprecationWarnings. Як його слід оновити?
пір

Дивіться відповідь @ LetsPlayYahtzee нижче
AJP

2
Простіша версія: dfTest [['A', 'B']] = dfTest [['A', 'B']]. Застосувати (MinMaxScaler (). Fit_transform)
Александр В.

12

Як згадується у коментарі pir, .apply(lambda el: scale.fit_transform(el))метод видасть таке попередження:

DeprecationWarning: передача масивів 1d, оскільки дані застаріли в 0,17, і підвищить ValueError в 0,19. Переформатуйте свої дані або за допомогою X.reshape (-1, 1), якщо ваші дані мають єдину функцію, або X.reshape (1, -1), якщо вона містить один зразок.

Перетворення ваших стовпців у numpy масиви повинно зробити цю роботу (я віддаю перевагу StandardScaler):

from sklearn.preprocessing import StandardScaler
scale = StandardScaler()

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

- Редагувати листопада 2018 (випробувано для панд 0.23.4 ) -

Як згадує Роб Мюррей у коментарях, у поточній (v0.23.4) версії панди .as_matrix()повертається FutureWarning. Тому його слід замінити на .values:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()

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

- Редагувати травень 2019 (випробувано на пандах 0.24.2 ) -

Як згадує joelostblom у коментарях, "Так 0.24.0, рекомендується використовувати .to_numpy()замість цього .values".

Оновлений приклад:

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
використання .valuesзамість того, .as_matrix()як as_matrix()зараз дає FutureWarning.
Роб Мюррей


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

Це має працювати без попереджень про амортизацію.


7

Це можна зробити, використовуючи pandasлише:

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
Я знаю, що я можу це зробити просто в пандах, але, можливо, я захочу в кінцевому підсумку застосувати інший метод склеарна, який не так просто написати самому. Мені більше цікаво з'ясувати, чому подання заявок на серію не працює, як я очікував, ніж я придумав суворо простіше рішення. Наступним моїм кроком буде запуск RandomForestRegressor, і я хочу переконатися, що я розумію, як Pandas і sklearn працюють разом.
літаючий фрикаделька

5
Ця відповідь небезпечна, оскільки df.max() - df.min()може бути 0, що призводить до виключення. Крім того, df.min()обчислюється вдвічі, що неефективно. Зауважте, що df.ptp()еквівалентно df.max() - df.min().
Акумен

3

Я знаю, що це дуже старий коментар, але все ж:

Замість використання однієї дужки (dfTest['A'])використовуйте подвійні дужки (dfTest[['A']]).

тобто: min_max_scaler.fit_transform(dfTest[['A']]).

Я вірю, що це дасть бажаний результат.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.