Normalizuj dane w pandach

133

Załóżmy, że mam ramkę danych pandy df:

Chcę obliczyć średnią kolumnową ramki danych.

To jest łatwe:

df.apply(average) 

następnie zakres mądry kolumny max (col) - min (col). To znowu jest łatwe:

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

Teraz dla każdego elementu chcę odjąć średnią z jego kolumny i podzielić przez zakres jego kolumny. Nie wiem, jak to zrobić

Każda pomoc / wskazówki są bardzo mile widziane.

Jason
źródło

Odpowiedzi:

226
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
Wouter Overmeire
źródło
Czy istnieje sposób, aby to zrobić, jeśli chcesz znormalizować podzbiór? Powiedz ten wiersz Ai Bwejdź w skład większego czynnika grupowania, który chcesz normalizować oddzielnie od Ci D.
Amyunimus
Wybierz podzbiór i oblicz jak poprzednio. Zobacz pandas.pydata.org/pandas-docs/stable/indexing.html, aby dowiedzieć się, jak indeksować i wybierać dane
Wouter Overmeire,
17
Jeśli chcesz, aby twoje wartości były> 0: df_norm = (df - df.min ()) / (df.max () - df.min ())
Dayvid Oliveira
1
powinno być df_norm = (df - df.min ()) / (df.max () - df.min ()) zamiast df.mean () w pierwszych nawiasach, aby uzyskać wartości od 0 do 1
jnPy
2
Jeśli twoja ramka danych ma ciągi w niektórych kolumnach, zobacz tę odpowiedź
netskink
75

Jeśli nie masz nic przeciwko zaimportowaniu sklearnbiblioteki, polecam metodę omówioną na tym blogu.

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
David S.
źródło
2
link do posta na blogu jest martwy. czy masz działający?
data
3
Odpowiednia metoda tworzenia normalnych danych jednostkowych nosi nazwę StandardScaler.
abeboparebop
Znalazłem podobne rozwiązanie w innym miejscu. Problem polegał na tym, że w części np_scaled pokazywał błąd oczekujący na tablicę 2D, ale wejście to tablica 1D i zaleca się użycie reshape (-1,1). Każdy pomysł, jak to rozwiązać, ponieważ zmiana kształtu również nie działa.?
kod martwy
Możesz otrzymywać ostrzeżenia w zależności od wersji numpy & sklearn, z którą pracujesz, ale ogólnie powinno to działać np_scaled = min_max_scaler.fit_transform(df.score.astype(float).values.reshape(-1, 1))
Jaeyoung Chun
33

Możesz użyć applydo tego i jest trochę schludniej:

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

Działa również dobrze groupby, jeśli wybierzesz odpowiednie kolumny:

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
naught101
źródło
2

Nieznacznie zmodyfikowane z: Python Pandas Dataframe: Normalizacja danych między 0,01 a 0,99? ale z niektórych komentarzy wynika, że ​​jest to istotne (przepraszam, jeśli uważane za ponowne przesłanie ...)

Chciałem, aby dostosowana normalizacja w tym regularnym percentylu danych lub z-score nie była odpowiednia. Czasami wiedziałem, jakie są możliwe maksymalne i minimalne wartości populacji, i dlatego chciałem zdefiniować ją inaczej niż moja próbka, inny punkt środkowy lub cokolwiek! Może to być często przydatne do przeskalowania i normalizowania danych dla sieci neuronowych, w których możesz potrzebować wszystkich danych wejściowych od 0 do 1, ale niektóre dane mogą wymagać skalowania w bardziej dostosowany sposób ... ponieważ percentyle i standardowe wartości zakładają pokrycie próbek populacji, ale czasami wiemy, że to nieprawda. Było to również dla mnie bardzo przydatne podczas wizualizacji danych w heatmapach. Zbudowałem więc funkcję niestandardową (użyłem dodatkowych kroków w kodzie tutaj, aby uczynić ją jak najbardziej czytelną):

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

To zajmie serię pand, a nawet tylko listę i znormalizuje ją do określonych niskich, środkowych i wysokich punktów. istnieje również współczynnik kurczenia się! aby umożliwić Ci skalowanie danych z dala od punktów końcowych 0 i 1 (musiałem to zrobić łącząc mapy kolorów w matplotlib: Pojedynczy pcolormesh z więcej niż jedną mapą kolorów używającą Matplotlib ) Więc prawdopodobnie zobaczysz jak działa kod, ale zasadniczo mówię, że mają wartości [-5,1,10] w próbie, ale chcą znormalizować w oparciu o zakres od -7 do 7 (więc wszystko powyżej 7, nasze „10” jest traktowane jako 7) z punktem środkowym równym 2, ale zmniejsz ją, aby pasowała do mapy kolorów 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]

Może również wywrócić dane na drugą stronę ... może się to wydawać dziwne, ale uznałem to za przydatne do mapowania ciepła. Powiedzmy, że chcesz mieć ciemniejszy kolor dla wartości bliższych 0, a nie hi / low. Możesz utworzyć mapę cieplną w oparciu o znormalizowane dane, gdzie 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]

Zatem teraz „2”, które jest najbliżej środka, zdefiniowane jako „1”, jest wartością najwyższą.

W każdym razie pomyślałem, że moja aplikacja jest odpowiednia, jeśli chcesz przeskalować dane w inny sposób, który może mieć przydatne aplikacje.

Vlox
źródło
Wszystkie instrukcje if / else można zastąpić słownikiem z funkcjami . Wygląda więc trochę czysto.
Roald
to całkiem fajne, będę o tym pamiętać następnym razem, dzięki!
Vlox
0

Oto jak to robisz z podziałem na kolumny:

[df[col].update((df[col] - df[col].min()) / (df[col].max() - df[col].min())) for col in df.columns]
Czad
źródło