Normaliser les données dans les pandas

131

Supposons que j'ai une trame de données pandas df :

Je veux calculer la moyenne par colonne d'un bloc de données.

C'est facile:

df.apply(average) 

puis 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 la diviser par la plage de sa colonne. Je ne sais pas comment faire ça

Toute aide / pointeur est très apprécié.

Jason
la source

Réponses:

225
In [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

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

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

In [95]: df_norm.mean()
Out[95]:
a   -2.081668e-17
b    4.857226e-17
c    1.734723e-17
d   -1.040834e-17

In [96]: df_norm.max() - df_norm.min()
Out[96]:
a    1
b    1
c    1
d    1
Wouter Overmeire
la source
Existe-t-il un moyen de le faire si vous souhaitez normaliser un sous-ensemble? Dites cette ligne Aet Bfont partie d'un facteur de regroupement plus large que vous souhaitez normaliser séparément de Cet D.
Amyunimus
Sélectionnez le sous-ensemble et calculez comme auparavant. Voir pandas.pydata.org/pandas-docs/stable/indexing.html pour savoir comment indexer et sélectionner des données
Wouter Overmeire
17
Si vous avez besoin que vos valeurs soient> 0: df_norm = (df - df.min ()) / (df.max () - df.min ())
Dayvid Oliveira
1
devrait être df_norm = (df - df.min ()) / (df.max () - df.min ()) plutôt que df.mean () dans les premiers crochets pour obtenir les valeurs entre 0 et 1
jnPy
2
Si votre dataframe a des chaînes dans certaines colonnes, voir cette réponse
netskink
73

Si cela ne vous dérange pas d'importer la sklearnbibliothèque, je recommanderais la méthode décrite 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
David S.
la source
2
le lien vers le billet de blog est mort. en avez-vous un qui fonctionne?
marts
3
La méthode correspondante pour créer des données normalisées à l'unité est appelée StandardScaler.
abeboparebop
J'ai trouvé une solution similaire ailleurs. Le problème était que dans la partie np_scaled, il affichait une erreur dans l'attente d'un tableau 2D mais l'entrée est un tableau 1D et il a recommandé d'utiliser reshape (-1,1). Toute idée de comment résoudre ce problème en tant que remodelage ne fonctionne pas non plus.?
deadcode
Vous pourriez recevoir des avertissements en fonction de la version de numpy & sklearn avec laquelle vous travaillez, mais en général, cela devrait fonctionner np_scaled = min_max_scaler.fit_transform(df.score.astype(float).values.reshape(-1, 1))
Jaeyoung Chun
33

Vous pouvez utiliser applypour cela, et c'est un peu plus soigné:

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

En outre, cela fonctionne bien avec groupby, si vous sélectionnez les colonnes appropriées:

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
rien101
la source
2

Légèrement modifié de: Python Pandas Dataframe: Normaliser les données entre 0,01 et 0,99? mais à partir de certains commentaires, il a pensé que c'était pertinent (désolé si considéré comme un republication cependant ...)

Je voulais une normalisation personnalisée dans ce percentile régulier de la donnée ou du score z n'était pas adéquate. Parfois, je savais quels étaient les max et min possibles de la population, et je voulais donc le définir autrement que mon échantillon, ou un point médian différent, ou autre! Cela peut souvent être utile pour redimensionner et normaliser les données pour les réseaux neuronaux où vous voudrez peut-être toutes les entrées entre 0 et 1, mais certaines de vos données devront peut-être être mises à l'échelle de manière plus personnalisée ... car les percentiles et stdevs supposent que votre échantillon couvre la population, mais parfois nous savons que ce n'est pas vrai. Cela m'a également été très utile lors de la visualisation de données dans des cartes thermiques. J'ai donc créé une fonction personnalisée (j'ai utilisé des étapes supplémentaires dans le code 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 x<low:
            r.append(0.)
        elif x>hi:
            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 dans une série de pandas, ou même juste une liste et la normalisera à vos points bas, centre et hauts spécifiés. il y a aussi un facteur de rétrécissement! pour vous permettre de réduire les données des points de terminaison 0 et 1 (j'ai dû le faire lors de la combinaison de cartes de couleurs dans matplotlib: Single pcolormesh avec plus d'une palette de couleurs en utilisant Matplotlib ) Vous pouvez donc probablement voir comment le code fonctionne, mais vous dites essentiellement ont des valeurs [-5,1,10] dans un échantillon, mais veulent normaliser sur la base d'une plage de -7 à 7 (donc tout ce qui est au-dessus de 7, notre "10" est traité comme un 7 effectivement) avec un point médian de 2, mais réduisez-le pour l'adapter à une palette de couleurs 256 RVB:

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

Cela peut aussi transformer vos données à l'envers ... cela peut sembler étrange, mais je l'ai trouvé utile pour la cartographie thermique. Supposons que vous souhaitiez une couleur plus foncée pour des valeurs plus proches de 0 que de hi / low. Vous pouvez utiliser une carte thermique basée sur des 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]

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

Quoi qu'il en soit, je pensais que mon application était pertinente si vous cherchez à redimensionner les données d'une autre manière qui pourrait avoir des applications utiles pour vous.

Vlox
la source
Vous pouvez remplacer toutes les instructions if / else par un dictionnaire avec des fonctions . Ça a l'air un peu plus propre alors.
Roald
c'est assez chouette, je garderai cela à l'esprit la prochaine fois, merci!
Vlox
0

Voici comment procéder par colonne:

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