Sử dụng numpy để xây dựng một mảng của tất cả các kết hợp của hai mảng


143

Tôi đang cố gắng chạy trên không gian tham số của hàm 6 tham số để nghiên cứu hành vi số của nó trước khi thử làm bất cứ điều gì phức tạp với nó vì vậy tôi đang tìm kiếm một cách hiệu quả để làm điều này.

Hàm của tôi lấy các giá trị float được cung cấp một mảng numpy 6 mờ làm đầu vào. Những gì tôi đã cố gắng làm ban đầu là thế này:

Đầu tiên tôi tạo một hàm lấy 2 mảng và tạo một mảng với tất cả các kết hợp các giá trị từ hai mảng

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

Sau đó, tôi thường reduce()áp dụng điều đó cho m bản sao của cùng một mảng:

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

Và sau đó tôi đánh giá chức năng của mình như thế này:

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

Điều này hoạt động nhưng nó quá chậm. Tôi biết không gian của các tham số là rất lớn, nhưng điều này không nên quá chậm. Tôi chỉ lấy mẫu 10 6 (một triệu) điểm trong ví dụ này và phải mất hơn 15 giây chỉ để tạo mảng values.

Bạn có biết cách nào hiệu quả hơn để làm điều này với numpy?

Tôi có thể sửa đổi cách hàm Flấy tham số nếu cần.


Đối với sản phẩm cartesian nhanh nhất tôi đã tìm thấy, xem câu trả lời này . (Vì câu hỏi được đặt câu khác hoàn toàn so với câu hỏi này, tôi cho rằng các câu hỏi không trùng lặp, nhưng giải pháp tốt nhất cho hai câu hỏi là như nhau.)
gửi

Câu trả lời:


127

Trong phiên bản mới hơn numpy(> 1.8.x), numpy.meshgrid()cung cấp triển khai nhanh hơn nhiều:

@ giải pháp của 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()chỉ sử dụng ở dạng 2D, bây giờ nó có khả năng ND. Trong trường hợp này, 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]])

Lưu ý rằng thứ tự của kết quả cuối cùng là hơi khác nhau.


14
np.stack(np.meshgrid([1, 2, 3], [4, 5], [6, 7]), -1).reshape(-1, 3)sẽ đưa ra thứ tự đúng
Eric

@CT Zhu Có cách nào dễ dàng để chuyển đổi điều này để ma trận giữ các mảng khác nhau khi các cột được sử dụng làm đầu vào thay thế không?
Dole

2
Cần lưu ý rằng lướigrid chỉ hoạt động cho các bộ phạm vi nhỏ hơn, tôi có một bộ lớn và tôi gặp lỗi: ValueError: kích thước được hỗ trợ tối đa cho một ndarray là 32, được tìm thấy 69
mikkom

157

Đây là một cách thực hiện thuần túy. Nó nhanh hơn khoảng 5 × so với sử dụng 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

46
bao giờ xem xét đệ trình này để được bao gồm trong numpy? đây không phải là lần đầu tiên tôi đi tìm chức năng này và tìm thấy bài đăng của bạn.
endolith

1
Có lỗi trong việc thực hiện này. Ví dụ, đối với mảng của chuỗi: mảng [0] .dtype = "| S3" và mảng [1] .dtype = "| S5". Vì vậy, cần phải tìm chuỗi dài nhất trong đầu vào và sử dụng loại của nó trong out = np.zeros ([n, len (mảng)], dtype = dtype)
norecces

38
FYI: dường như đã đưa nó vào gói scikit-learn tạifrom sklearn.utils.extmath import cartesian
Gus

2
Tôi mới nhận ra: điều này hơi khác so với itertools.combinations, vì hàm này tôn trọng thứ tự của các giá trị trong khi các kết hợp thì không, vì vậy hàm này trả về nhiều giá trị hơn các kết hợp. Vẫn rất ấn tượng, nhưng tiếc là không phải thứ tôi đang tìm kiếm :(
David Marx

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

36

itertools.combinations nói chung là cách nhanh nhất để có được các kết hợp từ bộ chứa Python (nếu bạn thực sự muốn kết hợp, nghĩa là sắp xếp KHÔNG lặp lại và không phụ thuộc vào thứ tự; đó không phải là mã của bạn đang làm, nhưng tôi không thể cho biết đó là vì mã của bạn có lỗi hay vì bạn đang sử dụng thuật ngữ sai).

Nếu bạn muốn một cái gì đó khác với sự kết hợp có lẽ các trình vòng lặp khác trong itertools, producthoặc permutations, có thể phục vụ bạn tốt hơn. Ví dụ: có vẻ như mã của bạn gần giống như:

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

Tất cả các trình vòng lặp này đều mang lại các bộ dữ liệu, không phải là danh sách hoặc mảng numpy, vì vậy nếu F của bạn rất khó tính về việc lấy một mảng cụ thể, bạn sẽ phải chấp nhận thêm chi phí xây dựng hoặc xóa và điền lại mỗi bước.


8

Bạn có thể làm một cái gì đó như thế này

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

cái nào cho

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
Có cách nào để NumPy chấp nhận hơn 32 mảng cho lướigrid không? Phương pháp này hiệu quả với tôi miễn là tôi không vượt qua hơn 32 mảng.
Joelmob

8

Việc thực hiện numpy sau đây nên được khoảng. Nhân đôi tốc độ của câu trả lời đã cho:

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
Có vẻ tốt. Theo các thử nghiệm thô sơ của tôi, điều này có vẻ nhanh hơn câu trả lời ban đầu cho tất cả các cặp, bộ ba và 4 bộ dữ liệu {1,2, ..., 100}. Sau đó, câu trả lời ban đầu chiến thắng. Ngoài ra, đối với những độc giả tương lai đang tìm cách tạo ra tất cả các kup của {1, ..., n}, np.indices((n,...,n)).reshape(k,-1).Tsẽ làm.
jme

Điều này chỉ hoạt động cho số nguyên, trong khi câu trả lời được chấp nhận cũng hoạt động cho phao.
FJC

7

Có vẻ như bạn muốn một lưới để đánh giá chức năng của mình, trong trường hợp đó bạn có thể sử dụng numpy.ogrid(mở) hoặc numpy.mgrid(bổ sung):

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


4

Đây là một cách khác, sử dụng NumPy thuần túy, không đệ quy, không hiểu danh sách và không rõ ràng cho các vòng lặp. Nó chậm hơn khoảng 20% ​​so với câu trả lời ban đầu và nó dựa trên 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

Ví dụ,

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

cho

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

Để triển khai thuần túy sản phẩm Cartesian của mảng 1D (hoặc danh sách trăn phẳng), chỉ cần sử dụng meshgrid(), cuộn các trục với transpose()và định hình lại cho tham số mong muốn:

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

Lưu ý điều này có quy ước về trục cuối cùng thay đổi nhanh nhất ("kiểu C" hoặc "hàng chính").

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

Nếu bạn muốn thay đổi trục đầu tiên nhanh nhất ("kiểu FORTRAN" hoặc "cột chính"), chỉ cần thay đổi ordertham số reshape()như sau:reshape((-1, N), order='F')


1

Pandas mergecung cấp một giải pháp nhanh chóng, ngây thơ cho vấn đề:

# 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.