itertools产品加速

时间:2011-01-17 02:20:49

标签: python numpy itertools

我使用itertools.product生成长度为13的4个元素的所有可能变体.4和13可以是任意的,但实际上,我得到4 ^ 13个结果,这是很多。我需要将结果作为Numpy数组,目前执行以下操作:

  c = it.product([1,-1,np.complex(0,1), np.complex(0,-1)], repeat=length)
  sendbuf = np.array(list(c))

在两者之间插入一些简单的分析代码,看起来第一行几乎是即时的,而转换为列表然后Numpy数组需要大约3个小时。 有没有办法让这更快?我可能会忽视它,这可能是非常明显的事情。

谢谢!

6 个答案:

答案 0 :(得分:13)

相当于itertools.product()的NumPy是numpy.indices(),但它只能获得0,...,k-1形式范围的乘积:

numpy.rollaxis(numpy.indices((2, 3, 3)), 0, 4)
array([[[[0, 0, 0],
         [0, 0, 1],
         [0, 0, 2]],

        [[0, 1, 0],
         [0, 1, 1],
         [0, 1, 2]],

        [[0, 2, 0],
         [0, 2, 1],
         [0, 2, 2]]],


       [[[1, 0, 0],
         [1, 0, 1],
         [1, 0, 2]],

        [[1, 1, 0],
         [1, 1, 1],
         [1, 1, 2]],

        [[1, 2, 0],
         [1, 2, 1],
         [1, 2, 2]]]])

对于您的特殊情况,您可以使用

a = numpy.indices((4,)*13)
b = 1j ** numpy.rollaxis(a, 0, 14)

(这不会在32位系统上运行,因为数组很大。从我可以测试的大小推断,它应该在不到一分钟的时间内运行。)

EIDT:提一下:对numpy.rollaxis()的调用或多或少具有考验性,以获得与itertools.product()相同的输出。如果你不关心索引的顺序,你可以省略它(但只要你没有任何后续操作可以将你的数组转换成一个连续的数组,它就很便宜。)

EDIT2:获得

的确切类似物
numpy.array(list(itertools.product(some_list, repeat=some_length)))

你可以使用

numpy.array(some_list)[numpy.rollaxis(
    numpy.indices((len(some_list),) * some_length), 0, some_length + 1)
    .reshape(-1, some_length)]

这完全不可读 - 只要告诉我是否应该进一步解释:)

答案 1 :(得分:5)

第一行似乎是瞬间,因为没有进行实际操作。生成器对象只是在构建时才会构建,并且只有在操作发生时才进行迭代。正如您所说,您获得了4^13 = 67108864个号码,所有这些都是在list来电期间计算并提供的。我看到np.array只接受列表或元组,因此您可以尝试从迭代器中创建一个元组并将其传递给np.array以查看是否存在任何性能差异并且它不会影响程序的整体性能。这可以通过尝试使用你的用例来确定,尽管some points表示元组稍快一些。

尝试使用元组而不是列表只需执行

sendbuf = np.array(tuple(c))

答案 2 :(得分:5)

您可以通过跳转到列表来加快速度:

numpy.fromiter(c, count=…)  # Using count also speeds things up, but it's optional

使用此函数,首先分配NumPy数组,然后逐个元素进行初始化,而不必完成列表构造的附加步骤。

PS fromiter()无法处理product()返回的元组,因此现在可能不是解决方案。如果fromiter()确实处理了dtype=object,那么这应该可行。

PPS :正如Joe Kington指出的那样,这可以通过putting the tuples in a structured array来实现。但是,这似乎并不总能加快速度。

答案 3 :(得分:2)

您可能想要尝试完全不同的方法:首先创建一个所需大小的空数组:

result = np.empty((4**length, length), dtype=complex)

然后使用NumPy的切片技能自己填充数组:

# Set up of the last "digit":
result[::4, length-1] = 1
result[1::4, length-1] = -1
result[2::4, length-1] = 1j
result[3::4, length-1] = -1j

你可以为其他“数字”(即结果[:,2],结果[:,1]和结果[:,0])的元素做类似的事情。整个事情当然可以放在一个遍历每个数字的循环中。

转置整个操作(np.empty((length, 4**length)…))值得尝试,因为它可能会带来速度增加(通过更好地使用内存缓存)。

答案 4 :(得分:1)

可能没有优化,但更不依赖于python类型的转换:

ints = [1,2,3,4]
repeat = 3

def prod(ints, repeat):
    w = repeat
    l = len(ints)
    h = l**repeat
    ints = np.array(ints)
    A = np.empty((h,w), dtype=int)
    rng = np.arange(h)
    for i in range(w):
        x = l**i
        idx = np.mod(rng,l*x)/x
        A[:,i] = ints[idx]
    return A   

答案 5 :(得分:0)

让numpy.meshgrid完成所有工作:

length = 13
x = [1, -1, 1j, -1j]
mesh = numpy.meshgrid(*([x] * length))
result = numpy.vstack([y.flat for y in mesh]).T
在我的笔记本上

需要约2分钟