Normalisasi data dalam panda


131

Misalkan saya memiliki bingkai data panda df:

Saya ingin menghitung rata-rata kolom dari bingkai data.

Ini mudah:

df.apply(average) 

maka kolom bijak kisaran maks (col) - min (col). Ini mudah lagi:

df.apply(max) - df.apply(min)

Sekarang untuk setiap elemen saya ingin mengurangi rata-rata kolomnya dan membaginya dengan rentang kolomnya. Saya tidak yakin bagaimana melakukan itu

Setiap bantuan / petunjuk sangat dihargai.

Jawaban:


225
In [92]: df
Out[92]:
           a         b          c         d
A  -0.488816  0.863769   4.325608 -4.721202
B -11.937097  2.993993 -12.916784 -1.086236
C  -5.569493  4.672679  -2.168464 -9.315900
D   8.892368  0.932785   4.535396  0.598124

In [93]: df_norm = (df - df.mean()) / (df.max() - df.min())

In [94]: df_norm
Out[94]:
          a         b         c         d
A  0.085789 -0.394348  0.337016 -0.109935
B -0.463830  0.164926 -0.650963  0.256714
C -0.158129  0.605652 -0.035090 -0.573389
D  0.536170 -0.376229  0.349037  0.426611

In [95]: df_norm.mean()
Out[95]:
a   -2.081668e-17
b    4.857226e-17
c    1.734723e-17
d   -1.040834e-17

In [96]: df_norm.max() - df_norm.min()
Out[96]:
a    1
b    1
c    1
d    1

Apakah ada cara untuk melakukan ini jika Anda ingin menormalkan suatu subset? Katakan baris itu Adan Bmerupakan bagian dari faktor pengelompokan yang lebih besar yang ingin Anda normalkan secara terpisah dari Cdan D.
Amyunimus

Pilih subset dan hitung seperti sebelumnya. Lihat pandas.pydata.org/pandas-docs/stable/indexing.html tentang cara mengindeks dan memilih data
Wouter Overmeire

17
Jika Anda membutuhkan nilainya menjadi> 0: df_norm = (df - df.min ()) / (df.max () - df.min ())
Dayvid Oliveira

1
harus df_norm = (df - df.min ()) / (df.max () - df.min ()) daripada df.mean () di kurung pertama untuk mendapatkan nilai antara 0 dan 1
jnPy

2
Jika dataframe Anda memiliki string di beberapa kolom, lihat jawaban
netskink

73

Jika Anda tidak keberatan mengimpor sklearnperpustakaan, saya akan merekomendasikan metode yang dibicarakan di blog ini .

import pandas as pd
from sklearn import preprocessing

data = {'score': [234,24,14,27,-74,46,73,-18,59,160]}
cols = data.columns
df = pd.DataFrame(data)
df

min_max_scaler = preprocessing.MinMaxScaler()
np_scaled = min_max_scaler.fit_transform(df)
df_normalized = pd.DataFrame(np_scaled, columns = cols)
df_normalized

2
tautan ke posting blog sudah mati. apakah Anda punya yang berfungsi?
Mart

3
Metode yang sesuai untuk membuat data dinormalisasi unit-normal disebut StandardScaler.
abeboparebop

Saya menemukan solusi serupa di tempat lain. Masalahnya adalah bahwa pada bagian np_scaled, ia menunjukkan kesalahan mengharapkan array 2D tetapi input adalah array 1D dan disarankan agar kami menggunakan membentuk kembali (-1,1). Adakah ide bagaimana menyelesaikan ini sebagai membentuk kembali juga tidak berfungsi.?
deadcode

Anda mungkin mendapatkan peringatan tergantung pada versi numpy & sklearn yang bekerja dengan Anda, tetapi secara umum, ini harus bekerja np_scaled = min_max_scaler.fit_transform(df.score.astype(float).values.reshape(-1, 1))
Jaeyoung Chun

33

Anda dapat menggunakan applyini, dan ini sedikit lebih rapi:

import numpy as np
import pandas as pd

np.random.seed(1)

df = pd.DataFrame(np.random.randn(4,4)* 4 + 3)

          0         1         2         3
0  9.497381  0.552974  0.887313 -1.291874
1  6.461631 -6.206155  9.979247 -0.044828
2  4.276156  2.002518  8.848432 -5.240563
3  1.710331  1.463783  7.535078 -1.399565

df.apply(lambda x: (x - np.mean(x)) / (np.max(x) - np.min(x)))

          0         1         2         3
0  0.515087  0.133967 -0.651699  0.135175
1  0.125241 -0.689446  0.348301  0.375188
2 -0.155414  0.310554  0.223925 -0.624812
3 -0.484913  0.244924  0.079473  0.114448

Juga, ini berfungsi dengan baik groupby, jika Anda memilih kolom yang relevan:

df['grp'] = ['A', 'A', 'B', 'B']

          0         1         2         3 grp
0  9.497381  0.552974  0.887313 -1.291874   A
1  6.461631 -6.206155  9.979247 -0.044828   A
2  4.276156  2.002518  8.848432 -5.240563   B
3  1.710331  1.463783  7.535078 -1.399565   B


df.groupby(['grp'])[[0,1,2,3]].apply(lambda x: (x - np.mean(x)) / (np.max(x) - np.min(x)))

     0    1    2    3
0  0.5  0.5 -0.5 -0.5
1 -0.5 -0.5  0.5  0.5
2  0.5  0.5  0.5 -0.5
3 -0.5 -0.5 -0.5  0.5

2

Sedikit dimodifikasi dari: Python Pandas Dataframe: Normalisasi data antara 0,01 dan 0,99? tetapi dari beberapa komentar berpikir itu relevan (maaf jika dianggap sebagai repost ...)

Saya ingin normalisasi khusus dalam persentil reguler datum atau z-score tidak memadai. Kadang-kadang saya tahu apa yang layak dan minimum dari populasi itu, dan karena itu ingin mendefinisikannya selain sampel saya, atau titik tengah yang berbeda, atau apa pun! Ini sering berguna untuk menskalakan dan menormalkan data untuk jaring saraf di mana Anda mungkin ingin semua input antara 0 dan 1, tetapi beberapa data Anda mungkin perlu diskalakan dengan cara yang lebih khusus ... karena persentil dan stdev mengasumsikan sampul sampel Anda populasi, tetapi kadang-kadang kita tahu ini tidak benar. Itu juga sangat berguna bagi saya ketika memvisualisasikan data dalam heatmaps. Jadi saya membangun fungsi khusus (menggunakan langkah-langkah tambahan dalam kode di sini untuk membuatnya terbaca mungkin):

def NormData(s,low='min',center='mid',hi='max',insideout=False,shrinkfactor=0.):    
    if low=='min':
        low=min(s)
    elif low=='abs':
        low=max(abs(min(s)),abs(max(s)))*-1.#sign(min(s))
    if hi=='max':
        hi=max(s)
    elif hi=='abs':
        hi=max(abs(min(s)),abs(max(s)))*1.#sign(max(s))

    if center=='mid':
        center=(max(s)+min(s))/2
    elif center=='avg':
        center=mean(s)
    elif center=='median':
        center=median(s)

    s2=[x-center for x in s]
    hi=hi-center
    low=low-center
    center=0.

    r=[]

    for x in s2:
        if x<low:
            r.append(0.)
        elif x>hi:
            r.append(1.)
        else:
            if x>=center:
                r.append((x-center)/(hi-center)*0.5+0.5)
            else:
                r.append((x-low)/(center-low)*0.5+0.)

    if insideout==True:
        ir=[(1.-abs(z-0.5)*2.) for z in r]
        r=ir

    rr =[x-(x-0.5)*shrinkfactor for x in r]    
    return rr

Ini akan mengambil seri panda, atau bahkan hanya daftar dan menormalkannya ke titik rendah, tengah, dan tinggi yang Anda tentukan. juga ada faktor menyusut! untuk memungkinkan Anda mengurangi data dari titik 0 dan 1 (saya harus melakukan ini ketika menggabungkan colormaps di matplotlib: PColormesh tunggal dengan lebih dari satu colormap menggunakan Matplotlib ) Jadi Anda mungkin dapat melihat bagaimana kode bekerja, tetapi pada dasarnya mengatakan Anda memiliki nilai [-5,1,10] dalam sampel, tetapi ingin menormalkan berdasarkan rentang -7 hingga 7 (jadi apa pun di atas 7, "10" kami diperlakukan sebagai 7 secara efektif) dengan titik tengah 2, tetapi menyusutkan agar sesuai dengan colormap 256 RGB:

#In[1]
NormData([-5,2,10],low=-7,center=1,hi=7,shrinkfactor=2./256)
#Out[1]
[0.1279296875, 0.5826822916666667, 0.99609375]

Ini juga dapat mengubah data Anda ke dalam ... ini mungkin tampak aneh, tetapi saya menemukan itu berguna untuk memetakan. Katakanlah Anda menginginkan warna yang lebih gelap untuk nilai yang lebih dekat ke 0 daripada hi / rendah. Anda dapat memetakan berdasarkan data yang dinormalisasi di mana insideout = True:

#In[2]
NormData([-5,2,10],low=-7,center=1,hi=7,insideout=True,shrinkfactor=2./256)
#Out[2]
[0.251953125, 0.8307291666666666, 0.00390625]

Jadi sekarang "2" yang paling dekat dengan pusat, didefinisikan sebagai "1" adalah nilai tertinggi.

Bagaimanapun, saya pikir aplikasi saya relevan jika Anda ingin mengubah skala data dengan cara lain yang bisa memiliki aplikasi yang berguna bagi Anda.


Anda dapat mengganti semua pernyataan if / else dengan kamus dengan fungsi . Terlihat sedikit lebih bersih.
Roald

itu cukup rapi, saya akan mengingatnya lain kali, terima kasih!
Vlox

0

Ini adalah bagaimana Anda melakukannya dengan bijaksana:

[df[col].update((df[col] - df[col].min()) / (df[col].max() - df[col].min())) for col in df.columns]
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.