rougier/numpy-100

An alternative solution for Q.76

iamyifan opened this issue · 7 comments

  1. Consider a one-dimensional array Z, build a two-dimensional array whose first row is (Z[0],Z[1],Z[2]) and each subsequent row is shifted by 1 (last row should be (Z[-3],Z[-2],Z[-1]) (★★★)
    hint: from numpy.lib import stride_tricks

# Author: Joe Kington / Erik Rigtorp
from numpy.lib import stride_tricks

def rolling(a, window):
shape = (a.size - window + 1, window)
strides = (a.strides[0], a.strides[0])
return stride_tricks.as_strided(a, shape=shape, strides=strides)
Z = rolling(np.arange(10), 3)
print(Z)

Same as the last issue, sliding_window_view is an easier function in NumPy. The new solution will be:

Z = np.arange(10)
print(sliding_window_view(Z, window_shape=(3)))
  1. Consider an array Z = [1,2,3,4,5,6,7,8,9,10,11,12,13,14], how to generate an array R = [[1,2,3,4], [2,3,4,5], [3,4,5,6], ..., [11,12,13,14]]? (★★★)
    hint: stride_tricks.as_strided

# Author: Stefan van der Walt

Z = np.arange(1,15,dtype=np.uint32)
R = stride_tricks.as_strided(Z,(11,4),(4,4))
print(R)

Q.81 can also be optimized by sliding_window_view.

Z = np.arange(1,15,dtype=np.uint32)
print(sliding_window_view(Z, 4))
  1. Extract all the contiguous 3x3 blocks from a random 10x10 matrix (★★★)
    hint: stride_tricks.as_strided

# Author: Chris Barker

Z = np.random.randint(0,5,(10,10))
n = 3
i = 1 + (Z.shape[0]-3)
j = 1 + (Z.shape[1]-3)
C = stride_tricks.as_strided(Z, shape=(i, j, n, n), strides=Z.strides + Z.strides)
print(C)

An alternative solution will be:

Z = np.random.randint(0,5,(10,10))
print(sliding_window_view(Z, (3, 3)))
  1. Consider a 16x16 array, how to get the block-sum (block size is 4x4)? (★★★)
    hint: np.add.reduceat

# Author: Robert Kern

Z = np.ones((16,16))
k = 4
S = np.add.reduceat(np.add.reduceat(Z, np.arange(0, Z.shape[0], k), axis=0),
np.arange(0, Z.shape[1], k), axis=1)
print(S)

# alternative solution:
# Author: Sebastian Wallkötter (@FirefoxMetzger)

Z = np.ones((16,16))
k = 4

windows = np.lib.stride_tricks.sliding_window_view(Z, (k, k))
S = windows[::k, ::k, ...].sum(axis=(-2, -1))

The alternative solution will be:

Z = np.ones((16, 16))
k = 4
print(sliding_window_view(Z, window_shape=(k, k))[::k, ::k].sum(-1).sum(-1))

@Jeff1999 It is of course a matter of taste, but .sum([-2, -1]) might be easier to read than .sum(-1).sum(-1).

Also, since you are omitting kwargs everywhere else, maybe sliding_window_view(Z, (k, k)) may be more consistent and shorter.

Can you post output of the original and new implementation to make sure we get the same output?

Perfect! If you can make (individual) PR, that would be fantastic.