I want to construct a matrix of shape (N,2N) in Python.
I can construct the matrix as follows
import numpy as np
N = 10 # 10,100,1000, whatever
some_vector = np.random.uniform(size=N)
some_matrix = np.zeros((N, 2*N))
for i in range(N):
some_matrix[i, 2*i] = 1
some_matrix[i, 2*i + 1] = some_vector[i]
So the result is a matrix of mostly zeros, but where on row i, the 2*i and 2*i + 1 columns are populated.
Is there a faster way to construct the matrix without the loop? Feels like there should be some broadcasting operation...
Edit: There have been some very fast, great answers! I am going to extend the question a touch to my actual use case.
Now suppose some_vector has a shape (N,T). I want to construct a matrix of shape (N,2*N,T) analogously to the previous case. The naive approach is:
N = 10 # 10,100,1000, whatever
T = 500 # or whatever
some_vector = np.random.uniform(size=(N,T))
some_matrix = np.zeros((N, 2*N,T))
for i in range(N):
for t in range(T):
some_matrix[i, 2*i,t] = 1
some_matrix[i, 2*i + 1,t] = some_vector[i,t]
Can we extend the previous answers to this new case?

Variant 1
You can replace the loop with a broadcasting assignment, which interleaves the columns of an identity matrix with the columns of a diagonal matrix:
This is concise, but not optimal. This requires allocating some unnecessary intermediate matrices (
aandb), which becomes increasing expensive asNgrows larges. This also involves performing random-access assignments to every position inc, instead of just the non-zero entries.This may be faster or slower than the implementation in the question for different values of
N.Variant 2
Similar idea Variant 1, but only performs a single allocation and avoids unnecessary assignments to zero entries.
We create a single vector of size
2*N**2, which essentially represents the rows ofsome_matrixconcatenated with one another. The non-zero positions are populated using broadcasting assignments. We then create an(N, 2*N)view into this vector usingnp.ndarray.reshape.Performance Comparison
For relatively small
N(N=100):For large
N(N=10_000):Setup:
Timing at
N=100:Timing at
N=1_000Timing at
N=10_000Tested using Python 3.10.12 and Numpy v1.26.0.