Normalizza i dati in panda


131

Supponiamo che io abbia un frame di dati Panda df:

Voglio calcolare la media saggia della colonna di un frame di dati.

Questo è facile:

df.apply(average) 

quindi l'intervallo saggio della colonna max (col) - min (col). Questo è di nuovo facile:

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

Ora per ogni elemento voglio sottrarre la media della sua colonna e dividerla per l'intervallo della sua colonna. Non sono sicuro di come farlo

Qualsiasi aiuto / puntatore è molto apprezzato.

Risposte:


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

C'è un modo per farlo se si desidera normalizzare un sottoinsieme? Pronuncia quella riga Ae Bfa parte di un fattore di raggruppamento più grande che desideri normalizzare separatamente da Ce D.
Amyunimus,

Seleziona il sottoinsieme e calcola come prima. Vedi pandas.pydata.org/pandas-docs/stable/indexing.html su come indicizzare e selezionare i dati
Wouter Overmeire,

17
Se hai bisogno che i tuoi valori siano> 0: df_norm = (df - df.min ()) / (df.max () - df.min ())
Dayvid Oliveira

1
dovrebbe essere df_norm = (df - df.min ()) / (df.max () - df.min ()) anziché df.mean () tra le prime parentesi per ottenere i valori compresi tra 0 e 1
jnPy

2
Se il tuo frame di dati ha stringhe in alcune colonne, vedi questa risposta
netskink

73

Se non ti dispiace importare la sklearnlibreria, consiglierei il metodo descritto in questo blog.

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
il link al post del blog è morto. ne hai uno funzionante?
marzo

3
Il metodo corrispondente per creare dati normalizzati unitari è chiamato StandardScaler.
abeboparebop

Ho trovato una soluzione simile in un altro posto. Il problema era che nella parte np_scaled mostrava un errore in attesa dell'array 2D ma l'input è 1D array e ci ha raccomandato di usare reshape (-1,1). Qualche idea su come risolverlo come reshape non funziona.
deadcode

Potresti ricevere avvisi a seconda della versione di numpy e sklearn con cui lavori, ma in generale dovrebbe funzionare np_scaled = min_max_scaler.fit_transform(df.score.astype(float).values.reshape(-1, 1))
Jaeyoung Chun,

33

Puoi usarlo applyper questo, ed è un po 'più ordinato:

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

Inoltre, funziona bene con groupby, se selezioni le colonne pertinenti:

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

Leggermente modificato da: Python Pandas Dataframe: normalizzare i dati tra 0,01 e 0,99? ma da alcuni dei commenti ha ritenuto che fosse pertinente (scusate se considerato un repost però ...)

Volevo che la normalizzazione personalizzata in quel normale percentile di dato o punteggio z non fosse adeguata. A volte sapevo quali fossero i massimi e i minimi possibili della popolazione e quindi volevo definirlo diverso dal mio campione, da un punto medio diverso o da qualunque altra cosa! Questo può spesso essere utile per riscalare e normalizzare i dati per le reti neurali in cui potresti desiderare tutti gli input tra 0 e 1, ma potrebbe essere necessario ridimensionare alcuni dei tuoi dati in un modo più personalizzato ... perché percentili e stdev presuppongono che le copertine dei tuoi campioni la popolazione, ma a volte sappiamo che questo non è vero. È stato anche molto utile per me durante la visualizzazione dei dati nelle mappe di calore. Quindi ho creato una funzione personalizzata (ho usato passaggi aggiuntivi nel codice qui per renderlo il più leggibile possibile):

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

Questo prenderà una serie di panda, o anche solo un elenco e lo normalizzerà ai punti bassi, centrali e alti specificati. inoltre c'è un fattore di restringimento! per permetterti di ridimensionare i dati lontano dagli endpoint 0 e 1 (ho dovuto farlo quando ho combinato i colormap in matplotlib: Single pcolormesh con più di un colormap usando Matplotlib ) Quindi puoi probabilmente vedere come funziona il codice, ma fondamentalmente dire che hanno valori [-5,1,10] in un campione, ma vogliono normalizzarsi in base a un intervallo da -7 a 7 (quindi qualsiasi cosa al di sopra di 7, il nostro "10" viene trattato come un 7 in modo efficace) con un punto medio di 2, ma riducilo per adattarlo a una mappa colori 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]

Può anche capovolgere i tuoi dati ... questo può sembrare strano, ma l'ho trovato utile per la produzione di calore. Supponi di voler un colore più scuro per valori più vicini a 0 anziché hi / low. È possibile creare una mappa di calore basata su dati normalizzati dove 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]

Quindi ora "2" che è il più vicino al centro, definito come "1" è il valore più alto.

Comunque, ho pensato che la mia applicazione fosse pertinente se stai cercando di ridimensionare i dati in altri modi che potrebbero avere applicazioni utili per te.


È possibile sostituire tutte le istruzioni if ​​/ else con un dizionario con funzioni . Sembra un po 'più pulito allora.
Roald,

è abbastanza carino, lo terrò a mente la prossima volta, grazie!
Vlox,

0

Ecco come lo fai in termini di colonne:

[df[col].update((df[col] - df[col].min()) / (df[col].max() - df[col].min())) for col in df.columns]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.