【问题标题】:Removing elements from an array that are in another array从一个数组中删除另一个数组中的元素
【发布时间】:2016-10-15 06:36:16
【问题描述】:

假设我有这些二维数组 AB

如何从A 中删除B 中的元素。 (集合论中的补码:A-B)

A=np.asarray([[1,1,1], [1,1,2], [1,1,3], [1,1,4]])
B=np.asarray([[0,0,0], [1,0,2], [1,0,3], [1,0,4], [1,1,0], [1,1,1], [1,1,4]])
#output = [[1,1,2], [1,1,3]]

更准确地说,我想做这样的事情。

data = some numpy array
label = some numpy array
A = np.argwhere(label==0) #[[1 1 1], [1 1 2], [1 1 3], [1 1 4]]
B = np.argwhere(data>1.5) #[[0 0 0], [1 0 2], [1 0 3], [1 0 4], [1 1 0], [1 1 1], [1 1 4]]
out = np.argwhere(label==0 and data>1.5) #[[1 1 2], [1 1 3]]

【问题讨论】:

  • 没有 == 会起作用,我只是猜测,我对 numpy 数组了解不多,从我的 python 控制台我得到了这个>>>[1,1,1]==[1,1,1] >>>True
  • 一个简单的非 numpy 解决方案 - [i for i in A for j in B if i==j]

标签: python arrays numpy


【解决方案1】:

list comprehension 有一个简单的解决方案,

A = [i for i in A if i not in B]

结果

[[1, 1, 2], [1, 1, 3]]

列表推导不会从数组中删除元素,它只是重新分配 - 如果要删除元素,请使用此方法:

for i in B:
     if i in A:
     A.remove(i)

【讨论】:

    【解决方案2】:

    基于 this solutionFind the row indexes of several values in a numpy array,这是一个基于 NumPy 的解决方案,内存占用更少,在处理大型数组时可能会很有用 -

    dims = np.maximum(B.max(0),A.max(0))+1
    out = A[~np.in1d(np.ravel_multi_index(A.T,dims),np.ravel_multi_index(B.T,dims))]
    

    示例运行 -

    In [38]: A
    Out[38]: 
    array([[1, 1, 1],
           [1, 1, 2],
           [1, 1, 3],
           [1, 1, 4]])
    
    In [39]: B
    Out[39]: 
    array([[0, 0, 0],
           [1, 0, 2],
           [1, 0, 3],
           [1, 0, 4],
           [1, 1, 0],
           [1, 1, 1],
           [1, 1, 4]])
    
    In [40]: out
    Out[40]: 
    array([[1, 1, 2],
           [1, 1, 3]])
    

    大型数组的运行时测试 -

    In [107]: def in1d_approach(A,B):
         ...:     dims = np.maximum(B.max(0),A.max(0))+1
         ...:     return A[~np.in1d(np.ravel_multi_index(A.T,dims),\
         ...:                     np.ravel_multi_index(B.T,dims))]
         ...: 
    
    In [108]: # Setup arrays with B as large array and A contains some of B's rows
         ...: B = np.random.randint(0,9,(1000,3))
         ...: A = np.random.randint(0,9,(100,3))
         ...: A_idx = np.random.choice(np.arange(A.shape[0]),size=10,replace=0)
         ...: B_idx = np.random.choice(np.arange(B.shape[0]),size=10,replace=0)
         ...: A[A_idx] = B[B_idx]
         ...: 
    

    基于 broadcasting 的解决方案的时间安排 -

    In [109]: %timeit A[np.all(np.any((A-B[:, None]), axis=2), axis=0)]
    100 loops, best of 3: 4.64 ms per loop # @Kasramvd's soln
    
    In [110]: %timeit A[~((A[:,None,:] == B).all(-1)).any(1)]
    100 loops, best of 3: 3.66 ms per loop
    

    基于更少内存占用的时间解决方案 -

    In [111]: %timeit in1d_approach(A,B)
    1000 loops, best of 3: 231 µs per loop
    

    进一步提升性能

    in1d_approach 通过将每一行视为一个索引元组来减少每一行。我们可以通过使用np.dot 引入矩阵乘法来更有效地做同样的事情,就像这样 -

    def in1d_dot_approach(A,B):
        cumdims = (np.maximum(A.max(),B.max())+1)**np.arange(B.shape[1])
        return A[~np.in1d(A.dot(cumdims),B.dot(cumdims))]
    

    让我们在更大的数组上对照之前的方法进行测试 -

    In [251]: # Setup arrays with B as large array and A contains some of B's rows
         ...: B = np.random.randint(0,9,(10000,3))
         ...: A = np.random.randint(0,9,(1000,3))
         ...: A_idx = np.random.choice(np.arange(A.shape[0]),size=10,replace=0)
         ...: B_idx = np.random.choice(np.arange(B.shape[0]),size=10,replace=0)
         ...: A[A_idx] = B[B_idx]
         ...: 
    
    In [252]: %timeit in1d_approach(A,B)
    1000 loops, best of 3: 1.28 ms per loop
    
    In [253]: %timeit in1d_dot_approach(A, B)
    1000 loops, best of 3: 1.2 ms per loop
    

    【讨论】:

    • 在我的程序中,in1d_approach 需要 30 秒,in1d_dot_approach 需要 45 秒。我的 numpy 数组使用 dtype=np.uint8。所以我用你的精确代码测试了它,dtype=np.uint8 参数用于 A,B。点函数给了我 567 纳秒,原始需要我 539 纳秒。有什么解释为什么较小的数据类型可以为原始函数提供更好的时序?
    • @JeeSeokYoon 好吧,因为精度较低的数据类型在二进制位方面占用的内存更少,因此会导致更少的内存占用,并且在大多数情况下,这会转化为更快的处理,因为它处理的数据更少,因为数量更少二进制位用于表示每个数字。您必须记住,在最低级别,CPU 处理二进制数据。希望这是有道理的!
    • 我在问为什么在处理浮点数时 in1d_approach(A,B) 比 in1d_dot_approach(A, B) 慢,而对于整数则更快?只是 numpy 是如何构建的吗?为什么矩阵乘法在浮点数上表现更好/在整数上表现更差(与其他方法相比)?
    【解决方案3】:

    这是一个带有广播的Numpythonic方法:

    In [83]: A[np.all(np.any((A-B[:, None]), axis=2), axis=0)]
    Out[83]: 
    array([[1, 1, 2],
           [1, 1, 3]])
    

    这是其他答案的时间:

    In [90]: def cal_diff(A, B):
       ....:     A_rows = A.view([('', A.dtype)] * A.shape[1])
       ....:     B_rows = B.view([('', B.dtype)] * B.shape[1])
       ....:     return np.setdiff1d(A_rows, B_rows).view(A.dtype).reshape(-1, A.shape[1])
       ....: 
    
    In [93]: %timeit cal_diff(A, B)
    10000 loops, best of 3: 54.1 µs per loop
    
    In [94]: %timeit A[np.all(np.any((A-B[:, None]), axis=2), axis=0)]
    100000 loops, best of 3: 9.41 µs per loop
    
    # Even better with Divakar's suggestion
    In [97]: %timeit A[~((A[:,None,:] == B).all(-1)).any(1)]
    100000 loops, best of 3: 7.41 µs per loop
    

    好吧,如果您正在寻找一种更快的方法,您应该寻找减少比较次数的方法。在这种情况下(不考虑顺序),您可以从您的行中生成一个唯一的数字,并比较可以通过将项目的 2 的幂求和来完成的数字。

    这是 Divakar 的 in1d 方法的基准:

    In [144]: def in1d_approach(A,B):
       .....:         dims = np.maximum(B.max(0),A.max(0))+1
       .....:         return A[~np.in1d(np.ravel_multi_index(A.T,dims),\
       .....:                          np.ravel_multi_index(B.T,dims))]
       .....: 
    
    In [146]: %timeit in1d_approach(A, B)
    10000 loops, best of 3: 23.8 µs per loop
    
    In [145]: %timeit A[~np.in1d(np.power(A, 2).sum(1), np.power(B, 2).sum(1))]
    10000 loops, best of 3: 20.2 µs per loop
    

    您可以使用np.diff 获得与订单无关的结果:

    In [194]: B=np.array([[0, 0, 0,], [1, 0, 2,], [1, 0, 3,], [1, 0, 4,], [1, 1, 0,], [1, 1, 1,], [1, 1, 4,], [4, 1, 1]])
    
    In [195]: A[~np.in1d(np.diff(np.diff(np.power(A, 2))), np.diff(np.diff(np.power(B, 2))))]
    Out[195]: 
    array([[1, 1, 2],
           [1, 1, 3]])
    
    In [196]: %timeit A[~np.in1d(np.diff(np.diff(np.power(A, 2))), np.diff(np.diff(np.power(B, 2))))]
    10000 loops, best of 3: 30.7 µs per loop
    

    Divakar 设置的基准测试:

    In [198]: B = np.random.randint(0,9,(1000,3))
    
    In [199]: A = np.random.randint(0,9,(100,3))
    
    In [200]: A_idx = np.random.choice(np.arange(A.shape[0]),size=10,replace=0)
    
    In [201]: B_idx = np.random.choice(np.arange(B.shape[0]),size=10,replace=0)
    
    In [202]: A[A_idx] = B[B_idx]
    
    In [203]: %timeit A[~np.in1d(np.diff(np.diff(np.power(A, 2))), np.diff(np.diff(np.power(B, 2))))]
    10000 loops, best of 3: 137 µs per loop
    
    In [204]: %timeit A[~np.in1d(np.power(A, 2).sum(1), np.power(B, 2).sum(1))]
    10000 loops, best of 3: 112 µs per loop
    
    In [205]: %timeit in1d_approach(A, B)
    10000 loops, best of 3: 115 µs per loop
    

    用更大的数组计时(Divakar 的解决方案稍微快一点):

    In [231]: %timeit A[~np.in1d(np.diff(np.diff(np.power(A, 2))), np.diff(np.diff(np.power(B, 2))))]
    1000 loops, best of 3: 1.01 ms per loop
    
    In [232]: %timeit A[~np.in1d(np.power(A, 2).sum(1), np.power(B, 2).sum(1))]
    1000 loops, best of 3: 880 µs per loop
    
    In [233]:  %timeit in1d_approach(A, B)
    1000 loops, best of 3: 807 µs per loop
    

    【讨论】:

    • 不错!即将发布相同的内容!
    • 实际使用equality 性能可能会更好:A[~((A[:,None,:] == B).all(-1)).any(1)]
    • 很好的解决方案:)
    • @Divakar 确实,这很好!
    • 不错的答案!关于加速stackoverflow.com/questions/40056275/…,请也回答这个问题
    【解决方案4】:

    如果你想用 numpy 的方式来做,

    import numpy as np
    
    A = np.array([[1, 1, 1,], [1, 1, 2], [1, 1, 3], [1, 1, 4]])
    B = np.array([[0, 0, 0], [1, 0, 2], [1, 0, 3], [1, 0, 4], [1, 1, 0], [1, 1, 1], [1, 1, 4]])
    A_rows = A.view([('', A.dtype)] * A.shape[1])
    B_rows = B.view([('', B.dtype)] * B.shape[1])
    
    diff_array = np.setdiff1d(A_rows, B_rows).view(A.dtype).reshape(-1, A.shape[1])
    

    正如@Rahul 建议的那样,对于一个非 numpy 简单的解决方案,

    diff_array = [i for i in A if i not in B]
    

    【讨论】:

    • 感谢您的提醒。已更新。
    【解决方案5】:

    另一个非 numpy 解决方案:

    [i for i in A if i not in B]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-16
      • 1970-01-01
      • 2022-11-28
      相关资源
      最近更新 更多