17

我使用 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 个小时。有没有办法让它更快?这可能是我忽略的非常明显的事情。

谢谢!

4

6 回答 6

19

NumPy 等价于itertools.product()is 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:顺便提一下:调用 tonumpy.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)]

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

于 2011-01-17T15:29:31.157 回答
5

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

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

sendbuf = np.array(tuple(c))
于 2011-01-17T02:37:53.370 回答
5

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

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

使用此函数,首先分配 NumPy 数组,然后逐个元素初始化,而无需执行列表构造的额外步骤。

PSfromiter()不处理返回的元组product(),所以现在这可能不是一个解决方案。但是,如果fromiter()确实处理了dtype=object,这应该可以工作。

PPS:正如 Joe Kington 所指出的,这可以通过将元组放在结构化数组中来实现。但是,这似乎并不总是会加快速度。

于 2011-01-17T13:36:32.680 回答
3

让 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 分钟

于 2015-12-05T00:00:20.103 回答
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

您可以对其他“数字”(即 result[:, 2]、result[:, 1] 和 result[:, 0] 的元素)执行类似的操作。整个事情当然可以放在一个遍历每个数字的循环中。

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

于 2011-01-17T15:43:10.807 回答
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   
于 2011-01-17T06:53:40.367 回答