changing the values of the diagonal of a matrix in numpy

Question:

how can I change the values of the diagonal of a matrix in numpy?

I checked Numpy modify ndarray diagonal, but the function there is not implemented in numpy v 1.3.0.

lets say we have a np.array X and I want to set all values of the diagonal to 0.

Asked By: pacodelumberg

||

Answers:

def replaceDiagonal(matrix, replacementList):
    for i in range(len(replacementList)):
        matrix[i][i] = replacementList[i]

Where size is n in an n x n matrix.

Answered By: Joel Cornett

Did you try numpy.fill_diagonal? See the following answer and this discussion. Or the following from the documentation (although currently broken):

http://docs.scipy.org/doc/numpy/reference/generated/numpy.fill_diagonal.html

Answered By: Arjun

If you’re using a version of numpy that doesn’t have fill_diagonal (the right way to set the diagonal to a constant) or diag_indices_from, you can do this pretty easily with array slicing:

# assuming a 2d square array
n = mat.shape[0]
mat[range(n), range(n)] = 0

This is much faster than an explicit loop in Python, because the looping happens in C and is potentially vectorized.

One nice thing about this is that you can also fill a diagonal with a list of elements, rather than a constant value (like diagflat, but for modifying an existing matrix rather than making a new one). For example, this will set the diagonal of your matrix to 0, 1, 2, …:

# again assuming 2d square array
n = mat.shape[0]
mat[range(n), range(n)] = range(n)

If you need to support more array shapes, this is more complicated (which is why fill_diagonal is nice…):

m[list(zip(*map(range, m.shape)))] = 0

(The list call is only necessary in Python 3, where zip returns an iterator.)

Answered By: Danica

Here’s another good way to do this. If you want a one-dimensional view of the array’s main diagonal use:

A.ravel()[:A.shape[1]**2:A.shape[1]+1]

For the i’th superdiagonal use:

A.ravel()[i:max(0,A.shape[1]-i)*A.shape[1]:A.shape[1]+1]

For the i’th subdiagonal use:

A.ravel()[A.shape[1]*i:A.shape[1]*(i+A.shape[1]):A.shape[1]+1]

Or in general, for the i’th diagonal where the main diagonal is 0, the subdiagonals are negative and the superdiagonals are positive, use:

A.ravel()[max(i,-A.shape[1]*i):max(0,(A.shape[1]-i))*A.shape[1]:A.shape[1]+1]

These are views and not copies, so they will run faster for extracting a diagonal, but any changes made to the new array object will apply to the original array.
On my machine these run faster than the fill_diagonal function when setting the main diagonal to a constant, but that may not always be the case. They can also be used to assign an array of values to a diagonal instead of just a constant.

Notes: for small arrays it may be faster to use the flat attribute of the NumPy array.
If speed is a major issue it could be worth it to make A.shape[1] a local variable.
Also, if the array is not contiguous, ravel() will return a copy, so, in order to assign values to a strided slice, it will be necessary to creatively slice the original array used to generate the strided slice (if it is contiguous) or to use the flat attribute.

Also, it was originally planned that in NumPy 1.10 and later the ‘diagonal’ method of arrays will return a view instead of a copy.
That change hasn’t yet been made though, but hopefully at some point this trick to get a view will no longer be necessary.
See http://docs.scipy.org/doc/numpy-dev/reference/generated/numpy.diagonal.html

Answered By: IanH
>>> a = numpy.random.rand(2,2)
>>> a
array([[ 0.41668355,  0.07982691], 
       [ 0.60790982,  0.0314224 ]])
>>> a - numpy.diag(numpy.diag(a))
array([[ 0.        ,  0.07982691],
       [ 0.60790982,  0.        ]])
Answered By: phubaba

You can use numpy.diag_indices_from() to get the indices of the diagonal elements of your array. Then set the value of those indices.

X[np.diag_indices_from(X)] = 0.

Example:

>>> import numpy as np
>>> X = np.random.rand(5, 5)

>>> print(X)
[[0.59480384 0.20133725 0.59147423 0.22640441 0.40898203]
 [0.65230581 0.57055258 0.97009881 0.58535275 0.32036626]
 [0.71524332 0.73424734 0.92461381 0.38704119 0.08147428]
 [0.18931865 0.97366736 0.11482649 0.82793141 0.13347333]
 [0.47402986 0.73329347 0.18892479 0.11883424 0.78718883]]

>>> X[np.diag_indices_from(X)] = 0
>>> print(X)
[[0.         0.20133725 0.59147423 0.22640441 0.40898203]
 [0.65230581 0.         0.97009881 0.58535275 0.32036626]
 [0.71524332 0.73424734 0.         0.38704119 0.08147428]
 [0.18931865 0.97366736 0.11482649 0.         0.13347333]
 [0.47402986 0.73329347 0.18892479 0.11883424 0.        ]]
Answered By: meduz

You can do the following.

Assuming your matrix is 4 * 4 matrix.

indices_diagonal = np.diag_indices(4)

yourarray[indices_diagonal] = Val
Answered By: Mohamed Ibrahim
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.