2012-09-21 26 views
78

Supongamos que tengo una trama de datos pandas df:datos Normalizar en pandas

quiero para calcular la columna sabia media de una trama de datos.

Esto es fácil:

df.apply(average) 

después la columna sabia gama max (col) - min (col). Esto es fácil de nuevo:

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

Ahora, para cada elemento, quiero restar el promedio de su columna y dividir por el rango de su columna. No estoy seguro de cómo hacer eso

Cualquier ayuda/sugerencias son muy apreciadas.

Respuesta

143
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 
+14

Era tan obvio que mi mente no podía aceptarlo :) – jason

+0

¿Hay alguna manera de hacer esto si quiere normalizar un subconjunto? Diga que la fila 'A' y' B' son parte de un factor de agrupamiento mayor que desea normalizar por separado de 'C' y' D'. – Amyunimus

+0

Seleccione el subconjunto y calcule como antes. Consulte http://pandas.pydata.org/pandas-docs/stable/indexing.html sobre cómo indexar y seleccionar datos –

23

Puede utilizar apply para esto, y que 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 muy bien con groupby, si 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 
48

Si no te importa importar la biblioteca sklearn, recomendaría el método que se habla en el blog this.

import pandas as pd 
from sklearn import preprocessing 

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

min_max_scaler = preprocessing.MinMaxScaler() 
np_scaled = min_max_scaler.fit_transform(df) 
df_normalized = pd.DataFrame(np_scaled) 
df_normalized 
+2

el enlace a la entrada del blog está muerto. ¿tienes uno que funcione? – marts

+0

@marts hecho ...! – astrojuanlu

+3

El método correspondiente para crear datos normalizados unitarios se llama StandardScaler. – abeboparebop

2

ligeramente modificada a partir de: Python Pandas Dataframe: Normalize data between 0.01 and 0.99? pero a partir de algunos de los comentarios que pensaron que era relevante (lo siento si se considera un repost aunque ...)

que quería personalizar la normalización en ese percentil regular de referencia o Z- puntaje no fue adecuado. A veces sabía cuál era el máximo y el mínimo factibles de la población, y por lo tanto quería definirla 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 puede desear que todas las entradas estén entre 0 y 1, pero algunos de sus datos pueden necesitar escalarse de una manera más personalizada ... porque percentiles y stdevs 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 heatmaps. Así que he construido una función personalizada (usado pasos adicionales en el código 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 tomará en una serie pandas, o incluso sólo una lista y normalizarla a su baja especificada , centro y puntos altos. ¡también hay un factor de reducción! para permitirle escalar los datos lejos de los puntos finales 0 y 1 (tuve que hacer esto al combinar mapas de color en matplotlib: Single pcolormesh with more than one colormap using Matplotlib). Así que es probable que vea cómo funciona el código, pero básicamente dice que tiene valores [-5,1, 10] en una muestra, pero quiere normalizar en función de un rango de -7 a 7 (por lo que cualquier valor superior a 7, nuestro "10" se trata como 7 de manera efectiva) con un punto medio de 2, pero reduzca para ajustarse a 256 RGB mapa de colores:

#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 de adentro hacia afuera ... esto puede parecer extraño, pero me pareció útil para heatmapping. Digamos que quiere un color más oscuro para valores más cercanos a 0 en lugar de alto/bajo. Se podría Zona de juego 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 es la más cercana al centro, que se define como "1" es el valor más alto.

De todos modos, pensé que mi aplicación era relevante si está buscando cambiar la escala de datos de otras formas que podrían tener aplicaciones útiles para usted.

+0

Puede reemplazar todas las instrucciones if/else con un [diccionario con funciones] (https://stackoverflow.com/questions/60208/replacements-for-switch-statement-in-python). Parece un poco más limpio entonces. – Roald

+0

que es bastante limpio, lo tendré en cuenta la próxima vez, ¡gracias! – Vlox