คอลัมน์ดาต้าเฟรมแพนด้าปรับขนาดด้วย sklearn


138

ฉันมีดาต้าเฟรมแพนด้าที่มีคอลัมน์ประเภทผสมและฉันต้องการใช้ 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

ฉันสับสนว่าทำไมการส่งซีรีส์ไปยังสเกลเลอร์จึงล้มเหลว ในรหัสการทำงานทั้งหมดของฉันด้านบนฉันหวังว่าจะส่งชุดข้อมูลไปยังเครื่องชั่งจากนั้นตั้งค่าคอลัมน์ dataframe = เป็นชุดที่ปรับขนาด ฉันเคยเห็นคำถามนี้ถามที่อื่น ๆ สองสามแห่ง แต่ยังไม่พบคำตอบที่ดี ความช่วยเหลือใด ๆ ที่เข้าใจว่าเกิดอะไรขึ้นที่นี่จะได้รับการชื่นชมอย่างมาก!


1
มันทำงานถ้าคุณทำเช่นนี้bad_output = min_max_scaler.fit_transform(dfTest['A'].values)? การเข้าถึงvaluesแอตทริบิวต์จะส่งคืนอาร์เรย์ numpy ด้วยเหตุผลบางอย่างบางครั้ง scikit learn api จะเรียกวิธีการที่ถูกต้องซึ่งทำให้ pandas ส่งคืนอาร์เรย์ numpy และบางครั้งก็ไม่ได้
EdChum

ดาต้าเฟรมของ Pandas เป็นวัตถุที่ค่อนข้างซับซ้อนและมีการประชุมที่ไม่ตรงกับอนุสัญญาของ scikit-learn หากคุณแปลงทุกอย่างเป็นอาร์เรย์ NumPy scikit-learn จะทำงานได้ง่ายขึ้นมาก
Fred Foo

@edChum - bad_output = in_max_scaler.fit_transform(dfTest['A'].values)ไม่ได้ผลเช่นกัน @larsmans - ใช่ฉันเคยคิดที่จะไปตามเส้นทางนี้ดูเหมือนจะยุ่งยาก ฉันไม่รู้ว่าเป็นบั๊กหรือไม่ที่ Pandas สามารถส่งผ่าน dataframe แบบเต็มไปยังฟังก์ชัน sklearn ได้ แต่ไม่ใช่ซีรี่ส์ ความเข้าใจของฉันเกี่ยวกับ dataframe คือมันเป็นซีรีส์ การอ่านในหนังสือ "Python for Data Analysis" ระบุว่าแพนด้าถูกสร้างขึ้นด้านบนของ numpy เพื่อให้ง่ายต่อการใช้งานในการประยุกต์ใช้ NumPy เป็นศูนย์กลาง
flyingmeatball

คำตอบ:


215

ฉันไม่แน่ใจว่าเวอร์ชันก่อนหน้านี้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) และจะทำให้เกิดข้อผิดพลาด .
ken

1
เพื่อเพิ่มคำตอบของ @ ken หากคุณต้องการดูว่าแพนด้าใช้ตรรกะการจัดทำดัชนีนี้อย่างไรและเหตุใดค่าทูเพิลจึงถูกตีความแตกต่างจากรายการคุณสามารถดูว่า DataFrames ใช้__getitem__วิธีการอย่างไร โดยเฉพาะคุณสามารถเปิด ipython และทำpd.DataFrame.__getitem__??; หลังจากที่คุณนำเข้าแพนด้าเป็น pd แน่นอน;)
LetsPlayYahtzee

4
ข้อควรปฏิบัติ: สำหรับผู้ที่ใช้การแยกข้อมูลรถไฟ / การทดสอบคุณจะต้องปรับให้พอดีกับข้อมูลการฝึกอบรมเท่านั้นไม่ใช่ข้อมูลการทดสอบของคุณ
David J.

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 มากมายเมื่อฉันเรียกใช้สคริปต์นี้ ควรปรับปรุงอย่างไร
pir

ดูคำตอบของ @ LetsPlayYahtzee ด้านล่าง
AJP

2
เวอร์ชันที่ง่ายกว่า: dfTest [['A', 'B']] = dfTest [['A', 'B']] ใช้ (MinMaxScaler (). fit_transform)
Alexandre V.

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 ) -

ดังที่ Rob Murray กล่าวไว้ในความคิดเห็นในเวอร์ชันปัจจุบัน (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
Rob Murray

1
ตั้งแต่0.24.0มันถูกแนะนำให้ใช้.to_numpy().valuesแทน
joelostblom

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
ฉันรู้ว่าทำได้แค่ในแพนด้า แต่ในที่สุดฉันก็อาจต้องการใช้วิธี sklearn แบบอื่นที่ไม่ง่ายอย่างที่คิด ฉันสนใจที่จะหาว่าทำไมการสมัครซีรีส์ถึงไม่ได้ผลตามที่ฉันคาดหวังไว้มากกว่าที่ฉันจะหาวิธีแก้ปัญหาที่ง่ายกว่านั้น ขั้นตอนต่อไปของฉันคือการเรียกใช้ RandomForestRegressor และฉันต้องการให้แน่ใจว่าฉันเข้าใจว่า Pandas และ Sklearn ทำงานร่วมกันอย่างไร
flyingmeatball

5
คำตอบนี้อันตรายเพราะdf.max() - df.min()อาจเป็น 0 ซึ่งนำไปสู่ข้อยกเว้น นอกจากนี้ยังdf.min()คำนวณสองครั้งซึ่งไม่มีประสิทธิภาพ โปรดทราบว่าdf.ptp()เทียบเท่ากับdf.max() - df.min().
Acumenus

3

ฉันรู้ว่ามันเป็นความคิดเห็นที่เก่ามาก แต่ก็ยัง:

แทนการใช้วงเล็บเดียวใช้วงเล็บคู่(dfTest['A'])(dfTest[['A']])

กล่าวคือ: min_max_scaler.fit_transform(dfTest[['A']]).

ฉันเชื่อว่าสิ่งนี้จะให้ผลลัพธ์ที่ต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.