Wie normalisiere ich ein NumPy-Array auf einen bestimmten Bereich?

135

Nach einigen Verarbeitungen an einem Audio- oder Bildarray muss es innerhalb eines Bereichs normalisiert werden, bevor es in eine Datei zurückgeschrieben werden kann. Dies kann folgendermaßen geschehen:

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

Gibt es eine weniger ausführliche und bequeme Möglichkeit, dies zu tun? matplotlib.colors.Normalize()scheint nicht verwandt zu sein.

Endolith
quelle

Antworten:

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

Mit /=und *=können Sie ein temporäres Zwischenarray entfernen und so Speicherplatz sparen. Multiplikation ist also billiger als Division

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

ist geringfügig schneller als

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

Da wir hier grundlegende Numpy-Methoden verwenden, denke ich, dass dies eine möglichst effiziente Lösung für Numpy ist.


In-Place-Vorgänge ändern den d-Typ des Container-Arrays nicht. Da die gewünschten normalisierten Werte Gleitkommazahlen sind, müssen die Arrays audiound den imageGleitkomma-Typ Gleitkomma haben, bevor die In-Place-Operationen ausgeführt werden. Wenn sie noch nicht vom Gleitkomma-Typ sind, müssen Sie sie mit konvertieren astype. Beispielsweise,

image = image.astype('float64')
unutbu
quelle
7
Warum ist Multiplikation billiger als Division?
Endolith
19
Ich weiß nicht genau warum. Ich bin jedoch zuversichtlich, dass die Behauptung rechtzeitig überprüft wurde. Mit der Multiplikation können Sie jeweils mit einer Ziffer arbeiten. Bei der Division, insbesondere bei großen Divisoren, müssen Sie mit vielen Ziffern arbeiten und "raten", wie oft der Divisor in die Dividende geht. Am Ende machen Sie viele Multiplikationsprobleme, um ein Teilungsproblem zu lösen. Der Computeralgorithmus für die Division ist möglicherweise nicht der gleiche wie die menschliche Long-Division, aber ich glaube trotzdem, dass er komplizierter ist als die Multiplikation.
Unutbu
14
Erwähnenswert ist wahrscheinlich eine Division durch Null für leere Bilder.
cjm2671
7
Die @ endolith-Multiplikation ist aufgrund der Art und Weise, wie sie auf Assembly-Ebene implementiert wird, kostengünstiger als die Division. Divisionsalgorithmen können nicht parallelisiert werden, ebenso wenig wie Multiplikationsalgorithmen. en.wikipedia.org/wiki/Binary_multiplier
mjones.udri
5
Das Minimieren der Anzahl von Divisionen zugunsten von Multiplikationen ist eine bekannte Optimierungstechnik.
mjones.udri
71

Wenn das Array sowohl positive als auch negative Daten enthält, würde ich gehen mit:

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

Wenn das Array enthält nan, könnte eine Lösung darin bestehen, sie einfach wie folgt zu entfernen:

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

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

Je nach Kontext möchten Sie jedoch möglicherweise nanunterschiedlich behandeln . ZB interpolieren Sie den Wert, ersetzen Sie ihn durch zB 0 oder lösen Sie einen Fehler aus.

Erwähnenswert, auch wenn es nicht die Frage von OP ist, Standardisierung :

e = (a - np.mean(a)) / np.std(a)
Tactopoda
quelle
2
Je nachdem, was Sie möchten, ist dies nicht korrekt, da die Daten umgedreht werden. Zum Beispiel setzt die Normalisierung auf [0, 1] das Maximum auf 0 und das Minimum auf 1. Für [0, 1] können Sie das Ergebnis einfach von 1 subtrahieren, um die richtige Normalisierung zu erhalten.
Alan Turing
Vielen Dank für den Hinweis, dass @AlanTuring sehr schlampig war. Der Code, wie veröffentlicht, funktionierte NUR, wenn die Daten sowohl positive als auch negative Werte enthielten. Dies kann bei Audiodaten häufig vorkommen. Die Antwort wird jedoch aktualisiert, um alle realen Werte zu normalisieren.
Tactopoda
1
Der letzte ist auch als erhältlich scipy.stats.zscore.
Lewistrick
d könnte das Vorzeichen von Samples umdrehen. Wenn Sie das Vorzeichen behalten möchten, können Sie f = a / np.max(np.abs(a))Folgendes verwenden: ... es sei denn, das gesamte Array enthält alle Nullen (vermeiden Sie DivideByZero).
Pimin Konstantin Kefaloukos
1
numpy.ptp()Gibt 0 zurück, wenn dies der Bereich ist, aber nanwenn es einen nanim Array gibt. Wenn der Bereich jedoch 0 ist, ist die Normalisierung nicht definiert. Dies wirft einen Fehler auf, wenn wir versuchen, mit 0 zu teilen.
Tactopoda
37

Sie können auch mit neu skalieren sklearn. Die Vorteile bestehen darin, dass Sie die Standardabweichung zusätzlich zur Mittelwertzentrierung der Daten normalisieren können und dies entweder auf der Achse, nach Features oder nach Datensätzen.

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

Die Keyword - Argumente axis, with_mean, with_stdsind selbsterklärend und werden in ihrem Standardzustand gezeigt. Das Argument copyführt die Operation direkt aus, wenn es auf gesetzt ist False. Dokumentation hier .

cjohnson318
quelle
X = Skala ([1,2,3,4], Achse = 0, with_mean = True, with_std = True, copy = True) gibt mir einen Fehler
Yfiua
X = scale (np.array ([1,2,3,4]), axis = 0, with_mean = True, with_std = True, copy = True) gibt mir ein Array von [0,0,0,0]
Yfiua
sklearn.preprocessing.scale () hat den Rückzug, dass Sie nicht wissen, was los ist. Was ist der Faktor? Welche Komprimierung des Intervalls?
MasterControlProgram
Diese Scikit-Vorverarbeitungsmethoden (scale, minmax_scale, maxabs_scale) dürfen nur entlang einer Achse verwendet werden (skalieren Sie also entweder die Samples (Zeilen) oder die Features (Spalten) einzeln. Dies ist in einem maschinellen Lernsetup sinnvoll, aber manchmal möchten Sie es um den Bereich über das gesamte Array zu berechnen, oder verwenden Sie Arrays mit mehr als zwei Dimensionen.
Toby
11

Sie können die Version "i" (wie in idiv, imul ..) verwenden, und sie sieht nicht schlecht aus:

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

Für den anderen Fall können Sie eine Funktion schreiben, um ein n-dimensionales Array durch Spalten zu normalisieren:

def normalize_columns(arr):
    rows, cols = arr.shape
    for col in xrange(cols):
        arr[:,col] /= abs(arr[:,col]).max()
u0b34a0f6ae
quelle
Können Sie das klarstellen? Die Klammern machen es anders als ohne?
Endolith
1
Klammern ändern nichts. der Punkt war, /=anstelle von = .. / ..
u0b34a0f6ae
7

Sie versuchen, die Werte audiozwischen -1 und +1 sowie imagezwischen 0 und 255 min-max zu skalieren .

Verwenden sklearn.preprocessing.minmax_scale, sollte Ihr Problem leicht lösen.

z.B:

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

und

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

Hinweis : Nicht zu verwechseln mit der Operation, bei der die Norm (Länge) eines Vektors auf einen bestimmten Wert (normalerweise 1) skaliert wird , der üblicherweise auch als Normalisierung bezeichnet wird.

gelb01
quelle
4

Eine einfache Lösung ist die Verwendung der Skalierer, die von der Bibliothek sklearn.preprocessing angeboten werden.

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)

Der Fehler X_rec-X ist Null. Sie können den feature_range an Ihre Bedürfnisse anpassen oder sogar einen Standard-Scaler sk.StandardScaler () verwenden.

Pantelis
quelle
3

Ich habe versucht , folgende diese , und bekam den Fehler

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

Das numpyArray, das ich zu normalisieren versuchte, war ein integerArray. Es scheint, dass sie das Typ-Casting in Versionen> veraltet 1.10haben, und Sie müssen es verwenden numpy.true_divide(), um das zu beheben.

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

imgwar ein PIL.ImageObjekt.

Verzogenes Gör
quelle