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

Python 使用numpy构建两个数组的所有组合的数组,python,arrays,multidimensional-array,numpy,Python,Arrays,Multidimensional Array,Numpy,我试图在对一个6参数函数进行任何复杂操作之前,对它的参数空间进行遍历,以研究它的数值行为,所以我正在寻找一种有效的方法来实现这一点 我的函数将6维numpy数组中给定的浮点值作为输入。我最初尝试做的是: 首先,我创建了一个函数,该函数接受2个数组,并生成一个包含两个数组中所有值组合的数组: from numpy import * def comb(a,b): c = [] for i in a: for j in b: c.append(r

我试图在对一个6参数函数进行任何复杂操作之前,对它的参数空间进行遍历,以研究它的数值行为,所以我正在寻找一种有效的方法来实现这一点

我的函数将6维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
然后,我使用
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)
这是可行的,但速度太慢了。我知道参数的空间很大,但这不会太慢。在本例中,我只采样了106(一百万)个点,仅创建数组
就花费了超过15秒的时间

你知道用numpy做这件事有什么更有效的方法吗

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

通常是从Python容器获取组合的最快方式(如果您确实需要组合,即不重复且独立于顺序的排列;这不是您的代码看起来要做的,但我无法判断这是因为您的代码有缺陷还是因为您使用了错误的术语)

如果您想要的是不同于组合的东西,可能是itertools中的其他迭代器,
product
permutations
,可能会更好地为您服务。例如,您的代码看起来与:

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

所有这些迭代器都会生成元组,而不是列表或numpy数组,因此如果您的F对获取特定的numpy数组很挑剔,那么您将不得不接受在每一步构造或清除并重新填充一个数组的额外开销。

这是一个纯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

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


你可以这样做

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

以下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

这里还有另一种方法,使用纯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)
给予

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

@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]])
以前仅为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]])

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

对于1D数组笛卡尔乘积(或平面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样式”或“行主”)


如果您想以最快的速度更改第一个轴(“FORTRAN样式”或“column major”),只需更改
整形()的
顺序
参数如下:
整形((-1,N),order='F')
您可以使用
np.array(itertools.product(a,b))

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

有没有想过把它提交到NUMPY中?这不是我第一次去寻找这个功能,找到了你的帖子。在这个实现中有bug。对于数组的字符串,例如:数组[0 ]。dType=“s3”和数组[1 ]。。因此需要在输入中查找最长的字符串,并使用其类型in-out=np.zeros([n,len(数组)],dtype=dtype)仅供参考:似乎已经从sklearn.utils.extmath import cartesian
进入了scikit学习包中,我刚刚意识到:这与itertools.combines略有不同,因为此函数尊重值的顺序,而组合不尊重,因此此函数返回的值比组合多。仍然非常令人印象深刻ve,但不幸的是不是我想要的:(
TypeError:slice索引必须是整数或无,或者具有笛卡尔(Array[1:],out=out[0:m,1:])抛出的{code>方法。
看起来不错。通过我的初步测试,这看起来比所有对、三元组和4元组的{1,2,…,100}的原始答案更快此外,对于希望生成{1,…,n},
np.索引((n,…,n)).的所有k元组的未来读者,重塑(k,-1).T
可以。这只适用于整数,而公认的答案也适用于浮点数。有没有办法让NumPy接受32个以上的网格数组?只要我传递的数组不超过32个,这种方法就可以使用。
np.stack(np.meshgrid([1,2,3],[4,5],[6,7]),-1)。重塑(-1,3)
将给出正确的order@CT朱:有没有一种简单的方法来转换它,以便将包含不同数组作为列的a矩阵用作输入?应该注意的是,meshgrid只适用于较小的范围集,我有一个较大的范围集,我得到了错误:ValueError:ndarray支持的最大维数是32,找到了吗69@mikkom, 没有任何东西可以处理大于32的集合。即使每个集合的大小为2,组合的数量也将是2**32,4 Gb。对于我发现的最快的笛卡尔积,请参见。(由于问题的措辞与此完全不同,我认为这些问题不是重复的,但两个问题的最佳解决方案是相同的。)np.array(列表(itertools.product(l,l2)))
 def cartprod(*arrays):
     N = len(arrays)
     return transpose(meshgrid(*arrays, indexing='ij'), 
                      roll(arange(N + 1), -1)).reshape(-1, N)
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]])
# 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)