使用 numpy 进行元素“输入”的 Pythonic 和有效方法

Posted

技术标签:

【中文标题】使用 numpy 进行元素“输入”的 Pythonic 和有效方法【英文标题】:Pythonic and efficient way to do an elementwise "in" using numpy 【发布时间】:2015-10-15 14:05:21 【问题描述】:

我正在寻找一种有效获取布尔数组的方法,其中给定两个大小相等的数组ab,如果a 的对应元素出现in,则每个元素都为真b的对应元素。

例如下面的程序:

a = numpy.array([1, 2, 3, 4])
b = numpy.array([[1, 2, 13], [2, 8, 9], [5, 6], [7]])
print(numpy.magic_function(a, b))

应该打印

[True, True, False, False]

记住这个函数应该相当于

[x in y for x, y in zip(a, b)]

numpy-针对ab 很大且b 的每个元素都相当小的情况进行了优化。

【问题讨论】:

旁白:如果你关心效率,你不应该使用像b 这样的数组:而不是一个快速的 numpy 二维数组 int dtype 你只有一个对象 dtype 的一维数组。 公平地说,首当其冲的工作(检查整数是否在 numpy 数组中)由您的列表理解处理。这可能是最有效(而且绝对是最 Pythonic)的方式。 @Kupiakos 好点子,我忘了补充一点,我正在寻找的用例是针对大 ab,而 b 的每个元素都很少。跨度> 基于docs.scipy.org/doc/numpy/reference/arrays.nditer.html,我想你想使用nditer或者in1d docs.scipy.org/doc/numpy/reference/generated/…。 由于b 的元素长度不同,它作为一个数组没有什么意义。要么把它变成一个真正的二维数组,要么把它当作一个列表。 【参考方案1】:

要利用 NumPy 的 broadcasting 规则,您应该首先使数组 b 平方,这可以使用 itertools.izip_longest 来实现:

from itertools import izip_longest

c = np.array(list(izip_longest(*b))).astype(float)

导致:

array([[  1.,   2.,   5.,   7.],
       [  2.,   8.,   6.,  nan],
       [ 13.,   9.,  nan,  nan]])

然后,根据广播规则,通过np.isclose(c, a),您会得到一个显示每个c[:, i]a[i] 之间差异的二维布尔数组,给出:

array([[ True,  True, False, False],
       [False, False, False, False],
       [False, False, False, False]], dtype=bool)

哪个可以用来获取你的答案:

np.any(np.isclose(c, a), axis=0)
#array([ True,  True, False, False], dtype=bool)

【讨论】:

【参考方案2】:

b中的小列表长度有上限吗?如果是这样,也许您可​​以将b 设为 1000x5 的矩阵,并使用nan 来填补太短的子数组的空白。然后您可以使用numpy.any 来获得您想要的答案,如下所示:

In [42]: a = np.array([1, 2, 3, 4])
    ...: b = np.array([[1, 2, 13], [2, 8, 9], [5, 6], [7]])

In [43]: bb = np.full((len(b), max(len(i) for i in b)), np.nan)

In [44]: for irow, row in enumerate(b):
    ...:     bb[irow, :len(row)] = row

In [45]: bb
Out[45]: 
array([[  1.,   2.,  13.],
       [  2.,   8.,   9.],
       [  5.,   6.,  nan],
       [  7.,  nan,  nan]])

In [46]: a[:,np.newaxis] == bb
Out[46]: 
array([[ True, False, False],
       [ True, False, False],
       [False, False, False],
       [False, False, False]], dtype=bool)

In [47]: np.any(a[:,np.newaxis] == bb, axis=1)
Out[47]: array([ True,  True, False, False], dtype=bool)

不知道这对您的数据是否更快。

【讨论】:

【参考方案3】:

总结

Sauldo Castro 的方法在迄今为止发布的方法中运行得最快。原始帖子中的生成器表达式是第二快的。

生成测试数据的代码:

import numpy
import random

alength = 100
a = numpy.array([random.randint(1, 6) for i in range(alength)])
b = []
for i in range(alength):
    length = random.randint(1, 5)
    element = []
    for i in range(length):
        element.append(random.randint(1, 6))
    b.append(element)
b = numpy.array(b)
print a, b

选项:

from itertools import izip_longest
def magic_function1(a, b): # From OP Martin Fixman
    return [x in y for x, y in zip(a, b)]  

def magic_function2(a, b): # What I thought might be better.
    bools = []
    for x, y in zip(a,b):
        found = False
        for j in y:
            if x == j:
                found=True
                break
        bools.append(found)

def magic_function3(a, b): # What I tried first
    bools = []
    for i in range(len(a)):
        found = False
        for j in range(len(b[i])):
            if a[i] == b[i][j]:
                found=True
                break
        bools.append(found)

def magic_function4(a, b): # From Bas Swinkels
    bb = numpy.full((len(b), max(len(i) for i in b)), numpy.nan)
    for irow, row in enumerate(b):
        bb[irow, :len(row)] = row
    a[:,numpy.newaxis] == bb
    return numpy.any(a[:,numpy.newaxis] == bb, axis=1)

def magic_function5(a, b): # From Sauldo Castro, revised version
    c = numpy.array(list(izip_longest(*b))).astype(float)
    return numpy.isclose(c, a), axis=0)  

时间 n_executions

n_executions = 100
clock = timeit.Timer(stmt="magic_function1(a, b)", setup="from __main__ import magic_function1, a, b")
print clock.timeit(n_executions), "seconds"
# Repeat with each candidate function

结果:

magic_function1 为 0.158078225475 秒 magic_function2 为 0.181080926835 秒 magic_function3 为 0.259621047822 秒 magic_function4 为 0.287054750224 秒 magic_function5 为 0.0839162196207 秒

【讨论】:

以上是关于使用 numpy 进行元素“输入”的 Pythonic 和有效方法的主要内容,如果未能解决你的问题,请参考以下文章

不用循环,python numpy 数组如何对每个元素进行操作?

如何在python的numpy数组中绑定相同索引的元素

numpy基础——ndarray对象

python练手,numpy

Python计算两个numpy数组的交集(Intersection)实战:两个输入数组的交集并排序获取交集元素及其索引如果输入数组不是一维的,它们将被展平(flatten),然后计算交集

Python:2D Numpy 数组(矩阵) - 求负数之和(行)