How to Create an Identity Matrix in NumPy

An identity matrix is a square matrix with ones on the main diagonal and zeros everywhere else. It's the matrix equivalent of the number 1—multiply any matrix by the identity matrix, and you get the...

Key Insights

  • Use numpy.eye() when you need flexibility—rectangular matrices, offset diagonals, or non-standard configurations
  • Use numpy.identity() when you want clean, readable code for standard square identity matrices
  • Always specify dtype explicitly for large matrices to avoid unnecessary memory consumption with the default float64

What Is an Identity Matrix?

An identity matrix is a square matrix with ones on the main diagonal and zeros everywhere else. It’s the matrix equivalent of the number 1—multiply any matrix by the identity matrix, and you get the original matrix back unchanged.

# A 3x3 identity matrix looks like this:
# [[1, 0, 0],
#  [0, 1, 0],
#  [0, 0, 1]]

Identity matrices appear constantly in linear algebra, machine learning, and scientific computing. You’ll use them for matrix inversion verification, coordinate transformations, regularization terms in regression, and neural network weight initialization. NumPy gives you two primary functions to create them: numpy.eye() and numpy.identity(). Understanding when to use each saves you time and prevents subtle bugs.

Using numpy.eye() — The Flexible Option

The eye() function is your go-to for most identity matrix needs. It offers parameters that identity() doesn’t, making it suitable for edge cases and non-standard requirements.

Basic Syntax

numpy.eye(N, M=None, k=0, dtype=float, order='C')
  • N: Number of rows
  • M: Number of columns (defaults to N if not specified)
  • k: Diagonal offset (0 is main diagonal, positive shifts up, negative shifts down)
  • dtype: Data type of the output array
  • order: Memory layout (‘C’ for row-major, ‘F’ for column-major)

Creating Standard Identity Matrices

import numpy as np

# Basic 3x3 identity matrix
I3 = np.eye(3)
print(I3)
# Output:
# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]]

# 4x4 identity matrix with integer dtype
I4_int = np.eye(4, dtype=int)
print(I4_int)
# Output:
# [[1 0 0 0]
#  [0 1 0 0]
#  [0 0 1 0]
#  [0 0 0 1]]

Rectangular Identity-Like Matrices

Sometimes you need a non-square matrix with ones on the diagonal. This is common in certain linear algebra operations and data augmentation scenarios.

# 3 rows, 5 columns
rect_matrix = np.eye(3, 5)
print(rect_matrix)
# Output:
# [[1. 0. 0. 0. 0.]
#  [0. 1. 0. 0. 0.]
#  [0. 0. 1. 0. 0.]]

# 5 rows, 3 columns
tall_matrix = np.eye(5, 3)
print(tall_matrix)
# Output:
# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]
#  [0. 0. 0.]
#  [0. 0. 0.]]

Offset Diagonals with the k Parameter

The k parameter shifts the diagonal. This is useful for creating banded matrices, shift operators, or building blocks for more complex matrix structures.

# Main diagonal (k=0, the default)
print(np.eye(4, k=0, dtype=int))
# Output:
# [[1 0 0 0]
#  [0 1 0 0]
#  [0 0 1 0]
#  [0 0 0 1]]

# Upper diagonal (k=1)
print(np.eye(4, k=1, dtype=int))
# Output:
# [[0 1 0 0]
#  [0 0 1 0]
#  [0 0 0 1]
#  [0 0 0 0]]

# Lower diagonal (k=-1)
print(np.eye(4, k=-1, dtype=int))
# Output:
# [[0 0 0 0]
#  [1 0 0 0]
#  [0 1 0 0]
#  [0 0 1 0]]

# Creating a tridiagonal-like structure
tridiag = np.eye(5, k=-1) + np.eye(5) + np.eye(5, k=1)
print(tridiag)
# Output:
# [[1. 1. 0. 0. 0.]
#  [1. 1. 1. 0. 0.]
#  [0. 1. 1. 1. 0.]
#  [0. 0. 1. 1. 1.]
#  [0. 0. 0. 1. 1.]]

Using numpy.identity() — The Simple Option

When you need a standard square identity matrix and nothing else, numpy.identity() communicates your intent more clearly.

Basic Syntax

numpy.identity(n, dtype=None)

That’s it. Two parameters. No diagonal offsets, no rectangular options—just a clean function for the most common case.

Basic Usage

import numpy as np

# 3x3 identity matrix
I3 = np.identity(3)
print(I3)
# Output:
# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]]

# With explicit dtype
I_float32 = np.identity(4, dtype=np.float32)
I_int = np.identity(4, dtype=int)
I_complex = np.identity(3, dtype=complex)

print(f"float32 identity:\n{I_float32}")
print(f"int identity:\n{I_int}")
print(f"complex identity:\n{I_complex}")

The function name itself documents what you’re doing. When someone reads np.identity(5), there’s zero ambiguity. Compare that to np.eye(5), which could theoretically have offset diagonals or be rectangular—you’d need to check the parameters to be sure.

Practical Differences: eye() vs identity()

Here’s when to use each:

Feature numpy.eye() numpy.identity()
Square matrices
Rectangular matrices
Diagonal offset
Code clarity for standard identity Good Better
Memory layout control

Use identity() when:

  • You need a standard square identity matrix
  • Code readability matters (it usually does)
  • You want to signal intent clearly to other developers

Use eye() when:

  • You need a rectangular matrix
  • You need an offset diagonal
  • You’re building complex matrix structures
  • You need control over memory layout

In practice, I default to identity() for standard cases and reach for eye() only when I need its extra features.

Common Use Cases

Verifying Matrix Inversion

The most common use of identity matrices is verifying that matrix inversion worked correctly. If A @ A_inv equals the identity matrix (within floating-point tolerance), your inversion succeeded.

import numpy as np

# Create a random invertible matrix
A = np.array([[4, 7], [2, 6]])

# Compute inverse
A_inv = np.linalg.inv(A)

# Verify: A @ A_inv should equal identity
result = A @ A_inv
I = np.identity(2)

print("A @ A_inv:")
print(result)
print("\nIs close to identity?", np.allclose(result, I))
# Output: True

Initializing Transformation Matrices

In computer graphics and robotics, you often start with an identity matrix and modify it to represent transformations.

import numpy as np

def create_translation_matrix_2d(tx, ty):
    """Create a 2D translation matrix using homogeneous coordinates."""
    T = np.identity(3)
    T[0, 2] = tx  # x translation
    T[1, 2] = ty  # y translation
    return T

def create_scale_matrix_2d(sx, sy):
    """Create a 2D scaling matrix."""
    S = np.identity(3)
    S[0, 0] = sx  # x scale
    S[1, 1] = sy  # y scale
    return S

# Translate by (5, 3) then scale by (2, 2)
T = create_translation_matrix_2d(5, 3)
S = create_scale_matrix_2d(2, 2)

# Combine transformations
combined = S @ T

print("Translation matrix:")
print(T)
print("\nScale matrix:")
print(S)
print("\nCombined transformation:")
print(combined)

Regularization in Machine Learning

Ridge regression adds a scaled identity matrix to prevent overfitting. This is called L2 regularization.

import numpy as np

def ridge_regression(X, y, lambda_reg=1.0):
    """
    Closed-form solution for ridge regression:
    w = (X^T X + λI)^(-1) X^T y
    """
    n_features = X.shape[1]
    I = np.identity(n_features)
    
    XtX = X.T @ X
    XtX_regularized = XtX + lambda_reg * I
    
    w = np.linalg.inv(XtX_regularized) @ X.T @ y
    return w

# Example usage
X = np.random.randn(100, 5)
y = np.random.randn(100)

weights = ridge_regression(X, y, lambda_reg=0.5)
print("Ridge regression weights:", weights)

Performance Considerations

For small matrices, performance differences are negligible. For large matrices, dtype selection matters significantly.

import numpy as np

# Compare memory usage for different dtypes
sizes = [100, 1000, 5000]

for size in sizes:
    I_float64 = np.identity(size, dtype=np.float64)
    I_float32 = np.identity(size, dtype=np.float32)
    I_int8 = np.eye(size, dtype=np.int8)
    
    print(f"\n{size}x{size} identity matrix:")
    print(f"  float64: {I_float64.nbytes / 1024 / 1024:.2f} MB")
    print(f"  float32: {I_float32.nbytes / 1024 / 1024:.2f} MB")
    print(f"  int8:    {I_int8.nbytes / 1024 / 1024:.2f} MB")

For a 5000x5000 matrix, float64 uses 200 MB while int8 uses only 25 MB. If you’re creating many identity matrices or working with memory constraints, choose your dtype deliberately rather than accepting the float64 default.

Both eye() and identity() have similar performance characteristics—they’re both O(n²) for creating an n×n matrix. The choice between them should be based on functionality needs and code clarity, not speed.

Conclusion

Creating identity matrices in NumPy is straightforward once you know which function to use:

  • numpy.identity(n): Use this for standard square identity matrices. It’s cleaner and communicates intent better.
  • numpy.eye(N, M, k): Use this when you need rectangular matrices, offset diagonals, or memory layout control.

For most day-to-day work, identity() is the right choice. Reserve eye() for cases where you genuinely need its additional parameters. And regardless of which function you use, always consider specifying dtype explicitly—especially for large matrices where the default float64 might waste memory you can’t afford.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.