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
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
Ś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 ... –
Przesunięcie' scipy' wykorzystuje interpolację splajnu. Więc oblicza nowe wartości, a nie tylko przesuwa istniejące. – hpaulj