I need to set the diagonal elements of a matrix to Inf.
An easy way to do it is to use np.fill_diagonal
.
np.fill_diagonal(my_matrix, float('inf')
However fill_diagonal
modifies the input matrix instead of returning a new matrix with the diagonal filled.
This doesn't work for me. I need the diagonals filled WITHOUT modifying the original matrix.
Of course I could clone the original matrix, so I will always keep a copy of the original matrix. However I don't really like this solution, since I will update my original matrix often and therefore I'll have to make copies of it every time I need the diagonal to be inf.
Is there a function that will do the same that fill_diagonal
but without modifying the input matrix? Something like:
new_matrix = np.fill_diagonal(original_matrix, float('inf')
Why I need this:
My matrix is a distance matrix between points and I want to compute at each step the two closest points. Of course the diagonal of this matrix is 0 (since the distance from a point to itself is 0). So my solution to make sure I don't take the same point is to set the diagonals to Inf.
However once the two points are found, I need to compute the average of the distances between this two points and the rest of the points, so I actually need the diagonals to be 0 instead of Inf.
Currently what I'm doing is:
Compute the average distance between this two points and the rest of them.
# fill diagonal with Inf to avoid taking the diagonals
np.fill_diagonal(data, float('inf'))
# find the minimum distance
idx = np.argmin(data)
# fill the diagonals back to 0
np.fill_diagonal(data, 0.0)
# get the coordinates of the minimum distance
row, col = np.unravel_index(idx,data.shape)
# compute the new node as the average distance between the two points
new_node = np.mean((data[:,row],data[:,col]),0)
# replace the first node (row) with the new node
data[:,row] = new_node
data[row,:] = new_node.T
# delete the second node (col) from the matrix
data = np.delete(data, col, 0) # delete row
data = np.delete(data, col, 1) # delete column
However I don't like the idea of setting diagonals to Inf and then back to 0, I would prefer just passing a function to argmax
that returns data with diagonal filled with Inf without actually modifying the matrix data.
Something like:
idx = np.argmin(return_filled_diagonals(data, float('Inf'))
# here I can operate with data as usual since it has not been modified.
orig_mat = np.array([[1.2,2,3],[4,5,6],[7,8,9]])
#set diagonal to inf without making a copy of the array.
orig_mat + np.where(np.eye(orig_mat.shape[0])>0,np.inf,0)
array([[ inf, 2., 3.],
[ 4., inf, 6.],
[ 7., 8., inf]])
#the original array remains untorched.
print(orig_mat)
[[ 1.2 2. 3. ]
[ 4. 5. 6. ]
[ 7. 8. 9. ]]