Normalizar dados em pandas

131

Suponha que eu tenha um quadro de dados do pandas df:

Quero calcular a média da coluna de um quadro de dados.

Isso é facil:

df.apply(average) 

então o intervalo sábio da coluna max (col) - min (col). Isso é fácil novamente:

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

Agora, para cada elemento, quero subtrair a média da coluna e dividir pelo intervalo da coluna. Eu não tenho certeza de como fazer isso

Qualquer ajuda / sugestões são muito apreciadas.

Jason
fonte

Respostas:

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
fonte
Existe uma maneira de fazer isso se você deseja normalizar um subconjunto? Diga essa linha Ae faça Bparte de um fator de agrupamento maior que você deseja normalizar separadamente de Ce D.
Amyunimus
Selecione o subconjunto e calcule como antes. Veja pandas.pydata.org/pandas-docs/stable/indexing.html sobre como indexar e selecionar dados
Wouter Overmeire
17
Se você precisa de seus valores a ser> 0: df_norm = (df - df.min ()) / (df.max () - df.min ())
Dayvid Oliveira
1
deve ser df_norm = (df - df.min ()) / (df.max () - df.min ()) em vez de df.mean () nos primeiros suportes para obter os valores entre 0 e 1
jnPy
2
Se o seu quadro de dados tiver seqüências de caracteres em algumas colunas, veja esta resposta
netskink
73

Se você não se importa de importar a sklearnbiblioteca, eu recomendaria o método discutido neste 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.
fonte
2
o link para a postagem do blog está morto. você tem um que trabalha?
marts 27/11
3
O método correspondente para criar dados normalizados normais da unidade é chamado StandardScaler.
Abeboparebop #
Encontrei uma solução semelhante em outro lugar. O problema era que, na parte np_scaled, ele mostrava um erro ao esperar a matriz 2D, mas a entrada é uma matriz 1D e recomendou o uso de remodelagem (-1,1). Alguma idéia de como resolver isso como remodelar também não está funcionando.?
deadcode
Você pode obter avisos, dependendo de qual versão do numpy & sklearn você trabalha, mas, em geral, isso deve funcionar np_scaled = min_max_scaler.fit_transform(df.score.astype(float).values.reshape(-1, 1))
Jaeyoung Chun
33

Você pode usar applyisso e é um pouco mais limpo:

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

Além disso, funciona bem groupbyse você selecionar as colunas relevantes:

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
naught101
fonte
2

Ligeiramente modificado de: Python Pandas Dataframe: Normaliza dados entre 0,01 e 0,99? mas, de alguns dos comentários, isso foi relevante (desculpe se considerado um repost ...)

Eu queria que a normalização personalizada desse percentil regular de referência ou z-score não fosse adequada. Às vezes eu sabia quais eram os máximos e os mínimos possíveis da população e, portanto, queria defini-lo além da minha amostra, ou um ponto médio diferente, ou o que seja! Isso geralmente pode ser útil para redimensionar e normalizar dados para redes neurais, onde você pode querer todas as entradas entre 0 e 1, mas alguns dados podem precisar ser redimensionados de uma maneira mais personalizada ... porque os percentis e stdevs pressupõem que sua amostra cobre a população, mas às vezes sabemos que isso não é verdade. Também foi muito útil para visualizar dados em mapas de calor. Então, eu criei uma função personalizada (usei etapas extras no código aqui para torná-lo o mais legível possível):

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

Isso inclui uma série de pandas, ou mesmo apenas uma lista, e normaliza-a nos pontos baixo, central e alto especificados. Também há um fator de contração! para permitir que você reduza os dados dos pontos de extremidade 0 e 1 (eu tive que fazer isso ao combinar mapas de cores no matplotlib: pcolormesh único com mais de um mapa de cores usando o Matplotlib ). Portanto, você provavelmente pode ver como o código funciona, mas basicamente diz tem valores [-5,1,10] em uma amostra, mas deseja normalizar com base em um intervalo de -7 a 7 (portanto, acima de 7, nosso "10" é tratado como um 7 efetivamente) com um ponto médio de 2, mas reduza-o para caber em um mapa de cores de 256 RGB:

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

Ele também pode transformar seus dados de dentro para fora ... isso pode parecer estranho, mas achei útil para o mapeamento de calor. Digamos que você queira uma cor mais escura para valores próximos de 0, em vez de alto / baixo. Você poderia fazer um mapa de calor com base em dados normalizados, onde 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]

Então agora "2", que está mais próximo do centro, definido como "1", é o valor mais alto.

De qualquer forma, achei que meu aplicativo era relevante se você estivesse procurando redimensionar dados de outras maneiras que poderiam ter aplicativos úteis para você.

Vlox
fonte
Você pode substituir todas as instruções if / else por um dicionário com funções . Parece um pouco mais limpo então.
Roald
isso é bem legal, vou lembrar disso da próxima vez, obrigado!
Vlox
0

É assim que você faz em colunas:

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