Bagaimana cara menormalkan array NumPy dalam rentang tertentu?

136

Setelah melakukan beberapa pemrosesan pada array audio atau gambar, perlu dinormalisasi dalam rentang sebelum dapat ditulis kembali ke file. Ini bisa dilakukan seperti ini:

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

Apakah ada cara fungsi kenyamanan yang kurang verbose untuk melakukan ini? matplotlib.colors.Normalize()sepertinya tidak berhubungan.

endolit
sumber

Jawaban:

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

Menggunakan /=dan *=memungkinkan Anda untuk menghilangkan array sementara menengah, sehingga menghemat beberapa memori. Penggandaan lebih murah daripada pembagian, jadi

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

sedikit lebih cepat dari

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

Karena kita menggunakan metode numpy dasar di sini, saya pikir ini adalah tentang solusi yang efisien dalam numpy.


Operasi di tempat tidak mengubah tipe array wadah. Karena nilai-nilai yang dinormalisasi yang diinginkan adalah float, audiodan imagearray harus memiliki tipe titik titik mengambang sebelum operasi di tempat dilakukan. Jika belum menggunakan tipe floating-point, Anda harus mengonversinya menggunakan astype. Sebagai contoh,

image = image.astype('float64')
unutbu
sumber
7
Mengapa multiplikasi lebih murah daripada pembagian?
endolith
19
Saya tidak tahu persis mengapa. Namun, saya yakin akan klaim tersebut, setelah memeriksanya dengan timeit. Dengan multiplikasi, Anda dapat bekerja dengan satu digit pada satu waktu. Dengan pembagian, terutama dengan pembagi besar, Anda harus bekerja dengan banyak digit, dan "tebak" berapa kali pembagi itu masuk ke dalam dividen. Anda akhirnya melakukan banyak masalah multiplikasi untuk menyelesaikan satu masalah divisi. Algoritma komputer untuk melakukan pembagian mungkin tidak sama dengan pembagian panjang manusia, tetapi bagaimanapun saya percaya itu lebih rumit daripada perkalian.
unutbu
14
Mungkin layak menyebutkan pembagian dengan nol untuk gambar kosong.
cjm2671
7
Multiplikasi @ endolith lebih murah daripada pembagian karena cara penerapannya di tingkat Majelis. Algoritma pembagian tidak dapat diparalelkan dan juga algoritma multiplikasi. en.wikipedia.org/wiki/Binary_multiplier
mjones.udri
5
Meminimalkan jumlah divisi yang mendukung perkalian adalah teknik optimasi yang diketahui dengan baik.
mjones.udri
73

Jika array berisi data positif dan negatif, saya akan pergi dengan:

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

Jika array berisi nan, satu solusi bisa dengan menghapusnya sebagai:

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

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

Namun, tergantung pada konteksnya Anda mungkin ingin memperlakukan secara nanberbeda. Misalnya menginterpolasi nilai, mengganti dengan misalnya 0, atau meningkatkan kesalahan.

Akhirnya, layak disebut bahkan jika itu bukan pertanyaan OP, standardisasi :

e = (a - np.mean(a)) / np.std(a)
Tactopoda
sumber
2
Tergantung pada apa yang Anda inginkan, ini tidak benar, karena membalik data. Misalnya normalisasi ke [0, 1] menempatkan maks pada 0 dan min pada 1. Untuk [0, 1], Anda dapat dengan mudah mengurangi hasil dari 1 untuk mendapatkan normalisasi yang benar.
Alan Turing
Terima kasih telah menunjukkannya di @AlanTuring yang sangat ceroboh. Kode, sebagaimana diposting, HANYA berfungsi jika data mengandung nilai positif dan negatif. Itu mungkin agak umum untuk data audio. Namun, jawaban diperbarui untuk menormalkan nilai-nilai nyata apa pun.
Tactopoda
1
Yang terakhir juga tersedia sebagai scipy.stats.zscore.
Lewistrick
d mungkin membalik tanda sampel. Jika Anda ingin menyimpan tanda itu, Anda dapat menggunakan: f = a / np.max(np.abs(a))... kecuali seluruh array bernilai nol (hindari DivideByZero).
Pimin Konstantin Kefaloukos
1
numpy.ptp()mengembalikan 0, jika itu adalah kisaran, tetapi nanjika ada satu nandalam array. Namun, jika rentangnya 0, normalisasi tidak ditentukan. Ini menimbulkan kesalahan ketika kami mencoba untuk membagi dengan 0.
Tactopoda
37

Anda juga dapat mengubah skala menggunakan sklearn. Keuntungannya adalah Anda dapat menyesuaikan normalisasi standar deviasi, di samping mean-centering data, dan Anda dapat melakukan ini pada sumbu, fitur, atau catatan.

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

Argumen kata kunci axis, with_mean, with_stdjelas diri, dan ditampilkan dalam keadaan standar. Argumen copymelakukan operasi di tempat jika diatur ke False. Dokumentasi di sini .

cjohnson318
sumber
X = skala ([1,2,3,4], sumbu = 0, with_mean = Benar, with_std = Benar, salin = Benar) memberi saya kesalahan
Yfiua
X = skala (np.array ([1,2,3,4]), sumbu = 0, with_mean = Benar, with_std = Benar, salin = Benar) memberi saya array [0,0,0,0]
Yfiua
sklearn.preprocessing.scale () memiliki backdraw yang Anda tidak tahu apa yang sedang terjadi. Apa faktornya? Apa kompresi interval?
MasterControlProgram
Metode preproses scikit ini (skala, minmax_scale, maxabs_scale) dimaksudkan untuk digunakan sepanjang satu sumbu saja (jadi skalakan sampel (baris) atau fitur (kolom) secara terpisah. Ini masuk akal dalam pengaturan pemelajahan mesin, tetapi kadang-kadang Anda ingin untuk menghitung rentang seluruh array, atau gunakan array dengan lebih dari dua dimensi
Toby
11

Anda dapat menggunakan versi "i" (seperti pada idiv, imul ..), dan tidak terlihat setengah buruk:

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

Untuk kasus lain, Anda dapat menulis fungsi untuk menormalkan array n-dimensi dengan colum:

def normalize_columns(arr):
    rows, cols = arr.shape
    for col in xrange(cols):
        arr[:,col] /= abs(arr[:,col]).max()
u0b34a0f6ae
sumber
Bisakah Anda mengklarifikasi ini? Tanda kurung membuatnya berperilaku berbeda dari tanpa?
endolith
1
kurung tidak mengubah apa pun. intinya adalah untuk menggunakan /=bukan = .. / ..
u0b34a0f6ae
7

Anda mencoba skala minimum nilai audioantara -1 dan +1 dan imageantara 0 dan 255.

Menggunakan sklearn.preprocessing.minmax_scale, harus dengan mudah menyelesaikan masalah Anda.

misalnya:

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

dan

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

Catatan : Tidak perlu bingung dengan operasi yang menskala norma (panjang) vektor ke nilai tertentu (biasanya 1), yang juga biasa disebut sebagai normalisasi.

yellow01
sumber
4

Solusi sederhana adalah menggunakan scaler yang ditawarkan oleh perpustakaan 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)

Kesalahan X_rec-X akan menjadi nol. Anda dapat menyesuaikan feature_range untuk kebutuhan Anda, atau bahkan menggunakan skaler standar sk.StandardScaler ()

Pantelis
sumber
3

Saya mencoba mengikuti ini , dan mendapat kesalahan

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

The numpyArray saya mencoba untuk menormalkan adalah integerarray yang. Tampaknya mereka menggunakan tipe casting di versi> 1.10, dan Anda harus menggunakannya numpy.true_divide()untuk menyelesaikannya.

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

imgadalah sebuah PIL.Imageobjek.

Anak manja
sumber