Jak znormalizować tablicę NumPy do określonego zakresu?


142

Po wykonaniu pewnego przetwarzania na tablicy audio lub obrazu, należy go znormalizować w pewnym zakresie, zanim będzie można go zapisać z powrotem do pliku. Można to zrobić w następujący sposób:

# Normalize audio channels to between -1.0 and +1.0
audio[:,0] = audio[:,0]/abs(audio[:,0]).max()
audio[:,1] = audio[:,1]/abs(audio[:,1]).max()

# Normalize image to between 0 and 255
image = image/(image.max()/255.0)

Czy istnieje mniej szczegółowy, wygodny sposób na zrobienie tego? matplotlib.colors.Normalize()nie wydaje się być powiązany.

Odpowiedzi:


147
audio /= np.max(np.abs(audio),axis=0)
image *= (255.0/image.max())

Używanie /=i *=pozwala wyeliminować pośrednią tablicę tymczasową, oszczędzając w ten sposób trochę pamięci. Mnożenie jest mniej kosztowne niż dzielenie, więc

image *= 255.0/image.max()    # Uses 1 division and image.size multiplications

jest nieznacznie szybszy niż

image /= image.max()/255.0    # Uses 1+image.size divisions

Ponieważ używamy tutaj podstawowych metod numpy, myślę, że jest to tak wydajne rozwiązanie w numpy, jak to tylko możliwe.


Operacje lokalne nie zmieniają typu dtype tablicy kontenerów. Ponieważ żądane znormalizowane wartości są zmiennoprzecinkowe, tablice audioi imagemuszą mieć zmiennoprzecinkowy typ dtype przed wykonaniem operacji w miejscu. Jeśli nie mają one jeszcze zmiennoprzecinkowego typu dtype, musisz je przekonwertować za pomocą astype. Na przykład,

image = image.astype('float64')

7
Dlaczego mnożenie jest tańsze niż dzielenie?
— endolit

19
Nie wiem dokładnie dlaczego. Jestem jednak pewien roszczenia, po sprawdzeniu go z czasem. Dzięki mnożeniu możesz pracować z jedną cyfrą na raz. Przy dzieleniu, szczególnie przy dużych dzielnikach, musisz pracować z wieloma cyframi i „zgadnąć”, ile razy dzielnik trafia do dywidendy. W końcu wykonujesz wiele zadań z mnożeniem, aby rozwiązać jeden problem z dzieleniem. Komputerowy algorytm dzielenia może nie być tym samym, co dzielenie długie u człowieka, ale mimo to uważam, że jest to bardziej skomplikowane niż mnożenie.
— unutbu

14
Prawdopodobnie warto wspomnieć o dzieleniu przez zero dla pustych obrazów.
— cjm2671

7
Mnożenie @endolitu jest tańsze niż dzielenie ze względu na sposób jego implementacji na poziomie Assembly. Algorytmy dzielenia nie mogą być równoległe, podobnie jak algorytmy mnożenia. en.wikipedia.org/wiki/Binary_multiplier
— mjones.udri

5
Minimalizacja liczby podziałów na korzyść mnożenia jest dobrze znaną techniką optymalizacji.
— mjones.udri

79

Jeśli tablica zawiera zarówno dane dodatnie, jak i ujemne, zastosowałbym:

import numpy as np

a = np.random.rand(3,2)

# Normalised [0,1]
b = (a - np.min(a))/np.ptp(a)

# Normalised [0,255] as integer: don't forget the parenthesis before astype(int)
c = (255*(a - np.min(a))/np.ptp(a)).astype(int)        

# Normalised [-1,1]
d = 2.*(a - np.min(a))/np.ptp(a)-1

Jeśli tablica zawiera nan, jednym rozwiązaniem może być po prostu usunięcie ich jako:

def nan_ptp(a):
    return np.ptp(a[np.isfinite(a)])

b = (a - np.nanmin(a))/nan_ptp(a)

Jednak w zależności od kontekstu możesz chcieć traktować naninaczej. Np. Interpoluj wartość, zastępując ją np. 0 lub zgłoś błąd.

Na koniec warto wspomnieć, nawet jeśli nie jest to pytanie OP, standaryzacja :

e = (a - np.mean(a)) / np.std(a)

2
W zależności od tego, czego chcesz, nie jest to poprawne, ponieważ zmienia dane. Na przykład normalizacja do [0, 1] ustawia maksimum na 0, a min na 1. Dla [0, 1] możesz po prostu odjąć wynik od 1, aby uzyskać prawidłową normalizację.
— Alan Turing

Dzięki za wskazanie tego @AlanTuring, który był bardzo niechlujny. Wysłany kod działał TYLKO wtedy, gdy dane zawierały zarówno wartości dodatnie, jak i ujemne. Może to być dość powszechne w przypadku danych audio. Jednak odpowiedź jest aktualizowana, aby znormalizować wszelkie rzeczywiste wartości.
— Tactopoda

1
Ostatni jest również dostępny jako scipy.stats.zscore.
— Lewistrick

d może odwrócić znak próbek. Jeśli chcesz zachować znak, możesz użyć: f = a / np.max(np.abs(a))... chyba, że ​​cała tablica zawiera wszystkie zera (unikaj DivideByZero).
— Pimin Konstantin Kefaloukos

1
numpy.ptp()zwraca 0, jeśli jest to zakres, ale nanjeśli istnieje taki nanw tablicy. Jeśli jednak zakres wynosi 0, normalizacja nie jest zdefiniowana. To rodzi błąd, ponieważ próbujemy podzielić przez 0.
— Tactopoda

37

Możesz również przeskalować za pomocą sklearn. Zaletą jest to, że oprócz wyśrodkowania danych można dostosować normalizację odchylenia standardowego i można to zrobić na dowolnej osi, według cech lub rekordów.

from sklearn.preprocessing import scale
X = scale( X, axis=0, with_mean=True, with_std=True, copy=True )

Argumenty słów kluczowych axis, with_mean, with_stdsą wymowne i są pokazane w ich domyślnego stanu. Argument copywykonuje operację w miejscu, jeśli jest ustawiony na False. Dokumentacja tutaj .


X = scale ([1,2,3,4], axis = 0, with_mean = True, with_std = True, copy = True) daje mi błąd
— Yfiua,

X = scale (np.array ([1,2,3,4]), axis = 0, with_mean = True, with_std = True, copy = True) daje mi tablicę [0,0,0,0]
— Yfiua

sklearn.preprocessing.scale () ma tę wadę, że nie wiesz, co się dzieje. Jaki jest tego czynnik? Jaka kompresja interwału?
— MasterControlProgram

Te metody wstępnego przetwarzania scikit (scale, minmax_scale, maxabs_scale) są przeznaczone do użycia tylko wzdłuż jednej osi (więc albo skaluj próbki (wiersze), albo cechy (kolumny) indywidualnie. Ma to sens w konfiguracji uczenia maszynowego, ale czasami chcesz aby obliczyć zakres dla całej tablicy lub użyj tablic z więcej niż dwoma wymiarami.
— Toby

11

Możesz użyć wersji „i” (jak w idiv, imul ..) i nie wygląda to tak źle:

image /= (image.max()/255.0)

W drugim przypadku możesz napisać funkcję normalizującą n-wymiarową tablicę za pomocą kolumn:

def normalize_columns(arr):
    rows, cols = arr.shape
    for col in xrange(cols):
        arr[:,col] /= abs(arr[:,col]).max()

Czy możesz to wyjaśnić? Nawiasy sprawiają, że zachowuje się inaczej niż bez?
— endolit

1
parafie niczego nie zmieniają. chodziło o użycie /=zamiast = .. / ..
— u0b34a0f6ae

7

Próbujesz skalować wartości min-max z zakresu audiood -1 do +1 oraz imageod 0 do 255.

Używanie sklearn.preprocessing.minmax_scalepowinno łatwo rozwiązać twój problem.

na przykład:

audio_scaled = minmax_scale(audio, feature_range=(-1,1))

i

shape = image.shape
image_scaled = minmax_scale(image.ravel(), feature_range=(0,255)).reshape(shape)

Uwaga : Nie należy mylić z operacją, która skaluje normę (długość) wektora do określonej wartości (zwykle 1), co jest również powszechnie określane jako normalizacja.


4

Prostym rozwiązaniem jest użycie skalerów oferowanych przez bibliotekę sklearn.preprocessing.

scaler = sk.MinMaxScaler(feature_range=(0, 250))
scaler = scaler.fit(X)
X_scaled = scaler.transform(X)
# Checking reconstruction
X_rec = scaler.inverse_transform(X_scaled)

Błąd X_rec-X będzie wynosił zero. Możesz dostosować feature_range do swoich potrzeb, a nawet użyć standardowego skalera sk.StandardScaler ()


3

Próbowałem postępować zgodnie z tym i otrzymałem błąd

TypeError: ufunc 'true_divide' output (typecode 'd') could not be coerced to provided output parameter (typecode 'l') according to the casting rule ''same_kind''

numpyTablica starałem normalizować był integertablicą. Wygląda na to, że przestarzałe rzutowanie typów w wersjach> 1.10i musisz użyć, numpy.true_divide()aby rozwiązać ten problem.

arr = np.array(img)
arr = np.true_divide(arr,[255.0],out=None)

imgbył PIL.Imageprzedmiotem.

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.