如何将 NumPy 数组标准化到一定范围内?

Posted

技术标签:

【中文标题】如何将 NumPy 数组标准化到一定范围内?【英文标题】:How to normalize a NumPy array to within a certain range? 【发布时间】:2010-12-16 15:23:55 【问题描述】:

对音频或图像数组进行一些处理后,需要在一定范围内对其进行归一化,然后才能将其写回文件。可以这样做:

# 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() 好像没有关系。

【问题讨论】:

【参考方案1】:
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。由于所需的标准化值是浮点数,audioimage 数组需要在执行就地操作之前具有浮点数据类型。 如果它们还不是浮点 dtype,则需要使用 astype 转换它们。例如,

image = image.astype('float64')

【讨论】:

为什么乘法比除法便宜? 我不知道为什么。但是,我对这个声明很有信心,我已经用 timeit 进行了检查。通过乘法,您可以一次使用一个数字。对于除法,尤其是大除数,您必须处理许多数字,并“猜测”除数有多少次进入被除数。你最终会做许多乘法问题来解决一个除法问题。做除法的计算机算法可能与人类的长除法不同,但我相信它比乘法更复杂。 对于空白图像,可能值得一提的是除以零。 @endolith 乘法比除法更便宜,因为它是在汇编级别实现的。除法算法和乘法算法一样不能并行化。 en.wikipedia.org/wiki/Binary_multiplier 最小化除法次数以支持乘法是一种众所周知的优化技术。【参考方案2】:

如果数组同时包含正数据和负数据,我会选择:

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 的问题,standardization:

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

【讨论】:

根据您的需要,这是不正确的,因为它会翻转数据。例如,对 [0, 1] 的归一化将最大值设为 0,将最小值设为 1。对于 [0, 1],您可以简单地从 1 中减去结果以获得正确的归一化。 感谢您指出@AlanTuring 非常草率。发布的代码仅在数据包含正值和负值时才有效。这对于音频数据来说可能相当普遍。但是,答案会更新以规范化任何实际值。 最后一个也可以scipy.stats.zscore. d 可能会翻转样本的符号。如果要保留符号,可以使用:f = a / np.max(np.abs(a))... 除非整个数组全为零(避免 DivideByZero)。 numpy.ptp() 如果是范围则返回 0,但如果数组中有一个 nan,则返回 nan。但是,如果范围为 0,则未定义归一化。当我们尝试除以 0 时,这会引发错误。【参考方案3】:

您还可以使用sklearn 重新调整大小。优点是除了对数据进行均值居中之外,您还可以调整标准偏差的标准化,并且您可以在任一轴上、按特征或按记录进行此操作。

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

关键字参数axiswith_meanwith_std 是不言自明的,并以其默认状态显示。如果参数copy 设置为False,则它会就地执行操作。文档here。

【讨论】:

X = scale( [1,2,3,4], axis=0, with_mean=True, with_std=True, copy=True ) 给我一个错误 X = scale( np.array([1,2,3,4]), axis=0, with_mean=True, with_std=True, copy=True ) 给我一个 [0 ,0,0,0] sklearn.preprocessing.scale() 有你不知道发生了什么的后退。是什么因素?区间压缩多少? 这些 scikit 预处理方法(scale、minmax_scale、maxabs_scale)只能沿一个轴使用(因此可以单独缩放样本(行)或特征(列)。这在机器学习设置,但有时您想计算整个数组的范围,或者使用超过二维的数组。 不适用于维度 > 2 的数组。【参考方案4】:

您正在尝试将 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)

注意:不要与将向量的norm(长度)缩放到某个值(通常为 1)的操作相混淆,这通常也称为归一化。

【讨论】:

【参考方案5】:

您可以使用“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()

【讨论】:

你能澄清一下吗?括号使它的行为与没有括号不同? 括号不会改变任何东西。关键是使用/= 而不是= .. / .. 【参考方案6】:

一个简单的解决方案是使用 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()

【讨论】:

不适用于一维数组 当然,如果您查阅函数的文档 (scikit-learn.org/stable/modules/generated/…) 数组 X: Xarray-like of shape (n_samples, n_features) 用于计算每个特征的最小值和最大值使用的数据用于以后沿特征轴缩放。你可以只做 X=X[..., np.newaxis] (多个样本,一个特征),它适用于一维数组。【参考方案7】:

我尝试关注this,得到了错误

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 对象。

【讨论】:

【参考方案8】:

This answer 到 similar question 为我解决了这个问题

np.interp(a, (a.min(), a.max()), (-1, +1))

【讨论】:

以上是关于如何将 NumPy 数组标准化到一定范围内?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 numpy 数组转换为标准 TensorFlow 格式?

创建numpy数组,其中值在其他两个相同大小排列的范围内

如何将 NumPy 数组标准化为单位向量?

如何将 NumPy 数组标准化为单位向量?

Python:使用pandas和numpy计算标准差的区别

python - 如何在python numpy中标准化二维数组的一维? [复制]