Home > OS >  Insert array into all places in another array
Insert array into all places in another array

Time:06-03

For two arrays, say, a = np.array([1,2,3,4]) and b = np.array([5,6]), is there a way, if any, to obtain a 2d array of the following form without looping:

[[5 6 1 2 3 4]
 [1 5 6 2 3 4]
 [1 2 5 6 3 4]
 [1 2 3 5 6 4]
 [1 2 3 4 5 6]]

i.e. to insert b in all possible places of a.

And if loops are unavoidable, how to do it the most computationally efficient way (a can be long, the length of b is irrelevant)?

Example of how it can be done using loops is trivial:

a = np.array([1,2,3,4])
b = np.array([5,6])
rows = len(a)   1
cols = len(a)   len(b)
res = np.empty([rows, cols])
for i in range(rows):
    res[i, i:len(b) i] = b
    res[i, len(b) i:] = a[i:]
    res[i, 0:i] = a[0:i]
print(rows.astype(int))

[[5 6 1 2 3 4]
 [1 5 6 2 3 4]
 [1 2 5 6 3 4]
 [1 2 3 5 6 4]
 [1 2 3 4 5 6]]

CodePudding user response:

Consider using numba acceleration. This happens to be what numba is best at. For your example, it can speed up nearly 6 times:

from timeit import timeit

import numpy as np
from numba import njit


a = np.arange(1, 5)
b = np.array([5, 6])


def fill(a, b):
    rows = a.size   1
    cols = a.size   b.size
    res = np.empty((rows, cols))
    for i in range(rows):
        res[i, i:b.size   i] = b
        res[i, b.size   i:] = a[i:]
        res[i, :i] = a[:i]
    return res


if __name__ == '__main__':
    print('before:', timeit(lambda: fill(a, b)))
    fill = njit(fill)
    print('after:', timeit(lambda: fill(a, b)))

Output:

before: 9.488150399993174
after: 1.6149254000047222

CodePudding user response:

a = [1,2,3,4]
b = [5,6]
c = [[*a[0:i],*b,*a[i:]] for i in range(len(a) 1)]

*operator is the fastest way to concat two lists in python

CodePudding user response:

I think you can use masking to add diagonal values b to the res array and add the rest of the cells in the array with values from a

import numpy as np

a = np.array([1,2,3,4])
b = np.array([5,6])

rows = len(a)   1
cols = len(a)   len(b)
res = np.empty([rows, cols])

# Frame for masking
frame = np.arange(rows * cols).reshape(rows, cols)

diag_mask = (frame % (res.shape[1]   1)) < (b.shape[0])
res[diag_mask] = np.tile(b, res.shape[0])
res[~diag_mask] = np.tile(a, res.shape[0])

CodePudding user response:

You can create a zero array based on the expected shape and then fill the desired indices by the b values and finally fill remained zero values by tile of the a array with the needed shape as:

shape, size = a.shape[0]   1, a.shape[0]   b.shape[0]
ind = np.lib.stride_tricks.sliding_window_view(np.arange(size), b.shape[0])
# [[0 1]
#  [1 2]
#  [2 3]
#  [3 4]
#  [4 5]]

zero_arr = np.zeros((shape, size), dtype=np.float64) 
zero_arr[np.arange(shape)[:, None], ind] = b
# [[5 6 0 0 0 0]
#  [0 5 6 0 0 0]
#  [0 0 5 6 0 0]
#  [0 0 0 5 6 0]
#  [0 0 0 0 5 6]]

zero_arr[zero_arr == 0] = np.tile(a, shape)
# [[5 6 1 2 3 4]
#  [1 5 6 2 3 4]
#  [1 2 5 6 3 4]
#  [1 2 3 5 6 4]
#  [1 2 3 4 5 6]]

This method will beat tax evader method in terms of performance, on larger arrays.

  • Related