Normalizuj kolumny ramki danych pand


226

Mam ramkę danych w pandach, w których każda kolumna ma inny zakres wartości. Na przykład:

df:

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

Masz pomysł, jak mogę znormalizować kolumny tej ramki danych, w których każda wartość mieści się w przedziale od 0 do 1?

Moje pożądane wyjście to:

A     B    C
1     1    1
0.765 0.5  0.7
0.8   0.7  0.18(which is 0.09/0.5)

1
jest funkcja zastosuj, np. frame.apply (f, oś = 1) gdzie f jest funkcją, która robi coś z wierszem ...
tschm

1
Normalizacja może nie być najodpowiedniejszym sformułowaniem, ponieważ dokumentacja scikit-learn definiuje ją jako „proces skalowania pojedynczych próbek w celu uzyskania normy jednostkowej ” (tj. Rząd po rzędzie, jeśli poprawnie ją otrzymam).
Skippy le Grand Gourou

Nie rozumiem, dlaczego skalowanie min_max jest uważane za normalizację! normalne musi mieć znaczenie w sensie rozkładu normalnego ze średnią zero i wariancją 1.
OverFlow Police

Jeśli odwiedzasz to pytanie w 2020 roku lub później, spójrz na odpowiedź @Poudel, otrzymasz inną odpowiedź normalizacji, jeśli używasz pand vs sklearn.
Bhishan Poudel

@Poudel jest to spowodowane ddofargumentem?
fffrost

Odpowiedzi:


223

Możesz użyć pakietu sklearn i powiązanych z nim narzędzi do wstępnego przetwarzania w celu normalizacji danych.

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)

Aby uzyskać więcej informacji, zapoznaj się z dokumentacją scikit-learn dotyczącą danych przetwarzania wstępnego: skalowanie funkcji do zakresu.


46
myślę, że to pozbędzie się nazw kolumn, co może być jednym z powodów, dla których op używa przede wszystkim ramek danych.
pietz

47
Spowoduje to normalizację wierszy, a nie kolumn, chyba że najpierw je transponujesz. Aby zrobić to, o co prosi Q:pd.DataFrame(min_max_scaler.fit_transform(df.T), columns=df.columns, index=df.index)
płyty grzewcze

26
@pietz, aby zachować nazwy kolumn, zobacz ten post . Zasadniczo zastąp ostatnią linię,df=pandas.DataFrame(x_scaled, columns=df.columns)
ijoseph

5
@ płyty To nie jest poprawne. Kod Sandmana normalizuje kolumny i kolumny. Jeśli transponujesz, otrzymasz niewłaściwy wynik.
petezurich

8
@petezurich Wygląda na to, że Sandman lub Praveen poprawili swój kod. Niestety nie można poprawić komentarzy;)
płyty grzewcze

397

jeden prosty sposób przy użyciu Pand : (tutaj chcę użyć średniej normalizacji)

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

aby użyć normalizacji min-max:

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

Edycja: Aby rozwiązać niektóre obawy, należy powiedzieć, że Pandy automatycznie stosują funkcję kolomnową w powyższym kodzie.


16
podoba mi się ten jest krótki, wyrazisty i zachowuje informacje w nagłówku. ale myślę, że musisz również odjąć min w mianowniku.
pietz

6
Nie sądzę, że to źle. Działa dla mnie pięknie - nie sądzę, aby mean () i std () musiały zwrócić ramkę danych, aby to zadziałało, a twój komunikat o błędzie nie oznacza, że ​​nie jest to ramka danych.
Strandtasche

24
nie jest to normalizacja kolumnowa. normalizuje to całą matrycę jako całość, co zapewni błędne wyniki.
Nguai al

6
Również mi pięknie działał. @Nguaial, możesz próbować tego na macierzy liczbowej, w którym to przypadku wynik byłby taki, jak powiedziałeś. Ale w przypadku ramek danych Pandas miary min, max, ... domyślnie stosują się do kolumn.
Pomocniczy

1
też mi się podoba
Isaac Sim

51

Na podstawie tego postu: /stats/70801/how-to-normalize-data-to-0-1-range

Możesz wykonać następujące czynności:

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

Nie musisz się martwić, czy twoje wartości są negatywne czy pozytywne. A wartości powinny być dobrze rozłożone między 0 a 1.


8
Uważaj, gdy wartości minimalna i maksymalna są takie same, mianownik ma wartość 0, a otrzymasz wartość NaN.
Hrushikesh Dhumal

36

Twoim problemem jest prosta transformacja działająca na kolumny:

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

frame.apply(f, axis=0)

Lub jeszcze bardziej zwięzłe:

   frame.apply(lambda x: x/x.max(), axis=0)

2
Ten lambdajest najlepszy :-)
Abu Shoeb,

4
czy to nie powinna być oś = 1, ponieważ pytanie dotyczy normalizacji kolumnowej?
Nguai al

Nie, z docs : axis [...] 0 or 'index': apply function to each column. Domyślnie jest axis=0tak, że ten linijkę można zapisać jeszcze krócej :-) Dzięki @tschm.
jorijnsmit

30

Jeśli lubisz korzystać z pakietu sklearn, możesz zachować nazwy kolumn i indeksów, używając loctakich pand :

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler() 
scaled_values = scaler.fit_transform(df) 
df.loc[:,:] = scaled_values

27

Prostota jest piękna:

df["A"] = df["A"] / df["A"].max()
df["B"] = df["B"] / df["B"].max()
df["C"] = df["C"] / df["C"].max()

Świetne i moim zdaniem najlepsze rozwiązanie!
Maciej A. Bednarz

6
Zauważ, że OP poprosił o zakres [0..1], a to rozwiązanie skaluje się do zakresu [-1..1]. Spróbuj tego z tablicą [-10, 10].
Alexander Sosnovshchenko

3
@AlexanderSosnovshchenko nie bardzo. Basil Musa zakłada, że ​​macierz PO jest zawsze nieujemna, dlatego podał to rozwiązanie. Jeśli jakaś kolumna ma wpis ujemny, kod ten NIE normalizuje się do zakresu [-1, 1]. Wypróbuj z tablicą [-5, 10]. Prawidłowy sposób normalizacji do [0,1] z wartościami ujemnymi podał odpowiedź df["A"] = (df["A"]-df["A"].min()) / (df["A"].max()-df["A"].min())
Ciny

proste ORAZ wyraźne
joshi123 12.12.18

Być może jeszcze prostsze: df /= df.max()- zakładając, że celem jest normalizacja każdej kolumny, indywidualnie.
n1k31t4

24

Możesz utworzyć listę kolumn, które chcesz znormalizować

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

Twoja ramka danych Pandas jest teraz znormalizowana tylko w odpowiednich kolumnach


Jeśli jednak chcesz mieć coś przeciwnego , wybierz listę kolumn, których NIE chcesz znormalizować, możesz po prostu utworzyć listę wszystkich kolumn i usunąć te niepożądane

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 ]

11

Myślę, że lepszy sposób na zrobienie tego w pandach jest po prostu

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

Edytuj Jeśli w twojej ramce danych są liczby ujemne, powinieneś użyć zamiast tego

df = df/df.loc[df.abs().idxmax()].astype(np.float64)

1
W przypadku, gdy wszystkie wartości kolumny są równe zero, to nie zadziała
ahajib

podzielenie bieżącej wartości przez maks. nie zapewni prawidłowej normalizacji, chyba że min wynosi 0.
pietz

Zgadzam się, ale o to prosił OT (patrz jego przykład)
Daniele

11

Rozwiązanie podane przez Sandmana i Praveen jest bardzo dobre. Jedyny problem z tym, jeśli masz zmienne jakościowe w innych kolumnach ramki danych, ta metoda będzie wymagać pewnych korekt.

Moje rozwiązanie tego typu problemu jest następujące:

 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])

2
Ta odpowiedź jest przydatna, ponieważ większość przykładów w Internecie stosuje jeden skaler do wszystkich kolumn, podczas gdy w rzeczywistości dotyczy to sytuacji, w której jeden skaler, powiedzmy MinMaxScaler, nie powinien mieć zastosowania do wszystkich kolumn.
demongolem,

10

Przykład różnych standaryzacji w Pythonie.

W celach informacyjnych zobacz ten artykuł w Wikipedii: https://en.wikipedia.org/wiki/Niezależne_estimacja_standardu_deviation

Przykładowe dane

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

Normalizacja za pomocą pand (daje obiektywne szacunki)

Podczas normalizacji po prostu odejmujemy średnią i dzielimy przez odchylenie standardowe.

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

Normalizacja za pomocą sklearn (daje tendencyjne oszacowania, inne niż pandy)

Jeśli zrobisz to samo, sklearnuzyskasz INNE wyjście!

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

Czy błędne szacunki sklearn powodują, że uczenie maszynowe jest mniej wydajne?

NIE.

Oficjalna dokumentacja sklearn.preprocessing.scale stwierdza, że ​​użycie stronniczego estymatora NIE NALEŻY wpływać na wydajność algorytmów uczenia maszynowego i możemy z nich bezpiecznie korzystać.

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.

Co ze skalowaniem MinMax?

W skalowaniu MinMax nie ma obliczeń odchylenia standardowego. Rezultat jest taki sam zarówno w przypadku pand, jak i 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

6

Możesz chcieć znormalizować niektóre kolumny, a inne pozostać niezmienione, podobnie jak niektóre zadania regresji, których etykiety danych lub kolumny kategorialne pozostają niezmienione. Sugeruję więc ten pythonowy sposób (jest to kombinacja odpowiedzi @shg i @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()))

5

To tylko prosta matematyka. Odpowiedź powinna być tak prosta jak poniżej.

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

2
def normalize(x):
    try:
        x = x/np.linalg.norm(x,ord=1)
        return x
    except :
        raise
data = pd.DataFrame.apply(data,normalize)

Z dokumentu pand struktura DataFrame może zastosować do siebie operację (funkcję).

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

Stosuje funkcję wzdłuż osi wejściowej DataFrame. Obiekty przekazywane do funkcji są obiektami serii mającymi indeks albo wskaźnika DataFrame (oś = 0), albo kolumny (oś = 1). Typ zwracany zależy od tego, czy przekazana funkcja agreguje, czy argumentu redukującego, jeśli DataFrame jest pusta.

Możesz zastosować niestandardową funkcję do obsługi DataFrame.


2
Dobrze byłoby wyjaśnić, dlaczego Twój kod rozwiązuje problem PO, aby ludzie mogli dostosować strategię, a nie tylko kopiować kod. Proszę przeczytać Jak napisać dobrą odpowiedź?
Pan T

2

Poniższa funkcja oblicza wynik 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

2

Oto jak to zrobić w kolumnach przy użyciu listy:

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

1

Możesz po prostu użyć funkcji pandas.DataFrame.transform 1 w następujący sposób:

df.transform(lambda x: x/x.max())

To rozwiązanie nie będzie działać, jeśli wszystkie wartości są ujemne. Rozważ [-1, -2, -3]. Dzielimy przez -1, a teraz mamy [1,2,3].
Dave Liu,


0

Możesz to zrobić w jednym wierszu

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

pobiera średnią dla każdej kolumny, a następnie odejmuje ją (średnia) od każdego wiersza (średnia dla konkretnej kolumny odejmuje tylko jej wiersz) i dzieli tylko według średniej. Wreszcie otrzymujemy znormalizowany zestaw danych.


0

Panda domyślnie wykonuje normalizację kolumnową. Wypróbuj poniższy kod.

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

Wartości wyjściowe będą w zakresie 0 i 1.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.