Skip to main content

Python NumPy: How to Sum Diagonal Elements in NumPy in Python

The sum of diagonal elements (called the trace) is a fundamental matrix property used in linear algebra, eigenvalue calculations, and matrix analysis. NumPy provides dedicated functions for this operation.

The trace() function directly returns the sum of diagonal elements:

import numpy as np

matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])

trace = np.trace(matrix)
print(trace) # 15 (1 + 5 + 9)

Visual Representation

import numpy as np

matrix = np.array([
[1, 2, 3], # ↙ 1 is on main diagonal
[4, 5, 6], # ↙ 5 is on main diagonal
[7, 8, 9] # ↙ 9 is on main diagonal
])

# Main diagonal: positions (0,0), (1,1), (2,2)
print(np.trace(matrix)) # 1 + 5 + 9 = 15

Using np.diagonal()

When you need the diagonal elements themselves (not just the sum):

import numpy as np

matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])

# Get diagonal elements as array
diag_elements = np.diagonal(matrix)
print(f"Diagonal: {diag_elements}") # [1 5 9]

# Then sum if needed
print(f"Sum: {np.sum(diag_elements)}") # 15

Output:

Diagonal: [1 5 9]
Sum: 15

When to Use Each

import numpy as np

matrix = np.array([[1, 2], [3, 4]])

# Just need the sum? Use trace()
total = np.trace(matrix)

# Need to process elements? Use diagonal()
diag = np.diagonal(matrix)
squared = diag ** 2
mean = diag.mean()

print(total) # 5
print(diag) # [1 4]
print(squared) # [ 1 16]
print(mean) # 2.5

Offset Diagonals

Access diagonals above or below the main diagonal using the offset parameter:

import numpy as np

matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])

# Main diagonal (offset=0, default)
print(f"Main (k=0): {np.trace(matrix, offset=0)}") # 15

# One above main diagonal (offset=1)
print(f"Upper (k=1): {np.trace(matrix, offset=1)}") # 8 (2 + 6)

# One below main diagonal (offset=-1)
print(f"Lower (k=-1): {np.trace(matrix, offset=-1)}") # 12 (4 + 8)

# Two above main diagonal (offset=2)
print(f"Upper (k=2): {np.trace(matrix, offset=2)}") # 3

Output:

Main (k=0): 15
Upper (k=1): 8
Lower (k=-1): 12
Upper (k=2): 3

Visualizing Offset Diagonals

Matrix:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

offset=2: [3] → sum = 3
offset=1: [2, 6] → sum = 8
offset=0: [1, 5, 9] (main) → sum = 15
offset=-1: [4, 8] → sum = 12
offset=-2: [7] → sum = 7

Non-Square Matrices

Trace works on rectangular matrices too:

import numpy as np

# More columns than rows
wide = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8]
])
print(f"Wide matrix trace: {np.trace(wide)}") # 7 (1 + 6)

# More rows than columns
tall = np.array([
[1, 2],
[3, 4],
[5, 6]
])
print(f"Tall matrix trace: {np.trace(tall)}") # 5 (1 + 4)

Output:

Wide matrix trace: 7
Tall matrix trace: 5

Anti-Diagonal Sum

The anti-diagonal runs from top-right to bottom-left:

import numpy as np

matrix = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])

# Anti-diagonal: flip horizontally, then get trace
anti_diag = np.trace(np.fliplr(matrix))
print(f"Anti-diagonal sum: {anti_diag}") # 15 (3 + 5 + 7)

# Or get elements using diagonal on flipped matrix
anti_elements = np.diagonal(np.fliplr(matrix))
print(f"Anti-diagonal elements: {anti_elements}") # [3 5 7]

Output:

Anti-diagonal sum: 15
Anti-diagonal elements: [3 5 7]

Trace Properties

The trace has useful mathematical properties:

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# Property 1: tr(A + B) = tr(A) + tr(B)
print(f"tr(A+B) = {np.trace(A + B)}") # 18
print(f"tr(A) + tr(B) = {np.trace(A) + np.trace(B)}") # 18

# Property 2: tr(cA) = c * tr(A)
c = 3
print(f"tr(3A) = {np.trace(c * A)}") # 15
print(f"3 * tr(A) = {c * np.trace(A)}") # 15

# Property 3: tr(A^T) = tr(A)
print(f"tr(A) = {np.trace(A)}") # 5
print(f"tr(A.T) = {np.trace(A.T)}") # 5

# Property 4: tr(AB) = tr(BA)
print(f"tr(AB) = {np.trace(A @ B)}") # 69
print(f"tr(BA) = {np.trace(B @ A)}") # 69

Output:

tr(A+B) = 18
tr(A) + tr(B) = 18
tr(3A) = 15
3 * tr(A) = 15
tr(A) = 5
tr(A.T) = 5
tr(AB) = 69
tr(BA) = 69

Sum of Eigenvalues

The trace equals the sum of eigenvalues:

import numpy as np

matrix = np.array([
[4, 2],
[1, 3]
])

# Calculate trace
trace = np.trace(matrix)
print(f"Trace: {trace}") # 7

# Calculate eigenvalues
eigenvalues = np.linalg.eigvals(matrix)
print(f"Eigenvalues: {eigenvalues}") # [5. 2.]
print(f"Sum of eigenvalues: {np.sum(eigenvalues)}") # 7.0

Output:

Trace: 7
Eigenvalues: [5. 2.]
Sum of eigenvalues: 7.0
note

The trace equals the sum of eigenvalues, and the determinant equals their product. These relationships are useful for checking calculations.

3D Arrays

For 3D arrays, trace operates on the last two dimensions by default:

import numpy as np

# Stack of 2x2 matrices
arr_3d = np.array([
[[1, 2], [3, 4]],
[[5, 6], [7, 8]]
])

# Trace of each 2D matrix
traces = np.trace(arr_3d, axis1=1, axis2=2)
print(f"Traces: {traces}") # [5 13]
# First matrix: 1+4=5, Second matrix: 5+8=13

Output:

Traces: [ 5 13]

Practical Example: Matrix Norm

The Frobenius norm can be computed using trace:

import numpy as np

matrix = np.array([
[1, 2],
[3, 4]
])

# Frobenius norm = sqrt(trace(A^T @ A))
frobenius_via_trace = np.sqrt(np.trace(matrix.T @ matrix))
print(f"Via trace: {frobenius_via_trace:.4f}")

# Compare with np.linalg.norm
frobenius_direct = np.linalg.norm(matrix, 'fro')
print(f"Direct: {frobenius_direct:.4f}")

Output:

Via trace: 5.4772
Direct: 5.4772

Summary

MethodReturnsUse Case
np.trace(A)Scalar (sum)Direct diagonal sum
np.diagonal(A)Array (elements)Need individual values
np.trace(A, offset=k)ScalarSum of offset diagonal
np.trace(np.fliplr(A))ScalarAnti-diagonal sum

Use np.trace() when you only need the sum of diagonal elements-it's more efficient than extracting the diagonal first. Use np.diagonal() when you need to process the individual diagonal values.