2015-06-04 25 views
6

Potrzebuję przesunąć tablicę 3D o wektor 3D przemieszczenia dla algorytmu. Jak na razie używam tej metody (admitedly bardzo brzydki):Numpy roll w kilku wymiarach

shiftedArray = np.roll(np.roll(np.roll(arrayToShift, shift[0], axis=0) 
            , shift[1], axis=1), 
          shift[2], axis=2) 

który pracuje, ale oznacza Dzwonię 3 rolki! (58% mojego czasu algorytm jest spędzony w nich, według mojego profilowania)

Od docs Numpy.roll:

parametry:
zmiana: int

osi: int, opcjonalnie

Brak wzmianki o parametrze tablicowym w parametrze ... Więc nie mogę mieć wielowymiarowego toczenia?

Myślałem, że mogę po prostu nazywają ten rodzaj funkcji (brzmi jak NumPy rzeczy do zrobienia):

np.roll(arrayToShift,3DshiftVector,axis=(0,1,2)) 

Może ze spłaszczonym wersją mojej tablicy zmodyfikowane? ale jak mogę obliczyć wektor przesunięcia? i czy ta zmiana jest naprawdę taka sama?

Jestem zaskoczony, nie łatwe rozwiązanie tego problemu, jak myślałem, że będzie to dość powszechne rzeczą do zrobienia (w porządku, nie że wspólnego, ale ...)

Jak więc --relatywnie - efektywnie przesuwać ndarray przez wektor N-Dimensional?

Odpowiedz

3

myślę scipy.ndimage.interpolation.shift zrobi co chcesz, od docs

shift: pływaka lub sekwencji, opcjonalnie

przesunięcie wzdłuż osi. Jeśli jest zmienny, przesunięcie jest takie samo dla każdej osi. Jeśli sekwencja, zmiana powinna zawierać jedną wartość dla każdej osi.

Czyli można wykonać następujące czynności,

from scipy.ndimage.interpolation import shift 
import numpy as np 

arrayToShift = np.reshape([i for i in range(27)],(3,3,3)) 

print('Before shift') 
print(arrayToShift) 

shiftVector = (1,2,3) 
shiftedarray = shift(arrayToShift,shift=shiftVector,mode='wrap') 

print('After shift') 
print(shiftedarray) 

co daje,

Before shift 
[[[ 0 1 2] 
    [ 3 4 5] 
    [ 6 7 8]] 

[[ 9 10 11] 
    [12 13 14] 
    [15 16 17]] 

[[18 19 20] 
    [21 22 23] 
    [24 25 26]]] 
After shift 
[[[16 17 16] 
    [13 14 13] 
    [10 11 10]] 

[[ 7 8 7] 
    [ 4 5 4] 
    [ 1 2 1]] 

[[16 17 16] 
    [13 14 13] 
    [10 11 10]]] 
+0

Nice! Sądzę, że miałem obsesję na punkcie rzucania numpy i googled. Tylko wtedy, gdy napisałem to pytanie SO, określiłem je jako "przesunięcie", które doprowadziłoby mnie prosto do tej funkcji! Trochę edycji, ponieważ używa splajnów dla nie-okrągłych zmian (ale używam wyłącznie liczb całkowitych), musiałem określić 'order = 0' (w przeciwnym razie ta metoda zajęła 100 razy dłużej niż moja stara metoda!= p) – Jiby

+0

Świetnie, cieszę się, że to też jest wydajne. Ostatnio znalazłem tylko przesunięcie scipy, odpowiadając na inne pytanie (http://stackoverflow.com/questions/30399534/shift-elements-in-a-numpy-array/30534478#30534478). Fortran ma wewnętrzną funkcję przesunięcia, więc sądzę, że scipy używa tego, dlatego może być znacznie szybszy niż numpy. Zgadnij, że 'order = 0' unika splajnów ... –

+0

Przesunięcie' scipy' wykorzystuje interpolację splajnu. Więc oblicza nowe wartości, a nie tylko przesuwa istniejące. – hpaulj

0

Wierzę, że roll jest powolny, ponieważ walcowanej macierzy nie można wyrazić jako widoku oryginalnych danych jako operacji plastra lub przekształcenia. Więc dane są kopiowane za każdym razem. Na tle zobaczyć: https://scipy-lectures.github.io/advanced/advanced_numpy/#life-of-ndarray

Co może być wart wypróbowania go do pierwszego klocka macierzy (w trybie „oblewania”), a następnie użyć plastry na wyściełanej tablicy dostać shiftedArray: http://docs.scipy.org/doc/numpy/reference/generated/numpy.pad.html

+0

prawda, a ja powinienem powiedzieć, że zacząłem od założenia, że ​​jedna (pojedyncza) kopia tablicy była w porządku, ale trzykrotne rzucenie z rzędu oznaczałoby w istocie 2 zbędne kopie, których chcę uniknąć. – Jiby

+0

Tak, ta metoda byłaby kopiowana raz, a następnie cięcie byłoby po prostu widokiem tych danych. – YXD

+0

'np.roll' używa' arange' do skonstruowania 'indexes' tuple, a następnie' take'. Czyni to wolniejsze zaawansowane indeksowanie. Teoretycznie możesz zbudować własną trójwymiarową krotkę i zastosować ją tylko raz. Ale to może być dużo pracy. – hpaulj

0

take w trybie wrap może być używany i myślę, że to nie zmienia tablicę w pamięci .

Oto implementacja przy użyciu @ wejść EdSmith za:

arrayToShift = np.reshape([i for i in range(27)],(3,3,3)) 
shiftVector = np.array((1,2,3)) 
ind = 3-shiftVector  
np.take(np.take(np.take(arrayToShift,range(ind[0],ind[0]+3),axis=0,mode='wrap'),range(ind[1],ind[1]+3),axis=1,mode='wrap'),range(ind[2],ind[2]+3),axis=2,mode='wrap') 

co daje taki sam jak OP:

np.roll(np.roll(np.roll(arrayToShift, shift[0], axis=0) , shift[1], axis=1),shift[2], axis=2) 

daje:

array([[[21, 22, 23], 
     [24, 25, 26], 
     [18, 19, 20]], 

     [[ 3, 4, 5], 
     [ 6, 7, 8], 
     [ 0, 1, 2]], 

     [[12, 13, 14], 
     [15, 16, 17], 
     [ 9, 10, 11]]]) 
3

Teoretycznie, stosując scipy.ndimage.interpolation.shift jako opisany przez @Ed Smith powinien działać, ale z powodu otwartego błędu (https://github.com/scipy/scipy/issues/1323), nie robi tego ". t dać wynik, który jest równoważny wielu połączeniom np.roll.


UPDATE: "Multi-roll" zdolności dodano numpy.roll w numpy wersji 1.12.0. Poniżej dwuwymiarowy przykład, w którym pierwsza oś jest walcowany o jedną pozycję, a druga oś jest zwinięta w trzech pozycjach:

In [7]: x = np.arange(20).reshape(4,5) 

In [8]: x 
Out[8]: 
array([[ 0, 1, 2, 3, 4], 
     [ 5, 6, 7, 8, 9], 
     [10, 11, 12, 13, 14], 
     [15, 16, 17, 18, 19]]) 

In [9]: numpy.roll(x, [1, 3], axis=(0, 1)) 
Out[9]: 
array([[17, 18, 19, 15, 16], 
     [ 2, 3, 4, 0, 1], 
     [ 7, 8, 9, 5, 6], 
     [12, 13, 14, 10, 11]]) 

To sprawia, że ​​kod poniżej przestarzała. Zostawię to dla potomności.


Poniższy kod definiuje funkcję, którą nazywam multiroll, która robi to, co chcesz. Oto przykład, w którym jest stosowana do tablicy z kształtu (500, 500, 500):

In [64]: x = np.random.randn(500, 500, 500) 

In [65]: shift = [10, 15, 20] 

Stosować wielokrotne wywołania np.roll generować oczekiwany wynik:

In [66]: yroll3 = np.roll(np.roll(np.roll(x, shift[0], axis=0), shift[1], axis=1), shift[2], axis=2) 

wygenerowania przesunięty tablicę stosując multiroll:

In [67]: ymulti = multiroll(x, shift) 

Sprawdź, czy mamy oczekiwany wynik:

In [68]: np.all(yroll3 == ymulti) 
Out[68]: True 

Na tablicy tej wielkości, co trzy zaproszenia do np.roll jest prawie trzy razy wolniej niż wezwanie do multiroll:

In [69]: %timeit yroll3 = np.roll(np.roll(np.roll(x, shift[0], axis=0), shift[1], axis=1), shift[2], axis=2) 
1 loops, best of 3: 1.34 s per loop 

In [70]: %timeit ymulti = multiroll(x, shift) 
1 loops, best of 3: 474 ms per loop 

Oto definicja multiroll:

from itertools import product 
import numpy as np 


def multiroll(x, shift, axis=None): 
    """Roll an array along each axis. 

    Parameters 
    ---------- 
    x : array_like 
     Array to be rolled. 
    shift : sequence of int 
     Number of indices by which to shift each axis. 
    axis : sequence of int, optional 
     The axes to be rolled. If not given, all axes is assumed, and 
     len(shift) must equal the number of dimensions of x. 

    Returns 
    ------- 
    y : numpy array, with the same type and size as x 
     The rolled array. 

    Notes 
    ----- 
    The length of x along each axis must be positive. The function 
    does not handle arrays that have axes with length 0. 

    See Also 
    -------- 
    numpy.roll 

    Example 
    ------- 
    Here's a two-dimensional array: 

    >>> x = np.arange(20).reshape(4,5) 
    >>> x 
    array([[ 0, 1, 2, 3, 4], 
      [ 5, 6, 7, 8, 9], 
      [10, 11, 12, 13, 14], 
      [15, 16, 17, 18, 19]]) 

    Roll the first axis one step and the second axis three steps: 

    >>> multiroll(x, [1, 3]) 
    array([[17, 18, 19, 15, 16], 
      [ 2, 3, 4, 0, 1], 
      [ 7, 8, 9, 5, 6], 
      [12, 13, 14, 10, 11]]) 

    That's equivalent to: 

    >>> np.roll(np.roll(x, 1, axis=0), 3, axis=1) 
    array([[17, 18, 19, 15, 16], 
      [ 2, 3, 4, 0, 1], 
      [ 7, 8, 9, 5, 6], 
      [12, 13, 14, 10, 11]]) 

    Not all the axes must be rolled. The following uses 
    the `axis` argument to roll just the second axis: 

    >>> multiroll(x, [2], axis=[1]) 
    array([[ 3, 4, 0, 1, 2], 
      [ 8, 9, 5, 6, 7], 
      [13, 14, 10, 11, 12], 
      [18, 19, 15, 16, 17]]) 

    which is equivalent to: 

    >>> np.roll(x, 2, axis=1) 
    array([[ 3, 4, 0, 1, 2], 
      [ 8, 9, 5, 6, 7], 
      [13, 14, 10, 11, 12], 
      [18, 19, 15, 16, 17]]) 

    """ 
    x = np.asarray(x) 
    if axis is None: 
     if len(shift) != x.ndim: 
      raise ValueError("The array has %d axes, but len(shift) is only " 
          "%d. When 'axis' is not given, a shift must be " 
          "provided for all axes." % (x.ndim, len(shift))) 
     axis = range(x.ndim) 
    else: 
     # axis does not have to contain all the axes. Here we append the 
     # missing axes to axis, and for each missing axis, append 0 to shift. 
     missing_axes = set(range(x.ndim)) - set(axis) 
     num_missing = len(missing_axes) 
     axis = tuple(axis) + tuple(missing_axes) 
     shift = tuple(shift) + (0,)*num_missing 

    # Use mod to convert all shifts to be values between 0 and the length 
    # of the corresponding axis. 
    shift = [s % x.shape[ax] for s, ax in zip(shift, axis)] 

    # Reorder the values in shift to correspond to axes 0, 1, ..., x.ndim-1. 
    shift = np.take(shift, np.argsort(axis)) 

    # Create the output array, and copy the shifted blocks from x to y. 
    y = np.empty_like(x) 
    src_slices = [(slice(n-shft, n), slice(0, n-shft)) 
        for shft, n in zip(shift, x.shape)] 
    dst_slices = [(slice(0, shft), slice(shft, n)) 
        for shft, n in zip(shift, x.shape)] 
    src_blks = product(*src_slices) 
    dst_blks = product(*dst_slices) 
    for src_blk, dst_blk in zip(src_blks, dst_blks): 
     y[dst_blk] = x[src_blk] 

    return y 
Powiązane problemy