Python Numpy:从给定范围生成组合的有效方法

Python Numpy:从给定范围生成组合的有效方法,python,arrays,numpy,combinations,Python,Arrays,Numpy,Combinations,我有一个n维数组,如下所示: np.array([[0,3],[0,3],[0,10]]) 在这个数组中,元素表示低值和高值。例:[0,3]指的是[0,1,2,3] 我需要使用上面给出的范围生成所有值的组合。 例如,我想要[0,0,0],[0,0,1]。。。[0,1,0] ... [3,3,10] 为了得到我想要的东西,我尝试了以下方法: ds = np.array([[0,3],[0,3],[0,10]]) nItems = int(reduce(lambda a,b: a * (b[1]

我有一个n维数组,如下所示:

np.array([[0,3],[0,3],[0,10]])
在这个数组中,元素表示低值和高值。例:
[0,3]
指的是
[0,1,2,3]

我需要使用上面给出的范围生成所有值的组合。 例如,我想要
[0,0,0],[0,0,1]。。。[0,1,0] ... [3,3,10]

为了得到我想要的东西,我尝试了以下方法:

ds = np.array([[0,3],[0,3],[0,10]])
nItems = int(reduce(lambda a,b: a * (b[1] - b[0] + 1), ds, 1))
myCombinations = np.zeros((nItems,))
nArrays = []
for x in range(ds.shape[0]):
    low = ds[x][0]
    high= ds[x][1]
    nitm = high - low + 1
    ar = [x+low for x in range(nitm) ]
    nArrays.append(ar)

myCombinations = cartesian(nArrays)
笛卡尔函数取自

我需要这样做几百万次


我的问题:有没有更好/更有效的方法

我想你要找的是
np.mgrid
。不幸的是,这将以不同于您需要的格式返回数组,因此您需要进行一些后期处理:

a = np.mgrid[0:4, 0:4, 0:11]     # All points in a 3D grid within the given ranges
a = np.rollaxis(a, 0, 4)         # Make the 0th axis into the last axis
a = a.reshape((4 * 4 * 11, 3))   # Now you can safely reshape while preserving order
解释

提供N维空间中的一组栅格点。让我试着用一个较小的例子来说明这一点,以使事情更清楚:

>>> a = np.mgrid[0:2, 0:2]
>>> a
array([[[0, 0],
        [1, 1]],

       [[0, 1],
        [0, 1]]])
因为我已经给出了两组范围,
0:2,0:2
,所以我得到了一个二维网格。
mgrid
返回的是与二维空间中的网格点(0,0)、(0,1)、(1,0)和(1,1)相对应的x值和y值
a[0]
告诉您四个点的x值是什么,而
a[1]
告诉您y值是什么

但你真正想要的是我写出来的实际网格点列表,而不是这些点的x和y值。第一个本能反应是根据需要重塑阵列:

>>> a.reshape((4, 2))
array([[0, 0],
       [1, 1],
       [0, 1],
       [0, 1]])
但很明显,这是行不通的,因为它有效地重塑了展平数组(通过按顺序读取所有元素获得的数组),而这不是您想要的

您要做的是向下查看
a
的第三维,并创建一个数组:

[ [a[0][0, 0], a[1][0, 0]],
  [a[0][0, 1], a[1][0, 1]],
  [a[0][1, 0], a[1][1, 0]],
  [a[0][1, 1], a[1][1, 1]] ]
上面写着“先告诉我第一个点(x1,y1),然后第二个点(x2,y2),…”等等。也许用某种数字来更好地解释这一点。这就是
a
的外观:

                you want to read
                in this direction
                 (0, 0)   (0, 1)
                   |        |
                   |        |
                   v        v

          /        0--------0            +----> axis0
 x-values |       /|       /|           /|
          |      / |      / |    axis1 / |
          \     1--------1  |         L  |
                |  |     |  |            v
          /     |  0-----|--1           axis2
 y-values |     | /      | /
          |     |/       |/
          \     0--------1

                |        |
                |        |
                v        v
              (1, 0)   (1, 1)
np.rollaxis
为您提供了一种方法
np.rollaxis(a,0,3)
在上面的示例中说“取第0个(或最外面的)轴并将其放入最后一个(或最里面的)轴。(注意:这里实际上只存在轴0,1和2。所以说“将第0个轴发送到第3个位置”是告诉python将第0个轴放在最后一个轴之后的一种方式)。您可能还想阅读

这开始看起来像您想要的,只是有一个额外的数组维度。我们希望合并维度0和1,以获得一个网格点数组。但是现在展开的数组以您期望的方式读取,您可以安全地对其进行重塑,以获得所需的结果

>>> a = a.reshape((4, 2))
>>> a
array([[0, 0],
       [0, 1],
       [1, 0],
       [1, 1]])
3D版本也有同样的功能,只是我无法为它制作一个图形,因为它是4D的。

您可以使用:

给定范围数组,您可以执行如下操作(我使用了两个非零低值来演示一般情况,并减少了输出的大小。)

如果所有范围的下限值均为0,则可以使用:


不,所有的低值都不是0,因此我不认为我可以使用
np.ndindex
。另一种方法对我有效。一旦我有了元组列表,我就可以将其转换为numpy数组。谢谢!!我刚刚注意到,运行该方法100000次,我的方法在9秒内得出结果,而使用itertools需要44秒。这种方法编写代码要简单得多,但我一直在考虑效率,因为我必须做几百万次。这是非常有效的(100000次运行大约需要4秒),但它非常混乱,您能解释一下它是如何工作的吗?(或者请给我指一些我能理解这一点的文档?)我已经为您的好处添加了一个解释,但是在我的计算机上,
itertools.product
实际上运行速度快了大约6倍。我的方法中的大部分时间都被
mgrid
本身消耗,所以您甚至无法通过避免
rollaxis
重塑
来摆脱它。出于好奇,Python和nump的版本是什么你在用什么?我刚刚意识到另一种实现
rollaxis
+
重塑
效果的方法是使用
zip(a[0].flatte(),a[1].flatte(),a[2].flatte())
。哇!谢谢你的解释!我正在运行python 2.7.6和numpy 1.8.1,我再次检查,结果在我的机器上是相似的。itertools需要更长的时间!
>>> a = a.reshape((4, 2))
>>> a
array([[0, 0],
       [0, 1],
       [1, 0],
       [1, 1]])
In [16]: from itertools import product

In [17]: values = list(product(range(4), range(4), range(11)))

In [18]: values[:5]
Out[18]: [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4)]

In [19]: values[-5:]
Out[19]: [(3, 3, 6), (3, 3, 7), (3, 3, 8), (3, 3, 9), (3, 3, 10)]
In [41]: ranges = np.array([[0, 3], [1, 3], [8, 10]])

In [42]: list(product(*(range(lo, hi+1) for lo, hi in ranges)))
Out[42]: 
[(0, 1, 8),
 (0, 1, 9),
 (0, 1, 10),
 (0, 2, 8),
 (0, 2, 9),
 (0, 2, 10),
 (0, 3, 8),
 (0, 3, 9),
 (0, 3, 10),
 (1, 1, 8),
 (1, 1, 9),
 (1, 1, 10),
 (1, 2, 8),
 (1, 2, 9),
 (1, 2, 10),
 (1, 3, 8),
 (1, 3, 9),
 (1, 3, 10),
 (2, 1, 8),
 (2, 1, 9),
 (2, 1, 10),
 (2, 2, 8),
 (2, 2, 9),
 (2, 2, 10),
 (2, 3, 8),
 (2, 3, 9),
 (2, 3, 10),
 (3, 1, 8),
 (3, 1, 9),
 (3, 1, 10),
 (3, 2, 8),
 (3, 2, 9),
 (3, 2, 10),
 (3, 3, 8),
 (3, 3, 9),
 (3, 3, 10)]
In [52]: values = list(np.ndindex(4, 4, 11))

In [53]: values[:5]
Out[53]: [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4)]

In [54]: values[-5:]
Out[34]: [(3, 3, 6), (3, 3, 7), (3, 3, 8), (3, 3, 9), (3, 3, 10)]