numpy를 사용하여 두 배열의 모든 조합으로 구성된 배열 만들기


143

6 매개 변수 함수의 매개 변수 공간을 실행하여 복잡한 동작을 시도하기 전에 숫자 동작을 연구하려고하므로 효율적인 방법을 찾고 있습니다.

내 함수는 6-dim 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

답변:


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 열로 다른 배열을 보유하는 행렬이 입력으로 사용되도록 이것을 변환하는 쉬운 방법이 있습니까?
Dole

2
meshgrid는 작은 범위 세트의 작동하는지 주목해야한다, 내가 큰 일을하고 난이 오류가 발생합니다 : ValueError를 일 : ndarray에 지원되는 최대 크기는 32, 69 발견
mikkom

158

다음은 순수한 숫자 구현입니다. 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

47
numpy에 포함되도록 제출 하시겠습니까? 이 기능을 찾아서 귀하의 게시물을 찾은 것은 이번이 처음이 아닙니다.
endolith

1
이 구현에는 버그가 있습니다. 문자열 배열의 경우 : arrays [0] .dtype = "| S3"및 arrays [1] .dtype = "| S5". 따라서 입력에서 가장 긴 문자열을 찾아서 형식을 out = np.zeros ([n, len (arrays)], dtype = dtype)로 사용해야합니다.
Junec

38
참고로, 그것을 다음 패키지에서 scikit-learn 패키지로 만든 것 같습니다from sklearn.utils.extmath import cartesian
Gus

2
방금 깨달았습니다 : itertools.combinations와 약간 다릅니다.이 함수는 값의 순서를 존중하지만 조합은 그렇지 않으므로 조합보다 많은 값을 반환합니다. 내가 :( 찾던 여전히 매우 인상적,하지만 불행히도
데이비드 막스

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 는 일반적으로 파이썬 컨테이너에서 조합을 얻는 가장 빠른 방법입니다 (실제로 반복을하지 않고 순서와 무관하게 조합을 원한다면 코드가 수행하는 것처럼 보이지 않지만 할 수는 없습니다. 코드가 버그가 있거나 잘못된 용어를 사용하고 있는지 여부를 알려줍니다).

itertools의 다른 반복자 product또는 조합과 다른 조합을 원하면 permutations더 나은 서비스를 제공 할 수 있습니다. 예를 들어, 코드는 다음과 거의 같습니다.

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가 meshgrid에 대해 32 개 이상의 배열을 허용하도록하는 방법이 있습니까? 이 방법은 32 개 이상의 배열을 전달하지 않는 한 효과적입니다.
Joelmob

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

이것은 정수에 대해서만 작동하지만 허용되는 답변은 부동 수에 대해서도 작동합니다.
FJC

7

그리드가 함수를 평가하기를 원하는 것처럼 보입니다.이 경우 numpy.ogrid(열기) 또는 numpy.mgrid(살아 남기 )를 사용할 수 있습니다 .

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 배열 (또는 플랫 파이썬 목록)의 직교 곱을 순수하게 구현하려면을 사용 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

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.