numpyを使用して2つの配列のすべての組み合わせの配列を構築する


143

6つのパラメーター関数のパラメーター空間を実行して数値の動作を調べてから、複雑な処理を行う前に、効率的な方法を模索しています。

私の関数は、入力として6次元のnumpy配列が指定された浮動小数点値を取ります。私が最初にやろうとしたのはこれです:

最初に、2つの配列を取り、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(百万)ポイントしかサンプリングしていませんvalues。配列を作成するだけで15秒以上かかりました。

numpyでこれを行うより効率的な方法を知っていますか?

F必要に応じて、関数が引数をとる方法を変更できます。


私が見つけた最速のデカルト積については、この回答を参照してください。(質問のフレーズがこれとはかなり異なるため、質問は重複していないと見なしますが、2つの質問に対する最良の解決策は同じです。)
sendle

回答:


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
69を発見し、ndarrayのためのサポートされる最大寸法は32です:ValueErrorを:小さな範囲のセットが、私は大規模なものを持っていると私はエラーを取得するための関数meshgridのみ動作することに注意すべき
mikkom

157

以下は、純粋で派手な実装です。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に含めるために提出することを検討したことがありますか?私がこの機能を探してあなたの投稿を見つけたのはこれが初めてではありません。
エンドリス

1
この実装にはバグがあります。たとえば、文字列の配列の場合:array [0] .dtype = "| S3"およびarray [1] .dtype = "| S5"。そう入力の中で最も長い文字列を見つけ、アウトでそのタイプを使用= np.zerosを([N、lenは(アレイ)]、DTYPE = DTYPE)で必要がある
norecces

38
FYI:でscikit-学ぶパッケージにそれを作っているようだ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__ methodスローされたcartesian(arrays[1:], out=out[0:m,1:])
Boern

36

itertools.combinationsは一般に、Pythonコンテナから組み合わせを取得する最も速い方法です(実際に組み合わせが必要な場合、つまり、繰り返しなしの配列で、順序に依存しない場合、これはコードが実行しているようには見えませんが、できません。コードにバグがあるのか​​、間違った用語を使用しているのかを確認してください)。

組み合わせとは異なる何かが必要な場合は、おそらくitertools productまたはの他のイテレータpermutationsが役立つでしょう。たとえば、コードはおおよそ次のように見えます。

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

これらのイテレータはすべて、リストや派手な配列ではなくタプルを生成するため、Fが特に派手な配列を取得することにこだわる場合は、各ステップで1つを作成またはクリアして再充填するという余分なオーバーヘッドを受け入れる必要があります。


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がmeshgridに32を超える配列を受け入れるようにする方法はありますか?この方法は、32個を超える配列を渡さない限り有効です。
Joelmob 2014

8

次の派手な実装はおよそです。与えられた答えの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 2016年

7

グリッドで関数を評価したいようです。この場合、numpy.ogrid(open)またはnumpy.mgrid(fleshed out)を使用できます。

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


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

1D配列(またはフラットpythonリスト)のデカルト積の純粋で派手な実装の場合は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スタイル」または「列優先」)、次orderreshape()ようにパラメーターを変更します。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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.