ทำให้คอลัมน์ของกรอบข้อมูลนุ่นเป็นปกติ


226

ฉันมี dataframe ใน pandas ที่แต่ละคอลัมน์มีช่วงค่าที่แตกต่างกัน ตัวอย่างเช่น:

DF:

A     B   C
1000  10  0.5
765   5   0.35
800   7   0.09

ความคิดใดที่ฉันสามารถทำให้คอลัมน์ปกติของ dataframe นี้มีค่าแต่ละค่าอยู่ระหว่าง 0 ถึง 1

ผลลัพธ์ที่ฉันต้องการคือ:

A     B    C
1     1    1
0.765 0.5  0.7
0.8   0.7  0.18(which is 0.09/0.5)

1
มีฟังก์ชั่นการใช้งานเช่น frame.apply (f, axis = 1) โดยที่ f คือฟังก์ชั่นที่ทำบางสิ่งบางอย่างกับแถว ...
tschm

1
การทำให้เป็นมาตรฐานอาจไม่ใช่คำที่เหมาะสมที่สุดเนื่องจากเอกสารการเรียนรู้แบบ Scikitกำหนดว่าเป็น "กระบวนการของการปรับขนาดตัวอย่างแต่ละตัวอย่างให้มีเกณฑ์ปกติของหน่วย " (เช่นแบบทีละแถวถ้าฉันได้รับอย่างถูกต้อง)
Skippy le Grand Gourou

ฉันไม่เข้าใจเลยว่าทำไมสเกล min_max ถูกพิจารณาว่าเป็นมาตรฐาน! ปกติต้องมีความหมายในแง่ของการแจกแจงแบบปกติที่มีค่าเฉลี่ยศูนย์และความแปรปรวน 1
โอเวอร์โฟลว์ตำรวจ

หากคุณกำลังเยี่ยมชมคำถามนี้ในปี 2020 หรือหลังจากนั้นให้ดูที่คำตอบโดย @Poudel คุณจะได้รับคำตอบที่แตกต่างจากการทำให้เป็นมาตรฐานถ้าคุณใช้ pandas vs sklearn
Bhishan Poudel

@Pelel นี่เป็นเพราะการddofโต้แย้งหรือไม่
fffrost

คำตอบ:


223

คุณสามารถใช้แพ็คเกจ sklearn และยูทิลิตี้การประมวลผลล่วงหน้าที่เกี่ยวข้องเพื่อทำให้ข้อมูลเป็นปกติ

import pandas as pd
from sklearn import preprocessing

x = df.values #returns a numpy array
min_max_scaler = preprocessing.MinMaxScaler()
x_scaled = min_max_scaler.fit_transform(x)
df = pd.DataFrame(x_scaled)

สำหรับข้อมูลเพิ่มเติมดูที่เอกสารการเรียนรู้ scikit เกี่ยวกับการประมวลผลข้อมูลล่วงหน้า: การปรับคุณสมบัติให้เป็นช่วง


46
ฉันคิดว่าสิ่งนี้จะกำจัดชื่อคอลัมน์ซึ่งอาจเป็นหนึ่งในเหตุผลที่ op กำลังใช้งาน dataframes ตั้งแต่แรก
pietz

47
การทำเช่นนี้จะทำให้แถวเป็นปกติและไม่ใช่คอลัมน์ยกเว้นว่าคุณได้ทำการแปลงก่อน ทำในสิ่งที่ถามถาม:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index)
เตาแก๊ส

26
@pietz เพื่อให้ชื่อคอลัมน์ดูโพสต์นี้ โดยทั่วไปแทนที่บรรทัดสุดท้ายด้วยdf=pandas.DataFrame(x_scaled, columns=df.columns)
ijoseph

5
@hobs สิ่งนี้ไม่ถูกต้อง รหัสของ Sandman ทำให้คอลัมน์เป็นแบบปกติและแบบต่อคอลัมน์ คุณได้รับผลลัพธ์ที่ผิดถ้าคุณย้าย
petezurich

8
@petezurich ดูเหมือนว่า Sandman หรือ Praveen แก้ไขรหัสแล้ว น่าเสียดายที่มันเป็นไปไม่ได้ที่จะแก้ไขความคิดเห็น;)
hobs

397

วิธีง่ายๆในการใช้Pandas : (ที่นี่ฉันต้องการใช้การทำให้เป็นมาตรฐาน

normalized_df=(df-df.mean())/df.std()

เพื่อใช้การทำให้เป็นมาตรฐานต่ำสุด:

normalized_df=(df-df.min())/(df.max()-df.min())

แก้ไข: เพื่อแก้ไขข้อกังวลบางอย่างต้องบอกว่า Pandas ใช้ฟังก์ชัน colomn-wise ในรหัสข้างต้นโดยอัตโนมัติ


16
ฉันชอบอันนี้ มันสั้นมันแสดงออกและรักษาข้อมูลส่วนหัว แต่ฉันคิดว่าคุณต้องลบ min ในตัวส่วนด้วย
pietz

6
ฉันไม่คิดว่ามันผิด ทำงานได้อย่างสวยงามสำหรับฉัน - ฉันไม่คิดว่า () และ std () ต้องส่งคืน dataframe เพื่อให้มันใช้งานได้และข้อความแสดงข้อผิดพลาดของคุณไม่ได้หมายความว่าพวกเขาไม่ใช่ dataframe เป็นปัญหา
Strandtasche

24
นี่ไม่ใช่คอลัมน์การทำให้เป็นมาตรฐานที่ชาญฉลาด นี่คือการทำให้เมทริกซ์ทั้งหมดเป็นปกติโดยรวมซึ่งจะให้ผลลัพธ์ที่ผิด
อัล

6
ยังทำงานให้ฉันอย่างสวยงาม @ ไม่เป็นทางการคุณอาจลองทำสิ่งนี้บนเมทริกซ์ numpy ซึ่งในกรณีนี้ผลลัพธ์จะเป็นสิ่งที่คุณพูด แต่สำหรับ Pandas dataframes มาตรการ min, max, ... จะมีผลกับคอลัมน์โดยปริยาย
เสริม

1
ฉันชอบอันนี้เหมือนกัน
Isaac Sim

51

อ้างอิงจากโพสต์นี้: /stats/70801/how-to-normalize-data-to-0-1-range

คุณสามารถทำสิ่งต่อไปนี้:

def normalize(df):
    result = df.copy()
    for feature_name in df.columns:
        max_value = df[feature_name].max()
        min_value = df[feature_name].min()
        result[feature_name] = (df[feature_name] - min_value) / (max_value - min_value)
    return result

คุณไม่จำเป็นต้องกังวลว่าค่าของคุณจะเป็นค่าลบหรือค่าบวก และค่าควรกระจายอย่างระหว่าง 0 และ 1


8
ระวังเมื่อค่า min และ max เท่ากันตัวส่วนของคุณคือ 0 และคุณจะได้รับค่า NaN
Hrushikesh Dhumal

36

ปัญหาของคุณคือการแปลงอย่างง่าย ๆ ที่กระทำกับคอลัมน์:

def f(s):
    return s/s.max()

frame.apply(f, axis=0)

หรือสั้นกว่า:

   frame.apply(lambda x: x/x.max(), axis=0)

2
lambdaหนึ่งที่ดีที่สุด :-)
อาบู Shoeb

4
สิ่งนี้ไม่ควรจะเป็นแกน = 1 เนื่องจากคำถามคือการทำให้คอลัมน์เป็นแนวปกติหรือไม่
อัล

ไม่มีจากเอกสารaxis [...] 0 or 'index': apply function to each column : ค่าเริ่มต้นเป็นจริงaxis=0เพื่อให้หนึ่งซับนี้สามารถเขียนได้สั้นกว่า :-) ขอบคุณ @tschm
jorijnsmit

30

ถ้าคุณชอบใช้แพ็คเกจ sklearn คุณสามารถเก็บชื่อคอลัมน์และดัชนีโดยใช้หมีแพนด้าlocอย่างเช่น:

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler() 
scaled_values = scaler.fit_transform(df) 
df.loc[:,:] = scaled_values

27

Simple is Beautiful:

df["A"] = df["A"] / df["A"].max()
df["B"] = df["B"] / df["B"].max()
df["C"] = df["C"] / df["C"].max()

ยอดเยี่ยมและในความคิดของฉันทางออกที่ดีที่สุด!
Maciej A. Bednarz

6
โปรดทราบว่า OP ขอช่วง [0..1] และโซลูชันนี้ปรับช่วงเป็น [-1..1] ลองทำสิ่งนี้กับอาร์เรย์ [-10, 10]
Alexander Sosnovshchenko

3
@AlexanderSosnovshchenko ไม่ได้จริงๆ เพรามูซากำลังสันนิษฐานว่าเมทริกซ์ของ OP นั้นไม่ใช่ลบเสมอนั่นคือสาเหตุที่เขาให้วิธีแก้ปัญหานี้ หากบางคอลัมน์มีรายการเชิงลบรหัสนี้จะไม่ทำให้เป็นมาตรฐานในช่วง [-1,1] ลองใช้กับอาร์เรย์ [-5, 10] วิธีที่ถูกต้องในการทำให้ปกติเป็น [0,1] ด้วยค่าลบได้รับคำตอบจาก Cinadf["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min())
facuq

ง่ายและชัดเจน
joshi123

บางทีอาจจะง่ายกว่า: df /= df.max()- สมมติว่าเป้าหมายคือการทำให้คอลัมน์แต่ละคอลัมน์เป็นปกติ
n1k31t4

24

คุณสามารถสร้างรายการของคอลัมน์ที่คุณต้องการทำให้ปกติ

column_names_to_normalize = ['A', 'E', 'G', 'sadasdsd', 'lol']
x = df[column_names_to_normalize].values
x_scaled = min_max_scaler.fit_transform(x)
df_temp = pd.DataFrame(x_scaled, columns=column_names_to_normalize, index = df.index)
df[column_names_to_normalize] = df_temp

ตอนนี้ Pandas Dataframe ของคุณถูกทำให้เป็นมาตรฐานที่คอลัมน์ที่คุณต้องการเท่านั้น


อย่างไรก็ตามหากคุณต้องการตรงกันข้ามให้เลือกรายการคอลัมน์ที่คุณไม่ต้องการทำให้เป็นมาตรฐานคุณสามารถสร้างรายการคอลัมน์ทั้งหมดและลบคอลัมน์ที่ไม่ต้องการได้

column_names_to_not_normalize = ['B', 'J', 'K']
column_names_to_normalize = [x for x in list(df) if x not in column_names_to_not_normalize ]

11

ฉันคิดว่าวิธีที่ดีกว่าในการทำแพนด้าคือ

df = df/df.max().astype(np.float64)

แก้ไขหากมีตัวเลขติดลบในกรอบข้อมูลคุณควรใช้แทน

df = df/df.loc[df.abs().idxmax()].astype(np.float64)

1
ในกรณีที่ค่าทั้งหมดของคอลัมน์เป็นศูนย์สิ่งนี้จะไม่ทำงาน
ahajib

การหารค่าปัจจุบันด้วยค่าสูงสุดจะไม่ทำให้เกิดการฟื้นฟูที่ถูกต้องเว้นแต่ว่าค่าต่ำสุดคือ 0
pietz

ฉันเห็นด้วย แต่นั่นคือสิ่งที่ OT กำลังขอ (ดูตัวอย่างของเขา)
Daniele

11

การแก้ปัญหาของ Sandman และ Praveen นั้นทำได้ดีมาก ปัญหาเดียวกับที่ถ้าคุณมีตัวแปรเด็ดขาดในคอลัมน์อื่น ๆ ของกรอบข้อมูลของคุณวิธีนี้จะต้องมีการปรับเปลี่ยนบางอย่าง

โซลูชันของฉันสำหรับปัญหาประเภทนี้กำลังติดตาม:

 from sklearn import preprocesing
 x = pd.concat([df.Numerical1, df.Numerical2,df.Numerical3])
 min_max_scaler = preprocessing.MinMaxScaler()
 x_scaled = min_max_scaler.fit_transform(x)
 x_new = pd.DataFrame(x_scaled)
 df = pd.concat([df.Categoricals,x_new])

2
คำตอบนี้มีประโยชน์เพราะตัวอย่างส่วนใหญ่บนอินเทอร์เน็ตใช้ตัวคัดกรองหนึ่งตัวกับคอลัมน์ทั้งหมดในขณะที่สิ่งนี้จะจัดการกับสถานการณ์ที่ตัวจำลองหนึ่งตัวพูด MinMaxScaler ไม่ควรใช้กับคอลัมน์ทั้งหมด
demongolem

10

ตัวอย่างของมาตรฐานที่แตกต่างในงูหลาม

สำหรับการอ้างอิงดูบทความวิกิพีเดียนี้: https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation

ตัวอย่างข้อมูล

import pandas as pd
df = pd.DataFrame({
               'A':[1,2,3],
               'B':[100,300,500],
               'C':list('abc')
             })
print(df)
   A    B  C
0  1  100  a
1  2  300  b
2  3  500  c

การทำให้เป็นมาตรฐานโดยใช้นุ่น (ให้การประมาณแบบไม่เอนเอียง)

เมื่อ normalizing เราก็แค่ลบค่าเฉลี่ยและหารด้วยค่าเบี่ยงเบนมาตรฐาน

df.iloc[:,0:-1] = df.iloc[:,0:-1].apply(lambda x: (x-x.mean())/ x.std(), axis=0)
print(df)
     A    B  C
0 -1.0 -1.0  a
1  0.0  0.0  b
2  1.0  1.0  c

การทำให้เป็นมาตรฐานโดยใช้ sklearn (ให้การประเมินแบบเอนเอียงต่างจากแพนด้า)

หากคุณทำสิ่งเดียวกันกับที่sklearnคุณจะได้รับผลลัพธ์ที่แตกต่างกัน!

import pandas as pd

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()


df = pd.DataFrame({
               'A':[1,2,3],
               'B':[100,300,500],
               'C':list('abc')
             })
df.iloc[:,0:-1] = scaler.fit_transform(df.iloc[:,0:-1].to_numpy())
print(df)
          A         B  C
0 -1.224745 -1.224745  a
1  0.000000  0.000000  b
2  1.224745  1.224745  c

การประเมินความลำเอียงที่แคบทำให้การเรียนรู้ของเครื่องลดลงหรือไม่

NO

เอกสารอย่างเป็นทางการของsklearn.preprocessing.scaleระบุว่าการใช้ตัวประมาณค่าแบบเอนเอียงคือ UNLIKELY เพื่อส่งผลกระทบต่อประสิทธิภาพของอัลกอริทึมการเรียนรู้ของเครื่องและเราสามารถใช้งานได้อย่างปลอดภัย

From official documentation:
We use a biased estimator for the standard deviation,
equivalent to numpy.std(x, ddof=0). 
Note that the choice of ddof is unlikely to affect model performance.

สิ่งที่เกี่ยวกับ MinMax Scaling?

ไม่มีการคำนวณค่าเบี่ยงเบนมาตรฐานในการปรับขนาด MinMax ดังนั้นผลลัพธ์จะเหมือนกันทั้งใน pandas และ scikit-learn

import pandas as pd
df = pd.DataFrame({
               'A':[1,2,3],
               'B':[100,300,500],
             })
(df - df.min()) / (df.max() - df.min())
     A    B
0  0.0  0.0
1  0.5  0.5
2  1.0  1.0


# Using sklearn
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler() 
arr_scaled = scaler.fit_transform(df) 

print(arr_scaled)
[[0.  0. ]
 [0.5 0.5]
 [1.  1. ]]

df_scaled = pd.DataFrame(arr_scaled, columns=df.columns,index=df.index)
print(df_scaled)
     A    B
0  0.0  0.0
1  0.5  0.5
2  1.0  1.0

6

คุณอาจต้องการให้บางคอลัมน์ถูกทำให้เป็นมาตรฐานและคอลัมน์อื่น ๆ ไม่เปลี่ยนแปลงเหมือนงานการถดถอยบางส่วนที่ป้ายข้อมูลหรือคอลัมน์ที่มีหมวดหมู่ไม่เปลี่ยนแปลงดังนั้นฉันขอแนะนำให้คุณใช้วิธี pythonic นี้ (เป็นการรวมกันของ @shg และ @Cina คำตอบ):

features_to_normalize = ['A', 'B', 'C']
# could be ['A','B'] 

df[features_to_normalize] = df[features_to_normalize].apply(lambda x:(x-x.min()) / (x.max()-x.min()))

5

มันเป็นคณิตศาสตร์ง่าย ๆ เท่านั้น คำตอบควรง่ายเหมือนด้านล่าง

normed_df = (df - df.min()) / (df.max() - df.min())

2
def normalize(x):
    try:
        x = x/np.linalg.norm(x,ord=1)
        return x
    except :
        raise
data = pd.DataFrame.apply(data,normalize)

จากเอกสารของนุ่นโครงสร้าง DataFrame สามารถใช้การดำเนินการ (ฟังก์ชัน) กับตัวเอง

DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)

ใช้ฟังก์ชันตามแกนป้อนเข้าของ DataFrame วัตถุที่ถูกส่งไปยังฟังก์ชั่นเป็นวัตถุชุดที่มีดัชนีดัชนีของ DataFrame (แกน = 0) หรือคอลัมน์ (แกน = 1) ประเภทการส่งคืนขึ้นอยู่กับว่าการรวมฟังก์ชันที่ส่งผ่านหรืออาร์กิวเมนต์ลดถ้า DataFrame ว่างเปล่า

คุณสามารถใช้ฟังก์ชั่นที่กำหนดเองเพื่อใช้งาน DataFrame


2
มันเป็นการดีที่จะอธิบายว่าทำไมรหัสของคุณแก้ปัญหา OPs เพื่อให้ผู้คนสามารถปรับกลยุทธ์ได้แทนที่จะคัดลอกรหัสของคุณ โปรดอ่านฉันจะเขียนคำตอบที่ดีได้อย่างไร
นาย T

2

ฟังก์ชั่นต่อไปนี้คำนวณคะแนน Z:

def standardization(dataset):
  """ Standardization of numeric fields, where all values will have mean of zero 
  and standard deviation of one. (z-score)

  Args:
    dataset: A `Pandas.Dataframe` 
  """
  dtypes = list(zip(dataset.dtypes.index, map(str, dataset.dtypes)))
  # Normalize numeric columns.
  for column, dtype in dtypes:
      if dtype == 'float32':
          dataset[column] -= dataset[column].mean()
          dataset[column] /= dataset[column].std()
  return dataset

2

นี่คือวิธีที่คุณทำคอลัมน์ฉลาดโดยใช้รายการความเข้าใจ:

[df[col].update((df[col] - df[col].min()) / (df[col].max() - df[col].min())) for col in df.columns]

1

คุณสามารถใช้ฟังก์ชันpandas.DataFrame.transform 1 ได้ด้วยวิธีนี้:

df.transform(lambda x: x/x.max())

วิธีนี้จะไม่ทำงานหากค่าทั้งหมดเป็นลบ พิจารณา [-1, -2, -3] เราหารด้วย -1 และตอนนี้เรามี [1,2,3]
Dave Liu


0

คุณสามารถทำได้ในหนึ่งบรรทัด

DF_test = DF_test.sub(DF_test.mean(axis=0), axis=1)/DF_test.mean(axis=0)

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


0

หมีแพนด้าทำการปรับสภาพคอลัมน์อย่างชาญฉลาดโดยค่าเริ่มต้น ลองรหัสด้านล่าง

X= pd.read_csv('.\\data.csv')
X = (X-X.min())/(X.max()-X.min())

ค่าเอาต์พุตจะอยู่ในช่วง 0 และ 1

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