2014-10-15 13 views
7

Czy jest lepszy sposób w numpy do płytek tablicę liczbę niecałkowitą liczbę razy? To załatwia sprawę, ale jest niezgrabne i nie łatwo uogólnić na n-wymiarach:numpy.tile Niecałkowitą liczbę razy

import numpy as np 
arr = np.arange(6).reshape((2, 3)) 
desired_shape = (5, 8) 
reps = tuple([x // y for x, y in zip(desired_shape, arr.shape)]) 
left = tuple([x % y for x, y in zip(desired_shape, arr.shape)]) 
tmp = np.tile(arr, reps) 
tmp = np.r_[tmp, tmp[slice(left[0]), :]] 
tmp = np.c_[tmp, tmp[:, slice(left[1])]] 

Daje:

array([[0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1]]) 

EDIT: WYNIKI

Niektóre testy trzech odpowiedzi uogólnionych na n-wymiary. Definicje te zostały wprowadzone w pliku newtile.py:

import numpy as np 

def tile_pad(a, dims): 
    return np.pad(a, tuple((0, i) for i in (np.array(dims) - a.shape)), 
        mode='wrap') 

def tile_meshgrid(a, dims): 
    return a[np.meshgrid(*[np.arange(j) % k for j, k in zip(dims, a.shape)], 
         sparse=True, indexing='ij')] 

def tile_rav_mult_idx(a, dims): 
    return a.flat[np.ravel_multi_index(np.indices(dims), a.shape, mode='wrap')] 

są tu linie atakujących:

python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_pad(np.arange(30).reshape(2, 3, 5), (3, 5, 7))' 
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_meshgrid(np.arange(30).reshape(2, 3, 5), (3, 5, 7))' 
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_rav_mult_idx(np.arange(30).reshape(2, 3, 5), (3, 5, 7))' 

python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_pad(np.arange(2310).reshape(2, 3, 5, 7, 11), (13, 17, 19, 23, 29))' 
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_meshgrid(np.arange(2310).reshape(2, 3, 5, 7, 11), (13, 17, 19, 23, 29))' 
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_rav_mult_idx(np.arange(2310).reshape(2, 3, 5, 7, 11), (13, 17, 19, 23, 29))' 

Wybrane wyniki z małych tablic (2 x 3 x 5):

pad:    10000 loops, best of 3: 106 usec per loop 
meshgrid:   10000 loops, best of 3: 56.4 usec per loop 
ravel_multi_index: 10000 loops, best of 3: 50.2 usec per loop 

Oto wyniki z większymi tablicami (2 x 3 x 5 x 7 x 11):

pad:    10 loops, best of 3: 25.2 msec per loop 
meshgrid:   10 loops, best of 3: 300 msec per loop 
ravel_multi_index: 10 loops, best of 3: 218 msec per loop 

Metoda z użyciem np.pad jest prawdopodobnie najbardziej wydajnym wyborem.

+0

Jak powinno być zachowanie podczas używania 'np.tile()' z liczbami niecałkowitymi? –

+1

@SaulloCastro: Być może mój tytuł jest trochę mylący. Moim zdaniem 'np.tile' powinno * nie * przyjmować niecałkowite argumenty do' reps'. Co chcę osiągnąć to * analogiczne * do tego, co by się stało, gdyby 'np.tile' wziął niecałkowite argumenty do' reps' ** i ** jeśli przekazana liczba niecałkowita daje liczbę całkowitą wierszy/kolumn/etc w macierzy wyjściowej. Najbliższym analogicznym przykładem, jaki znam, jest argument "length.out" funkcji 'rep()' w języku 'R'. – drammock

Odpowiedz

2

Innym rozwiązaniem, które jest jeszcze bardziej zwięzły:

arr = np.arange(6).reshape((2, 3)) 
desired_shape = np.array((5, 8)) 

pads = tuple((0, i) for i in (desired_shape-arr.shape)) 
# pads = ((0, add_rows), (0, add_columns), ...) 
np.pad(arr, pads, mode="wrap") 

ale jest wolniejszy dla małych tablic (znacznie szybciej dużych chociaż). O dziwo, np.pad nie zaakceptuje np.array dla pads.

0

Nie jestem pewien co do wymiarów n, ale można rozważyć użycie hstack i vstack.

arr = np.arange(6).reshape((2, 3)) 

nx, ny = shape(arr) 
Nx, Ny = 5, 8 # These are the new shapes 
iX, iY = Nx//nx+1, Ny//ny+1 

result = vstack(tuple([ hstack(tuple([arr]*iX))[:, :Nx] ]*iY))[:Ny, : ] 

Istnieje dstack, ale wątpię, czy to pomoże. Nie do końca pewna co do 3 i wyższych wymiarów.

2

Oto bardzo zwięzły sposób:

In [57]: a 
Out[57]: 
array([[0, 1, 2], 
     [3, 4, 5]]) 

In [58]: old = a.shape 

In [59]: new = (5, 8) 

In [60]: a[(np.arange(new[0]) % old[0])[:,None], np.arange(new[1]) % old[1]] 
Out[60]: 
array([[0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1]]) 

Poniżej n-wymiarowej uogólnienie:

def rep_shape(a, shape): 
    indices = np.meshgrid(*[np.arange(k) % j for j, k in zip(a.shape, shape)], 
          sparse=True, indexing='ij') 
    return a[indices] 

Na przykład

In [89]: a 
Out[89]: 
array([[0, 1, 2], 
     [3, 4, 5]]) 

In [90]: rep_shape(a, (5, 8)) 
Out[90]: 
array([[0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1]]) 

In [91]: rep_shape(a, (4, 2)) 
Out[91]: 
array([[0, 1], 
     [3, 4], 
     [0, 1], 
     [3, 4]]) 

In [92]: b = np.arange(24).reshape(2,3,4) 

In [93]: b 
Out[93]: 
array([[[ 0, 1, 2, 3], 
     [ 4, 5, 6, 7], 
     [ 8, 9, 10, 11]], 

     [[12, 13, 14, 15], 
     [16, 17, 18, 19], 
     [20, 21, 22, 23]]]) 

In [94]: rep_shape(b, (3,4,5)) 
Out[94]: 
array([[[ 0, 1, 2, 3, 0], 
     [ 4, 5, 6, 7, 4], 
     [ 8, 9, 10, 11, 8], 
     [ 0, 1, 2, 3, 0]], 

     [[12, 13, 14, 15, 12], 
     [16, 17, 18, 19, 16], 
     [20, 21, 22, 23, 20], 
     [12, 13, 14, 15, 12]], 

     [[ 0, 1, 2, 3, 0], 
     [ 4, 5, 6, 7, 4], 
     [ 8, 9, 10, 11, 8], 
     [ 0, 1, 2, 3, 0]]]) 

Oto jak działa pierwszy przykład ...

Pomysł polega na użyciu tablic do indeksu a. Spójrz na np.arange(new[0] % old[0]):

In [61]: np.arange(new[0]) % old[0] 
Out[61]: array([0, 1, 0, 1, 0]) 

Każda wartość w tej tablicy daje wiersz a do użycia w wyniku. Similary,

In [62]: np.arange(new[1]) % old[1] 
Out[62]: array([0, 1, 2, 0, 1, 2, 0, 1]) 

daje kolumny a używać w wyniku.Na te tablice indeksowe, aby stworzyć efekt 2-D, musimy przekształcić jeden w pierwszej kolumnie:

In [63]: (np.arange(new[0]) % old[0])[:,None] 
Out[63]: 
array([[0], 
     [1], 
     [0], 
     [1], 
     [0]]) 

Kiedy matryce są wykorzystywane jako wskaźniki, one nadawane. Oto co indeksy broadcast wyglądać następująco:

n [65]: i, j = np.broadcast_arrays((np.arange(new[0]) % old[0])[:,None], np.arange(new[1]) % old[1]) 

In [66]: i 
Out[66]: 
array([[0, 0, 0, 0, 0, 0, 0, 0], 
     [1, 1, 1, 1, 1, 1, 1, 1], 
     [0, 0, 0, 0, 0, 0, 0, 0], 
     [1, 1, 1, 1, 1, 1, 1, 1], 
     [0, 0, 0, 0, 0, 0, 0, 0]]) 

In [67]: j 
Out[67]: 
array([[0, 1, 2, 0, 1, 2, 0, 1], 
     [0, 1, 2, 0, 1, 2, 0, 1], 
     [0, 1, 2, 0, 1, 2, 0, 1], 
     [0, 1, 2, 0, 1, 2, 0, 1], 
     [0, 1, 2, 0, 1, 2, 0, 1]]) 

Są array index że musimy wygenerować tablicę z kształtem (5, 8):

In [68]: a[i,j] 
Out[68]: 
array([[0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1], 
     [3, 4, 5, 3, 4, 5, 3, 4], 
     [0, 1, 2, 0, 1, 2, 0, 1]]) 

Kiedy tablice indeksowe są podane jak w przykład na początku (tj. przy użyciu (np.arange(new[0]) % old[0])[:,None] w pierwszym otworze indeksu), numpy faktycznie nie generuje tych tablic indeksów w pamięci, tak jak zrobiłem to z i i j. i i j wyświetlają zawartość efektywną podczas nadawania.

Funkcja rep_shape wykonuje to samo, używając np.meshgrid do generowania tablic indeksowych dla każdego "gniazda" o prawidłowych kształtach do nadawania.

2

Może nie bardzo wydajny, ale bardzo zwięzły:

arr = np.arange(6).reshape((2, 3)) 
desired_shape = (5, 8) 

arr.flat[np.ravel_multi_index(np.indices(desired_shape), arr.shape, mode='wrap')] 
+0

Nice. Generalizuje również do wymiarów n, o ile mogę to stwierdzić: testowane za pomocą 'arr = np.arange (30) .reshape ((2, 3, 5))' i 'desired_shape = (5, 8, 13)' – drammock

+0

Działa to świetnie. Potencjalną wadą jest to, że w przypadku n-wymiarowym 'np.indices (desired_shape)' tworzy tymczasową tablicę o kształcie '(n,) + desired_shape' (np. (3, 5, 8, 13), gdy' desired_shape' to (5, 8, 13)). Ale to nie jest problem, jeśli tablice są małe. –

Powiązane problemy