web-dev-qa-db-fra.com

Normaliser les données dans pandas

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

Je veux calculer la moyenne en colonnes d'un bloc de données.

C'est facile:

df.apply(average) 

puis la largeur de la 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é.

120
jason
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
207
Wouter Overmeire

Si cela ne vous dérange pas d'importer la bibliothèque sklearn, je recommanderais la méthode avec conversation this 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
67
David S.

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

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
32
naught101

Légèrement modifié à partir de: Python Pandas Dataframe: Normaliser les données entre 0.01 et 0.99? mais certains commentaires l'ont jugé pertinent (désolé si vous considérez qu'il s'agit d'un transfert ultérieur ...)

Je souhaitais une normalisation personnalisée en ce sens que le pourcentage normal de données ou de z-scores n’était pas adéquat. Parfois, je savais quel était le maximum et le minimum réalisables de la population et je voulais donc le définir autrement que pour mon échantillon, ou un point médian différent, ou peu importe! Cela peut souvent être utile pour redimensionner et normaliser les données de réseaux neuronaux où vous souhaitez peut-être toutes les entrées comprises entre 0 et 1, mais certaines de vos données devront peut-être être redimensionnées de manière plus personnalisée ... car les centiles et les stdevs supposent que votre échantillon couvre la population, mais parfois nous savons que ce n'est pas vrai. Cela m'a aussi été très utile lors de la visualisation de données dans des cartes thermiques. Donc, j'ai construit une fonction personnalisée (utilisé 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 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 une série de pandas, ou même simplement une liste et la normalisera en fonction des points bas, centraux et élevés que vous avez spécifiés. il y a aussi un facteur de contraction! pour vous permettre de réduire les données des points finaux 0 et 1 (je devais le faire lors de la combinaison de cartes de couleurs dans matplotlib: n seul pcolormeh avec plusieurs couleurs utilisant Matplotlib ) le code fonctionne, mais dit en gros que vous avez des valeurs [-5,1,10] dans un échantillon, mais que vous voulez normaliser sur une plage de -7 à 7 (si tout ce qui dépasse 7, notre "10" est traité comme un 7 de manière efficace ) avec un point médian de 2, mais réduisez-le pour s’adapter à une palette de couleurs de 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 également transformer vos données de l'intérieur ... cela peut sembler étrange, mais je l'ai trouvé utile pour la cartographie thermique. Supposons que vous souhaitiez une couleur plus sombre pour les valeurs proches de 0 plutôt que haut/bas. Vous pouvez créer 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]

Donc 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 envisagiez de redimensionner les données d'une autre manière qui pourrait avoir des applications utiles pour vous.

2
Vlox

Voici comment procéder en colonnes:

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