使用numpy构建两个数组的所有组合的数组

Raf*_*ini 132 python arrays numpy multidimensional-array

我试图在6参数函数的参数空间上运行以研究它的数值行为,然后再尝试做任何复杂的事情,所以我正在寻找一种有效的方法来做到这一点.

给定6-dim numpy数组作为输入,我的函数采用浮点值.我最初尝试做的是:

首先,我创建了一个函数,它接受2个数组并生成一个数组,其中包含来自两个数组的所有值组合

from numpy import *
def comb(a,b):
    c = []
    for i in a:
        for j in b:
            c.append(r_[i,j])
    return c
Run Code Online (Sandbox Code Playgroud)

然后我习惯reduce()将它应用于相同数组的m个副本:

def combs(a,m):
    return reduce(comb,[a]*m)
Run Code Online (Sandbox Code Playgroud)

然后我评估我的功能如下:

values = combs(np.arange(0,1,0.1),6)
for val in values:
    print F(val)
Run Code Online (Sandbox Code Playgroud)

这有效,但它太慢了.我知道参数的空间很大,但这不应该太慢.在这个例子中我只抽取了10 6(一百万)个点,并且创建数组花了超过15秒values.

你知道用numpy做这个更有效的方法吗?

F如果有必要,我可以修改函数获取它的参数的方式.

pv.*_*pv. 151

这是一个纯粹的实现.它是ca. 比使用itertools快5倍.


import numpy as np

def cartesian(arrays, out=None):
    """
    Generate a cartesian product of input arrays.

    Parameters
    ----------
    arrays : list of array-like
        1-D arrays to form the cartesian product of.
    out : ndarray
        Array to place the cartesian product in.

    Returns
    -------
    out : ndarray
        2-D array of shape (M, len(arrays)) containing cartesian products
        formed of input arrays.

    Examples
    --------
    >>> cartesian(([1, 2, 3], [4, 5], [6, 7]))
    array([[1, 4, 6],
           [1, 4, 7],
           [1, 5, 6],
           [1, 5, 7],
           [2, 4, 6],
           [2, 4, 7],
           [2, 5, 6],
           [2, 5, 7],
           [3, 4, 6],
           [3, 4, 7],
           [3, 5, 6],
           [3, 5, 7]])

    """

    arrays = [np.asarray(x) for x in arrays]
    dtype = arrays[0].dtype

    n = np.prod([x.size for x in arrays])
    if out is None:
        out = np.zeros([n, len(arrays)], dtype=dtype)

    m = n / arrays[0].size
    out[:,0] = np.repeat(arrays[0], m)
    if arrays[1:]:
        cartesian(arrays[1:], out=out[0:m,1:])
        for j in xrange(1, arrays[0].size):
            out[j*m:(j+1)*m,1:] = out[0:m,1:]
    return out
Run Code Online (Sandbox Code Playgroud)

  • 有没有考虑过提交这个包含在numpy中?这不是我第一次寻找这个功能并发现你的帖子. (41认同)
  • 仅供参考:似乎已经将它变成了来自sklearn.utils.extmath import cartesian的scikit-learn包. (37认同)
  • `TypeError:切片索引必须是整数或无或者有一个由_ cartesian抛出的__index__方法(数组[1:],out = out [0:m,1:])` (6认同)
  • 我刚刚意识到:这与itertools.combinations稍有不同,因为此函数遵守值的排序,而组合不遵循值的顺序,因此此函数返回的值比组合要多。仍然非常令人印象深刻,但不幸的是,我一直没有寻找:( (2认同)
  • 对于后代,可以在这里找到仅使用 itertools.combinations 的高性能替代方案:http://stackoverflow.com/questions/16003217/nd-version-of-itertools-combinations-in-numpy (2认同)
  • @Boern 或其他任何遇到 `TypeError` 的人,该函数是为 python 2 编写的,你可能会尝试用 python 3 运行它。你可以更改两行:`m = n / arrays[0].size` --> `m = int(n / arrays[0].size)` 和 `for j in xrange(1, arrays[0].size):` --> `for j in range(1, arrays[0) ].大小):` (2认同)

CT *_*Zhu 102

在较新版本numpy(> 1.8.x)中,numpy.meshgrid()提供了更快的实现:

@pv的解决方案

In [113]:

%timeit cartesian(([1, 2, 3], [4, 5], [6, 7]))
10000 loops, best of 3: 135 µs per loop
In [114]:

cartesian(([1, 2, 3], [4, 5], [6, 7]))

Out[114]:
array([[1, 4, 6],
       [1, 4, 7],
       [1, 5, 6],
       [1, 5, 7],
       [2, 4, 6],
       [2, 4, 7],
       [2, 5, 6],
       [2, 5, 7],
       [3, 4, 6],
       [3, 4, 7],
       [3, 5, 6],
       [3, 5, 7]])
Run Code Online (Sandbox Code Playgroud)

numpy.meshgrid()仅用于2D,现在它具有ND功能.在这种情况下,3D:

In [115]:

%timeit np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
10000 loops, best of 3: 74.1 µs per loop
In [116]:

np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)

Out[116]:
array([[1, 4, 6],
       [1, 5, 6],
       [2, 4, 6],
       [2, 5, 6],
       [3, 4, 6],
       [3, 5, 6],
       [1, 4, 7],
       [1, 5, 7],
       [2, 4, 7],
       [2, 5, 7],
       [3, 4, 7],
       [3, 5, 7]])
Run Code Online (Sandbox Code Playgroud)

请注意,最终结果的顺序略有不同.

  • `np.stack(np.meshgrid([1,2,3],[4,5],[6,7]), - 1).reshape(-1,3)`将给出正确的顺序 (12认同)
  • 应该注意的是,meshgrid 仅适用于较小的范围集,我有一个较大的范围集,并且出现错误: ValueError: ndarray 的最大支持维度为 32,发现 69 (2认同)
  • @mikkom,没有任何东西可以处理大于 32 的集合。即使每个集合的大小为 2,组合的数量也将是 2**32, 4 Gb。 (2认同)

Ale*_*lli 33

itertools.combinations一般是从Python容器中获取组合的最快方法(如果你确实需要组合,即没有重复和独立于顺序的安排;那不是你的代码似乎在做什么,但我不能告诉我这是因为你的代码是错误的,还是因为你使用的是错误的术语.

如果你想要一些不同于组合的东西,也许在itertools中的其他迭代器,product或者permutations,可能会更好地为你服务.例如,看起来您的代码与以下内容大致相同:

for val in itertools.product(np.arange(0, 1, 0.1), repeat=6):
    print F(val)
Run Code Online (Sandbox Code Playgroud)

所有这些迭代器都会产生元组,而不是列表或numpy数组,所以如果你的F特别挑选得到一个numpy数组,你将不得不接受构造或清理的额外开销,并在每一步重新填充一个.


小智 8

你可以做这样的事情

import numpy as np

def cartesian_coord(*arrays):
    grid = np.meshgrid(*arrays)        
    coord_list = [entry.ravel() for entry in grid]
    points = np.vstack(coord_list).T
    return points

a = np.arange(4)  # fake data
print(cartesian_coord(*6*[a])
Run Code Online (Sandbox Code Playgroud)

这使

array([[0, 0, 0, 0, 0, 0],
   [0, 0, 0, 0, 0, 1],
   [0, 0, 0, 0, 0, 2],
   ..., 
   [3, 3, 3, 3, 3, 1],
   [3, 3, 3, 3, 3, 2],
   [3, 3, 3, 3, 3, 3]])
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法让NumPy接受超过32个MeshGrid数组?只要我不传递超过32个数组,该方法就对我有效。 (2认同)

Ste*_*alt 8

以下numpy实现应该是约.2x给定答案的速度:

def cartesian2(arrays):
    arrays = [np.asarray(a) for a in arrays]
    shape = (len(x) for x in arrays)

    ix = np.indices(shape, dtype=int)
    ix = ix.reshape(len(arrays), -1).T

    for n, arr in enumerate(arrays):
        ix[:, n] = arrays[n][ix[:, n]]

    return ix
Run Code Online (Sandbox Code Playgroud)

  • 看起来挺好的。通过我的初步测试,对于 {1,2,...,100} 的所有对、三元组和 4 元组,这看起来比原始答案更快。之后,原始答案获胜。此外,对于希望生成 {1,...,n} 的所有 k 元组的未来读者,`np.indices((n,...,n)).reshape(k,-1).T` 将做。 (2认同)
  • 这仅适用于整数,而接受的答案也适用于浮点数。 (2认同)

ste*_*ert 7

看起来你想要一个网格来评估你的功能,在这种情况下你可以使用numpy.ogrid(打开)或numpy.mgrid(充实):

import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]
Run Code Online (Sandbox Code Playgroud)


éta*_*ogy 7

这是另一种方式,使用纯 NumPy,没有递归,没有列表理解,也没有明确的 for 循环。它比原始答案慢约 20%,并且基于 np.meshgrid。

def cartesian(*arrays):
    mesh = np.meshgrid(*arrays)  # standard numpy meshgrid
    dim = len(mesh)  # number of dimensions
    elements = mesh[0].size  # number of elements, any index will do
    flat = np.concatenate(mesh).ravel()  # flatten the whole meshgrid
    reshape = np.reshape(flat, (dim, elements)).T  # reshape and transpose
    return reshape
Run Code Online (Sandbox Code Playgroud)

例如,

x = np.arange(3)
a = cartesian(x, x, x, x, x)
print(a)
Run Code Online (Sandbox Code Playgroud)

[[0 0 0 0 0]
 [0 0 0 0 1]
 [0 0 0 0 2]
 ..., 
 [2 2 2 2 0]
 [2 2 2 2 1]
 [2 2 2 2 2]]
Run Code Online (Sandbox Code Playgroud)


RBF*_*F06 5

对于一维数组(或平面 python 列表)的笛卡尔积的纯 numpy 实现,只需使用meshgrid(),用 滚动轴transpose(),然后重塑为所需的输出:

 def cartprod(*arrays):
     N = len(arrays)
     return transpose(meshgrid(*arrays, indexing='ij'), 
                      roll(arange(N + 1), -1)).reshape(-1, N)
Run Code Online (Sandbox Code Playgroud)

请注意,这有最后一个轴变化最快的约定(“C 风格”或“行主”)。

In [88]: cartprod([1,2,3], [4,8], [100, 200, 300, 400], [-5, -4])
Out[88]: 
array([[  1,   4, 100,  -5],
       [  1,   4, 100,  -4],
       [  1,   4, 200,  -5],
       [  1,   4, 200,  -4],
       [  1,   4, 300,  -5],
       [  1,   4, 300,  -4],
       [  1,   4, 400,  -5],
       [  1,   4, 400,  -4],
       [  1,   8, 100,  -5],
       [  1,   8, 100,  -4],
       [  1,   8, 200,  -5],
       [  1,   8, 200,  -4],
       [  1,   8, 300,  -5],
       [  1,   8, 300,  -4],
       [  1,   8, 400,  -5],
       [  1,   8, 400,  -4],
       [  2,   4, 100,  -5],
       [  2,   4, 100,  -4],
       [  2,   4, 200,  -5],
       [  2,   4, 200,  -4],
       [  2,   4, 300,  -5],
       [  2,   4, 300,  -4],
       [  2,   4, 400,  -5],
       [  2,   4, 400,  -4],
       [  2,   8, 100,  -5],
       [  2,   8, 100,  -4],
       [  2,   8, 200,  -5],
       [  2,   8, 200,  -4],
       [  2,   8, 300,  -5],
       [  2,   8, 300,  -4],
       [  2,   8, 400,  -5],
       [  2,   8, 400,  -4],
       [  3,   4, 100,  -5],
       [  3,   4, 100,  -4],
       [  3,   4, 200,  -5],
       [  3,   4, 200,  -4],
       [  3,   4, 300,  -5],
       [  3,   4, 300,  -4],
       [  3,   4, 400,  -5],
       [  3,   4, 400,  -4],
       [  3,   8, 100,  -5],
       [  3,   8, 100,  -4],
       [  3,   8, 200,  -5],
       [  3,   8, 200,  -4],
       [  3,   8, 300,  -5],
       [  3,   8, 300,  -4],
       [  3,   8, 400,  -5],
       [  3,   8, 400,  -4]])
Run Code Online (Sandbox Code Playgroud)

如果您想最快地更改第一个轴(“FORTRAN 样式”或“列主”),只需更改如下order参数reshape()reshape((-1, N), order='F')


Wil*_*ong 5

您可以使用 np.array(itertools.product(a, b))

  • np.array(列表(itertools.product(l, l2))) (6认同)