Normalizar columnas de marco de datos de pandas

227

Tengo un marco de datos en pandas donde cada columna tiene un rango de valores diferente. Por ejemplo:

df:

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

¿Alguna idea de cómo puedo normalizar las columnas de este marco de datos donde cada valor está entre 0 y 1?

Mi salida deseada es:

A     B    C
1     1    1
0.765 0.5  0.7
0.8   0.7  0.18(which is 0.09/0.5)
ahajib
fuente
1
hay una función de aplicación, por ejemplo, frame.apply (f, axis = 1) donde f es una función que hace algo con una fila ...
tschm
1
Es posible que la normalización no sea la redacción más adecuada, ya que la documentación de scikit-learn lo define como "el proceso de escalar muestras individuales para tener la norma de la unidad " (es decir, fila por fila, si lo entiendo correctamente).
Skippy le Grand Gourou
No lo entiendo, ¿por qué la escala min_max se considera normalización? normal tiene que tener significado en el sentido de distribución normal con media cero y varianza 1.
OverFlow Police
Si está visitando esta pregunta en 2020 o más tarde, mire la respuesta de @Poudel, obtendrá una respuesta diferente de normalización si usa pandas vs sklearn.
Bhishan Poudel
@Poudel, ¿se debe al ddofargumento?
fffrost

Respuestas:

224

Puede usar el paquete sklearn y sus utilidades de preprocesamiento asociadas para normalizar los datos.

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)

Para obtener más información vistazo a la scikit-learn documentación de pre-procesamiento de datos: escala cuenta con una gama.

Sandman
fuente
46
Creo que esto eliminará los nombres de las columnas, lo que podría ser una de las razones por las que op está usando marcos de datos en primer lugar.
pietz
47
Esto normalizará las filas y no las columnas, a menos que lo transponga primero. Para hacer lo que pide la Q:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index)
hobs
26
@pietz para mantener los nombres de las columnas, mira esta publicación . Básicamente reemplace la última línea con,df=pandas.DataFrame(x_scaled, columns=df.columns)
ijoseph
55
@hobs Esto no es correcto. El código de Sandman normaliza las columnas y las columnas. Obtiene el resultado incorrecto si transpone.
petezurich 01 de
8
@petezurich Parece que Sandman o Praveen corrigieron su código. Desafortunadamente, no es posible corregir los comentarios;)
hobs
398

Una manera fácil mediante el uso de Pandas : (aquí quiero usar la normalización media)

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

para usar la normalización min-max:

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

Editar: para abordar algunas inquietudes, es necesario decir que Pandas aplica automáticamente la función de columnas en el código anterior.

Cina
fuente
16
me gusta este. es breve, es expresivo y conserva la información del encabezado. pero creo que también debes restar el mínimo en el denominador.
pietz
66
No creo que esté mal. Funciona muy bien para mí: no creo que mean () y std () necesiten devolver un marco de datos para que esto funcione y su mensaje de error no implica que no sean un marco de datos es un problema.
Strandtasche
24
esto no es una columna de normalización sabia. Esto es normalizar toda la matriz como un todo que proporcionará resultados incorrectos.
Nguai al
66
También me funcionó muy bien. @Nguaial, podría estar intentando esto en una matriz numpy, en cuyo caso el resultado sería lo que dijo. Pero para los marcos de datos de Pandas, las medidas min, max, ... se aplican en forma de columna por defecto.
Auxiliar el
1
este también me gusta
Isaac Sim
51

Basado en esta publicación: /stats/70801/how-to-normalize-data-to-0-1-range

Puedes hacer lo siguiente:

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

No necesita preocuparse si sus valores son negativos o positivos. Y los valores deben estar bien distribuidos entre 0 y 1.

Michael Aquilina
fuente
8
Tenga cuidado cuando los valores mínimo y máximo son iguales, su denominador es 0 y obtendrá un valor NaN.
Hrushikesh Dhumal 01 de
36

Su problema es en realidad una simple transformación que actúa sobre las columnas:

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

frame.apply(f, axis=0)

O incluso más conciso:

   frame.apply(lambda x: x/x.max(), axis=0)
tschm
fuente
2
El lambdamejor :-)
Abu Shoeb
44
¿No se supone que esto es axis = 1 ya que la pregunta es la normalización de columnas?
Nguai al
No, a partir de los documentos : axis [...] 0 or 'index': apply function to each column. El valor predeterminado es en realidad, axis=0por lo que esta línea única se puede escribir aún más corta :-) Gracias @tschm.
jorijnsmit
30

Si le gusta usar el paquete sklearn, puede mantener la columna y los nombres de índice usando pandas locasí:

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler() 
scaled_values = scaler.fit_transform(df) 
df.loc[:,:] = scaled_values
j triste
fuente
27

Lo simple es hermoso:

df["A"] = df["A"] / df["A"].max()
df["B"] = df["B"] / df["B"].max()
df["C"] = df["C"] / df["C"].max()
Albahaca Musa
fuente
¡Genial y en mi opinión la mejor solución!
Maciej A. Bednarz
66
Tenga en cuenta que OP solicitó el rango [0..1] y esta solución escala al rango [-1..1]. Intente esto con la matriz [-10, 10].
Alexander Sosnovshchenko
3
@AlexanderSosnovshchenko no realmente. Basil Musa está asumiendo que la matriz del OP no siempre es negativa, por eso ha dado esta solución. Si alguna columna tiene una entrada negativa, entonces este código NO se normaliza al rango [-1,1]. Pruébelo con la matriz [-5, 10]. La forma correcta de normalizar a [0,1] con valores negativos fue dada por la respuesta de Cinadf["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min())
facuq
simple Y explícito
joshi123
Quizás aún más simple: df /= df.max()- suponiendo que el objetivo es normalizar todas y cada una de las columnas, individualmente.
n1k31t4
24

Puede crear una lista de columnas que desea normalizar.

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

Su marco de datos de Pandas ahora está normalizado solo en las columnas que desea


Sin embargo , si desea lo contrario , seleccione una lista de columnas que NO desea normalizar, simplemente puede crear una lista de todas las columnas y eliminar las que no desee.

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 ]
raullalves
fuente
11

Creo que una mejor manera de hacerlo en pandas es simplemente

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

Editar Si en su marco de datos hay números negativos, debe usarlos en su lugar

df = df/df.loc[df.abs().idxmax()].astype(np.float64)
Daniele
fuente
1
En caso de que todos los valores de una columna sean cero, esto no funcionará
ahajib
dividir el valor actual por el máximo no le dará una normalización correcta a menos que el mínimo sea 0.
pietz
Estoy de acuerdo, pero eso es lo que el OT estaba pidiendo (ver su ejemplo)
Daniele
11

La solución dada por Sandman y Praveen está muy bien. El único problema con eso es que si tiene variables categóricas en otras columnas de su marco de datos, este método necesitará algunos ajustes.

Mi solución a este tipo de problema es la siguiente:

 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])
cibermatemáticas
fuente
2
Esta respuesta es útil porque la mayoría de los ejemplos en Internet aplican un escalador a todas las columnas, mientras que esto realmente aborda la situación en la que un escalador, digamos MinMaxScaler, no debería aplicarse a todas las columnas.
demongolem
10

Ejemplo de diferentes estandarizaciones en python.

Para una referencia, mire este artículo de Wikipedia: https://en.wikipedia.org/wiki/Unlimited_estimation_of_standard_deviation

Datos de ejemplo

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

Normalización usando pandas (Da estimaciones imparciales)

Al normalizar, simplemente restamos la media y la dividimos por desviación estándar.

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

Normalización usando sklearn (Da estimaciones sesgadas, diferentes de los pandas)

Si hace lo mismo con sklearnusted, ¡obtendrá DIFERENTES resultados!

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

¿Las estimaciones sesgadas de sklearn hacen que el aprendizaje automático sea menos potente?

NO.

La documentación oficial de sklearn.preprocessing.scale establece que el uso del estimador sesgado NO PROBABLEMENTE afecta el rendimiento de los algoritmos de aprendizaje automático y podemos usarlos de manera segura.

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.

¿Qué pasa con la escala MinMax?

No hay cálculo de desviación estándar en la escala MinMax. Por lo tanto, el resultado es el mismo tanto en pandas como en 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
Bhishan Poudel
fuente
6

Es posible que desee que algunas columnas se normalicen y las otras no cambien, como algunas de las tareas de regresión cuyas etiquetas de datos o columnas categóricas no cambian, así que le sugiero esta forma pitónica (es una combinación de respuestas @shg y @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()))
Masoud Masoumi Moghadam
fuente
5

Es solo matemática simple. La respuesta debería ser tan simple como a continuación.

normed_df = (df - df.min()) / (df.max() - df.min())
Yuan
fuente
2
def normalize(x):
    try:
        x = x/np.linalg.norm(x,ord=1)
        return x
    except :
        raise
data = pd.DataFrame.apply(data,normalize)

Desde el documento de pandas, la estructura DataFrame puede aplicar una operación (función) a sí misma.

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

Aplica la función a lo largo del eje de entrada de DataFrame. Los objetos pasados ​​a las funciones son objetos de la Serie que tienen índice, ya sea el índice del Marco de datos (eje = 0) o las columnas (eje = 1). El tipo de retorno depende de si los agregados de funciones pasados ​​o el argumento de reducción si el DataFrame está vacío.

Puede aplicar una función personalizada para operar el DataFrame.

shg
fuente
2
Sería bueno explicar por qué su código resuelve el problema de los OP, para que las personas puedan adaptar la estrategia en lugar de simplemente copiar su código. Por favor lea ¿Cómo escribo una buena respuesta?
Sr. T
2

La siguiente función calcula la puntuación 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
gogasca
fuente
2

Así es como lo hace en columnas usando la comprensión de la lista:

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

Simplemente puede usar la función pandas.DataFrame.transform 1 de esta manera:

df.transform(lambda x: x/x.max())
antonjs
fuente
Esta solución no funcionará si todos los valores son negativos. Considere [-1, -2, -3]. Dividimos por -1, y ahora tenemos [1,2,3].
Dave Liu
1
df_normalized = df / df.max(axis=0)
Davoud Taghawi-Nejad
fuente
0

Puedes hacer esto en una línea

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

toma la media para cada una de las columnas y luego la resta (media) de cada fila (la media de una columna en particular resta solo de su fila) y divide solo por la media. Finalmente, lo que obtenemos es el conjunto de datos normalizado.

Rishi Bansal
fuente
0

Pandas realiza la normalización de columnas en forma predeterminada. Prueba el siguiente código.

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

Los valores de salida estarán en el rango de 0 y 1.

faiz
fuente