python - 根据元素出现在另一个数组中的次数,删除 NumPy 数组中元素的最高效方法是什么?

标签 python arrays numpy

假设我有两个 Numpy 数组:

a = np.array([1,2,2,3,3,3])
b = np.array([2,2,3])

我想从 a 中删除 b 中的所有元素,它们在 b 中出现的次数相同。即

diff(a, b)
>>> np.array([1,3,3])

请注意,对于我的用例,b 将始终是 a 的子集,并且两者都可能是无序的,但是像 numpy.setdiff1d 这样的类似集合的方法不会削减它,因为删除每个元素一定次数很重要。

我目前的懒惰解决方案如下:

def diff(a, b):
    for el in b:
        idx = (el == a).argmax()
        if a[idx] == el:
            a = np.delete(a, idx)
    return a

但我想知道是否有更高效或更紧凑的“numpy-esque”编写方式?

最佳答案

这是基于 np.searchsorted 的矢量化方法-

import pandas as pd

def diff_v2(a, b):
    # Get sorted orders
    sidx = a.argsort(kind='stable')
    A = a[sidx]
    
    # Get searchsorted indices per sorted order
    idx = np.searchsorted(A,b)
    
    # Get increments
    s = pd.Series(idx)
    inc = s.groupby(s).cumcount().values
    
    # Delete elemnents off traced back positions
    return np.delete(a,sidx[idx+inc])

进一步优化

让我们在 groupby cumcount 部分使用 NumPy -

# Perform groupby cumcount on sorted array
def groupby_cumcount(idx):
    mask = np.r_[False,idx[:-1]==idx[1:],False]
    ids = mask[:-1].cumsum()
    count = np.diff(np.flatnonzero(~mask))
    return ids - np.repeat(ids[~mask[:-1]],count)

def diff_v3(a, b):
    # Get sorted orders
    sidx = a.argsort(kind='stable')
    A = a[sidx]
    
    # Get searchsorted indices per sorted order
    idx = np.searchsorted(A,b)
    
    # Get increments
    idx = np.sort(idx)
    inc = groupby_cumcount(idx)
    
    # Delete elemnents off traced back positions
    return np.delete(a,sidx[idx+inc])

基准测试

使用带有 10000 的设置~2x 的元素a 的重复次数和 ba 的一半大小.

In [52]: np.random.seed(0)
    ...: a = np.random.randint(0,5000,10000)
    ...: b = a[np.random.choice(len(a), 5000,replace=False)]

In [53]: %timeit diff(a,b)
    ...: %timeit diff_v2(a,b)
    ...: %timeit diff_v3(a,b)
108 ms ± 821 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
3.85 ms ± 53.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.89 ms ± 15.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

接下来,在 100000元素 -

In [54]: np.random.seed(0)
    ...: a = np.random.randint(0,50000,100000)
    ...: b = a[np.random.choice(len(a), 50000,replace=False)]

In [55]: %timeit diff(a,b)
    ...: %timeit diff_v2(a,b)
    ...: %timeit diff_v3(a,b)
4.45 s ± 20.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
37.5 ms ± 661 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
28 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

对于正数和排序输出

我们可以使用 np.bincount -

def diff_v4(a, b):
    C = np.bincount(a)
    C -= np.bincount(b,minlength=len(C))
    return np.repeat(np.arange(len(C)), C)

关于python - 根据元素出现在另一个数组中的次数,删除 NumPy 数组中元素的最高效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62613826/

相关文章:

javascript - 如何用Python requests-html抓取评分?

javascript - 动态访问数组值

java - 如何迭代数组并跳过一些?

python - 添加到 Pandas 的窗口

python - pip install numpy pandas 失败?

python - 如何在pandas中的多级pivot_table上使用nlargest?

python - 无法在 tensorflow keras中附加张量板回调

python - 在 Pandas 数据框中展平嵌套的Json

arrays - 假设大小的数组 : Colon vs. 星号 - DIMENSION( :) arr vs. arr(*)

python - 广播 NumPy 数组时实际发生了什么