Normalizza colonne di frame di dati Panda


227

Ho un frame di dati in Panda in cui ogni colonna ha un intervallo di valori diverso. Per esempio:

df:

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

Hai idea di come posso normalizzare le colonne di questo frame di dati in cui ciascun valore è compreso tra 0 e 1?

L'output desiderato è:

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'è una funzione apply, ad esempio frame.apply (f, axis = 1) dove f è una funzione che fa qualcosa con una riga ...
tschm

1
La normalizzazione potrebbe non essere la formulazione più appropriata, poiché la documentazione di scikit-learn la definisce come "il processo di ridimensionamento dei singoli campioni in modo da avere una norma unitaria " (cioè riga per riga, se la ottengo correttamente).
Skippy le Grand Gourou

Non capisco, perché il ridimensionamento min_max è considerato normalizzazione! normal deve avere un significato nel senso della normale distribuzione con zero medio e varianza 1.
OverFlow Police

Se stai visitando questa domanda nel 2020 o successivamente, guarda la risposta di @Poudel, otterrai una risposta diversa sulla normalizzazione se usi panda contro sklearn.
Bhishan Poudel,

@Poudel è dovuto ddofall'argomento?
Fffrost

Risposte:


224

È possibile utilizzare il pacchetto sklearn e le utilità di preelaborazione associate per normalizzare i dati.

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)

Per ulteriori informazioni, consultare la documentazione di scikit-learn sui dati di preelaborazione: ridimensionare le funzionalità in un intervallo.


46
penso che questo eliminerà i nomi delle colonne, che potrebbe essere uno dei motivi per cui op sta usando i frame di dati in primo luogo.
pietz,

47
Ciò normalizzerà le righe e non le colonne, a meno che tu non le trasponga prima. Per fare ciò che la Q chiede:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index)
piani cottura

26
@pietz per mantenere i nomi delle colonne, vedi questo post . Sostanzialmente sostituisci l'ultima riga con,df=pandas.DataFrame(x_scaled, columns=df.columns)
ijoseph, il

5
@hobs Questo non è corretto. Il codice di Sandman normalizza per colonna e per colonna. Si ottiene il risultato sbagliato se si traspone.
petezurich,

8
@petezurich Sembra che Sandman o Praveen abbiano corretto il loro codice. Sfortunatamente, non è possibile correggere i commenti;)
Piani cottura

398

un modo semplice usando Panda : (qui voglio usare la normalizzazione media)

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

per utilizzare la normalizzazione min-max:

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

Modifica: per rispondere ad alcune preoccupazioni, è necessario dire che Pandas applica automaticamente la funzione colomn nel codice sopra.


16
Mi piace questa. è breve, espressivo e conserva le informazioni dell'intestazione. ma penso che sia necessario sottrarre anche il minimo nel denominatore.
pietz,

6
Non penso sia sbagliato. Funziona magnificamente per me - non credo che mean () e std () debbano restituire un frame di dati per farlo funzionare e il tuo messaggio di errore non implica che non essere un frame di dati sia un problema.
Strandtasche,

24
questa non è una normalizzazione saggia della colonna. questo sta normalizzando l'intera matrice nel suo insieme che fornirà risultati errati.
Nguai al

6
Ha funzionato magnificamente anche per me. @Nguaial potresti provarlo su una matrice intorpidita, nel qual caso il risultato sarebbe quello che hai detto. Ma per i frame di dati Pandas, le misure min, max, ... si applicano alla colonna per impostazione predefinita.
Ausiliario il

1
mi piace anche questo
Isaac Sim il

51

Sulla base di questo post: /stats/70801/how-to-normalize-data-to-0-1-range

Puoi fare quanto segue:

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

Non devi preoccuparti se i tuoi valori sono negativi o positivi. E i valori dovrebbero essere ben distribuiti tra 0 e 1.


8
Fai attenzione quando i valori min e max sono uguali, il tuo denominatore è 0 e otterrai un valore NaN.
Hrushikesh Dhumal,

36

Il tuo problema è in realtà una semplice trasformazione che agisce sulle colonne:

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

frame.apply(f, axis=0)

O ancora più conciso:

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

2
L' lambdauno è il migliore :-)
Abu Shoeb

4
questo non dovrebbe essere axis = 1 poiché la domanda è la normalizzazione saggia della colonna?
Nguai al

No, dai documenti : axis [...] 0 or 'index': apply function to each column. Il valore predefinito è in realtà axis=0quindi questo one-liner può essere scritto ancora più breve :-) Grazie @tschm.
jorijnsmit,

30

Se ti piace usare il pacchetto sklearn, puoi mantenere i nomi delle colonne e degli indici usando i panda in questo locmodo:

from sklearn.preprocessing import MinMaxScaler

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

27

Semplice è bello:

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

Grande e secondo me la migliore soluzione!
Maciej A. Bednarz,

6
Si noti che OP ha richiesto l'intervallo [0..1] e questa soluzione è scalabile nell'intervallo [-1..1]. Prova questo con l'array [-10, 10].
Alexander Sosnovshchenko,

3
@AlexanderSosnovshchenko non proprio. Basil Musa presume che la matrice del PO sia sempre non negativa, ecco perché ha dato questa soluzione. Se una colonna ha una voce negativa, questo codice NON si normalizza nell'intervallo [-1,1]. Provalo con l'array [-5, 10]. Il modo corretto di normalizzare a [0,1] con valori negativi è stato dato dalla risposta della Cinadf["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min())
facuq

semplice ED esplicito
joshi123

Forse ancora più semplice: df /= df.max()- supponendo che l'obiettivo sia normalizzare ogni singola colonna, individualmente.
n1k31t4

24

È possibile creare un elenco di colonne che si desidera normalizzare

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

Il tuo Dataframe Pandas ora è normalizzato solo nelle colonne che desideri


Tuttavia , se vuoi il contrario , seleziona un elenco di colonne che NON vuoi normalizzare, puoi semplicemente creare un elenco di tutte le colonne e rimuovere quelle non desiderate

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

Penso che un modo migliore per farlo in Panda sia giusto

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

Modifica Se nella cornice dei dati sono presenti numeri negativi, è necessario utilizzare invece

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

1
Nel caso in cui tutti i valori di una colonna siano zero, questo non funzionerà
ahajib,

dividere il valore corrente per il massimo non ti darà una corretta normalizzazione a meno che il minimo non sia 0.
pietz

Sono d'accordo, ma è quello che chiedeva l'OT (vedi il suo esempio)
Daniele,

11

La soluzione data da Sandman e Praveen è molto buona. L'unico problema è che se si hanno variabili categoriche in altre colonne del frame di dati, questo metodo richiederà alcune modifiche.

La mia soluzione a questo tipo di problema è la seguente:

 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
Questa risposta è utile perché la maggior parte degli esempi su Internet applica uno scaler a tutte le colonne, mentre in realtà risolve la situazione in cui uno scaler, ad esempio MinMaxScaler, non dovrebbe applicarsi a tutte le colonne.
demongolem,

10

Esempio di diverse standardizzazioni in Python.

Per riferimento guarda questo articolo di Wikipedia: https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation

Dati di esempio

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

Normalizzazione usando i panda (fornisce stime imparziali)

Quando si normalizza semplicemente sottraggiamo la media e dividiamo per deviazione standard.

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

Normalizzazione usando sklearn (Fornisce stime distorte, diverse dai panda)

Se fai la stessa cosa con sklearnte otterrai un DIVERSO output!

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

Le stime distorte di sklearn rendono l'apprendimento automatico meno potente?

NO.

La documentazione ufficiale di sklearn.preprocessing.scale afferma che l'utilizzo dello stimatore distorto è SENZA ALCUNA influenza sulle prestazioni degli algoritmi di apprendimento automatico e possiamo tranquillamente utilizzarli.

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.

Che dire di MinMax Scaling?

Non esiste alcun calcolo della deviazione standard nel ridimensionamento MinMax. Quindi il risultato è lo stesso in entrambi i panda e 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

Potresti voler normalizzare alcune colonne e altre invariate come alcune delle attività di regressione quali etichette di dati o colonne categoriali sono invariate Quindi ti suggerisco questo modo pitonico (È una combinazione di risposte @shg e @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

È solo matematica semplice. La risposta dovrebbe essere semplice come di seguito.

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)

Dal documento di Panda, la struttura DataFrame può applicare un'operazione (funzione) a se stesso.

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

Applica la funzione lungo l'asse di input di DataFrame. Gli oggetti passati alle funzioni sono oggetti della serie con indice dell'indice del DataFrame (asse = 0) o delle colonne (asse = 1). Il tipo restituito dipende dal fatto che gli aggregati di funzioni passati o dall'argomento di riduzione se DataFrame è vuoto.

È possibile applicare una funzione personalizzata per utilizzare DataFrame.


2
Sarebbe bene spiegare perché il tuo codice risolve il problema dei PO, quindi le persone possono adattare la strategia piuttosto che copiare semplicemente il tuo codice. Si prega di leggere Come posso scrivere una buona risposta?
Mr. T

2

La seguente funzione calcola il punteggio 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

Ecco come lo fai in base alla colonna usando la comprensione dell'elenco:

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

1

Puoi semplicemente usare la funzione pandas.DataFrame.transform 1 in questo modo:

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

Questa soluzione non funzionerà se tutti i valori sono negativi. Considera [-1, -2, -3]. Dividiamo per -1 e ora abbiamo [1,2,3].
Dave Liu,


0

Puoi farlo in una riga

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

prende la media per ciascuna colonna e la sottrae (media) da ogni riga (la media di una colonna particolare sottrae solo dalla sua riga) e si divide solo per la media. Infine, ciò che otteniamo è l'insieme di dati normalizzato.


0

Panda esegue normalmente la normalizzazione delle colonne. Prova il codice qui sotto.

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

I valori di uscita saranno compresi tra 0 e 1.

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.