Як нормалізувати масив NumPy в межах певного діапазону?


136

Виконавши деяку обробку на масиві аудіо чи зображень, її потрібно нормалізувати в межах діапазону, перш ніж можна буде записати назад у файл. Це можна зробити так:

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

Чи є менш детальний, зручний функціональний спосіб зробити це? matplotlib.colors.Normalize()схоже, не пов'язані.

Відповіді:


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

Використання /=та *=дозволяє усунути проміжний тимчасовий масив, заощаджуючи таким чином деяку кількість пам'яті. Множення менш дороге, ніж ділення, так

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

незначно швидше, ніж

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

Оскільки тут ми використовуємо основні методи нумерування, я думаю, що це приблизно настільки ефективне рішення, як це можливо.


Операції на місці не змінюють тип матриці контейнерів. Так як бажані нормовані значення поплавців, то audioі imageмасиви повинні мати з плаваючою комою точкою DTYPE перед операцією в місці виконується. Якщо вони вже не є типом з плаваючою комою, вам потрібно буде їх перетворити за допомогою astype. Наприклад,

image = image.astype('float64')

7
Чому множення менш дороге, ніж ділення?
ендоліт

19
Я не знаю точно, чому. Однак я впевнений у претензії, перевіривши її з часом. При множенні ви можете працювати з однією цифрою одночасно. З поділом, особливо з великими дільниками, ви повинні працювати з багатьма цифрами і "здогадуватися", скільки разів дільник переходить у дивіденд. Ви вирішите зробити багато задач на множення для вирішення однієї задачі на поділ. Комп'ютерний алгоритм для ділення може не збігатися з довгим поділом людини, але все ж я вважаю, що це складніше, ніж множення.
unutbu

14
Напевно, варто згадати ділення на нуль для порожніх зображень.
cjm2671

7
Примноження @endolith дешевше, ніж ділення через спосіб його реалізації на рівні складання. Алгоритми ділення не можуть бути паралельними, а також алгоритми множення. en.wikipedia.org/wiki/Binary_multiplier
mjones.udri

5
Мінімізація кількості поділів на користь множення - це добре відома методика оптимізації.
mjones.udri

73

Якщо масив містить як позитивні, так і негативні дані, я б перейшов до:

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

Якщо масив містить nan, одним із рішень може бути просто видалити їх як:

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

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

Однак, залежно від контексту, ви можете по- nanрізному ставитися до цього . Наприклад, інтерполюйте значення, замінивши, наприклад, 0, або підвищивши помилку.

Нарешті, варто згадати, навіть якщо це не питання ОП, стандартизація :

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

2
Залежно від того, що ви хочете, це не правильно, оскільки воно перевертає дані. Наприклад, нормалізація на [0, 1] ставить максимум у 0, а хв на 1. Для [0, 1] можна просто відняти результат від 1, щоб отримати правильну нормалізацію.
Алан Тьюрінг

Дякуємо, що вказали на це @AlanTuring, що було дуже неохайно. Код, як розміщено, ТОЛЬКО працював, якщо дані містили як позитивні, так і негативні значення. Це може бути досить поширеним для аудіоданих. Однак відповідь оновлюється, щоб нормалізувати будь-які реальні значення.
Тактопода

1
Останній також доступний як scipy.stats.zscore.
Lewistrick

d може перевернути знак зразків. Якщо ви хочете зберегти знак, ви можете використовувати: f = a / np.max(np.abs(a))... якщо весь масив не дорівнює всім нулям (уникайте DivideByZero).
Пімін Костянтин Кефалукос

1
numpy.ptp()повертає 0, якщо це діапазон, але nanякщо nanв масиві є такий. Однак якщо діапазон дорівнює 0, нормалізація не визначена. Це спричиняє помилку, коли ми намагаємось ділити з 0.
Tactopoda

37

Ви також можете змінити масштаб за допомогою sklearn. Переваги полягають у тому, що ви можете налаштувати нормалізацію стандартного відхилення, окрім середнього центрування даних, і що це можна зробити на будь-якій осі, за ознаками або за записами.

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

Ключове слово аргументи axis, with_mean, with_stdсамооб'ясняющіе і приведені в стан по замовчуванням. Аргумент copyвиконує операцію на місці, якщо вона встановлена False. Документація тут .


X = шкала ([1,2,3,4], вісь = 0, with_mean = True, with_std = True, копія = True) дає мені помилку
Yfiua

X = шкала (np.array ([1,2,3,4]), вісь = 0, with_mean = True, with_std = True, копія = True) дає мені масив [0,0,0,0]
Іфіа

sklearn.preprocessing.scale () має зворотний зв'язок, який ви не знаєте, що відбувається. Що таке фактор? Яке стиснення інтервалу?
MasterControlProgram

Ці методи попередньої обробки scikit (масштаб, minmax_scale, maxabs_scale) призначені для використання лише вздовж однієї осі (тому або масштабуйте зразки (рядки), або функції (стовпці) окремо. Це має сенс у налаштуваннях, які навчаються в машині, але іноді потрібно щоб обчислити діапазон по всьому масиву або використовувати масиви з більш ніж двома вимірами
Toby

11

Ви можете використовувати версію "i" (як у idiv, imul ..) версії, і це не виглядає наполовину погано:

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

В іншому випадку ви можете написати функцію для нормалізації n-мірного масиву колонками:

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

Ви можете уточнити це? В дужках змушують себе вести себе інакше, ніж без?
ендоліт

1
парантези нічого не змінюють. справа полягала у використанні /=замість = .. / ..
u0b34a0f6ae

7

Ви намагаєтеся мінімізувати масштаби значень від audio-1 до +1 та imageміж 0 і 255.

Використовуючи sklearn.preprocessing.minmax_scale, слід легко вирішити вашу проблему.

наприклад:

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

і

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

Примітка : Не слід плутати операцію, яка масштабує норму (довжину) вектора до певного значення (зазвичай 1), яке також прийнято називати нормалізацією.


4

Просте рішення - використання шкал, запропонованих бібліотекою 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)

Похибка X_rec-X буде дорівнює нулю. Ви можете налаштувати функцію_порядку під свої потреби або навіть використовувати стандартний масштабувач sk.StandardScaler ()


3

Я спробував виконати це , і отримав помилку

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

numpyМасив , який я намагався нормалізують був integerмасив. Здається, у них є застарілий тип кастингу у версіях> 1.10, і вам доведеться використовувати їх numpy.true_divide()для вирішення.

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

imgбув PIL.Imageоб’єктом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.