142 votes

Normaliser les données dans pandas

Supposons que j'ai un dataframe pandas df:

Je veux calculer la moyenne par colonne d'un dataframe.

C'est facile:

df.apply(moyenne)

puis le max de la plage par colonne max(col) - min(col). C'est encore facile:

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

Maintenant, pour chaque élément, je veux soustraire la moyenne de sa colonne et diviser par la plage de sa colonne. Je ne suis pas sûr comment faire cela

Toute aide/pointeurs sont grandement appréciés.

232voto

Wouter Overmeire Points 6676
Dans [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

Dans [93] : df_norm = (df - df.mean()) / (df.max() - df.min())

Dans [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

Dans [95] : df_norm.mean()
Out[95] :
a -2,081668e-17
b 4,857226e-17
c 1,734723e-17
d -1,040834e-17

Dans [96] : df_norm.max() - df_norm.min()
Out[96] :
a 1
b 1
c 1
d 1

82voto

davidshen84 Points 1836

Si cela ne vous dérange pas d'importer la bibliothèque sklearn, je recommanderais la méthode discutée sur ce blog.

import pandas as pd
from sklearn import preprocessing

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

min_max_scaler = preprocessing.MinMaxScaler()
np_scaled = min_max_scaler.fit_transform(df)
df_normalized = pd.DataFrame(np_scaled, columns = cols)
df_normalized

35voto

naught101 Points 2023

Vous pouvez utiliser apply pour cela, et c'est un peu plus propre:

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

Aussi, cela fonctionne bien avec groupby, si vous sélectionnez les colonnes pertinentes:

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

2voto

Vlox Points 407

Légèrement modifié à partir de: Python Pandas Dataframe: Normaliser les données entre 0.01 et 0.99? mais suite à certains commentaires, j'ai pensé que c'était pertinent (désolé si cela est considéré comme un repost cependant...)

Je voulais une normalisation personnalisée car le percentile régulier des données ou le z-score n'était pas adéquat. Parfois je savais quel était le maximum et le minimum possibles de la population, et donc je voulais le définir autrement que mon échantillon, ou un midpoint différent, ou autre chose! Cela peut souvent être utile pour redimensionner et normaliser des données pour les réseaux neuronaux où vous voulez que toutes les entrées soient entre 0 et 1, mais certaines de vos données peuvent nécessiter d'être redimensionnées de manière plus personnalisée... car les percentiles et les écarts-types supposent que votre échantillon couvre la population, mais parfois nous savons que ce n'est pas vrai. C'était également très utile pour moi lorsque je visualisais des données dans des cartes thermiques. J'ai donc créé une fonction personnalisée (j'ai ajouté des étapes supplémentaires dans le code ici pour le rendre aussi lisible que possible) :

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 xhi:
            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

Cela prendra une série pandas, ou même juste une liste et la normalisera à vos points bas, centraux et hauts spécifiés. il y a aussi un facteur de rétrécissement ! pour vous permettre de réduire l'échelle des données loin des extrémités 0 et 1 (j'ai dû le faire lorsque je combinai les cartes de couleur dans matplotlib : Tracer une seule pcolormesh avec plus d'une carte de couleur en utilisant Matplotlib) Donc vous pouvez probablement voir comment le code fonctionne, mais en gros, disons que vous avez des valeurs [-5,1,10] dans un échantillon, mais que vous voulez normaliser selon une plage de -7 à 7 (donc tout ce qui dépasse 7, notre "10" est traité comme un 7 efficacement) avec un midpoint de 2, mais le rétrécir pour s'adapter à une colormap RGB de 256 :

#In[1]
NormData([-5,2,10],low=-7,center=1,hi=7,shrinkfactor=2./256)
#Out[1]
[0.1279296875, 0.5826822916666667, 0.99609375]

Il peut également retourner vos données à l'envers... cela peut sembler étrange, mais j'ai trouvé cela utile pour la cartographie thermique. Disons que vous voulez une couleur plus sombre pour les valeurs plus proches de 0 plutôt que hi/low. Vous pouvez cartographier thermiquement sur la base de données normalisées où 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]

Maintenant, "2" qui est plus proche du centre, défini comme "1" est la valeur la plus élevée.

En tout cas, j'ai pensé que mon application était pertinente si vous cherchez à redimensionner les données de manière différente, ce qui pourrait vous être utile.

0voto

Chad Points 640

C'est ainsi que vous le faites colonne par colonne :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X