Python相当于MATLAB的"ismember"函数

zd5*_*151 20 python optimization matlab numpy

在尝试优化代码的许多尝试之后,似乎最后一个资源是尝试使用多个核运行下面的代码.我不知道如何转换/重新构建我的代码,以便使用多个内核可以更快地运行.如果我能获得指导以实现最终目标,我将不胜感激.最终目标是能够尽快为阵列A和B运行此代码,其中每个阵列包含大约700,000个元素.这是使用小数组的代码.700k元素数组被注释掉了.

import numpy as np

def ismember(a,b):
    for i in a:
        index = np.where(b==i)[0]
        if index.size == 0:
            yield 0
        else:
            yield index


def f(A, gen_obj):
    my_array = np.arange(len(A))
    for i in my_array:
        my_array[i] = gen_obj.next()
    return my_array


#A = np.arange(700000)
#B = np.arange(700000)
A = np.array([3,4,4,3,6])
B = np.array([2,5,2,6,3])

gen_obj = ismember(A,B)

f(A, gen_obj)

print 'done'
# if we print f(A, gen_obj) the output will be: [4 0 0 4 3]
# notice that the output array needs to be kept the same size as array A.
Run Code Online (Sandbox Code Playgroud)

我想要做的是模仿一个名为ismember [2] 的MATLAB函数(格式为:[Lia,Locb] = ismember(A,B).我只是试图获得该Locb部分.

来自Matlab:Locb,包含B中每个值的最低索引,A是B的成员.输出数组Locb在A不是B的成员的地方包含0

其中一个主要问题是我需要能够尽可能高效地执行此操作.为了测试,我有两个700k元素的数组.创建生成器并遍历生成器的值似乎不能快速完成工作.

sfs*_*man 17

在担心多核之前,我会通过使用字典来消除ismember函数中的线性扫描:

def ismember(a, b):
    bind = {}
    for i, elt in enumerate(b):
        if elt not in bind:
            bind[elt] = i
    return [bind.get(itm, None) for itm in a]  # None can be replaced by any other "not in b" value
Run Code Online (Sandbox Code Playgroud)

您的原始实现需要对A中的每个元素进行B中元素的完整扫描O(len(A)*len(B)).上面的代码需要对B进行一次全扫描才能生成字典Bset.通过使用dict,您可以有效地使A中每个元素的每个元素的查找为常量,从而进行操作O(len(A)+len(B)).如果这仍然太慢,那么担心使上述功能在多个内核上运行.

编辑:我还略微修改了你的索引.Matlab使用0,因为它的所有数组都从索引1开始.Python/numpy启动数组为0,所以如果你的数据集看起来像这样

A = [2378, 2378, 2378, 2378]
B = [2378, 2379]
Run Code Online (Sandbox Code Playgroud)

没有元素返回0,那么你的结果将排除A的所有元素.上面的例程返回None没有索引而不是0.返回-1是一个选项,但Python会将其解释为数组中的最后一个元素. None如果它被用作数组的索引,将引发异常.如果您想要不同的行为,请将Bind.get(item,None)表达式中的第二个参数更改为您想要返回的值.

  • @ z5151不,这是简单的算法分析.使用[Big-O表示法](http://en.wikipedia.org/wiki/Big_O_notation):`np.where`必须执行`B`的线性扫描,这需要`O(len(B))`操作.然后使用需要`O(len(A))`运算的外循环,使原始算法大致为'O(len(A)*len(B))`运算.生成`Bind`需要`len(B)`操作.字典实现为[哈希表](http://en.wikipedia.org/wiki/Hash_table),它具有常量的"O(1)"查找,因此扫描A是"O(len(A))"; 整体复杂性是'O(len(A)+ len(B))`. (7认同)

The*_*eke 13

sfstewman的优秀答案很可能为您解决了这个问题.

我只想补充一下如何在numpy中完全相同.

我使用了numpy 独特in1d函数.

B_unique_sorted, B_idx = np.unique(B, return_index=True)
B_in_A_bool = np.in1d(B_unique_sorted, A, assume_unique=True)
Run Code Online (Sandbox Code Playgroud)
  • B_unique_sorted包含已B排序的唯一值.
  • B_idx为这些值保留了原始的索引B.
  • B_in_A_bool是一个布尔数组,其大小B_unique_sorted存储值B_unique_sorted是否在A.
    注意:我需要在A中查找(来自B的唯一值),因为我需要输出相对于B_idx
    注意:我认为它A已经是唯一的.

现在你可以B_in_A_bool用来获得常见的val

B_unique_sorted[B_in_A_bool]
Run Code Online (Sandbox Code Playgroud)

和他们各自的原始指数 B

B_idx[B_in_A_bool]
Run Code Online (Sandbox Code Playgroud)

最后,我假设这比纯Python for循环要快得多,尽管我没有测试它.

  • 小心!这不会保留索引的顺序!尝试使用range(1,6)和[5,1]。如果不需要索引的顺序,我想您可以只使用np.in1d()然后使用np.nonzero()[0] (2认同)