You can use numpy.tile , numpy.repeat, and numpy.eye.
rep = 3
lst = np.array([1,2,3,4])
res = np.tile(np.eye(rep), (len(lst),1))*np.repeat(lst, rep)[:,None]
print(res)
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]
[2. 0. 0.]
[0. 2. 0.]
[0. 0. 2.]
[3. 0. 0.]
[0. 3. 0.]
[0. 0. 3.]
[4. 0. 0.]
[0. 4. 0.]
[0. 0. 4.]]
Explanation:
>>> np.tile(np.eye(3), (2,1))
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
>>> np.repeat([3,4], 3)[:,None]
array([[3],
[3],
[3],
[4],
[4],
[4]])
>>> np.tile(np.eye(3), (2,1)) * np.repeat([3,4], 3)[:,None]
array([[3., 0., 0.],
[0., 3., 0.],
[0., 0., 3.],
[4., 0., 0.],
[0., 4., 0.],
[0., 0., 4.]])
Benchmark on colab(Because you want an efficient approach)
Variable is len(arr) and eye(3)

Code of benchmark:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
import time
bench = []
for num in np.power(np.arange(10,1500,5),2):
arr = np.arange(num)
start = time.time()
col = 3
size = arr.size
ret1 = np.zeros((col * size, col), arr.dtype)
for i in range(col):
ret1[i::col, i] = arr
bench.append({'len_arr':num, 'Method':'Mechanic_Pig', 'Time':time.time() - start})
start = time.time()
N = 3
M = N*len(arr)
ret2 = np.zeros((M, N), dtype=int)
idx = np.arange(M)
ret2[idx, idx%N] = np.repeat(arr, N)
bench.append({'len_arr':num, 'Method':'mozway', 'Time':time.time() - start})
start = time.time()
ret3 = np.tile(np.eye(3), (len(arr),1))*np.repeat(arr, 3)[:,None]
bench.append({'len_arr':num, 'Method':'Imahdi', 'Time':time.time() - start})
start = time.time()
ret4 = np.einsum('j,ik->jki', arr, np.eye(3)).reshape(-1, 3)
bench.append({'len_arr':num, 'Method':'Michael_Szczesn', 'Time':time.time() - start})
plt.subplots(1,1, figsize=(10,7))
df = pd.DataFrame(bench)
sns.lineplot(data=df, x="len_arr", y="Time", hue="Method", style="Method")
plt.show()
# Check result of different approaches are equal or not
print(((ret1 == ret2).all() == (ret1 == ret3).all() == (ret1 == ret4).all() == (ret2 == ret3).all() == (ret2 == ret4).all() == (ret3 == ret4).all()))
# True