How to Do Efficient Array Multiplications using NumPy in Python
NumPy offers multiple ways to perform multiplication, each serving a distinct mathematical purpose. Confusing element-wise multiplication with matrix multiplication is a common source of bugs in scientific computing.
This guide explains the differences between numpy.multiply, numpy.dot, numpy.matmul, and the operators * and @.
Setup and Sample Data
Before exploring the operations, let's define two 2x2 matrices (A and B) to serve as our base examples.
import numpy as np
# Create sample 2x2 arrays
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
print(f"Matrix A:\n{A}")
print(f"Matrix B:\n{B}")
Output:
Matrix A:
[[1 2]
[3 4]]
Matrix B:
[[5 6]
[7 8]]
Element-wise Multiplication (* and np.multiply)
Element-wise multiplication multiplies the value at A[i][j] with the value at B[i][j]. The resulting matrix has the same shape as the inputs.
Using np.multiply
This function is explicit and performs strict element-by-element multiplication.
import numpy as np
# Create sample 2x2 arrays
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# ✅ Correct: Element-wise multiplication
C = np.multiply(A, B)
print(f"np.multiply(A, B):\n{C}")
Output:
np.multiply(A, B):
[[ 5 12]
[21 32]]
Using the * Operator
The asterisk * is the shorthand for element-wise multiplication. It also supports scalar broadcasting, where a single number is multiplied against every element in the array.
import numpy as np
# Create sample 2x2 arrays
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# ✅ Correct: Element-wise multiplication using operator
C_operator = A * B
print(f"A * B:\n{C_operator}")
# ✅ Correct: Scalar multiplication (Broadcasting)
D_scalar = A * 2
print(f"A * 2:\n{D_scalar}")
Output:
A * B:
[[ 5 12]
[21 32]]
A * 2:
[[2 4]
[6 8]]
If you are coming from MATLAB or linear algebra textbooks, remember that in Python/NumPy, A * B is not a matrix dot product. It is element-wise.
Matrix Multiplication (@, np.dot, np.matmul)
Matrix multiplication (the dot product of rows and columns) results in a matrix where C[i][j] is the dot product of the i-th row of A and the j-th column of B.
Using np.dot
This is the classic function for matrix multiplication.
import numpy as np
# Create sample 2x2 arrays
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# ✅ Correct: Standard matrix multiplication
C_dot = np.dot(A, B)
print(f"np.dot(A, B):\n{C_dot}")
Output:
np.dot(A, B):
[[19 22]
[43 50]]
Calculations: Row 1 [1, 2] dot Col 1 [5, 7] = 1*5 + 2*7 = 19.
Using the @ Operator and np.matmul
np.matmul is the modern standard for matrix multiplication. The @ operator (introduced in Python 3.5) is syntactic sugar for np.matmul.
import numpy as np
# Create sample 2x2 arrays
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# ✅ Correct: Matrix multiplication using @ (Recommended)
C_at = A @ B
print(f"A @ B:\n{C_at}")
# ✅ Correct: Using explicit function
C_matmul = np.matmul(A, B)
print(f"np.matmul(A, B):\n{C_matmul}")
Output:
A @ B:
[[19 22]
[43 50]]
np.matmul(A, B):
[[19 22]
[43 50]]
Advanced: The Difference Between dot and matmul
For 2D arrays, np.dot and np.matmul behave identically. However, for higher-dimensional arrays (3D+), they diverge significantly:
np.matmul(or@): Treats the arrays as a stack of matrices residing in the last two indices. It broadcasts accordingly (Batch Matrix Multiplication).np.dot: Performs a sum product over the last axis of the first array and the second-to-last of the second array (Tensor contraction).
import numpy as np
# Create sample 2x2 arrays
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# Define two 3-D arrays (Batch of 2, 2x2 matrices)
a_3d = np.array([[[1, 2], [3, 4]],
[[5, 6], [7, 8]]])
b_3d = np.array([[[9, 10], [11, 12]],
[[13, 14], [15, 16]]])
# Case 1: matmul (Batch Processing)
# Result shape: (2, 2, 2)
res_matmul = np.matmul(a_3d, b_3d)
print(f"Matmul Shape: {res_matmul.shape}")
# Case 2: dot (Tensor Contraction)
# Result shape: (2, 2, 2, 2)
res_dot = np.dot(a_3d, b_3d)
print(f"Dot Shape: {res_dot.shape}")
Output:
Matmul Shape: (2, 2, 2)
Dot Shape: (2, 2, 2, 2)
For most modern Machine Learning and Data Science applications involving batches of data, np.matmul (or @) is usually what you want.
Conclusion
To perform multiplication in NumPy efficiently and correctly:
- Use
*for Element-wise multiplication (e.g., scaling an image, masking). - Use
@for Matrix multiplication (e.g., linear algebra, neural network layers). - Use
np.matmulif you need specific broadcasting rules for high-dimensional arrays (batches). - Avoid
np.dotfor high-dimensional arrays unless you specifically intend to perform tensor contraction.