Computing the angle between two vectors is a fundamental operation in linear algebra with applications across machine learning, computer graphics, physics simulations, and data science. Whether you're calculating similarity between feature vectors, determining the direction of movement, or analyzing forces, knowing how to find the angle between vectors is essential.
In this guide, you'll learn multiple methods to compute the angle between vectors in Python, using pure math, NumPy, and the cross product, with clear explanations of the underlying formulas and practical examples.
The Math Behind Vector Angles
The angle between two vectors u and v is derived from the dot product formula:
u · v = ‖u‖ ‖v‖ cos(θ)
Solving for θ:
θ = arccos( (u · v) / (‖u‖ ‖v‖) )
Where:
- u · v is the dot product of the vectors
- ‖u‖ and ‖v‖ are the magnitudes (lengths) of the vectors
- θ is the angle between them
Using Pure Python (No Libraries)
You can compute the angle using only Python's built-in math module, no external libraries required:
import math
def angle_between_vectors(u, v):
"""
Compute the angle between two vectors using the dot product.
Args:
u, v: Lists or tuples representing vectors.
Returns:
Tuple of (angle_radians, angle_degrees).
"""
dot_product = sum(a * b for a, b in zip(u, v))
magnitude_u = math.sqrt(sum(a ** 2 for a in u))
magnitude_v = math.sqrt(sum(b ** 2 for b in v))
cos_theta = dot_product / (magnitude_u * magnitude_v)
cos_theta = max(-1.0, min(1.0, cos_theta))
angle_rad = math.acos(cos_theta)
angle_deg = math.degrees(angle_rad)
return angle_rad, angle_deg
u = [3, 4]
v = [5, -2]
angle_rad, angle_deg = angle_between_vectors(u, v)
print(f"Angle in radians: {angle_rad:.4f}")
print(f"Angle in degrees: {angle_deg:.2f}°")Output:
Angle in radians: 1.3078
Angle in degrees: 74.93°Using NumPy (Recommended)
NumPy provides optimized functions for vector operations, making the computation cleaner and significantly faster for large arrays:
import numpy as np
def angle_between_vectors(u, v):
"""Compute the angle between two vectors using NumPy."""
u = np.array(u, dtype=float)
v = np.array(v, dtype=float)
cos_theta = np.dot(u, v) / (np.linalg.norm(u) * np.linalg.norm(v))
angle_rad = np.arccos(np.clip(cos_theta, -1.0, 1.0))
return angle_rad, np.degrees(angle_rad)
u = [3, 4]
v = [5, -2]
angle_rad, angle_deg = angle_between_vectors(u, v)
print(f"Angle in radians: {angle_rad:.4f}")
print(f"Angle in degrees: {angle_deg:.2f}°")Output:
Angle in radians: 1.3078
Angle in degrees: 74.93°Key NumPy functions used:
| Function | Purpose |
|---|---|
np.dot(u, v) |
Computes the dot product |
np.linalg.norm(u) |
Computes the vector magnitude |
np.clip(val, -1, 1) |
Clamps value to valid arccos range |
np.arccos(val) |
Computes the inverse cosine |
np.degrees(rad) |
Converts radians to degrees |
Using the Cross Product (3D Vectors)
For 3-dimensional vectors, the cross product provides an alternative method. The magnitude of the cross product relates to the sine of the angle:
‖u × v‖ = ‖u‖ ‖v‖ sin(θ)
However, the most robust approach combines both the dot product and cross product using atan2, which correctly handles all quadrants and avoids the precision issues of arccos and arcsin alone:
import numpy as np
def angle_between_3d_vectors(u, v):
"""
Compute the angle between two 3D vectors using atan2
with both cross product and dot product for maximum accuracy.
"""
u = np.array(u, dtype=float)
v = np.array(v, dtype=float)
cross = np.cross(u, v)
sin_theta = np.linalg.norm(cross)
cos_theta = np.dot(u, v)
angle_rad = np.arctan2(sin_theta, cos_theta)
return angle_rad, np.degrees(angle_rad)
u = [3, 2, 1]
v = [1, 0, 2]
angle_rad, angle_deg = angle_between_3d_vectors(u, v)
print(f"Angle in radians: {angle_rad:.4f}")
print(f"Angle in degrees: {angle_deg:.2f}°")Output:
Angle in radians: 0.9303
Angle in degrees: 53.30°Special Cases to Handle
Zero Vectors
A zero vector has no direction, so the angle is undefined:
import numpy as np
def safe_angle(u, v):
"""Compute angle with zero-vector protection."""
u = np.array(u, dtype=float)
v = np.array(v, dtype=float)
norm_u = np.linalg.norm(u)
norm_v = np.linalg.norm(v)
if norm_u == 0 or norm_v == 0:
raise ValueError("Cannot compute angle with a zero vector.")
cos_theta = np.dot(u, v) / (norm_u * norm_v)
angle_rad = np.arccos(np.clip(cos_theta, -1.0, 1.0))
return angle_rad, np.degrees(angle_rad)
try:
safe_angle([0, 0], [1, 2])
except ValueError as e:
print(f"Error: {e}")Output:
Error: Cannot compute angle with a zero vector.Common Angle Results
import numpy as np
def angle_deg(u, v):
u, v = np.array(u, dtype=float), np.array(v, dtype=float)
cos_t = np.dot(u, v) / (np.linalg.norm(u) * np.linalg.norm(v))
return np.degrees(np.arccos(np.clip(cos_t, -1, 1)))
print(f"Parallel: {angle_deg([1, 0], [3, 0]):.1f}°")
print(f"Opposite: {angle_deg([1, 0], [-1, 0]):.1f}°")
print(f"Perpendicular: {angle_deg([1, 0], [0, 1]):.1f}°")
print(f"45 degrees: {angle_deg([1, 0], [1, 1]):.1f}°")Output:
Parallel: 0.0°
Opposite: 180.0°
Perpendicular: 90.0°
45 degrees: 45.0°Computing Angles for Multiple Vector Pairs
When processing many vector pairs (common in machine learning), NumPy's vectorized operations are far more efficient than looping:
import numpy as np
def batch_angles(vectors_u, vectors_v):
"""Compute angles between corresponding pairs of vectors."""
u = np.array(vectors_u, dtype=float)
v = np.array(vectors_v, dtype=float)
dots = np.sum(u * v, axis=1)
norms_u = np.linalg.norm(u, axis=1)
norms_v = np.linalg.norm(v, axis=1)
cos_theta = dots / (norms_u * norms_v)
angles_rad = np.arccos(np.clip(cos_theta, -1.0, 1.0))
return np.degrees(angles_rad)
u_vectors = [[1, 0], [0, 1], [1, 1], [3, 4]]
v_vectors = [[0, 1], [0, 1], [-1, 1], [5, -2]]
angles = batch_angles(u_vectors, v_vectors)
for u, v, angle in zip(u_vectors, v_vectors, angles):
print(f"{u} ↔ {v}: {angle:.2f}°")Output:
[1, 0] ↔ [0, 1]: 90.00°
[0, 1] ↔ [0, 1]: 0.00°
[1, 1] ↔ [-1, 1]: 90.00°
[3, 4] ↔ [5, -2]: 74.93°Quick Comparison of Methods
| Method | Dimensions | Accuracy | Dependencies | Best For |
|---|---|---|---|---|
| Pure Python (dot product) | Any | Good (with clamping) | None | Simple scripts, no dependencies |
| NumPy (dot product) | Any | Good (with np.clip) |
numpy |
General-purpose, production code |
Cross product + atan2 |
3D only | ✅ Best | numpy |
Maximum numerical stability |
| Batch NumPy | Any | Good | numpy |
Processing many vector pairs |
Conclusion
Computing the angle between vectors in Python is straightforward once you understand the underlying math:
- Use the dot product formula with
arccosfor a clean, general-purpose solution. This works for vectors of any dimension. - Use NumPy for cleaner syntax and significantly better performance, especially with large or numerous vectors.
- Use the cross product with
atan2for 3D vectors when you need maximum numerical stability. - Always clamp the cosine value to
[-1, 1]before passing it toarccosto prevent floating-point domain errors.
For most applications, the NumPy dot product approach strikes the best balance of simplicity, performance, and reliability.