Normalizar columnas del marco de datos de pandas

333

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)
6
  • 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 16/10/2014 a las 22:30
  • 2
    La normalización puede no ser la redacción más adecuada, ya que la documentación de scikit-learn la define como "el proceso de escalar muestras individuales para tener una norma unitaria " (es decir, fila por fila, si la obtengo correctamente). Skippy le Grand Gourou 5/03/19 a las 16:58
  • 1
    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 21/04/19 a las 2:21
  • 4
    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. usermay14 29/01/20 a las 20:10
  • @Poudel, ¿esto se debe al ddofargumento? fffrost 4 abr.20 a las 20:26
346

Puede utilizar 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.

8
  • 64
    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 16 de enero de 2017 a las 21:02
  • 64
    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 20 de enero de 2017 a las 23:47
  • 32
    @pietz para mantener los nombres de las columnas, vea esta publicación . Básicamente reemplace la última línea con,df=pandas.DataFrame(x_scaled, columns=df.columns)ijoseph 26/06/17 a las 18:52
  • 5
    @hobs Esto no es correcto. El código de Sandman se normaliza por columnas y por columnas. Obtiene un resultado incorrecto si transpone. petezurich 1/04/18 a las 14:10
  • 9
    @petezurich Parece que Sandman o Praveen corrigieron su código. Desafortunadamente, no es posible corregir los comentarios;)hobs 3/04/18 a las 21:25
601

una forma fácil usando 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 columna en el código anterior.

7
  • 1
    ¿Se puede hacer de alguna manera con la función de ventana? Lo que quiero decir con eso es calcular max () y min () basados, por ejemplo, en las últimas 10 observaciones. krakowi 15 nov 2019 a las 10:12
  • si quieres ahorrar algo de columna - hacesnormalized_df['TARGET'] = df['TARGET']Roman Filippov 22 dic 2019 a las 9:53
  • Comparando esto con MinMaxScaler(), ¿cuál sería más rápido en un caso en el que las características sean mayores que 1000? ¿Y usa menos memoria? SajidSalim 24 abr.20 a las 17:04
  • 1
    esta es una buena solución, pero necesita muchos controles menos hermosos para evitar la división por cero erroresTeddy Ward 7 de mayo de 2020 a las 21:26
  • 1
    Hizo una nueva pregunta; Si alguien sabe la respuesta, por favor ilumíneme : stackoverflow.com/questions/61726904/…Psychotechnopath 12 de mayo de 2020 a las 11:02
64

Basado en esta publicación: https://stats.stackexchange.com/questions/70801/how-to-normalize-data-to-0-1-range

Puede 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 por si sus valores son negativos o positivos. Y los valores deben estar bien distribuidos entre 0 y 1.

3
  • 11
    Tenga cuidado cuando los valores mínimo y máximo son iguales, su denominador es 0 y obtendrá un valor de NaN. Hrushikesh Dhumal 1 feb 2019 a las 6:02
  • 1
    @HrushikeshDhumal, No es necesario normalizar entonces, ya que todos los valores serían iguales. CITIZENDOT 26 oct.20 a las 9:13
  • @AppajiChintimi, esta solución se aplica a datos completos, si no ha realizado una verificación de cordura, podría tener problemas. Hrushikesh Dhumal 29/10/20 a las 23:54
53

Su problema es en realidad una transformación simple 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)
5
  • 4
    El lambdauno es el mejor :-)Abu Shoeb 8 dic 2018 a las 23:49
  • 6
    ¿No se supone que esto es axis = 1 ya que la pregunta es la normalización de columnas? Nguai al 26/04/19 a las 23:27
  • 3
    No, a partir de los documentos : axis [...] 0 or 'index': apply function to each column. En realidad, el valor predeterminado es axis=0que esta frase se pueda escribir aún más corta :-) Gracias @tschm. jorijnsmit 11/04/20 a las 15:01
  • Esto solo es correcto si el mínimo es 0, que no es algo que realmente debas asumirQFSW 21/11/20 a las 17:19
  • Mi ejemplo estaba destinado a ilustrar cómo aplicar funciones en columnas de marcos de datos. Obviamente, como siempre, debe prestar atención a los casos de esquina, por ejemplo, aquí el máximo podría ser cero y resultar en un problema. No estoy seguro de entender a @QFSW. tschm 22/11/20 a las 18:28
39

Si le gusta usar el paquete sklearn, puede mantener los nombres de las columnas y los índices usando pandas locasí:

from sklearn.preprocessing import MinMaxScaler

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

Ejemplo detallado de métodos de normalización

  • Normalización de pandas (sin sesgo)
  • Normalización de Sklearn (sesgada)
  • ¿Afecta el aprendizaje automático sesgado frente a imparcial?
  • Escala mix-max

Referencias: Wikipedia: Estimación imparcial de la desviación estándar

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 sin sesgo)

Al normalizar, simplemente restamos la media y la dividimos por la 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 pandas)

¡Si haces lo mismo con sklearnobtendrás resultados DIFERENTES!

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 de estimador sesgado NO ES PROBABLE que afecte el rendimiento de los algoritmos de aprendizaje automático y podemos usarlos de manera segura.

De documentación oficial:

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 MinMax Scaling?

No hay cálculo de desviación estándar en la escala MinMax. Entonces, 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
32

Tenga cuidado con esta respuesta , ya que SOLO funciona con datos que oscilan entre [0, n]. Esto no funciona para ningún rango de datos.


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()
5
  • 7
    Tenga en cuenta que OP solicitó el rango [0..1] y esta solución se escala al rango [-1..1]. Intente esto con la matriz [-10, 10]. Alexander Sosnovshchenko 28/04/18 a las 9:20
  • 3
    @AlexanderSosnovshchenko no realmente. Basil Musa asume que la matriz del OP siempre es no negativa, por eso ha dado esta solución. Si alguna columna tiene una entrada negativa, 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())Pepe Mandioca 9/11/18 a las 13:24
  • Quizás incluso más simple: df /= df.max()- asumiendo que el objetivo es normalizar todas y cada una de las columnas, individualmente. n1k31t4 31 de mayo de 2020 a las 22:26
  • 1
    Esta respuesta es incorrecta. La suposición no negativa no se puede hacer aquí, ya que ni el OP ni los futuros lectores lo declararon. Además, incluso lo estrictamente positivo no funciona aquí: [1, 10]se normalizará en [0.1, 1]lugar de [0,1]. Gulzar 12 de mayo a las 11:53
  • 1
    Gracias @Gulzar, soy el autor de esta respuesta y TBH, me sorprendió que se votara a favor 29 veces. Basil Musa 18 de mayo a las 15:12
31

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

Creo que una mejor manera de hacer eso en pandas es simplemente

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

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

df = df/df.loc[df.abs().idxmax()].astype(np.float64)
3
  • 1
    En caso de que todos los valores de una columna sean cero, esto no funcionaráahajib 2 de septiembre de 2015 a las 23:23
  • dividir el valor actual por el máximo no le dará una normalización correcta a menos que el mínimo sea 0.pietz 16 de enero de 2017 a las 21:16
  • Estoy de acuerdo, pero eso es lo que pedía el AT (ver su ejemplo)Daniele 21/02/2017 a las 14:33
11

La solución dada por Sandman y Praveen está muy bien. El único problema con eso 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])
1
  • 2
    Esta respuesta es útil porque la mayoría de los ejemplos en Internet aplican un escalador a todas las columnas, mientras que esto en realidad aborda la situación en la que un escalador, digamos MinMaxScaler, no debería aplicarse a todas las columnas. demongolem 10 de septiembre de 2018 a las 17:07
9

Es posible que desee que algunas de las columnas se normalicen y las otras no se modifiquen, como algunas de las tareas de regresión, cuyas etiquetas de datos o columnas categóricas no se modifican. Así que le sugiero esta forma pitónica (es una combinación de las 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()))
8
df_normalized = df / df.max(axis=0)
7

Es solo una simple matemática. La respuesta debería ser tan simple como la siguiente.

normed_df = (df - df.min()) / (df.max() - df.min())
3

Así es como se hace en columnas usando la comprensión de listas:

[df[col].update((df[col] - df[col].min()) / (df[col].max() - df[col].min())) for col in df.columns]
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 de DataFrame puede aplicar una operación (función) a sí misma.

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

Applies function along input axis of DataFrame. Objects passed to functions are Series objects having index either the DataFrame’s index (axis=0) or the columns (axis=1). Return type depends on whether passed function aggregates, or the reduce argument if the DataFrame is empty.

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

1
  • 3
    Sería bueno explicar por qué su código resuelve el problema de los OP, para que la gente pueda adaptar la estrategia en lugar de simplemente copiar su código. Por favor lea ¿Cómo escribo una buena respuesta? Mr. T 13/04/18 a las 9:53
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

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

df.transform(lambda x: x/x.max())
2
  • 2
    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 5/12/19 a las 19:42
  • Para manejar correctamente los números negativos: df.transform (lambda x: x / abs (x) .max ())nvd 25 mar a las 21:26
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 se resta de su fila solamente) y divide por la media solamente. Finalmente, lo que obtenemos es el conjunto de datos normalizados.

0

Pandas realiza la normalización de columnas de forma predeterminada. Pruebe 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.

0

Oye, usa la función de aplicación con lambda que acelera el proceso:

def normalize(df_col):

  # Condition to exclude 'ID' and 'Class' feature
  if (str(df_col.name) != str('ID') and str(df_col.name)!=str('Class')):
        max_value = df_col.max()
        min_value = df_col.min()

        #It avoids NaN and return 0 instead
        if max_value == min_value:
          return 0

        sub_value = max_value - min_value
        return np.divide(np.subtract(df_col,min_value),sub_value)
  else:
        return df_col

 df_normalize = df.apply(lambda x :normalize(x))
-5

Si sus datos están sesgados positivamente, la mejor manera de normalizarlos es usar la transformación de registro:

df = np.log10(df)