Normalizar datos en pandas

131

Supongamos que tengo un marco de datos de pandas df:

Quiero calcular la columna sabia media de un marco de datos.

Esto es facil:

df.apply(average) 

entonces el rango sabio de columna max (col) - min (col). Esto es fácil nuevamente:

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

Ahora, para cada elemento, quiero restar la media de su columna y dividirla por el rango de su columna. No estoy seguro de cómo hacerlo

Cualquier ayuda / punteros son muy apreciados.

jason
fuente

Respuestas:

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
Wouter Overmeire
fuente
¿Hay alguna manera de hacer esto si desea normalizar un subconjunto? Diga esa fila Ay Bforme parte de un factor de agrupación más grande que desea normalizar por separado de Cy D.
Amyunimus
Seleccione el subconjunto y calcule como antes. Vea pandas.pydata.org/pandas-docs/stable/indexing.html sobre cómo indexar y seleccionar datos
Wouter Overmeire
17
Si necesita que sus valores sean> 0: df_norm = (df - df.min ()) / (df.max () - df.min ())
Dayvid Oliveira
1
debe ser df_norm = (df - df.min ()) / (df.max () - df.min ()) en lugar de df.mean () en los primeros paréntesis para obtener los valores entre 0 y 1
jnPy
2
Si su marco de datos tiene cadenas en algunas columnas, vea esta respuesta
netskink
73

Si no le importa importar la sklearnbiblioteca, le recomendaría el método que se habla en este 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
David S.
fuente
2
El enlace a la publicación del blog está muerto. ¿tienes uno que funcione?
marts
3
El método correspondiente para crear datos normalizados por unidad normal se llama StandardScaler.
abeboparebop
Encontré una solución similar en otro lugar. El problema era que en la parte np_scaled, mostraba un error al esperar una matriz 2D, pero la entrada es una matriz 1D y recomendó que utilizáramos la remodelación (-1,1). ¿Alguna idea de cómo resolver esto como remodelar tampoco está funcionando?
código muerto
Es posible que reciba advertencias dependiendo de la versión de numpy & sklearn con la que trabaje, pero en general, esto debería funcionar np_scaled = min_max_scaler.fit_transform(df.score.astype(float).values.reshape(-1, 1))
Jaeyoung Chun
33

Puedes usar applyesto, y es un poco más ordenado:

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

Además, funciona bien groupbysi selecciona las columnas relevantes:

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
nada101
fuente
2

Ligeramente modificado de: Python Pandas Dataframe: ¿Normalizar datos entre 0.01 y 0.99? pero de algunos de los comentarios pensé que era relevante (lo siento si se considera una nueva publicación ...)

Quería una normalización personalizada en ese percentil regular de referencia o la puntuación z no era adecuada. ¡A veces sabía cuáles eran los valores máximos y mínimos posibles de la población y, por lo tanto, quería definirlos aparte de mi muestra, o un punto medio diferente, o lo que sea! Esto a menudo puede ser útil para reescalar y normalizar datos para redes neuronales donde es posible que desee todas las entradas entre 0 y 1, pero es posible que algunos de sus datos necesiten escalarse de una manera más personalizada ... porque los percentiles y estándares asumen que su muestra cubre la población, pero a veces sabemos que esto no es cierto. También fue muy útil para mí al visualizar datos en mapas de calor. Así que construí una función personalizada (usé pasos adicionales en el código aquí para que sea lo más legible posible):

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

Esto incluirá una serie de pandas, o incluso solo una lista, y la normalizará a los puntos bajos, centrales y altos especificados. ¡También hay un factor de contracción! para permitirle reducir la escala de los datos de los puntos finales 0 y 1 (tuve que hacer esto al combinar mapas de color en matplotlib: pcolormesh único con más de un mapa de colores usando Matplotlib ) Entonces, probablemente pueda ver cómo funciona el código, pero básicamente diga que tener valores [-5,1,10] en una muestra, pero desea normalizar en función de un rango de -7 a 7 (por lo tanto, cualquier cosa por encima de 7, nuestro "10" se trata como un 7 efectivamente) con un punto medio de 2, pero encogerlo para que se ajuste a un mapa de color 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]

También puede convertir sus datos al revés ... esto puede parecer extraño, pero lo encontré útil para el mapeo de calor. Supongamos que desea un color más oscuro para valores más cercanos a 0 en lugar de alto / bajo. Podría realizar un mapa de calor basado en datos normalizados donde 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]

Así que ahora "2", que está más cerca del centro, definido como "1" es el valor más alto.

De todos modos, pensé que mi aplicación era relevante si estaba buscando reescalar datos de otras maneras que podrían tener aplicaciones útiles para usted.

Vlox
fuente
Puede reemplazar todas las declaraciones if / else con un diccionario con funciones . Parece un poco más limpio entonces.
Roald
eso está bastante bien, lo tendré en cuenta la próxima vez, ¡gracias!
Vlox
0

Así es como lo haces en columna:

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