简体   繁体   English

了解numpy.where的运行时以及等效的替代方法

[英]Understanding the runtime of numpy.where and equivalent alternatives

According to http://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html , if x and y are given and input arrays are 1-D, where is equivalent to [xv if c else yv for (c,xv, yv) in zip(x!=0, 1/x, x)] . 根据http://docs.scipy.org/doc/numpy/reference/produced/numpy.where.html ,如果x和y给定且输入数组为一维,则等效于[xv if c else yv for (c,xv, yv) in zip(x!=0, 1/x, x)] When doing runtime benchmarks, however, they have significantly different speeds: 但是,在进行运行时基准测试时,它们的速度差异很大:

x = np.array(range(-500, 500))

%timeit np.where(x != 0, 1/x, x)
10000 loops, best of 3: 23.9 µs per loop

%timeit [xv if c else yv for (c,xv, yv) in zip(x!=0, 1/x, x)]
1000 loops, best of 3: 232 µs per loop

Is there a way I can rewrite the second form so that it has a similar runtime to the first? 有没有办法可以重写第二种形式,使其具有与第一种形式相似的运行时间? The reason I ask is because I'd like to use a slightly modified version of the second case to avoid division by zero errors: 我问的原因是因为我想使用第二种情况的稍作修改的版本,以避免除以零错误:

[1 / xv if c else xv for (c,xv) in zip(x!=0, x)]

Another question: the first case returns a numpy array while the second case returns a list. 另一个问题:第一种情况返回一个numpy数组,而第二种情况返回一个列表。 Is the most efficient way to have the second case return an array is to first make a list and then convert the list to an array? 使第二种情况返回数组的最有效方法是首先创建一个列表,然后将列表转换为数组吗?

np.array([xv if c else yv for (c,xv, yv) in zip(x!=0, 1/x, x)])

Thanks! 谢谢!

You just asked about 'delaying' the 'where': 您刚刚问过“延迟”“位置”:

numpy.where : how to delay evaluating parameters? numpy.where:如何延迟评估参数?

and someone else just asked about divide by zero: 而其他人刚刚问过零除:

Replace all elements of a matrix by their inverses 用其逆矩阵替换矩阵的所有元素

When people say that where is similar to the list comprehension, they attempt to describe the action, not the actual implementation. 当人们说where是类似于列表理解,他们试图描述的行动,而不是实际执行。

np.where called with just one argument is the same as np.nonzero . 仅用一个参数调用的np.wherenp.nonzero相同。 This quickly (in compiled code) loops through the argument, and collects the indices of all non-zero values. 快速(在已编译的代码中)循环遍历参数,并收集所有非零值的索引。

np.where when called with 3 arguments, returns a new array, collecting values from the 2 and 3rd arguments based on the nonzero values. np.where当使用3个参数调用时,将返回一个新数组,并基于nonzero值从2和3rd参数中收集值。 But it's important to realize that those arguments must be other arrays. 但是重要的是要意识到那些参数必须是其他数组。 They are not functions that it evaluates element by element. 它们不是按元素评估的函数。

So the where is more like: 所以where更像是:

m1 = 1/xv
m2 = xv
[v1 if c else v2 for (c, v1, v2) in zip(x!=0, m1, m2)]

It's easy to run this iteration in compiled code because it just involves 3 arrays of matching size (matching via broadcasting). 在编译的代码中运行此迭代很容易,因为它只涉及3个匹配大小的数组(通过广播匹配)。

np.array([...]) is a reasonable way of converting a list (or list comprehension) into an array. np.array([...])是一种将列表(或列表理解)转换为数组的合理方法。 It may be a little slower than some alternatives because np.array is a powerful general purpose function. 它可能会比某些替代方法慢一些,因为np.array是强大的通用函数。 np.fromiter([], dtype) may be faster in some cases, because it isn't as general (you have to specify dtype, and it it only works with 1d). 在某些情况下, np.fromiter([], dtype)可能会更快,因为它不那么通用(您必须指定dtype,并且它仅适用于1d)。

There are 2 time proven strategies for getting more speed in element-by-element calculations: 有2种行之有效的策略可以提高逐元素计算的速度:

  • use packages like numba and cython to rewrite the problem as c code 使用numbacython等软件包将问题重写为c代码

  • rework your calculations to use existing numpy methods. 重新计算以使用现有的numpy方法。 The use of masking to avoid divide by zero is a good example of this. 使用屏蔽来避免被零除就是一个很好的例子。

===================== =====================

np.ma.where , the version for masked arrays is written in Python. np.ma.where ,掩码数组的版本是用Python编写的。 Its code might be instructive. 其代码可能具有启发性。 Note in particular this piece: 请特别注意以下内容:

# Construct an empty array and fill it
d = np.empty(fc.shape, dtype=ndtype).view(MaskedArray)
np.copyto(d._data, xv.astype(ndtype), where=fc)
np.copyto(d._data, yv.astype(ndtype), where=notfc)

It makes a target, and then selectively copies values from the 2 inputs arrays, based on the condition array. 它成为目标,然后根据条件数组有选择地从2个输入数组中复制值。

You can avoid division by zero while maintaining performance by using advanced indexing: 通过使用高级索引,可以在保持性能的同时避免被零除:

x = np.arange(-500, 500)

result = np.empty(x.shape, dtype=float) # set the dtype to whatever is appropriate
nonzero = x != 0
result[nonzero] = 1/x[nonzero]
result[~nonzero] = 0

If you for some reason want to bypass an error with numpy it might be worth looking into the errstate context: 如果出于某种原因要绕过numpy错误,则可能值得研究errstate上下文:

x  = np.array(range(-500, 500))

with np.errstate(divide='ignore'): #ignore zero-division error
    x = 1/x
x[x!=x] = 0 #convert inf and NaN's to 0

Consider changing the array in place by using np.put() : 考虑使用np.put() 在适当位置更改数组:

In [56]: x = np.linspace(-1, 1, 5)

In [57]: x
Out[57]: array([-1. , -0.5,  0. ,  0.5,  1. ])

In [58]: indices = np.argwhere(x != 0)

In [59]: indices
Out[59]:
array([[0],
       [1],
       [3],
       [4]], dtype=int64)

In [60]: np.put(x, indices, 1/x[indices])

In [61]: x
Out[61]: array([-1., -2.,  0.,  2.,  1.])

The approach above does not create a new array, which could be very convenient if x is a large array. 上面的方法不会创建新的数组,如果x是一个大数组,这可能会非常方便。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM