如何一次从数组中删除多个值

时间:2015-06-06 05:17:39

标签: python arrays numpy

有人可以为我提供更好的(更简单,更易读,更Pythonic,更高效等)从数组中删除多个值的方法,而不是以下内容:

import numpy as np

# The array.
x = np.linspace(0, 360, 37)

# The values to be removed.
a = 0
b = 180
c = 360

new_array = np.delete(x, np.where(np.logical_or(np.logical_or(x == a,
                                                              x == b),
                                                x == c)))

这个问题的一个好答案会产生与上面代码相​​同的结果(即new_array),但是在处理浮点数之间的相等性方面可能会比上面的代码更好。

奖金

有人可以向我解释为什么会产生错误的结果吗?

In [5]: np.delete(x, x == a)
/usr/lib/python2.7/dist-packages/numpy/lib/function_base.py:3254: FutureWarning: in the future insert will treat boolean arrays and array-likes as boolean index instead of casting it to integer
  "of casting it to integer", FutureWarning)
Out[5]: 
array([  20.,   30.,   40.,   50.,   60.,   70.,   80.,   90.,  100.,
        110.,  120.,  130.,  140.,  150.,  160.,  170.,  180.,  190.,
        200.,  210.,  220.,  230.,  240.,  250.,  260.,  270.,  280.,
        290.,  300.,  310.,  320.,  330.,  340.,  350.,  360.])

已删除值0和10,而不是仅删除0(a)。

注意,x == a符合预期(因此问题出在np.delete内):

In [6]: x == a
Out[6]: 
array([ True, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False, False], dtype=bool)

请注意np.delete(x, np.where(x == a))会产生正确的结果。因此,在我看来np.delete无法处理布尔索引。

3 个答案:

答案 0 :(得分:5)

您的代码似乎有点复杂。我想知道你是否考虑过numpy的布尔矢量索引。

在设置相同的设置后,我为您的代码计时:

In [175]: %%timeit
   .....: np.delete(x, np.where(np.logical_or(np.logical_or(x == a, x == b), x == c)))
   .....:
10000 loops, best of 3: 32.9 µs per loop

然后我计时两个独立的布尔索引应用程序。

In [176]: %%timeit
   .....: x1 = x[x != a]
   .....: x2 = x1[x1 != b]
   .....: new_array = x2[x2 != c]
   .....:
100000 loops, best of 3: 6.56 µs per loop

最后,为了方便编程并将技术扩展到任意数量的排除值,我重写了与循环相同的代码。这会慢一些,因为需要先制作副本,但它仍然非常值得尊重。

In [177]: %%timeit
   .....: new_array = x.copy()
   .....: for val in (a, b, c):
   .....:     new_array = new_array[new_array != val]
   .....:
100000 loops, best of 3: 7.61 µs per loop

我认为真正的好处在于编程的清晰度。最后,我认为最好验证三个算法 给出相同的结果......

In [179]: new_array1 = np.delete(x,
   .....:                 np.where(np.logical_or(np.logical_or(x == a, x == b), x == c)))

In [180]: x1 = x[x != a]

In [181]: x2 = x1[x1 != b]

In [182]: new_array2 = x2[x2 != c]

In [183]: new_array3 = x.copy()

In [184]: for val in (a, b, c):
   .....:         new_array3 = new_array3[new_array3 != val]
   .....:

In [185]: all(new_array1 == new_array2)
Out[185]: True

In [186]: all(new_array1 == new_array3)
Out[186]: True

要处理浮点比较问题,您需要使用numpy的isclose()函数。正如预期的那样,这会将时间发送到地狱:

In [188]: %%timeit
   .....: new_array = x.copy()
   .....: for val in (a, b, c):
   .....:     new_array = new_array[~np.isclose(new_array, val)]
   .....:
10000 loops, best of 3: 126 µs per loop

您的奖金的答案包含在警告中,但除非您知道FalseTrue在数字上分别等于零和一,否则警告并非非常有用。所以你的代码相当于

np.delete(1, 1)

正如警告所表明的那样,numpy团队最终打算使用np.delete()的布尔参数的结果可能会改变,但目前它只需要索引参数。

答案 1 :(得分:3)

您还可以使用np.ravel获取values的索引,然后使用np.delete

删除它们
In [32]: r =  [a,b,c]

In [33]: indx = np.ravel([np.where(x == i) for i in r])

In [34]: indx
Out[34]: array([ 0, 18, 36])

In [35]: np.delete(x, indx)
Out[35]: 
array([  10.,   20.,   30.,   40.,   50.,   60.,   70.,   80.,   90.,
        100.,  110.,  120.,  130.,  140.,  150.,  160.,  170.,  190.,
        200.,  210.,  220.,  230.,  240.,  250.,  260.,  270.,  280.,
        290.,  300.,  310.,  320.,  330.,  340.,  350.])

答案 2 :(得分:1)

您可以借用np.allclose方法来测试浮点数是否相等:

def float_equal(x,y,rtol=1.e-5, atol=1.e-8):
   return np.less_equal(abs(x-y), atol + rtol * abs(y))

np.delete(x,np.where(np.logical_or.reduce([float_equal(x,y) for y in [0,180,360]])))

where部分产生:

(array([ 0, 18, 36]),)

float_equal可能会更改为针对x广播y,从而消除了列表理解。

我使用的事实logical_orufunc,并且有reduce方法。

你不需要where;只需使用logical_or的结果作为布尔索引:

I = np.logical_or.reduce([float_equal(x,y) for y in [0,180,360]])
x[~I]

(使用这个小例子,直接使用布尔值比np.delete(np.where(...))方法快2倍。)

使用此x==生成相同的内容:

np.where(np.logical_or.reduce([x==y for y in [0,180,360]]))
# (array([ 0, 18, 36]),)

这种矢量化方法也是如此:

abc = np.array([0,180,360])
np.where(np.sum(x==abc[:,None],axis=0))
# (array([ 0, 18, 36]),)

x==abc[:,None](3,37)布尔数组; np.sum的行为类似逻辑或。

我的float_equal也是这样的:

float_equal(x,abc[:,None]).sum(axis=0)