Python NumPy: How to Catch a NumPy Warning Like an Exception in Python
NumPy operations like dividing by zero, taking the logarithm of a negative number, or overflow in calculations produce warnings rather than raising exceptions. While warnings don't stop your program, they can indicate serious issues in your computations. In some cases, you want to catch these warnings and handle them just like exceptions - logging them, providing fallback values, or stopping execution.
This guide covers several methods to convert NumPy warnings into catchable exceptions.
Understanding the Problem
By default, NumPy operations that produce invalid results emit a warning but continue execution:
import numpy as np
arr = np.array([1.0, 2.0, 3.0])
result = arr / 0
print(result)
Output:
RuntimeWarning: divide by zero encountered in divide
[inf inf inf]
The program continues with inf values instead of stopping. In many scenarios, you want to catch this and handle it explicitly.
Using np.errstate() (Recommended)
The np.errstate() context manager lets you control how NumPy handles floating-point errors within a specific block of code. Setting an error category to 'raise' causes NumPy to raise a FloatingPointError exception:
import numpy as np
arr = np.array([1.0, 5.0, 4.0])
with np.errstate(divide='raise'):
try:
result = arr / 0
except FloatingPointError:
print("❌ Error: Division by zero detected!")
Output:
❌ Error: Division by zero detected!
Available Error Categories
You can control different types of floating-point errors independently:
import numpy as np
# Raise on all floating-point issues
with np.errstate(divide='raise', invalid='raise', over='raise', under='raise'):
try:
result = np.sqrt(-1) # Invalid operation
except FloatingPointError as e:
print(f"Caught: {e}")
Output:
Caught: invalid value encountered in sqrt
| Category | Triggers On | Example |
|---|---|---|
divide | Division by zero | 1.0 / 0 |
invalid | Invalid operation (e.g., 0/0, sqrt(-1)) | np.sqrt(-1) |
over | Overflow (result too large) | np.float64(1e308) * 10 |
under | Underflow (result too small) | np.float64(1e-308) / 1e308 |
Setting All Categories at Once
import numpy as np
with np.errstate(all='raise'):
try:
result = np.array([0.0]) / np.array([0.0]) # 0/0 = invalid
except FloatingPointError as e:
print(f"Caught: {e}")
Output:
Caught: invalid value encountered in divide
np.errstate() is recommendednp.errstate() is a context manager, so it only affects the code inside the with block. The original error settings are automatically restored afterward, preventing side effects on other parts of your program.
Using np.seterr() for Global Configuration
np.seterr() changes how NumPy handles floating-point errors globally for the entire program:
import numpy as np
# Save original settings
original_settings = np.seterr(all='raise')
try:
result = np.array([1.0, 2.0]) / 0
except FloatingPointError as e:
print(f"Caught globally: {e}")
# Restore original settings
np.seterr(**original_settings)
Output:
Caught globally: divide by zero encountered in divide
Available Actions
| Action | Behavior |
|---|---|
'raise' | Raise a FloatingPointError exception |
'warn' | Print a RuntimeWarning (default) |
'ignore' | Silently ignore the issue |
'call' | Call a custom function |
'print' | Print a warning to stdout |
'log' | Log the warning |
np.seterr() affects the entire programUnlike np.errstate() which is scoped to a with block, np.seterr() changes settings globally. Always save and restore the original settings to avoid unexpected behavior in other parts of your code:
# Save original
old = np.seterr(divide='raise')
# ... your code ...
# Restore original
np.seterr(**old)
Prefer np.errstate() for localized error handling.
Using the warnings Module
Python's built-in warnings module can convert warnings into exceptions, including NumPy's RuntimeWarning:
import warnings
import numpy as np
with warnings.catch_warnings():
warnings.simplefilter("error") # Treat all warnings as errors
try:
result = np.array([1.0, 2.0, 3.0]) / 0
except RuntimeWarning as e:
print(f"Caught warning: {e}")
Output:
Caught warning: divide by zero encountered in divide
Filtering Specific Warning Types
You can target only specific warning categories:
import warnings
import numpy as np
with warnings.catch_warnings():
# Only catch RuntimeWarning, not all warnings
warnings.filterwarnings("error", category=RuntimeWarning)
try:
result = np.log(-1) # Produces RuntimeWarning
except RuntimeWarning as e:
print(f"Caught: {e}")
Output:
Caught: invalid value encountered in log
Practical Example: Safe Division with Fallback
Here's a real-world function that catches division-related warnings and provides fallback values:
import numpy as np
def safe_divide(a, b, default=0.0):
"""Divide arrays a/b, replacing invalid results with a default value."""
with np.errstate(divide='raise', invalid='raise'):
try:
result = a / b
except FloatingPointError:
# Fall back to element-wise handling
result = np.where(b != 0, a / np.where(b != 0, b, 1), default)
return result
numerator = np.array([10, 20, 30, 0])
denominator = np.array([2, 0, 5, 0])
result = safe_divide(numerator, denominator, default=-1)
print("Result:", result)
Output:
Result: [ 5. -1. 6. -1.]
Positions where the denominator is zero are replaced with the default value (-1) instead of producing inf or nan.
Practical Example: Data Validation Pipeline
import numpy as np
def validate_computation(data):
"""Run computation with strict error checking."""
errors = []
with np.errstate(all='raise'):
# Check for division issues
try:
ratios = data['values'] / data['weights']
except FloatingPointError:
errors.append("Division error in ratio calculation")
ratios = np.zeros_like(data['values'])
# Check for log of non-positive values
try:
log_values = np.log(data['values'])
except FloatingPointError:
errors.append("Invalid values for logarithm")
log_values = np.full_like(data['values'], np.nan)
return {
'ratios': ratios,
'log_values': log_values,
'errors': errors
}
data = {
'values': np.array([10.0, 0.0, -5.0, 20.0]),
'weights': np.array([2.0, 0.0, 1.0, 4.0])
}
result = validate_computation(data)
print("Ratios:", result['ratios'])
print("Log values:", result['log_values'])
print("Errors:", result['errors'])
Output:
Ratios: [0. 0. 0. 0.]
Log values: [nan nan nan nan]
Errors: ['Division error in ratio calculation', 'Invalid values for logarithm']
Comparison of Methods
| Method | Scope | Exception Type | Best For |
|---|---|---|---|
np.errstate() | Local (with block) | FloatingPointError | Recommended - localized handling |
np.seterr() | Global | FloatingPointError | Program-wide settings |
warnings.catch_warnings() | Local (with block) | RuntimeWarning | Catching all Python warnings |
Conclusion
NumPy warnings can mask serious computation errors in your data pipeline.
-
Using
np.errstate()with'raise'is the safest and most Pythonic approach: it converts floating-point issues into catchableFloatingPointErrorexceptions within a scoped block, without affecting the rest of your program. -
Use
np.seterr()when you need global settings -
Use
warningsmodule when you want to catch broader Python warning categories.
By treating warnings as exceptions where appropriate, you ensure that invalid computations are detected and handled rather than silently producing incorrect results.