Как нормализовать массив 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

Поскольку здесь мы используем базовые методы numpy, я думаю, что это настолько эффективное решение в numpy, насколько это возможно.


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

image = image.astype('float64')

7
Почему умножение дешевле деления?
— endolith

19
Не знаю точно почему. Однако я уверен в претензии, проверив ее timeit. С умножением вы можете работать с одной цифрой за раз. При делении, особенно с большими делителями, вы должны работать со многими цифрами и «угадывать», сколько раз делитель входит в делимое. Чтобы решить одну задачу деления, вы выполняете множество задач умножения. Компьютерный алгоритм деления может отличаться от человеческого деления в столбик, но, тем не менее, я считаю, что он сложнее умножения.
— 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, или вызовите ошибку.

Наконец, стоит упомянуть, даже если это не вопрос OP, стандартизация :

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

2
В зависимости от того, что вы хотите, это неверно, поскольку оно переворачивает данные. Например, нормализация к [0, 1] ставит max на 0, а min на 1. Для [0, 1] вы можете просто вычесть результат из 1, чтобы получить правильную нормализацию.
— Алан Тьюринг

Спасибо, что указали на @AlanTuring, это было очень небрежно. Код в том виде, в котором он был опубликован, работал ТОЛЬКО, если данные содержали как положительные, так и отрицательные значения. Это может быть довольно распространено для аудиоданных. Однако ответ обновляется для нормализации любых реальных значений.
— Tactopoda

1
Последний также доступен как scipy.stats.zscore.
— Льюистрик

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 )

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


X = scale ([1,2,3,4], axis = 0, with_mean = True, with_std = True, copy = True) выдает ошибку
— Yfiua

X = scale (np.array ([1,2,3,4]), axis = 0, with_mean = True, with_std = True, copy = True) дает мне массив [0,0,0,0]
— Yfiua 06

sklearn.preprocessing.scale () имеет заднюю часть, что вы не знаете, что происходит. Что это за фактор? Какая компрессия интервала?
— MasterControlProgram

Эти методы предварительной обработки scikit (scale, minmax_scale, maxabs_scale) предназначены для использования только по одной оси (поэтому можно масштабировать выборки (строки) или элементы (столбцы) по отдельности. Это имеет смысл в настройке машинного обучения, но иногда вам нужно для вычисления диапазона по всему массиву или используйте массивы с более чем двумя измерениями.
— Тоби

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

Вы можете это прояснить? Скобки заставляют его вести себя иначе, чем без?
— endolith

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

7

Вы пытаетесь масштабировать min-max значения от 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 будет равна нулю. Вы можете настроить feature_range под свои нужды или даже использовать стандартный скейлер 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.