Normaliser les colonnes du bloc de données pandas

227

J'ai une trame de données dans les pandas où chaque colonne a une plage de valeurs différente. Par exemple:

df:

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

Une idée comment je peux normaliser les colonnes de cette trame de données où chaque valeur est comprise entre 0 et 1?

Ma sortie souhaitée est:

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
la source
1
il y a une fonction apply, par exemple frame.apply (f, axis = 1) où f est une fonction qui fait quelque chose avec une ligne ...
tschm
1
La normalisation n'est peut-être pas la formulation la plus appropriée, car la documentation de scikit-learn la définit comme "le processus de mise à l'échelle d'échantillons individuels pour avoir une norme d'unité " (c'est-à-dire ligne par ligne, si je comprends bien).
Skippy le Grand Gourou
Je ne comprends pas, pourquoi la mise à l'échelle min_max est considérée comme une normalisation! normal doit avoir un sens dans le sens d'une distribution normale avec un zéro moyen et une variance 1.
OverFlow Police
Si vous visitez cette question en 2020 ou plus tard, regardez la réponse de @Poudel, vous obtenez une réponse différente de normalisation si vous utilisez pandas vs sklearn.
Bhishan Poudel
@Poudel est-ce dû à l' ddofargument?
fffrost

Réponses:

224

Vous pouvez utiliser le package sklearn et ses utilitaires de prétraitement associés pour normaliser les données.

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)

Pour plus d'informations, consultez la documentation de scikit-learn sur le prétraitement des données: mise à l'échelle des fonctionnalités sur une plage.

Marchand de sable
la source
46
Je pense que cela supprimera les noms des colonnes, ce qui pourrait être l'une des raisons pour lesquelles op utilise des cadres de données en premier lieu.
pietz
47
Cela normalisera les lignes et non les colonnes, sauf si vous les transposez d'abord. Pour faire ce que le Q demande:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index)
plaques de cuisson
26
@pietz pour conserver les noms des colonnes, consultez cet article . Remplacer essentiellement la dernière ligne par,df=pandas.DataFrame(x_scaled, columns=df.columns)
ijoseph
5
@hobs Ce n'est pas correct. Le code de Sandman normalise colonne par colonne et par colonne. Vous obtenez le mauvais résultat si vous transposez.
petezurich
8
@petezurich Il semble que Sandman ou Praveen aient corrigé leur code. Malheureusement, il n'est pas possible de corriger les commentaires;)
plaques de cuisson
399

un moyen simple en utilisant Pandas : (ici, je veux utiliser la normalisation moyenne)

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

pour utiliser la normalisation min-max:

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

Modifier: Pour répondre à certaines préoccupations, il faut dire que Pandas applique automatiquement la fonction par colonne dans le code ci-dessus.

Cina
la source
16
J'aime celui la. c'est court, c'est expressif et ça préserve les informations d'en-tête. mais je pense que vous devez également soustraire le min dans le dénominateur.
pietz
6
Je ne pense pas que ce soit mal. Fonctionne très bien pour moi - je ne pense pas que mean () et std () aient besoin de renvoyer une trame de données pour que cela fonctionne et votre message d'erreur n'implique pas qu'ils ne sont pas une trame de données est un problème.
Strandtasche
24
ce n'est pas une normalisation par colonne. cela normalise la matrice entière dans son ensemble, ce qui donnera de mauvais résultats.
Nguai al
6
A également fonctionné magnifiquement pour moi. @Nguaial vous pourriez essayer ceci sur une matrice numpy auquel cas le résultat serait ce que vous avez dit. Mais pour les cadres de données Pandas, les mesures min, max, ... s'appliquent par défaut aux colonnes.
Auxiliary
1
j'aime aussi celui-ci
Isaac Sim
51

Basé sur ce post: /stats/70801/how-to-normalize-data-to-0-1-range

Vous pouvez effectuer les opérations suivantes:

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

Vous n'avez pas besoin de vous inquiéter de savoir si vos valeurs sont négatives ou positives. Et les valeurs doivent être bien réparties entre 0 et 1.

Michael Aquilina
la source
8
Soyez prudent lorsque les valeurs min et max sont identiques, votre dénominateur est 0 et vous obtiendrez une valeur NaN.
Hrushikesh Dhumal
36

Votre problème est en fait une simple transformation agissant sur les colonnes:

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

frame.apply(f, axis=0)

Ou encore plus laconique:

   frame.apply(lambda x: x/x.max(), axis=0)
tschm
la source
2
L' lambdaun est le meilleur :-)
Abu Shoeb
4
n'est-ce pas censé être axe = 1 puisque la question est la normalisation par colonne?
Nguai al
Non, des docs : axis [...] 0 or 'index': apply function to each column. La valeur par défaut est en fait axis=0donc ce one-liner peut être écrit encore plus court :-) Merci @tschm.
jorijnsmit
30

Si vous aimez utiliser le package sklearn, vous pouvez conserver les noms de colonne et d'index en utilisant des pandas loccomme ceci:

from sklearn.preprocessing import MinMaxScaler

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

Le simple est beau:

df["A"] = df["A"] / df["A"].max()
df["B"] = df["B"] / df["B"].max()
df["C"] = df["C"] / df["C"].max()
Basil Musa
la source
Super et à mon avis la meilleure solution!
Maciej A. Bednarz
6
Notez que OP a demandé une plage de [0..1] et cette solution évolue vers la plage de [-1..1]. Essayez ceci avec le tableau [-10, 10].
Alexander Sosnovshchenko
3
@AlexanderSosnovshchenko pas vraiment. Basil Musa suppose que la matrice du PO est toujours non négative, c'est pourquoi il a donné cette solution. Si une colonne a une entrée négative, ce code ne se normalise PAS dans la plage [-1,1]. Essayez-le avec le tableau [-5, 10]. La bonne façon de normaliser à [0,1] avec des valeurs négatives a été donnée par la réponse de Cinadf["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min())
facuq
simple ET explicite
joshi123
Peut-être encore plus simple: df /= df.max()- en supposant que l'objectif est de normaliser chaque colonne, individuellement.
n1k31t4
24

Vous pouvez créer une liste de colonnes que vous souhaitez normaliser

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

Votre cadre de données Pandas est désormais normalisé uniquement dans les colonnes que vous souhaitez


Cependant , si vous voulez le contraire , sélectionnez une liste de colonnes que vous ne voulez PAS normaliser, vous pouvez simplement créer une liste de toutes les colonnes et supprimer celles non souhaitées

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
la source
11

Je pense qu'une meilleure façon de le faire chez les pandas est juste

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

Modifier Si, dans votre bloc de données, des nombres négatifs sont présents, vous devez utiliser à la place

df = df/df.loc[df.abs().idxmax()].astype(np.float64)
Daniele
la source
1
Si toutes les valeurs d'une colonne sont nulles, cela ne fonctionnera pas
ahajib
diviser la valeur actuelle par le max ne vous donnera pas une normalisation correcte à moins que le min ne soit 0.
pietz
Je suis d'accord, mais c'est ce que l'OT demandait (voir son exemple)
Daniele
11

La solution donnée par Sandman et Praveen est très bien. Le seul problème avec cela, si vous avez des variables catégorielles dans d'autres colonnes de votre bloc de données, cette méthode nécessitera quelques ajustements.

Ma solution à ce type de problème est la suivante:

 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])
cyber-math
la source
2
Cette réponse est utile car la plupart des exemples sur Internet appliquent un scaler à toutes les colonnes, alors que cela résout le cas où un scaler, par exemple le MinMaxScaler, ne devrait pas s'appliquer à toutes les colonnes.
demongolem
10

Exemple de différentes standardisations en python.

Pour référence, consultez cet article wikipedia: https://en.wikipedia.org/wiki/Unbias_estimation_of_standard_deviation

Exemples de données

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

Normalisation à l'aide de pandas (donne des estimations impartiales)

Lors de la normalisation, nous soustrayons simplement la moyenne et divisons par l'écart-type.

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

Normalisation à l'aide de sklearn (donne des estimations biaisées, différentes des pandas)

Si vous faites la même chose avec sklearnvous obtiendrez une sortie DIFFÉRENTE!

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

Est-ce que les estimations biaisées de sklearn rendent l'apprentissage machine moins puissant?

NON.

La documentation officielle de sklearn.preprocessing.scale indique que l'utilisation d'un estimateur biaisé N'EST PAS PROBABLE pour affecter les performances des algorithmes d'apprentissage automatique et que nous pouvons les utiliser en toute sécurité.

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'en est-il de la mise à l'échelle MinMax?

Il n'y a pas de calcul d'écart type dans la mise à l'échelle MinMax. Le résultat est donc le même pour les pandas et pour 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
la source
6

Vous voudrez peut-être que certaines colonnes soient normalisées et les autres inchangées, comme certaines tâches de régression dont les étiquettes de données ou les colonnes catégorielles sont inchangées.Je vous suggère donc cette méthode pythonique (c'est une combinaison de réponses @shg et @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
la source
5

Ce ne sont que des mathématiques simples. La réponse devrait être aussi simple que ci-dessous.

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

A partir du document de pandas, la structure DataFrame peut appliquer une opération (fonction) à elle-même.

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

Applique la fonction le long de l'axe d'entrée de DataFrame. Les objets passés aux fonctions sont des objets Series ayant un index soit l'index du DataFrame (axe = 0) ou les colonnes (axe = 1). Le type de retour dépend de l'agrégation de fonctions passée ou de l'argument de réduction si le DataFrame est vide.

Vous pouvez appliquer une fonction personnalisée pour faire fonctionner le DataFrame.

shg
la source
2
Il serait bon d'expliquer pourquoi votre code résout le problème des OP, afin que les gens puissent adapter la stratégie plutôt que de simplement copier votre code. Veuillez lire Comment écrire une bonne réponse?
M. T
2

La fonction suivante calcule le score 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
la source
2

Voici comment procéder par colonne en utilisant la compréhension de liste:

[df[col].update((df[col] - df[col].min()) / (df[col].max() - df[col].min())) for col in df.columns]
Tchad
la source
1

Vous pouvez simplement utiliser la fonction pandas.DataFrame.transform 1 de cette manière:

df.transform(lambda x: x/x.max())
antonjs
la source
Cette solution ne fonctionnera pas si toutes les valeurs sont négatives. Considérez [-1, -2, -3]. Nous divisons par -1, et maintenant nous avons [1,2,3].
Dave Liu
1
df_normalized = df / df.max(axis=0)
Davoud Taghawi-Nejad
la source
0

Vous pouvez le faire en une seule ligne

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

il prend la moyenne de chacune des colonnes, puis la soustrait (moyenne) de chaque ligne (moyenne de la colonne particulière soustrait de sa ligne uniquement) et divise par la moyenne uniquement. Enfin, nous obtenons l'ensemble de données normalisé.

Rishi Bansal
la source
0

Pandas effectue la normalisation par colonne par défaut. Essayez le code ci-dessous.

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

Les valeurs de sortie seront comprises entre 0 et 1.

faiz
la source