Explanation of fill_diagonal syntax - python

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'.

"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"

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.])

Related

Creating many state vectors and saving them in a file

I want to create m number of matrices, each of which is an n x 1 numpy arrays. Moreover those matrices should have only two nonzero entries in the two rows, all other rows should have 0 as their entries, meaning that matrix number m=1 should have entries m[0,:]=m[1,:]=1, rest elements are 0. And similarly the last matrix m=m should have entries like m[n-1,:]=m[n,:]=1, where rest of the elements in other rows are 0. So for consecutive two matrices, the nonzero elements shift by two rows. And finally, I would like them to be stored into a dictionary or in a file.
What would be a neat way to do this?
Is this what you're looking for?
In [2]: num_rows = 10 # should be divisible by 2
In [3]: np.repeat(np.eye(num_rows // 2), 2, axis=0)
Out[3]:
array([[1., 0., 0., 0., 0.],
[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.],
[0., 0., 0., 0., 1.]])
In terms of storage in a file, you can use np.save and np.load.
Note that the default data type for np.eye will be float64. If you expect your values to be small when you begin integrating or whatever you're planning on doing with your state vectors, I'd recommend setting the data type appropriately (like np.uint8 for positive integers < 256 for example).

shape 3D numpy array python

I want to create 3D array with a dimension which depends on the length of a 2D list
Method 1 :
the length of list_1 is 2 and each element has the same length
list_1 = [[1,2,3,4,5], [1,2,3,4,5]]
array_1 = np.zeros((2,len(list_1), 114))
the shape of array_1 is (2,2,114)
method 2 :
the length of list_2 is also 2 but elements do not have the same length
list_2 = [[1,2,3,4,5],[1,2,3,4]]
array_2 = [np.zeros((len(list_2[i]),114)) for i in range(len(list_2))]
array_2 = np.array(list_2, dtype=object)
In this case the shape of array_2 is (2,)
Does someone know the reason ? I do not understand why I do not get the same shape.
Is there a way to get the same shape ?
You made a list of 2 arrays, that differ in the number of rows:
In [130]: [np.zeros((len(list_2[i]), 3)) for i in range(len(list_2))]
Out[130]:
[array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]),
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])]
np.array cannot combine those into one 3d array, so it makes a shape 2 array, containing those same 2 arrays:
In [131]: np.array(_, object)
Out[131]:
array([array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]),
array([[0., 0., 0.],
[0., 0., 0.]
[0., 0., 0.],
[0., 0., 0.]])], dtype=object)
If you don't include the object dtype it warns about making a "ragged array".
Using len(list_1) for the first case, and len(list_2[i]) in the second, are two very different situations. In one you are using length of the list itself, and the the length of the sublists.
If you truncated the sublists to the same length:
In [137]: np.array([np.zeros((len(list_2[i][:4]),114)) for i in range(len(list_2))],object).shape
Out[137]: (2, 4, 114)
The subarrays now have the same shape (4,114), and can be combined into one 3d array.

Move an element in the array row-wise and shifting the others?

I am working with numpy arrays and and I would like to move a certain value located at certain index by 2 positions on the right while shifting the others element involved on the left by one position each. To be more precise, here below is what I would like to achieve:
from:
start = [0., 1., 2.],
[0., 0., 0.],
[5., 0., 1.]
to:
end = [1., 2., 0.],
[0., 0., 0.],
[5., 0., 1.]
As you can see from the first row, 0 has been moved by two position on the right and 1 and 2 by one position on the left. So far, I succeded by moving an element by one position and moving the other by defining:
def right_move(arr, index, n_steps:int):
row = index[0]
col = index[1]
try:
arr[row,col], arr[row, col + n_steps] = arr[row, col + n_steps], arr[row, col]
except:
pass
return arr
where n_steps=1. If I input n_steps=2, it won't work since the element in the middle of the swapping remain unchanged.
Can someone help me? Other easier solution are more then welcome!
Thanks
You can use numpy.roll:
import numpy as np
arr = np.array([[0., 1., 2.],
[0., 0., 0.],
[5., 0., 1.]])
arr[0] = np.roll(arr[0], 2)
arr
# output:
# [[1., 2., 0.],
# [0., 0., 0.],
# [5., 0., 1.]]
It is supposed the functionality of np.roll. However, np.roll doesn't allow roll on individual row. It piqued my interest to implement a function using as_strided to extend this functionality to np.roll
from numpy.lib.stride_tricks import as_strided
def custom_roll(arr, r_tup):
m = np.asarray(r_tup)
arr_roll = arr[:, [*range(arr.shape[1]),*range(arr.shape[1]-1)]].copy() #need `copy`
strd_0, strd_1 = arr_roll.strides
n = arr.shape[1]
result = as_strided(arr_roll, (*arr.shape, n), (strd_0 ,strd_1, strd_1))
return result[np.arange(arr.shape[0]), (n-m)%n]
Just passing a tuple indicate number of steps for each row. In your case, it rolls only row 0 either 2 positions forward or -1 position backward. so the tuple is (2,0,0) or (-1,0,0)
start = np.array([[0., 1., 2.],
[0., 0., 0.],
[5., 0., 1.])
out = custom_roll(start, (2,0,0))
Out[780]:
array([[1., 2., 0.],
[0., 0., 0.],
[5., 0., 1.]])
Or
out = custom_roll(start, (-1,0,0))
Out[782]:
array([[1., 2., 0.],
[0., 0., 0.],
[5., 0., 1.]])
Roll first and last row: (2,0,2)
out = custom_roll(start, (2,0,2))
Out[784]:
array([[1., 2., 0.],
[0., 0., 0.],
[0., 1., 5.]])

how can I insert an element to Numpy matrix by row and column indices

I want to create a matrix with Numpy, but I need to add every element by its row and column indices.
for example:
my_matrix = np.matrix(np.zeros((5, 5)))
my_matrix.insert(row_index=2, column_index=1, value=10)
output:
matrix([[0., 0., 0., 0., 0.],
[10., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
How can I do that?
Do you want to add or insert values?
The add function that you mentioned is used as an element-wise addition.
Example:
np.add([1, 2], [2, 3])
Out[41]: array([3, 5])
If you really want to create a matrix a matrix by inserting values with column and row indices, create the matrix first and insert your values afterwards.
number_rows = 10
number_cols = 20
arr = np.empty((number_rows, number_cols))
arr[2, 1] = 10
The use of np.matrix is discouraged, if not actually deprecated. It is rarely needed, except for some backward compatibility cases.
In [1]: arr = np.zeros((5,5))
In [2]: arr
Out[2]:
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.]])
In [3]: mat = np.matrix(arr)
In [4]: mat
Out[4]:
matrix([[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.]])
Indexing one row of arr produces a 1d array
In [5]: arr[2]
Out[5]: array([0., 0., 0., 0., 0.])
Indexing one row of mat produces a 2d matrix, with shape (1,5)
In [6]: mat[2]
Out[6]: matrix([[0., 0., 0., 0., 0.]])
We can access an element in the 1d array:
In [7]: arr[2][1]
Out[7]: 0.0
but this indexing of the mat tries to access a row, and gives an error:
In [8]: mat[2][1]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-8-212ad5378f8e> in <module>
----> 1 mat[2][1]
...
IndexError: index 1 is out of bounds for axis 0 with size 1
In both cases it is better to access an element with the tuple syntax, rather than the chained one:
In [9]: arr[2,1]
Out[9]: 0.0
In [10]: mat[2,1]
Out[10]: 0.0
This indexing also works for setting values. But try to avoid iterating to set individual values. Try to find ways of creating the whole array with the desired values directly, with whole array methods, not iteration.

NumPy error saying that matrices are not aligned

If I run:
x = np.zeros(6)
y = np.zeros([7, 6])
z = y * x
Then everything is fine, and there are no Python errors.
However, I am using a Python module (call if foo) containing a function (call if bar), which returns a 7x6 NumPy array. It has the same shape as y above, and the same data type (float64). But when I run the following:
x = np.zeros(6)
y = foo.bar()
z = y * x
I get the following error:
ValueError: shapes (7,6) and (1,6) not aligned: 6 (dim 1) != 1 (dim 0)
But as far as I can tell, y is exactly the same format in these two examples, with the same shape and data type. What's causing this error, and why is it not caused in the first example?
In [446]: x = np.zeros(6)
...: y = np.zeros([7, 6])
...: z = y * x
In [447]: z.shape
Out[447]: (7, 6)
Here we are doing element-wise multiplication, a (7,6) with a (6,). By broadcasting the (6,) becomes (1,6) and then (7,6) to match y.
Evidently in the foo.bar case, y is np.matrix subclass:
In [454]: y1 = np.matrix(y)
In [455]: y1*x
---...
219 # This promotes 1-D vectors to row vectors
--> 220 return N.dot(self, asmatrix(other))
...
ValueError: shapes (7,6) and (1,6) not aligned: 6 (dim 1) != 1 (dim 0)
Note the different display for y1:
In [456]: y1
Out[456]:
matrix([[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.]])
With np.matrix * is defined as np.dot, the matrix product. The x is also converted np.matrix, producing a (1,6) matrix. The error message follows from the definition of matrix multiplication.
np.multiply can be used force the element-wise multiplication. Note the class of the result:
In [458]: np.multiply(y1,x)
Out[458]:
matrix([[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.]])
Because of confusions like this np.matrix is being discouraged.
I don't know which version you're running, but I am running version 1.16.3 on Python 3.
It seems to me you're defining your x differently than in your example snippet. You seem to be defining it as a 6x1 matrix instead of a "vector", which is considered on Numpy to only have one dimension. Try multiplying y by np.zeros([6,1]) and you'll see an error.
Bottom line is:
Use the .shape property a lot when debugging, it's very useful when you're doing matrix multiplication.
On np, multiplication between a matrix and a one dimensional array is carried differently than multiplications between 2 matrices.

Categories