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.

endolit
źródło

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')
unutbu
źródło
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)
Tactopoda
źródło
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 .

cjohnson318
źródło
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()
u0b34a0f6ae
źródło
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.

fabda01
źródło
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 ()

Pantelis
źródło
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.

srdg
źródło