Roll Rows of a Matrix Independently

Roll rows of a matrix independently

Sure you can do it using advanced indexing, whether it is the fastest way probably depends on your array size (if your rows are large it may not be):

rows, column_indices = np.ogrid[:A.shape[0], :A.shape[1]]

# Use always a negative shift, so that column_indices are valid.
# (could also use module operation)
r[r < 0] += A.shape[1]
column_indices = column_indices - r[:, np.newaxis]

result = A[rows, column_indices]

Shift rows of a numpy array independently

Inspired by Roll rows of a matrix independently's solution, here's a vectorized one based on np.lib.stride_tricks.as_strided -

from skimage.util.shape import view_as_windows as viewW

def strided_indexing_roll(a, r):
# Concatenate with sliced to cover all rolls
p = np.full((a.shape[0],a.shape[1]-1),np.nan)
a_ext = np.concatenate((p,a,p),axis=1)

# Get sliding windows; use advanced-indexing to select appropriate ones
n = a.shape[1]
return viewW(a_ext,(1,n))[np.arange(len(r)), -r + (n-1),0]

Sample run -

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

In [77]: r
Out[77]: array([ 2, 0, -1])

In [78]: strided_indexing_roll(a, r)
Out[78]:
array([[nan, nan, 4.],
[ 1., 2., 3.],
[ 0., 5., nan]])

Numpy: how to roll 1 row in an array of arrays

You can do this by selecting the row you want to operate on and using numpy.roll.

Let's say we want to roll the first row one place to the right:

import numpy as np

grid = np.array([['A', 'D', 'G'],
['B', 'E', 'H'],
['C', 'F', 'I']])

grid[0] = np.roll(grid[0], 1)
print grid

This yields:

[['G' 'A' 'D']
['B' 'E' 'H']
['C' 'F' 'I']]

Notice that we're modifying the original array.

You should decide whether you want to operate on the array in-place (modifying the original) or if you want to make a copy each time. Repeated calls will have different effects depending on what you decide:

import numpy as np

def independent_roll_inplace(arr, ind, amount):
arr[ind] = np.roll(arr[ind], amount)

def independent_roll_copy(arr, ind, amount):
arr = arr.copy()
arr[ind] = np.roll(arr[ind], amount)
return arr

grid = np.array([['A', 'D', 'G'],
['B', 'E', 'H'],
['C', 'F', 'I']])

As an example of the difference, if we make a copy each time, we start "fresh" with the original grid. Repeated calls have no effect on the original:

print 'Roll the second row one place'
print independent_roll_copy(grid, 1, 1)
print 'Roll the second row two places'
print independent_roll_copy(grid, 1, 2)
print 'Roll the second row three places'
print independent_roll_copy(grid, 1, 3)

This yields:

Roll the second row one place
[['A' 'D' 'G']
['H' 'B' 'E']
['C' 'F' 'I']]
Roll the second row two places
[['A' 'D' 'G']
['E' 'H' 'B']
['C' 'F' 'I']]
Roll the second row three places
[['A' 'D' 'G']
['B' 'E' 'H']
['C' 'F' 'I']]

However, if we're modifying the original each time, we'd get the same result by rolling one place multiple times:

for _ in range(3):
print 'Roll the first row one place, modifying the original'
independent_roll_inplace(grid, 0, 1)
print grid

Yielding:

Roll the second row one place, modifying the original
[['A' 'D' 'G']
['H' 'B' 'E']
['C' 'F' 'I']]
Roll the second row one place, modifying the original
[['A' 'D' 'G']
['E' 'H' 'B']
['C' 'F' 'I']]
Roll the second row one place, modifying the original
[['A' 'D' 'G']
['B' 'E' 'H']
['C' 'F' 'I']]

numpy.roll horizontally on a 2D ndarray with different values

By specifying a tuple in np.roll you can roll an array along various axes. For example, np.roll(a, (3,2), axis=(0,1)) will shift each element of a by 3 places along axis 0, and it will also shift each element by 2 places along axis 1. np.roll does not have an option to roll each row by a different amount. You can do it though for example as follows:

import numpy as np

a = np.array([
[6, 3, 9, 2, 3],
[1, 7, 8, 1, 2],
[5, 4, 2, 2, 4],
[3, 9, 7, 6, 5],
])
shifts = np.c_[[1,2,1,3]]
a[np.c_[:a.shape[0]], (np.r_[:a.shape[1]] - shifts) % a.shape[1]]

It gives:

array([[3, 6, 3, 9, 2],
[1, 2, 1, 7, 8],
[4, 5, 4, 2, 2],
[7, 6, 5, 3, 9]])

python: shift each matrix inside a tensor independantly

Approach #1

Adapting strided-based solution from the same linked Q&A for performance -

from skimage.util.shape import view_as_windows

def roll_along_second_axis_3dar(a, r):
r = np.asarray(r)
a_ext = np.concatenate((a,a[:,:-1,:]),axis=1)
n = a.shape[1]
idx = (n-r)%n
w = view_as_windows(a_ext,(1,n,1))[...,0,:,0]
return w[np.arange(len(idx)),idx].swapaxes(1,2)

Sample run -

In [11]: a
Out[11]:
array([[[44, 47, 64],
[67, 67, 9],
[83, 21, 36],
[87, 70, 88]],

[[88, 12, 58],
[65, 39, 87],
[46, 88, 81],
[37, 25, 77]]])

In [12]: roll_along_second_axis_3dar(a, r=[-1,1])
Out[12]:
array([[[67, 67, 9],
[83, 21, 36],
[87, 70, 88],
[44, 47, 64]],

[[37, 25, 77],
[88, 12, 58],
[65, 39, 87],
[46, 88, 81]]])

Approach #2

Going with your try, seems you were close enough. We could get to the final output with few modifications/corrections -

d1, d2, d3 = np.ogrid[:AB.shape[0], :AB.shape[1], :AB.shape[2]]
r[r < 0] += AB.shape[1]
D2 = ((d2 - r).transpose(2,1,0))%AB.shape[1]
out = AB[d1,D2,d3]

Numpy - multiple numpy.roll of 1D input array

Approach #1 : For elegance

Here's one way with broadcasting -

In [44]: a
Out[44]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [45]: rolls
Out[45]: array([-1, 0, 1, 2])

In [46]: a[(np.arange(len(a))[:,None]-rolls) % len(a)]
Out[46]:
array([[1, 0, 9, 8],
[2, 1, 0, 9],
[3, 2, 1, 0],
[4, 3, 2, 1],
[5, 4, 3, 2],
[6, 5, 4, 3],
[7, 6, 5, 4],
[8, 7, 6, 5],
[9, 8, 7, 6],
[0, 9, 8, 7]])

Approach #2 : For memory/perf-efficiency

Idea mostly borrowed from - this post.

We can leverage np.lib.stride_tricks.as_strided based scikit-image's view_as_windows to get sliding windows. More info on use of as_strided based view_as_windows.

from skimage.util.shape import view_as_windows

def multiroll_stridedview(a, r):
r = np.asarray(r)

# Concatenate with sliced to cover all rolls
a_ext = np.concatenate((a,a[:-1]))

# Get sliding windows; use advanced-indexing to select appropriate ones
n = len(a)
return view_as_windows(a_ext,n)[:,(n-r)%n]


Related Topics



Leave a reply



Submit