2

Suppose I have:

arr1 = np.array([[1,2,3,4,5], [6,7,8,9,10], [11,12,13,14,15], [16,17,18,19,20], [21,22,23,24,25]])

And the empty matrix:

matrix = np.zeros((10, 10))
matrix[:] = np.NaN

I want to populate matrix with each element within arr1, but diagonally. This is the expected output:

array([[ nan, nan, nan,  nan,  nan, nan, nan, nan, nan, nan],
       [ 1,   nan, nan,  nan,  nan, nan, nan, nan, nan, nan],
       [ 6,   2,   nan,  nan,  nan, nan, nan, nan, nan, nan],
       [ 11,  7,   3,    nan,  nan, nan, nan, nan, nan, nan],
       [ 16,  12,  8,    4,    nan, nan, nan, nan, nan, nan],
       [ 21,  17,  13,   9,    5,   nan, nan, nan, nan, nan],
       [ nan, 22,  18,   14,   10,  nan, nan, nan, nan, nan],
       [ nan, nan, 23,   19,   15,  nan, nan, nan, nan, nan],
       [ nan, nan, nan,  24,   20,  nan, nan, nan, nan, nan],
       [ nan, nan,  nan, nan,  25,  nan, nan, nan, nan, nan]])

This is what I have tried so far without succeeding:

arr1 = np.array([[1,2,3,4,5], [6,7,8,9,10], [11,12,13,14,15], [16,17,18,19,20], [21,22,23,24,25]])
matrix = np.zeros((10, 10))
matrix[:] = np.NaN

for i, array in enumerate(arr1):                                 
    for row_matrix in matrix:
        row_matrix = np.diag(array, -i-1)
        break

This is the output I have from the above code:

array([[ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 21, 0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0, 22,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0, 23,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0, 24,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0, 25,  0,  0,  0,  0,  0]])
XioHu
  • 123
  • 4

2 Answers2

1

Try:

for i, col in enumerate(arr1.T, 1):
    matrix[i : i + len(col), i - 1] = col

print(matrix)

Prints:

[[nan nan nan nan nan nan nan nan nan nan]
 [ 1. nan nan nan nan nan nan nan nan nan]
 [ 6.  2. nan nan nan nan nan nan nan nan]
 [11.  7.  3. nan nan nan nan nan nan nan]
 [16. 12.  8.  4. nan nan nan nan nan nan]
 [21. 17. 13.  9.  5. nan nan nan nan nan]
 [nan 22. 18. 14. 10. nan nan nan nan nan]
 [nan nan 23. 19. 15. nan nan nan nan nan]
 [nan nan nan 24. 20. nan nan nan nan nan]
 [nan nan nan nan 25. nan nan nan nan nan]]
Andrej Kesely
  • 168,389
  • 15
  • 48
  • 91
  • 1
    I must confess that I was expecting this one to be slower. Because there is still one for loop (better than 2, sure). And my timings show how when n grows, OP's method explode in timing (only 2 times slower for 5x5, but 150 times slower for 5000x5000), but this method doesn't. But in fact, it is quite normal. Because it iterates only rows. And after a while, the time spent in iterating rows grows only as O(n), while time spend inside numpy's operation grows as O(n²). So, the more n grow, the less it matters. – chrslg Nov 11 '22 at 00:34
1

Without any iteration

n=len(arr1)
arr2=np.full((2*n, 2*n+1), np.nan)
arr2[:n,1:n+1]=arr1.T
arr2.resize((2*n,2*n))
matrix=arr2.T

Just taking advantage of the fact that there a n values, and n+1 nan in between, when you read "left to right, row by row" the transposed wanted result. So, well, we do that simply in a 10x11 matrix, in which, when copied, values of arr1 have the exact same property (5 values, then 6 nan, per rows). So, after resize to 10x10, those 5 values then 6 nans imply a shift).

So, everything is about copying the data in a 1 column too big matrix, then resize it. Plus some transpose play.

Timings

For your 5x5 example

Method Timing
Yours 35.3 μs
Andrej's 15.7 μs
This one 9.1 μs

On 5000x5000 example

Method Timing
Yours 70.24 sec
Andrej's 0.54 sec
This one 0.31 sec
chrslg
  • 9,023
  • 5
  • 17
  • 31