如何使用矢量化从数组中最接近值的数组中选择值?

时间:2016-11-21 04:20:30

标签: python performance numpy vectorization

我有一个值数组,我想根据哪个选项线性最接近的选择数组替换。

catch是在运行时定义的选项的大小。

import numpy as np
a = np.array([[0, 0, 0], [4, 4, 4], [9, 9, 9]])
choices = np.array([1, 5, 10])

如果选择的大小是静态的,我只需使用np.where

d = np.where(np.abs(a - choices[0]) > np.abs(a - choices[1]), 
      np.where(np.abs(a - choices[0]) > np.abs(a - choices[2]), choices[0], choices[2]),
         np.where(np.abs(a - choices[1]) > np.abs(a - choices[2]), choices[1], choices[2]))

获得输出:

>>d
>>[[1, 1, 1], [5, 5, 5], [10, 10, 10]]

有没有办法在保留矢量化的同时更加动态地执行此操作。

3 个答案:

答案 0 :(得分:3)

remove中减去选项,找到结果最小值的索引,替换。

a

将额外维度添加到a = np.array([[0, 0, 0], [4, 4, 4], [9, 9, 9]]) choices = np.array([1, 5, 10]) b = a[:,:,None] - choices np.absolute(b,b) i = np.argmin(b, axis = -1) a = choices[i] print a >>> [[ 1 1 1] [ 5 5 5] [10 10 10]] a = np.array([[0, 3, 0], [4, 8, 4], [9, 1, 9]]) choices = np.array([1, 5, 10]) b = a[:,:,None] - choices np.absolute(b,b) i = np.argmin(b, axis = -1) a = choices[i] print a >>> [[ 1 1 1] [ 5 10 5] [10 1 10]] >>> ,以便从a的每个元素中减去choices的每个元素。 a在第三维broadcastchoices This link has a decent graphic EricsBroadcastingDoca是(3,3,3)。 Index Array是一个非常好的解释,并在最后有一个图形的三维示例。

对于第二个例子:

b.shape

最终作业使用Integer Array Indexing或{{3}}。

在第二个示例中,请注意元素>>> print b [[[ 1 5 10] [ 2 2 7] [ 1 5 10]] [[ 3 1 6] [ 7 3 2] [ 3 1 6]] [[ 8 4 1] [ 0 4 9] [ 8 4 1]]] >>> print i [[0 0 0] [1 2 1] [2 0 2]] >>> 存在 tie ,其中一个或五个可能已被替换。

答案 1 :(得分:2)

更详细地解释wwii的excellent answer

我们的想法是创建一个新维度,负责使用numpy broadcastinga的每个元素与choices中的每个元素进行比较。使用ellipsis syntax

可以轻松地在a中为任意数量的维度完成此操作
>>> b = np.abs(a[..., np.newaxis] - choices)
array([[[ 1,  5, 10],
        [ 1,  5, 10],
        [ 1,  5, 10]],
       [[ 3,  1,  6],
        [ 3,  1,  6],
        [ 3,  1,  6]],
       [[ 8,  4,  1],
        [ 8,  4,  1],
        [ 8,  4,  1]]])

沿您刚创建的轴(最后一个轴,标签为-1)取argmin,为您提供要替换的choices中所需的索引:

>>> np.argmin(b, axis=-1)
array([[0, 0, 0],
       [1, 1, 1],
       [2, 2, 2]])

最终允许您从choices中选择这些元素:

>>> d = choices[np.argmin(b, axis=-1)]
>>> d
array([[ 1,  1,  1],
       [ 5,  5,  5],
       [10, 10, 10]])

对于非对称形状:

让我们说a形状(2, 5)

>>> a = np.arange(10).reshape((2, 5))
>>> a
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

然后你得到:

>>> b = np.abs(a[..., np.newaxis] - choices)
>>> b
array([[[ 1,  5, 10],
        [ 0,  4,  9],
        [ 1,  3,  8],
        [ 2,  2,  7],
        [ 3,  1,  6]],

       [[ 4,  0,  5],
        [ 5,  1,  4],
        [ 6,  2,  3],
        [ 7,  3,  2],
        [ 8,  4,  1]]])

这很难理解,但它所说的是,b已经形成:

>>> b.shape
(2, 5, 3)

前两个维度来自a的形状,也是(2, 5)。最后一个维度是您刚刚创建的维度。为了更好的主意:

>>> b[:, :, 0]  # = abs(a - 1)
array([[1, 0, 1, 2, 3],
       [4, 5, 6, 7, 8]])
>>> b[:, :, 1]  # = abs(a - 5)
array([[5, 4, 3, 2, 1],
       [0, 1, 2, 3, 4]])
>>> b[:, :, 2]  # = abs(a - 10)
array([[10,  9,  8,  7,  6],
       [ 5,  4,  3,  2,  1]])

请注意,对于每个b[:, :, i]achoices[i]之间的绝对差异i = 1, 2, 3

希望这有助于更清楚地解释这一点。

答案 2 :(得分:2)

我爱broadcasting并且自己也会这样做。但是,对于大型数组,我想建议使用np.searchsorted的另一种方法来保持内存效率,从而实现性能优势,如此 -

def searchsorted_app(a, choices):
    lidx = np.searchsorted(choices, a, 'left').clip(max=choices.size-1)
    ridx = (np.searchsorted(choices, a, 'right')-1).clip(min=0)
    cl = np.take(choices,lidx) # Or choices[lidx]
    cr = np.take(choices,ridx) # Or choices[ridx]
    mask = np.abs(a - cl) > np.abs(a - cr)
    cl[mask] = cr[mask]
    return cl

请注意,如果choices中的元素未排序,我们需要使用sorter添加其他参数np.searchsorted

运行时测试 -

In [160]: # Setup inputs
     ...: a = np.random.rand(100,100)
     ...: choices = np.sort(np.random.rand(100))
     ...: 

In [161]: def broadcasting_app(a, choices): # @wwii's solution
     ...:     return choices[np.argmin(np.abs(a[:,:,None] - choices),-1)]
     ...: 

In [162]: np.allclose(broadcasting_app(a,choices),searchsorted_app(a,choices))
Out[162]: True

In [163]: %timeit broadcasting_app(a, choices)
100 loops, best of 3: 9.3 ms per loop

In [164]: %timeit searchsorted_app(a, choices)
1000 loops, best of 3: 1.78 ms per loop

相关文章:Find elements of array one nearest to elements of array two