İki dizinin tüm birleşimlerinden oluşan bir dizi oluşturmak için numpy kullanma


143

Bunu yapmak için verimli bir yol arıyor bu yüzden onunla karmaşık bir şey yapmaya çalışmadan önce sayısal davranışını incelemek için bir 6 parametre işlevinin parametre alanı üzerinden çalışıyorum.

Benim fonksiyonum girdi olarak 6-dim numpy dizi verilen float değerlerini alır. Başlangıçta yapmaya çalıştığım şey şuydu:

İlk önce 2 diziyi alan ve iki diziden gelen değerlerin tüm kombinasyonlarını içeren bir dizi oluşturdum

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

Sonra reduce()aynı dizinin m kopyalarına uygulamak için kullanılır :

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

Sonra fonksiyonumu şöyle değerlendiriyorum:

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

Bu işe yarıyor ama çok yavaş. Parametrelerin uzayının çok büyük olduğunu biliyorum, ama bu çok yavaş olmamalı. Bu örnekte sadece 10 6 (bir milyon) puan örnekledim ve sadece diziyi oluşturmak 15 saniyeden fazla sürdü values.

Numpy ile bunu yapmanın daha etkili bir yolunu biliyor musunuz?

FGerekirse işlevin argümanları alma şeklini değiştirebilirim .


Bulduğum en hızlı kartezyen ürün için bu cevaba bakınız . (Soru bu sorudan oldukça farklı bir şekilde ifade edildiğinden, soruların kopya olmadığını düşünüyorum, ancak iki soru için en iyi çözüm aynıdır.)
17'de senderle

Yanıtlar:


127

numpy(> 1.8.x) ' in daha yeni sürümünde, numpy.meshgrid()çok daha hızlı bir uygulama sağlar:

@ pv'nin çözümü

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()sadece 2B olarak kullanın, şimdi ND yeteneğine sahiptir. Bu durumda, 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]])

Nihai sonucun sırasının biraz farklı olduğuna dikkat edin.


14
np.stack(np.meshgrid([1, 2, 3], [4, 5], [6, 7]), -1).reshape(-1, 3)doğru emri verecek
Eric

@CT Zhu Bunu, farklı dizileri sütun olarak tutan bir matrisin girdi olarak kullanılması için bunu dönüştürmenin kolay bir yolu var mı?
Dole

2
Meshgrid'in yalnızca daha küçük aralık kümeleri için çalıştığı, büyük bir tane aldığım ve hata alıyorum: ValueError: bir ndarray için desteklenen maksimum boyut 32, 69 bulundu
mikkom

158

İşte saf numpy bir uygulama. Itertools kullanmaktan yaklaşık 5 kat daha hızlıdır.


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
hiç bu numpy dahil edilecek gönderme düşünün? Bu işlevselliği aramaya ilk kez gitmedim ve yazınızı buldum.
endolith

1
Bu uygulamada hata var. Örneğin dizelerin dizileri için: diziler [0] .dtype = "| S3" ve diziler [1] .dtype = "| S5". Bu nedenle, girişteki en uzun dizeyi bulmaya ve türünü out = np.zeros ([n, len (diziler)], dtype = dtype)
2013'te

38
FYI: Scikit-öğrenim paketini yapmış gibi görünüyorfrom sklearn.utils.extmath import cartesian
Gus

2
Az önce fark ettim: bu işlev kombinasyonların vermediği halde değerlerin sırasına saygı duyduğundan itertools.combinations'dan biraz farklıdır, bu nedenle bu işlev kombinasyonlardan daha fazla değer döndürür. Hala çok etkileyici, ama ne yazık ki aradığım şey değil :(
David Marx

6
TypeError: slice indices must be integers or None or have an __index__ methodtarafından atılancartesian(arrays[1:], out=out[0:m,1:])
Boern

36

itertools.combinations genel olarak bir Python konteynerinden kombinasyon almanın en hızlı yoludur (aslında kombinasyonlar istiyorsanız, yani tekrarlar OLMADAN ve siparişten bağımsız düzenlemeler istiyorsanız; kodunuzun yaptığı şey bu değil, ama yapamam kodunuzun hatalı veya yanlış terminolojiyi kullandığınızdan emin olun).

Kombinasyonlardan farklı bir şey istiyorsanız, belki de itertools'taki diğer yineleyiciler productveya permutationssize daha iyi hizmet edebilir. Örneğin, kodunuz kabaca aynıdır:

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

Tüm bu yineleyiciler listeler veya numpy dizileri değil tuples verir, bu nedenle F'niz özellikle numpy dizi alma konusunda seçici ise, her adımda bir tane oluşturmak veya temizlemek ve yeniden doldurmak için ek yükü kabul etmeniz gerekir.


8

Böyle bir şey yapabilirsin

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

hangi verir

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
MeshPrid için 32'den fazla diziyi kabul etmenin bir yolu var mı? 32'den fazla diziyi geçmediğim sürece bu yöntem benim için çalışıyor.
Joelmob

8

Aşağıdaki numpy uygulaması yakl. Verilen cevabın hızı 2 kat:

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
İyi görünüyor. İlkel testlerime göre, bu, {1,2, ..., 100} tüm çiftler, üçlüler ve 4 grup için orijinal cevaptan daha hızlı görünüyor. Bundan sonra, orijinal cevap kazanır. Ayrıca, {1, ..., n} ' np.indices((n,...,n)).reshape(k,-1).Tnin tüm k-gruplarını oluşturmak isteyen gelecekteki okuyucular için de yapacaktır.
jme

Bu sadece tamsayılar için geçerliyken, kabul edilen cevap aynı zamanda float'lar için de geçerlidir.
FJC

7

Görünüşe göre bir ızgaranın işlevinizi değerlendirmesini istiyorsunuz, bu durumda numpy.ogrid(aç) veya numpy.mgrid(et dışarı) kullanabilirsiniz:

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


4

İşte başka bir yol, saf NumPy kullanarak, özyineleme yok, liste kavrama yok ve döngüler için açık değil. Orijinal yanıttan yaklaşık% 20 daha yavaş ve np.meshgrid'e dayanıyor.

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

Örneğin,

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

verir

[[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 dizilerinin (veya düz python listelerinin) Kartezyen ürününün saf numpy uygulaması için, sadece meshgrid()eksenleri kullanın , transpose()döndürün ve istenen çıkışa yeniden şekillendirin:

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

Bunun en son eksen değiştirme hızının ("C stili" veya "satır-büyük") olduğunu unutmayı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]])

İlk ekseni en hızlı şekilde değiştirmek istiyorsanız ("FORTRAN stili" veya "sütun-majör"), şu orderparametreyi değiştirin reshape():reshape((-1, N), order='F')


1

Pandalar mergesoruna naif ve hızlı bir çözüm sunar:

# 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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.