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


143

我试图在尝试使用6参数函数的参数空间之前先研究它的数值行为,然后再尝试对其进行复杂的处理,因此我正在寻找一种有效的方法来执行此操作。

我的函数采用给定6维numpy数组作为输入的float值。我最初尝试做的是:

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

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

然后我将reduce()其应用于同一数组的m个副本:

def combs(a,m):
    return reduce(comb,[a]*m)

然后我像这样评估我的功能:

values = combs(np.arange(0,1,0.1),6)
for val in values:
    print F(val)

这有效,但是太慢了。我知道参数的空间很大,但这不应该太慢。在此示例中,我仅采样了10 6(一百万)个点,仅花费了15秒以上的时间便创建了数组values

您知道使用numpy进行此操作的更有效的方法吗?

F如果需要,我可以修改函数接受参数的方式。


对于我发现的最快的笛卡尔积,请参见此答案。(由于问题的措词与这一问题完全不同,我认为这些问题不是重复的,但对两个问题的最佳解决方案是相同的。)
senderle

Answers:


127

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]])

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]])

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


14
np.stack(np.meshgrid([1, 2, 3], [4, 5], [6, 7]), -1).reshape(-1, 3)将给出正确的顺序
Eric

@CT Zhu是否有一种简单的方法可以对其进行转换,以便将以不同数组作为列的矩阵用作输入?
多尔

2
应该注意的是,meshgrid仅适用于较小范围的集合,我有一个很大的集合,但出现错误:ValueError:ndarray的最大支持尺寸为32,发现为69
mikkom

157

这是一个纯粹的numpy实现。它比使用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

46
是否考虑过将其提交到numpy中?这不是我第一次寻找此功能并找到您的帖子。
endlith 2013年

1
此实现中有错误。例如,对于字符串数组:arrays [0] .dtype =“ | S3”和arrays [1] .dtype =“ | S5”。因此,有在发现在输入的最长字符串,并以出利用其类型= np.zeros([N,LEN(阵列)],D型细胞= D型)需要
norecces

38
仅供参考:在from sklearn.utils.extmath import cartesian
Gus

2
我刚刚意识到:这与itertools.combinations略有不同,因为此函数遵循值的顺序,而组合不遵循值的顺序,因此此函数返回的值比组合要多。仍然非常令人印象深刻,但不幸的是我没有寻找:(
David Marx

6
TypeError: slice indices must be integers or None or have an __index__ methodcartesian(arrays[1:], out=out[0:m,1:])
Boern

36

通常,itertools.combinations是从Python容器中获取组合的最快方法(如果您实际上确实想要组合,即无重复且无顺序的安排;那不是您的代码看起来正在做的事情,但是我做不到判断这是因为您的代码有错误还是因为您使用了错误的术语)。

如果您想要的不是组合,则itertools中的其他迭代器productpermutations可能会为您提供更好的服务。例如,看起来您的代码与以下代码大致相同:

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

所有这些迭代器都会生成元组,而不是列表或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])

这使

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]])

2
有没有办法让NumPy接受32个以上的网格网格数组?只要我不传递超过32个数组,该方法就对我有效。
Joelmob 2014年

8

以下numpy实现应为大约。给定答案速度的2倍:

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

1
看起来挺好的。根据我的基本测试,对于{1,2,...,100}的所有成对,三元组和4元组,这看起来比原始答案要快。之后,原始答案将获胜。同样,对于将来希望生成{1,...,n}的所有k元组的读者来说,np.indices((n,...,n)).reshape(k,-1).T也可以使用。
jme 2014年

这仅适用于整数,而可接受的答案也适用于浮点数。
FJC

7

看起来您想让网格评估您的功能,在这种情况下,您可以使用numpy.ogrid(打开)或numpy.mgrid(完善):

import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]

6

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


np.array(list(itertools.product(l,l2)))
ZirconCode

4

这是使用纯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

例如,

x = np.arange(3)
a = cartesian(x, x, x, x, x)
print(a)

[[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]]

3

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

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

请注意,这具有最后一个轴更改最快的约定(“ 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]])

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


1

熊猫merge提供了一个天真的,快速的解决方案:

# given the lists
x, y, z = [1, 2, 3], [4, 5], [6, 7]

# get dfs with same, constant index 
x = pd.DataFrame({'x': x}, index=np.repeat(0, len(x))
y = pd.DataFrame({'y': y}, index=np.repeat(0, len(y))
z = pd.DataFrame({'z': z}, index=np.repeat(0, len(z))

# get all permutations stored in a new df
df = pd.merge(x, pd.merge(y, z, left_index=True, righ_index=True),
              left_index=True, right_index=True)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.