panda dataframe kolom scaling dengan sklearn

137

Saya memiliki kerangka data panda dengan kolom tipe campuran, dan saya ingin menerapkan min_max_scaler sklearn ke beberapa kolom. Idealnya, saya ingin melakukan transformasi ini di tempat, tetapi belum menemukan cara untuk melakukan itu. Saya telah menulis kode berikut yang berfungsi:

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

Saya ingin tahu apakah ini adalah cara yang disukai / paling efisien untuk melakukan transformasi ini. Apakah ada cara saya bisa menggunakan df.apply itu akan lebih baik?

Saya juga terkejut saya tidak bisa mendapatkan kode berikut untuk bekerja:

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

Jika saya meneruskan seluruh dataframe ke scaler itu berfungsi:

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

Saya bingung mengapa melewatkan seri ke scaler gagal. Dalam kode kerja lengkap saya di atas, saya berharap hanya meneruskan seri ke scaler kemudian mengatur kolom dataframe = ke seri yang diskalakan. Saya telah melihat pertanyaan ini menanyakan beberapa tempat lain, tetapi belum menemukan jawaban yang baik. Bantuan apa pun yang memahami apa yang terjadi di sini akan sangat dihargai!

bola terbang
sumber
1
Apakah ini berhasil jika Anda melakukan ini bad_output = min_max_scaler.fit_transform(dfTest['A'].values)? mengakses valuesatribut mengembalikan array numpy, untuk beberapa alasan terkadang scikit learn api akan memanggil metode yang benar yang membuat panda mengembalikan array numpy dan terkadang tidak.
EdChum
Kerangka data panda adalah objek yang cukup rumit dengan konvensi yang tidak cocok dengan konvensi scikit-learn. Jika Anda mengonversikan semuanya menjadi array NumPy, scikit-belajar menjadi jauh lebih mudah untuk dikerjakan.
Fred Foo
@edChum - bad_output = in_max_scaler.fit_transform(dfTest['A'].values)juga tidak berfungsi. @ Larsmans - ya saya sudah berpikir tentang turun rute ini, sepertinya merepotkan. Saya tidak tahu apakah ini bug atau tidak bahwa Pandas dapat meneruskan kerangka data lengkap ke fungsi sklearn, tetapi bukan seri. Pemahaman saya tentang sebuah dataframe adalah bahwa itu adalah dict of series. Membaca dalam buku "Python untuk Analisis Data", ia menyatakan bahwa panda dibangun di atas numpy untuk membuatnya mudah digunakan dalam aplikasi NumPy-centric.
flyingmeatball

Jawaban:

214

Saya tidak yakin apakah versi sebelumnya pandasmencegah ini tetapi sekarang cuplikan berikut ini berfungsi dengan baik untuk saya dan menghasilkan apa yang Anda inginkan tanpa harus menggunakanapply

>>> 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
sumber
80
Rapi! Versi yang lebih umumdf[df.columns] = scaler.fit_transform(df[df.columns])
citynorman
6
@RajeshThevar Tanda kurung luar adalah tanda kurung khas panda, memberi tahu panda untuk memilih kolom dari kerangka data. Kurung bagian dalam menunjukkan daftar. Anda memberikan daftar kepada pemilih panda. Jika Anda hanya menggunakan tanda kurung tunggal - dengan satu nama kolom diikuti oleh yang lain, dipisahkan oleh koma - panda mengartikan ini seolah-olah Anda mencoba memilih kolom dari dataframe dengan kolom multi-level (MultiIndex) dan akan melempar keyerror .
ken
1
untuk menambahkan jawaban @ ken jika Anda ingin melihat dengan tepat bagaimana panda mengimplementasikan logika pengindeksan ini dan mengapa tuple nilai akan ditafsirkan berbeda dari daftar, Anda dapat melihat bagaimana DataFrames mengimplementasikan __getitem__metode ini. Secara khusus Anda dapat membuka ipython Anda dan melakukannya pd.DataFrame.__getitem__??; setelah Anda mengimpor panda sebagai pd tentu saja;)
LetsPlayYahtzee
4
Catatan praktis: bagi mereka yang menggunakan data data kereta / pengujian, Anda hanya ingin cocok dengan data pelatihan Anda, bukan data pengujian Anda.
David J.
1
Untuk skala semua kecuali kolom cap waktu, gabungkan dengan columns =df.columns.drop('timestamps') df[df.columns] = scaler.fit_transform(df[df.columns]
intotecho
19

Seperti ini?

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
sumber
3
Saya mendapatkan banyak DeprecationWarnings ketika saya menjalankan skrip ini. Bagaimana cara memperbaruinya?
pir
Lihat jawaban @ LetsPlayYahtzee di bawah ini
AJP
2
Versi yang lebih sederhana: dfTest [['A', 'B']] = dfTest [['A', 'B']]. Apply (MinMaxScaler (). Fit_transform)
Alexandre V.
12

Seperti yang disebutkan dalam komentar pir - .apply(lambda el: scale.fit_transform(el))metode ini akan menghasilkan peringatan berikut:

DeprecationWarning: Melewati array 1d karena data tidak digunakan lagi di 0.17 dan akan menaikkan ValueError di 0.19. Bentuk ulang data Anda menggunakan X.reshape (-1, 1) jika data Anda memiliki fitur tunggal atau X.reshape (1, -1) jika berisi satu sampel.

Mengubah kolom Anda menjadi array numpy harus melakukan pekerjaan (saya lebih suka StandardScaler):

from sklearn.preprocessing import StandardScaler
scale = StandardScaler()

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

- Edit Nov 2018 (Diuji untuk panda 0.23.4 ) -

Seperti yang dikatakan Rob Murray dalam komentar, dalam versi panda saat ini (v0.23.4) .as_matrix()kembali FutureWarning. Karena itu, harus diganti dengan .values:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()

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

- Edit Mei 2019 (Diuji untuk panda 0.24.2 ) -

Seperti yang disebutkan joelostblom dalam komentar, "Karena 0.24.0, disarankan untuk menggunakan .to_numpy()daripada .values."

Contoh yang diperbarui:

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
Obligasi Hasil Rendah
sumber
1
gunakan .valuesdi tempat .as_matrix()seperti as_matrix()sekarang memberi a FutureWarning.
Rob Murray
10
df = pd.DataFrame(scale.fit_transform(df.values), columns=df.columns, index=df.index)

Ini harus bekerja tanpa peringatan penyusutan.

athlonshi
sumber
7

Anda dapat melakukannya pandashanya dengan menggunakan :

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
sumber
6
Saya tahu bahwa saya bisa melakukannya hanya dalam panda, tetapi saya mungkin ingin akhirnya menerapkan metode sklearn berbeda yang tidak mudah untuk menulis sendiri. Saya lebih tertarik untuk mencari tahu mengapa melamar seri tidak bekerja seperti yang saya harapkan daripada saya datang dengan solusi yang lebih sederhana. Langkah saya selanjutnya adalah menjalankan RandomForestRegressor, dan saya ingin memastikan saya mengerti bagaimana Panda dan sklearn bekerja bersama.
flyingmeatball
5
Jawaban ini berbahaya karena df.max() - df.min()bisa 0, yang mengarah ke pengecualian. Selain itu, df.min()dihitung dua kali yang tidak efisien. Catatan yang df.ptp()setara dengan df.max() - df.min().
Acumenus
3

Saya tahu ini komentar yang sangat lama, tapi tetap saja:

Alih-alih menggunakan braket tunggal (dfTest['A']), gunakan tanda kurung ganda (dfTest[['A']]).

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

Saya yakin ini akan memberikan hasil yang diinginkan.

LEMAH
sumber