numpyを使用して、2つの配列のすべての組み合わせの配列を構築する
-
05-07-2019 - |
質問
6パラメーター関数のパラメーター空間を実行して、数値の振る舞いを調べてから、複雑なことを試みる前に、効率的な方法を探しています。
私の関数は、入力として6次元のnumpy配列が与えられたfloat値を取ります。私が最初にしようとしたことはこれでした:
最初に、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 (100万)ポイントしかサンプリングしておらず、配列 values
を作成するのに15秒以上かかりました。
numpyでこれを行うより効率的な方法を知っていますか?
関数 F
が必要に応じて引数を取る方法を変更できます。
解決
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]])
最終結果の順序はわずかに異なることに注意してください。
他のヒント
これは、純粋なnumpyの実装です。だよ5× itertoolsを使用するよりも高速です。
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
itertools.combinations は、一般的に組み合わせを取得する最も速い方法です。 Pythonコンテナから(実際に組み合わせが必要な場合、つまり、繰り返しのない、順序に依存しない配置。それはコードが実行しているようには見えませんが、それはコードがバグだからなのか、それとも間違った用語を使用しています)。
itertools、 product
または permutations
の他のイテレータなどの組み合わせとは異なるものが必要な場合は、より役立つかもしれません。たとえば、コードは次のようになります:
for val in itertools.product(np.arange(0, 1, 0.1), repeat=6):
print F(val)
これらのイテレータはすべて、リストやnumpy配列ではなくタプルを生成します。したがって、Fが具体的にnumpy配列を取得することにこだわりがある場合は、各ステップで1つの構築またはクリアおよび再充填の追加オーバーヘッドを受け入れる必要があります。
次の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.ogrid
(オープン)または numpy.mgrid
(肉付け済み)を使用できます。
import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]
このようなことができます
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]])
np.array(itertools.product(a、b))
純粋な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]]
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スタイル"または" row-major")。
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]])
first 軸を最速(" FORTRANスタイル"または" column-major")に変更する場合は、の
: order
パラメーターを変更するだけです次のようなreshape() reshape((-1、N)、order = 'F')