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)

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.


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.


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.


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)

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

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()

¡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 ]

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)

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])

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

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()))

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())

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.


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

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]

1

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

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

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


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.


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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.