如何将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()似乎无关。

Answers:


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。由于所需的标准化值是浮点型,因此在执行就地操作之前,audioand image数组需要具有浮点dtype。如果它们还不是浮点dtype,则需要使用进行转换astype。例如,

image = image.astype('float64')

7
为什么乘法比除法便宜?
endlith

19
我不知道为什么。但是,我已对请求进行了及时检查,对此声明充满信心。使用乘法,可以一次使用一位数字。对于除法,尤其是对于大除数,您必须使用许多数字,并且“猜测”除数进入除数的次数。您最终要做许多乘法问题来解决一个除法问题。进行除法运算的计算机算法可能与人的长除法不同,但是我相信它比乘法更复杂。
unutbu

14
可能值得一提的是空白图像除以零。
cjm2671 2014年

7
@endolith乘法比除法便宜,因为它是在汇编级别实现的。除法算法和乘法算法不能并行化。en.wikipedia.org/wiki/Binary_multiplier
mjones.udri

5
使乘法的数量最小化是乘法的一项众所周知的优化技术。
mjones.udri '16

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非常草率。所发布的代码仅在数据同时包含正值和负值时才起作用。对于音频数据,这可能相当普遍。但是,答案会更新以规范化任何实际值。
塔克托波达

1
最后一个也可以作为获得scipy.stats.zscore
Lewistrick

d可能会翻转样品的迹象。如果要保留符号,则可以使用:f = a / np.max(np.abs(a))...除非整个数组都为零(避免使用DivideByZero)。
Pimin Konstantin Kefaloukos

1
numpy.ptp()如果是范围,nan则返回0,但如果nan数组中存在1 。但是,如果范围为0,则未定义归一化。当我们试图用分0这就提出了一个错误
Tactopoda

37

您也可以使用重新缩放sklearn。优势在于,除了对数据进行均值居中之外,还可以调整标准差的归一化,并且可以在任一轴上,通过要素或按记录进行校准。

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

关键词参数axiswith_meanwith_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 2016年

sklearn.preprocessing.scale()具有您不知道发生了什么的退步。是什么因素?什么压缩间隔?
MasterControlProgram

这些scikit预处理方法(scale,minmax_scale,maxabs_scale)只能沿一个轴使用(因此可以分别缩放样本(行)或要素(列)。在机器学习设置中这是有意义的,但有时您需要计算整个数组的范围,或使用二维以上的数组
Toby

11

您可以使用“ i”版本(如idiv中的imul ..),它看起来还不错:

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

在另一种情况下,您可以编写一个函数来通过colums标准化n维数组:

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

你能澄清一下吗?括号使行为与没有行为有所不同?
endlith

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将为零。您可以根据需要调整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对象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.