Normalizar colunas do quadro de dados do pandas

227

Eu tenho um quadro de dados em pandas onde cada coluna tem um intervalo de valores diferente. Por exemplo:

df:

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

Alguma idéia de como posso normalizar as colunas desse quadro de dados em que cada valor está entre 0 e 1?

Minha saída desejada é:

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
fonte
1
há uma função, aplicar por exemplo frame.apply (f, eixo = 1), onde f é uma função que faz algo com uma fileira ...
tschm
1
Normalização pode não ser a expressão mais apropriada, pois a documentação do scikit-learn a define como "o processo de dimensionar amostras individuais para ter uma norma de unidade " (ou seja, linha por linha, se eu entendi corretamente).
Skippy le Grand Gourou
Não entendi, por que o min_max scaling é considerado normalização! normal, tem que ter significado, no sentido da distribuição normal com média zero e variância 1.
Polícia OverFlow
Se você está visitando esta pergunta em 2020 ou mais tarde, veja a resposta de @Poudel, você obtém uma resposta diferente da normalização se usar pandas vs sklearn.
Bhishan Poudel 29/01
@Oudel isso é devido ao ddofargumento?
fffrost 04/04

Respostas:

223

Você pode usar o pacote sklearn e seus utilitários de pré-processamento associados para normalizar os dados.

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)

Para mais informações olhada no scikit-learn documentação sobre o pré-processamento de dados: escala apresenta para um intervalo.

Sandman
fonte
46
Eu acho que isso vai se livrar dos nomes das colunas, o que pode ser uma das razões pelas quais op está usando dataframes em primeiro lugar.
Pietz
47
Isso normalizará as linhas e não as colunas, a menos que você as transponha primeiro. Para fazer o que o Q pede:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index)
hobs
26
@pietz para manter os nomes das colunas, consulte este post . Substitua basicamente a última linha por,df=pandas.DataFrame(x_scaled, columns=df.columns)
ijoseph 26/06
5
@ hobs Isso não está correto. O código de Sandman normaliza colunas e colunas. Você obtém o resultado errado se você transpõe.
petezurich
8
@petezurich Parece que Sandman ou Praveen corrigiram seu código. Infelizmente, não é possível corrigir comentários;)
hobs
398

Uma maneira fácil de usar o Pandas : (aqui eu quero usar a normalização média)

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

para usar a normalização min-max:

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

Editar: Para resolver algumas preocupações, é preciso dizer que o Pandas aplica automaticamente a função de cores no código acima.

Cina
fonte
16
eu gosto deste. é curto, é expressivo e preserva as informações do cabeçalho. mas acho que você precisa subtrair o min no denominador também.
Pietz
6
Eu não acho que está errado. Funciona lindamente para mim - acho que mean () e std () precisam retornar um dataframe para que isso funcione e sua mensagem de erro não implica que eles não sejam um dataframe é um problema.
Strandtasche #
24
isso não é normalização em colunas. isso está normalizando toda a matriz como um todo, o que fornecerá resultados errados.
Nguai al 26/04/19
6
Também funcionou para mim lindamente. @ Naialial, você pode estar tentando isso em uma matriz numpy; nesse caso, o resultado seria o que você disse. Mas para quadros de dados do Pandas, as medidas min, max, ... são aplicadas em colunas por padrão.
Auxiliar
1
Eu também gosto deste
Isaac Sim
51

Com base nesta postagem: /stats/70801/how-to-normalize-data-to-0-1-range

Você pode fazer o seguinte:

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

Você não precisa se preocupar se seus valores são negativos ou positivos. E os valores devem ser bem distribuídos entre 0 e 1.

Michael Aquilina
fonte
8
Tenha cuidado quando os valores mínimo e máximo forem iguais, seu denominador é 0 e você obterá um valor NaN.
Hrushikesh Dhumal 01/02/19
36

Seu problema é realmente uma transformação simples que atua nas colunas:

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

frame.apply(f, axis=0)

Ou ainda mais conciso:

   frame.apply(lambda x: x/x.max(), axis=0)
tschm
fonte
2
O lambdaprimeiro é o melhor :-)
Abu Shoeb
4
isso não deveria ser o eixo = 1, pois a pergunta é normalização em colunas?
Nguai al 26/04/19
Não, a partir dos documentos : axis [...] 0 or 'index': apply function to each column. O padrão é, na verdade, axis=0para que essa linha única possa ser escrita ainda mais curta :-) Obrigado @tschm.
jorijnsmit 11/04
30

Se você gosta de usar o pacote sklearn, pode manter os nomes de colunas e índices usando pandas da seguinte locmaneira:

from sklearn.preprocessing import MinMaxScaler

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

Simples é bonito:

df["A"] = df["A"] / df["A"].max()
df["B"] = df["B"] / df["B"].max()
df["C"] = df["C"] / df["C"].max()
Basil Musa
fonte
Ótimo e na minha opinião a melhor solução!
Maciej A. Bednarz
6
Observe que o OP solicitou o intervalo [0..1] e esta solução é dimensionada para o intervalo [-1..1]. Tente isso com a matriz [-10, 10].
Alexander Sosnovshchenko
3
@AlexanderSosnovshchenko não de verdade. Basil Musa está assumindo que a matriz do OP sempre é negativa, por isso ele deu essa solução. Se alguma coluna tiver uma entrada negativa, esse código NÃO será normalizado para o intervalo [-1,1]. Experimente com a matriz [-5, 10]. A maneira correta para normalizar a [0,1] com valores negativos foi dada pelo de Cina respostadf["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min())
facuq
simples e explícita
joshi123
Talvez ainda mais simples: df /= df.max()- supondo que o objetivo seja normalizar cada coluna individualmente.
n1k31t4
24

Você pode criar uma lista de colunas que deseja normalizar

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

Seu Dataframe do Pandas agora está normalizado apenas nas colunas que você deseja


No entanto , se você desejar o contrário , selecione uma lista de colunas que NÃO deseja normalizar, basta criar uma lista de todas as colunas e remover as que não são desejadas.

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
fonte
11

Eu acho que a melhor maneira de fazer isso nos pandas é apenas

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

Editar Se houver números negativos no seu quadro de dados, você deve usar

df = df/df.loc[df.abs().idxmax()].astype(np.float64)
Daniele
fonte
1
No caso de todos os valores de uma coluna são zero isso não vai funcionar
ahajib
dividindo o valor atual pelo máximo não vai lhe dar uma normalização correta a menos que o min é 0.
Pietz
Eu concordo, mas é isso que o OT estava pedindo (ver seu exemplo)
Daniele
11

A solução dada por Sandman e Praveen está muito bem. O único problema é que, se você tiver variáveis ​​categóricas em outras colunas do seu quadro de dados, esse método precisará de alguns ajustes.

Minha solução para esse tipo de problema é a seguinte:

 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
fonte
2
Essa resposta é útil porque a maioria dos exemplos na Internet aplica um redimensionador a todas as colunas, enquanto que na verdade trata da situação em que um redimensionador, digamos o MinMaxScaler, não deve se aplicar a todas as colunas.
Demongolem # 10/18
10

Exemplo de diferentes padronizações em python.

Para referência, consulte este artigo da wikipedia: https://en.wikipedia.org/wiki/Unbeased_estimation_of_standard_deviation

Dados de exemplo

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

Normalização usando pandas (fornece estimativas imparciais)

Ao normalizar, subtraímos a média e dividimos pelo desvio padrão.

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

Normalização usando o sklearn (fornece estimativas tendenciosas, diferentes dos pandas)

Se você fizer a mesma coisa sklearn, obterá DIFERENTES resultados!

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

As estimativas tendenciosas do sklearn tornam o aprendizado de máquina menos poderoso?

NÃO.

A documentação oficial do sklearn.preprocessing.scale afirma que o uso do estimador enviesado NÃO É PROVÁVEL para afetar o desempenho dos algoritmos de aprendizado de máquina e podemos usá-los com segurança.

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.

E o MinMax Scaling?

Não há cálculo de desvio padrão na escala MinMax. Portanto, o resultado é o mesmo nos pandas e no 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
fonte
6

Você pode querer que algumas das colunas sejam normalizadas e as outras permaneçam inalteradas, como algumas das tarefas de regressão, cujos rótulos de dados ou colunas categóricas permanecem inalteradas. Por isso, sugiro que você use este método python (é uma combinação das respostas @shg e @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
fonte
5

É apenas matemática simples. A resposta deve ser tão simples quanto abaixo.

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

No documento dos pandas, a estrutura DataFrame pode aplicar uma operação (função) a si mesma.

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

Aplica a função ao longo do eixo de entrada do DataFrame. Objetos passados ​​para funções são objetos de série que têm o índice do DataFrame (eixo = 0) ou as colunas (eixo = 1). O tipo de retorno depende se a função passada é agregada ou o argumento de redução se o DataFrame estiver vazio.

Você pode aplicar uma função personalizada para operar o DataFrame.

shg
fonte
2
Seria bom explicar por que seu código resolve o problema dos OPs, para que as pessoas possam adaptar a estratégia em vez de apenas copiar seu código. Por favor, leia Como eu escrevo uma boa resposta?
Mr. T
2

A seguinte função calcula a pontuação 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
fonte
2

É assim que você faz colunas usando a compreensão da lista:

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

Você pode simplesmente usar a função pandas.DataFrame.transform 1 desta maneira:

df.transform(lambda x: x/x.max())
antonjs
fonte
Esta solução não funcionará se todos os valores forem negativos. Considere [-1, -2, -3]. Dividimos por -1 e agora temos [1,2,3].
Dave Liu
1
df_normalized = df / df.max(axis=0)
Davoud Taghawi-Nejad
fonte
0

Você pode fazer isso em uma linha

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

leva média para cada coluna e subtrai-a (média) de cada linha (média de uma coluna específica subtrai apenas sua linha) e divide-a apenas por média. Finalmente, o que obtemos é o conjunto de dados normalizados.

Rishi Bansal
fonte
0

O Pandas faz a normalização de colunas por padrão. Experimente o código abaixo.

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

Os valores de saída estarão na faixa de 0 e 1.

faiz
fonte