Bình thường hóa các cột của khung dữ liệu gấu trúc


226

Tôi có một khung dữ liệu trong gấu trúc nơi mỗi cột có phạm vi giá trị khác nhau. Ví dụ:

df:

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

Bất kỳ ý tưởng làm thế nào tôi có thể bình thường hóa các cột của khung dữ liệu này trong đó mỗi giá trị nằm trong khoảng từ 0 đến 1?

Đầu ra mong muốn của tôi là:

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
có một hàm áp dụng, ví dụ frame.apply (f, trục = 1) trong đó f là một hàm thực hiện một việc gì đó với một hàng ...
tschm

1
Chuẩn hóa có thể không phải là từ ngữ thích hợp nhất, vì tài liệu scikit-learn định nghĩa nó là "quá trình nhân rộng các mẫu riêng lẻ để có định mức đơn vị " (nghĩa là theo từng hàng, nếu tôi hiểu đúng).
Skippy le Grand Gourou

Tôi không hiểu, tại sao tỷ lệ min_max được coi là bình thường hóa! bình thường phải có ý nghĩa theo nghĩa phân phối bình thường với số không trung bình và phương sai 1.
Cảnh sát OverFlow

Nếu bạn đang truy cập câu hỏi này vào năm 2020 hoặc sau đó, hãy xem câu trả lời của @Poudel, bạn sẽ nhận được câu trả lời khác nhau về việc bình thường hóa nếu bạn sử dụng gấu trúc vs sklearn.
Bhishan Poudel

@Poudel đây có phải là do ddoftranh luận?
fffrost

Câu trả lời:


223

Bạn có thể sử dụng gói sklearn và các tiện ích tiền xử lý liên quan của nó để chuẩn hóa dữ liệu.

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)

Để biết thêm thông tin, hãy xem tài liệu tìm hiểu về scikit về dữ liệu tiền xử lý: nhân rộng các tính năng cho một phạm vi.


46
Tôi nghĩ rằng điều này sẽ loại bỏ các tên cột, đó có thể là một trong những lý do op sử dụng dataframes ở vị trí đầu tiên.
pietz

47
Điều này sẽ bình thường hóa các hàng và không phải các cột, trừ khi bạn hoán đổi nó trước. Để làm những gì Q yêu cầu:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index)
hobs

26
@pietz để giữ tên cột, xem bài này . Về cơ bản thay thế dòng cuối cùng bằng,df=pandas.DataFrame(x_scaled, columns=df.columns)
ijoseph

5
@hobs Điều này không đúng. Mã của Sandman bình thường hóa cột và mỗi cột. Bạn nhận được kết quả sai nếu bạn chuyển đổi.
petezurich

8
@petezurich Có vẻ như Sandman hoặc Praveen đã sửa mã của họ. Thật không may, không thể sửa bình luận;)
hobs

397

một cách dễ dàng bằng cách sử dụng Pandas : (ở đây tôi muốn sử dụng chuẩn hóa trung bình)

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

để sử dụng chuẩn hóa tối thiểu:

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

Chỉnh sửa: Để giải quyết một số lo ngại, cần phải nói rằng Pandas tự động áp dụng chức năng thông minh trong mã ở trên.


16
tôi thích cái này. nó ngắn, nó biểu cảm và nó bảo tồn thông tin tiêu đề. nhưng tôi nghĩ bạn cũng cần phải trừ min trong mẫu số.
pietz

6
Tôi không nghĩ nó sai. Hoạt động tốt đối với tôi - Tôi không nghĩ mean () và std () cần trả về một khung dữ liệu để nó hoạt động và thông báo lỗi của bạn không ngụ ý rằng chúng không phải là một khung dữ liệu là một vấn đề.
Strandtasche

24
Đây không phải là bình thường hóa cột. điều này là bình thường hóa toàn bộ ma trận sẽ cung cấp kết quả sai.
Nguai al

6
Cũng làm việc cho tôi đẹp. @Nguaial bạn có thể đang thử điều này trên một ma trận numpy trong trường hợp đó kết quả sẽ là những gì bạn nói. Nhưng đối với các tệp dữ liệu Pandas, các biện pháp tối thiểu, tối đa, ... áp dụng theo cột theo mặc định.
Phụ trợ

1
tôi cũng thích cái này
Isaac Sim

51

Dựa trên bài đăng này: /stats/70801/how-to-n normalize-data-to-0-1-range

Bạn có thể làm như sau:

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

Bạn không cần phải lo lắng về việc giá trị của bạn là âm hay dương. Và các giá trị nên được trải đều giữa 0 và 1.


8
Hãy cẩn thận khi các giá trị tối thiểu và tối đa giống nhau, mẫu số của bạn bằng 0 và bạn sẽ nhận được giá trị NaN.
Hrushikesh Dhumal

36

Vấn đề của bạn thực sự là một biến đổi đơn giản hoạt động trên các cột:

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

frame.apply(f, axis=0)

Hoặc thậm chí ngắn gọn hơn:

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

2
Người lambdagiỏi nhất là :-)
Abu Shoeb

4
Đây không phải là trục = 1 vì câu hỏi là bình thường hóa cột?
Nguai al

Không, từ các tài liệu : axis [...] 0 or 'index': apply function to each column. Mặc định là thực tế axis=0để lớp lót này có thể được viết thậm chí ngắn hơn :-) Cảm ơn @tschm.
jorijnsmit

30

Nếu bạn thích sử dụng gói sklearn, bạn có thể giữ tên cột và chỉ mục bằng cách sử dụng gấu trúc locnhư vậy:

from sklearn.preprocessing import MinMaxScaler

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

27

Đơn giản là đẹp:

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

Tuyệt vời và theo tôi là giải pháp tốt nhất!
Maciej A. Bednarz

6
Lưu ý, OP đã yêu cầu phạm vi [0..1] và giải pháp này chia tỷ lệ thành phạm vi [-1..1]. Hãy thử điều này với mảng [-10, 10].
Alexander Sosnovshchenko

3
@AlexanderSosnovshchenko không thực sự. Basil Musa cho rằng ma trận của OP luôn không âm, đó là lý do tại sao ông đưa ra giải pháp này. Nếu một số cột có mục nhập âm thì mã này KHÔNG bình thường hóa thành phạm vi [-1,1]. Hãy thử nó với mảng [-5, 10]. Cách chính xác để bình thường hóa thành [0,1] với các giá trị âm được đưa ra bởi câu trả lời của Cinadf["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min())
facuq

đơn giản và rõ ràng
joshi123

Có lẽ thậm chí còn đơn giản hơn: df /= df.max()- giả sử mục tiêu là bình thường hóa từng cột, từng cá nhân.
n1k31t4

24

Bạn có thể tạo một danh sách các cột mà bạn muốn bình thường hóa

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

Dữ liệu Pandasrame của bạn hiện chỉ được chuẩn hóa tại các cột bạn muốn


Tuy nhiên , nếu bạn muốn ngược lại , hãy chọn một danh sách các cột mà bạn không muốn bình thường hóa, bạn chỉ cần tạo một danh sách tất cả các cột và loại bỏ các cột không mong muốn

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

Tôi nghĩ rằng một cách tốt hơn để làm điều đó trong gấu trúc chỉ là

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

Chỉnh sửa Nếu có trong số dữ liệu của bạn trong khung dữ liệu, bạn nên sử dụng thay thế

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

1
Trong trường hợp tất cả các giá trị của một cột bằng 0 thì điều này sẽ không hoạt động
ahajib

chia giá trị hiện tại cho tối đa sẽ không cho bạn chuẩn hóa chính xác trừ khi tối thiểu là 0.
pietz

Tôi đồng ý, nhưng đó là những gì Cựu ước đã yêu cầu (xem ví dụ của anh ấy)
Daniele

11

Giải pháp được đưa ra bởi Sandman và Praveen là rất tốt. Vấn đề duy nhất là nếu bạn có các biến phân loại trong các cột khác của khung dữ liệu thì phương thức này sẽ cần một số điều chỉnh.

Giải pháp của tôi cho loại vấn đề này là như sau:

 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
Câu trả lời này rất hữu ích vì hầu hết các ví dụ trên internet áp dụng một bộ chia tỷ lệ cho tất cả các cột, trong khi điều này thực sự giải quyết tình huống trong đó một bộ chia tỷ lệ, nói MinMaxScaler, không nên áp dụng cho tất cả các cột.
demongolem

10

Ví dụ về các tiêu chuẩn khác nhau trong python.

Để tham khảo xem bài viết trên wikipedia này: https://en.wikipedia.org/wiki/Unbiased_estimation_of_stiteria_deviation

Dữ liệu mẫu

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

Bình thường hóa bằng cách sử dụng gấu trúc (Cung cấp ước tính không thiên vị)

Khi bình thường hóa, chúng ta chỉ cần trừ trung bình và chia cho độ lệch chuẩn.

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

Chuẩn hóa bằng sklearn (Đưa ra ước tính sai lệch, khác với gấu trúc)

Nếu bạn làm điều tương tự với sklearnbạn sẽ nhận được đầu ra KHÁC BIỆT!

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

Các ước tính thiên vị của sklearn có làm cho Machine Learning kém mạnh mẽ hơn không?

KHÔNG.

Tài liệu chính thức của sklearn.pre Processing.scale nói rằng việc sử dụng công cụ ước tính thiên vị là KHÔNG GIỚI HẠN để ảnh hưởng đến hiệu suất của các thuật toán học máy và chúng ta có thể sử dụng chúng một cách an toàn.

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.

Điều gì về quy mô MinMax?

Không có tính toán Độ lệch chuẩn trong tỷ lệ MinMax. Vì vậy, kết quả là giống nhau ở cả gấu trúc và 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

Bạn có thể muốn có một số cột được chuẩn hóa và các cột khác không thay đổi như một số tác vụ hồi quy mà nhãn dữ liệu hoặc cột phân loại không thay đổi. Vì vậy, tôi đề xuất cho bạn cách thức pythonic này (Đó là sự kết hợp của câu trả lời @shg và @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

Nó chỉ là toán học đơn giản. Câu trả lời nên đơn giản như dưới đây.

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)

Từ tài liệu về gấu trúc, cấu trúc DataFrame có thể áp dụng một hoạt động (chức năng) cho chính nó.

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

Áp dụng chức năng dọc theo trục đầu vào của DataFrame. Các đối tượng được truyền cho các hàm là các đối tượng Sê-ri có chỉ mục hoặc chỉ mục của DataFrame (trục = 0) hoặc các cột (trục = 1). Kiểu trả về phụ thuộc vào việc tổng hợp hàm đã qua hay đối số rút gọn nếu DataFrame trống.

Bạn có thể áp dụng một chức năng tùy chỉnh để vận hành DataFrame.


2
Sẽ là tốt để giải thích, tại sao mã của bạn giải quyết vấn đề OP, vì vậy mọi người có thể điều chỉnh chiến lược thay vì chỉ sao chép mã của bạn. Xin vui lòng đọc Làm thế nào để tôi viết một câu trả lời tốt?
Ông T

2

Hàm sau tính toán điểm 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

Đây là cách bạn thực hiện nó một cách khôn ngoan bằng cách sử dụng tính năng hiểu danh sách:

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

1

Bạn chỉ có thể sử dụng hàm pandas.DataFrame.transform 1 theo cách này:

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

Giải pháp này sẽ không hoạt động nếu tất cả các giá trị âm. Xét [-1, -2, -3]. Chúng ta chia cho -1, và bây giờ chúng ta có [1,2,3].
Dave Liu


0

Bạn có thể làm điều này trong một dòng

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

nó có nghĩa là cho mỗi cột và sau đó trừ nó (trung bình) từ mỗi hàng (chỉ có nghĩa là trừ cột cụ thể từ hàng của nó) và chỉ chia cho trung bình. Cuối cùng, chúng tôi những gì chúng tôi nhận được là tập dữ liệu chuẩn hóa.


0

Pandas không bình thường hóa cột theo mặc định. Hãy thử mã dưới đây.

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

Các giá trị đầu ra sẽ nằm trong phạm vi 0 và 1.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.