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.