Python numpy数组的例外情况
我希望在第二个数组中保留的值周围移除常数范围内的某些值。i、 e.我有一个大型np数组,我想使用另一个特定值数组,比如[20,50,90210],删除该数组中的值+-3。因此,如果我的大数组是[14,21,48,54,92215],我希望返回[14,54215]。这些值是双精度的,所以我试图避免创建一个大的掩码数组来删除特定的值,而是使用一个范围 您提到要避免使用大型遮罩阵列。除非您的“大数组”和“特定值”数组都非常大,否则我不会试图避免这种情况。通常,使用Python numpy数组的例外情况,python,arrays,numpy,indexing,Python,Arrays,Numpy,Indexing,我希望在第二个数组中保留的值周围移除常数范围内的某些值。i、 e.我有一个大型np数组,我想使用另一个特定值数组,比如[20,50,90210],删除该数组中的值+-3。因此,如果我的大数组是[14,21,48,54,92215],我希望返回[14,54215]。这些值是双精度的,所以我试图避免创建一个大的掩码数组来删除特定的值,而是使用一个范围 您提到要避免使用大型遮罩阵列。除非您的“大数组”和“特定值”数组都非常大,否则我不会试图避免这种情况。通常,使用numpy时,最好允许创建相对较大的临
numpy
时,最好允许创建相对较大的临时数组
然而,如果您确实需要更严格地控制内存使用,那么您有几个选择。一个典型的技巧是只对操作的一部分进行矢量化,并在较短的输入上进行迭代(这在下面的第二个示例中显示)。它避免了在Python中使用嵌套循环,并且可以显著减少所涉及的内存使用
我将展示三种不同的方法。还有其他几种(如果您真的需要严格的控制和性能,可以选择C或Cython),但希望这能给您一些想法
另一方面,对于这些小输入,数组创建的开销将压倒差异。我所指的速度和内存使用仅适用于大型(>1e6个元素)阵列
完全矢量化,但大部分内存使用
最简单的方法是一次计算所有距离,然后将遮罩缩小到与初始阵列相同的形状。例如:
import numpy as np
vals = np.array([14,21,48,54,92,215])
other = np.array([20,50,90,210])
dist = np.abs(vals[:,None] - other[None,:])
mask = np.all(dist > 3, axis=1)
result = vals[mask]
部分矢量化的中间内存使用
另一个选项是为“特定值”数组中的每个元素迭代构建掩码。这将迭代较短的“特定值”数组的所有元素(在本例中,也称为other
):
速度最慢,但内存使用率最低
最后,如果您确实想减少内存使用,可以迭代大数组中的每个项:
import numpy as np
vals = np.array([14,21,48,54,92,215])
other = np.array([20,50,90,210])
result = []
for num in vals:
if np.all(np.abs(num - other) > 3):
result.append(num)
在这种情况下,临时列表可能比以前版本中的掩码占用更多内存。但是,如果需要,可以使用np.fromiter
来避免临时列表。下面的定时比较显示了这方面的示例
时间比较
让我们比较一下这些函数的速度。我们将在“大数组”中使用10000000个元素,在“特定值”数组中使用4个值。这些函数的相对速度和内存使用很大程度上取决于这两个数组的大小,所以你只应该把它看作是一个模糊的准则。
import numpy as np
vals = np.random.random(1e7)
other = np.array([0.1, 0.5, 0.8, 0.95])
tolerance = 0.05
def basic(vals, other, tolerance):
dist = np.abs(vals[:,None] - other[None,:])
mask = np.all(dist > tolerance, axis=1)
return vals[mask]
def intermediate(vals, other, tolerance):
mask = np.ones(len(vals), dtype=bool)
for num in other:
dist = np.abs(vals - num)
mask &= dist > tolerance
return vals[mask]
def slow(vals, other, tolerance):
def func(vals, other, tolerance):
for num in vals:
if np.all(np.abs(num - other) > tolerance):
yield num
return np.fromiter(func(vals, other, tolerance), dtype=vals.dtype)
在这种情况下,部分矢量化的版本胜出。在大多数情况下,VAL
明显长于其他
的情况下,这是意料之中的。然而,第一个例子(basic
)几乎同样快,而且可以说更简单
In [7]: %timeit basic(vals, other, tolerance)
1 loops, best of 3: 1.45 s per loop
In [8]: %timeit intermediate(vals, other, tolerance)
1 loops, best of 3: 917 ms per loop
In [9]: %timeit slow(vals, other, tolerance)
1 loops, best of 3: 2min 30s per loop
无论您选择哪种实现方式,这些都是常见的矢量化“技巧”,在许多问题中都会出现。在Python、Matlab、R等高级语言中,如果内存使用有问题,尝试完全矢量化,然后混合矢量化和显式循环通常是有用的。哪一个最好通常取决于输入的相对大小,但在高级科学编程中优化速度与内存使用时,这是一种常见的模式。您可以尝试:
def closestmatch(x, y):
val = np.abs(x-y)
return(val.min()>=3)
然后:
如果
a
比b
大得多,这是有道理的。然而,一旦你在b
数组中超过几十万个元素,它就会非常慢。非常感谢。我尝试了一些类似于bottom的方法来处理一个巨大的光谱数据集,但速度非常慢。由于我对数百到数千个长度为2150的双精度一维数组使用了一组常量异常,因此我认为使用常量掩码实际上是可行的。同时请记住,内存使用可能不像您想象的那么大。一个100万元素的64位浮点numpy阵列几乎正好是8MB内存(8MB+几十字节的开销)。相同长度的布尔掩码只有1MB。无论如何,您可以在内存中创建一些临时副本,而且它几乎总是比在Python中迭代数组更快。不过,无论哪种方式,numpy
都可以在需要时对内存进行严格控制。
def closestmatch(x, y):
val = np.abs(x-y)
return(val.min()>=3)
b[np.array([closestmatch(a, x) for x in b])]