roshoka
roshoka

Reputation: 171

Explanation of fill_diagonal syntax

I'm having trouble understanding how np.fill_diagonal is implemented here.

I found a post here explaining a way to fill the sub and super diagonals with certain values but I don't really understand the arguments of the function. Here is the code:

a = np.zeros((4, 4))
b = np.ones(3)
np.fill_diagonal(a[1:], b)
np.fill_diagonal(a[:,1:], -b)

I don't understand how fill_diagonal is used here. I thought that the second argument had to be a scalar. Also, I don't understand what is happening with the slices of 'a'.

Upvotes: 0

Views: 542

Answers (2)

hpaulj
hpaulj

Reputation: 231475

Your examples involve filling slices, views, of the original array.

In [79]: a = np.zeros((4, 4)) 
    ...: b = np.arange(1,5)                                                                                  
In [80]:        

The simple case - filling the whole array:

In [80]: np.fill_diagonal(a,b)                                                                               
In [81]: a                                                                                                   
Out[81]: 
array([[1., 0., 0., 0.],
       [0., 2., 0., 0.],
       [0., 0., 3., 0.],
       [0., 0., 0., 4.]])

fill_diagonal takes an array to be filled, and values to put in the diagonal. The docs does say scalar, but that's overly restrictive. As I show, it can be a 1d array of the right size.

In [82]: a = np.zeros((4, 4)) 
    ...: b = np.arange(1,4)                                                                                  

filling the last 3 rows:

In [83]: a[1:]                                                                                               
Out[83]: 
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
In [84]: np.fill_diagonal(a[1:],b)                                                                           
In [85]: a                                                                                                   
Out[85]: 
array([[0., 0., 0., 0.],
       [1., 0., 0., 0.],
       [0., 2., 0., 0.],
       [0., 0., 3., 0.]])

In [86]: a = np.zeros((4, 4)) 
    ...: b = np.arange(1,4)

filling the last 3 columns:

In [87]: a[:,1:]                                                                                             
Out[87]: 
array([[0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])
In [88]: np.fill_diagonal(a[:,1:],b)                                                                         
In [89]: a                                                                                                   
Out[89]: 
array([[0., 1., 0., 0.],
       [0., 0., 2., 0.],
       [0., 0., 0., 3.],
       [0., 0., 0., 0.]])

The key is that fill_diagonal works in-place, and the a[1:] and a[:,1:] produce views of a.

Look at the slice of a after filling:

In [90]: a[:,1:]                                                                                             
Out[90]: 
array([[1., 0., 0.],
       [0., 2., 0.],
       [0., 0., 3.],
       [0., 0., 0.]])

The docs demonstrate the use with np.fliplr(a). That too, creates a view which can be modified in place.

The actual write is done with:

a.flat[:end:step] = val

where end and step have been calculated from the dimensions. For example to fill a 3x3 array, we can write to every 4th element.

In [96]: a[:,1:].ravel()                                                                                     
Out[96]: array([1., 0., 0., 0., 2., 0., 0., 0., 3., 0., 0., 0.])

Upvotes: 0

fendall
fendall

Reputation: 524

"For an array a with a.ndim >= 2, the diagonal is the list of locations with indices a[i, ..., i] all identical. This function modifies the input array in-place, it does not return a value." (Source) The documentation for this method says b should be a scalar, however if b is an array of length equal to the length of the diagonal of the input array, then it will fill the values of b in for the diagonal.

The key is that the number of elements in b is equal to the number of elements along the diagonals of each sub-array of a. The nth diagonal value of the sub-array is filled in with the nth value of b.

The first sub-array of a that is modified is all but the first row of a (this means 3 rows, 4 columns), so the number of diagonal elements is 3.

The second sub-array of a is the last three columns (4 x 3 matrix) of a which also has only 3 diagonal elements.

==========================================================================
Thanks G. Anderson for the comment. I'm editing this into the post to draw attention to it:

"It's worth noting that b doesn't have to have the same length as the diagonal it's filling. if b is longer, then n elements of the diagonal will be filled with the first n elements of b. If n is shorter than the diagonal, then b will be repeated to fill the diagonal"

Upvotes: 1

Related Questions