Circular Shift of Vector (Equivalent to Numpy.Roll)

Circular shift of vector (equivalent to numpy.roll)

How about using head and tail...

roll <- function( x , n ){
if( n == 0 )
return( x )
c( tail(x,n) , head(x,-n) )
}

roll(1:5,2)
#[1] 4 5 1 2 3

# For the situation where you supply 0 [ this would be kinda silly! :) ]
roll(1:5,0)
#[1] 1 2 3 4 5

One cool thing about using head and tail... you get a reverse roll with negative n, e.g.

roll(1:5,-2)
[1] 3 4 5 1 2

how to do circular shift in numpy

Why not just roll with a negative number?

>>> import numpy as np
>>> a = np.arange(10)
>>> np.roll(a,2)
array([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])
>>> np.roll(a,-2)
array([2, 3, 4, 5, 6, 7, 8, 9, 0, 1])

Circular shift of vector by distance n

You can make use of head and tail to create a function like this:

shifter <- function(x, n = 1) {
if (n == 0) x else c(tail(x, -n), head(x, n))
}

Usage:

a <- 1:4

shifter(a)
# [1] 2 3 4 1

shifter(a, 2)
# [1] 3 4 1 2

(Or, library(SOfun); shifter(a) where you can get SOfun from here).

Numpy roll in several dimensions

In theory, using scipy.ndimage.interpolation.shift as described by @Ed Smith should work, but because of a bug (https://github.com/scipy/scipy/issues/1323), it didn't give a result that is equivalent to multiple calls of np.roll.


UPDATE: "Multi-roll" capability was added to numpy.roll in numpy version 1.12.0. Here's a two-dimensional example, in which the first axis is rolled one position and the second axis is rolled three positions:

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

This makes the code below obsolete. I'll leave it there for posterity.


The code below defines a function I call multiroll that does what you want. Here's an example in which it is applied to an array with shape (500, 500, 500):

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

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

Use multiple calls to np.roll to generate the expected result:

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

Generate the shifted array using multiroll:

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

Verify that we got the expected result:

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

For an array this size, making three calls to np.roll is almost three times slower than a call to 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

Here's the definition of 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

Shift elements in a numpy array

For those who want to just copy and paste the fastest implementation of shift, there is a benchmark and conclusion(see the end). In addition, I introduce fill_value parameter and fix some bugs.

Benchmark

import numpy as np
import timeit

# enhanced from IronManMark20 version
def shift1(arr, num, fill_value=np.nan):
arr = np.roll(arr,num)
if num < 0:
arr[num:] = fill_value
elif num > 0:
arr[:num] = fill_value
return arr

# use np.roll and np.put by IronManMark20
def shift2(arr,num):
arr=np.roll(arr,num)
if num<0:
np.put(arr,range(len(arr)+num,len(arr)),np.nan)
elif num > 0:
np.put(arr,range(num),np.nan)
return arr

# use np.pad and slice by me.
def shift3(arr, num, fill_value=np.nan):
l = len(arr)
if num < 0:
arr = np.pad(arr, (0, abs(num)), mode='constant', constant_values=(fill_value,))[:-num]
elif num > 0:
arr = np.pad(arr, (num, 0), mode='constant', constant_values=(fill_value,))[:-num]

return arr

# use np.concatenate and np.full by chrisaycock
def shift4(arr, num, fill_value=np.nan):
if num >= 0:
return np.concatenate((np.full(num, fill_value), arr[:-num]))
else:
return np.concatenate((arr[-num:], np.full(-num, fill_value)))

# preallocate empty array and assign slice by chrisaycock
def shift5(arr, num, fill_value=np.nan):
result = np.empty_like(arr)
if num > 0:
result[:num] = fill_value
result[num:] = arr[:-num]
elif num < 0:
result[num:] = fill_value
result[:num] = arr[-num:]
else:
result[:] = arr
return result

arr = np.arange(2000).astype(float)

def benchmark_shift1():
shift1(arr, 3)

def benchmark_shift2():
shift2(arr, 3)

def benchmark_shift3():
shift3(arr, 3)

def benchmark_shift4():
shift4(arr, 3)

def benchmark_shift5():
shift5(arr, 3)

benchmark_set = ['benchmark_shift1', 'benchmark_shift2', 'benchmark_shift3', 'benchmark_shift4', 'benchmark_shift5']

for x in benchmark_set:
number = 10000
t = timeit.timeit('%s()' % x, 'from __main__ import %s' % x, number=number)
print '%s time: %f' % (x, t)

benchmark result:

benchmark_shift1 time: 0.265238
benchmark_shift2 time: 0.285175
benchmark_shift3 time: 0.473890
benchmark_shift4 time: 0.099049
benchmark_shift5 time: 0.052836

Conclusion

shift5 is winner! It's OP's third solution.

Numpy: equivalent of numpy.roll but only for data visualisation

This is not possible, sorry. The rolled array cannot be described by a different set of strides, which would be necessary for a NumPy view to work.

Roll array, uniform circular shift

No need to implement it yourself, there is a built-in function for this

julia> circshift(arr, 2)  
5-element Array{Int64,1}:
4
5
1
2
3

It's also (slightly) more efficient than roll2 proposed above:

julia> @btime circshift($arr, 2);
68.563 ns (1 allocation: 128 bytes)

julia> @btime roll2($arr, 2);
70.605 ns (4 allocations: 256 bytes)

Note, however, that none of the proposed functions operates in-place. They all create a new array. There is also the built-in circshift!(dest, src, shift) which operates in a preallocated dest (which, however, must be != src).

Shift a sequence by certain interval in R

One way could be:

n <- 3
c(x[-c(1:n)], x[1:n])

[1] 4 5 6 7 1 2 3

In a form of a function:

shift_seq <- function(x, n) {
c(x[-c(1:n)], x[1:n])
}

python numpy roll with padding

I don't think that you are going to find an easier way to do this that is built-in. The touch-up seems quite simple to me:

y = np.roll(x,1,axis=1)
y[:,0] = 0

If you want this to be more direct then maybe you could copy the roll function to a new function and change it to do what you want. The roll() function is in the site-packages\core\numeric.py file.



Related Topics



Leave a reply



Submit