Reputation: 171
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
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
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