NumPy - np.logical_and/or/not/xor
NumPy's logical functions provide element-wise boolean operations on arrays. While Python's `&`, `|`, `~`, and `^` operators work on NumPy arrays, the explicit logical functions offer better control,...
Key Insights
- NumPy’s logical functions (
np.logical_and,np.logical_or,np.logical_not,np.logical_xor) operate element-wise on arrays and handle edge cases like NaN values more reliably than Python’s native operators - These functions accept
whereandoutparameters for conditional operations and memory-efficient in-place modifications, critical for large-scale data processing - Understanding broadcasting rules and performance characteristics enables efficient multi-dimensional boolean indexing and complex filtering operations on arrays
Understanding NumPy Logical Operations
NumPy’s logical functions provide element-wise boolean operations on arrays. While Python’s &, |, ~, and ^ operators work on NumPy arrays, the explicit logical functions offer better control, clearer intent, and handle broadcasting more predictably.
import numpy as np
# Basic comparison: operators vs functions
a = np.array([True, True, False, False])
b = np.array([True, False, True, False])
# Using operators
result_op = a & b
# Using functions
result_func = np.logical_and(a, b)
print(result_op) # [True False False False]
print(result_func) # [True False False False]
The key difference emerges when working with non-boolean arrays. NumPy’s logical functions automatically convert inputs to boolean values:
x = np.array([0, 1, 2, 3])
y = np.array([0, 0, 1, 1])
# Logical functions treat non-zero as True
result = np.logical_and(x, y)
print(result) # [False False True True]
# This differs from bitwise AND
bitwise = x & y
print(bitwise) # [0 0 0 1]
np.logical_and: Intersection Logic
np.logical_and returns True where both input arrays have truthy values. This is essential for compound filtering conditions.
# Real-world example: filtering data
temperatures = np.array([15, 22, 28, 35, 18, 25])
humidity = np.array([45, 60, 75, 80, 50, 65])
# Find comfortable conditions: temp between 20-30 and humidity < 70
temp_good = np.logical_and(temperatures >= 20, temperatures <= 30)
humidity_good = humidity < 70
comfortable = np.logical_and(temp_good, humidity_good)
print(comfortable) # [False True False False False True]
# Apply filter
comfortable_temps = temperatures[comfortable]
print(comfortable_temps) # [22 25]
Multi-dimensional arrays showcase broadcasting capabilities:
# 2D sensor data: rows=sensors, cols=time points
sensor_data = np.array([
[1.2, 0.8, 1.5, 2.1],
[0.5, 1.1, 0.9, 1.8],
[2.0, 1.9, 2.2, 2.5]
])
# Check multiple conditions across different axes
above_threshold = sensor_data > 1.0
below_max = sensor_data < 2.0
valid_readings = np.logical_and(above_threshold, below_max)
print(valid_readings)
# [[True False True False]
# [False True False True]
# [False True False False]]
np.logical_or: Union Logic
np.logical_or returns True when at least one input is truthy. Critical for inclusive filtering.
# Alert system: trigger on either condition
pressure = np.array([980, 1013, 1020, 950, 1015])
wind_speed = np.array([5, 15, 8, 45, 12])
# Alert if pressure < 970 OR wind > 40
low_pressure = pressure < 970
high_wind = wind_speed > 40
alert = np.logical_or(low_pressure, high_wind)
print(alert) # [False False False True False]
print(f"Alert indices: {np.where(alert)[0]}") # Alert indices: [3]
Combining with reduce for checking conditions across arrays:
# Check if any sensor in a group exceeds threshold
sensor_groups = np.array([
[0.5, 0.6, 0.7],
[1.2, 0.8, 0.9],
[0.4, 0.3, 0.5]
])
threshold = 1.0
# Check each group (row) for any value > threshold
any_exceeded = np.logical_or.reduce(sensor_groups > threshold, axis=1)
print(any_exceeded) # [False True False]
np.logical_not: Negation and Inversion
np.logical_not inverts boolean values, useful for finding complementary sets.
# Data validation: find invalid entries
data = np.array([1.5, np.nan, 2.3, np.inf, -1.2, 4.5])
# Identify valid numbers (not NaN, not infinite)
is_valid = np.logical_and(~np.isnan(data), ~np.isinf(data))
# Or using logical_not
is_valid = np.logical_and(
np.logical_not(np.isnan(data)),
np.logical_not(np.isinf(data))
)
print(is_valid) # [True False True False True True]
clean_data = data[is_valid]
print(clean_data) # [1.5 2.3 -1.2 4.5]
Practical application in mask inversion:
# Image processing: invert selection mask
image_mask = np.array([
[True, True, False],
[False, True, False],
[True, False, False]
])
inverted_mask = np.logical_not(image_mask)
print(inverted_mask)
# [[False False True]
# [True False True]
# [False True True]]
# Count selected vs unselected pixels
selected = np.sum(image_mask)
unselected = np.sum(inverted_mask)
print(f"Selected: {selected}, Unselected: {unselected}")
# Selected: 4, Unselected: 5
np.logical_xor: Exclusive Or Logic
np.logical_xor returns True only when inputs differ. Useful for change detection and toggle operations.
# Change detection between states
previous_state = np.array([True, True, False, False, True])
current_state = np.array([True, False, True, False, False])
changes = np.logical_xor(previous_state, current_state)
print(changes) # [False True True False True]
print(f"Changed indices: {np.where(changes)[0]}")
# Changed indices: [1 2 4]
Real-world application in feature comparison:
# Compare feature flags between versions
version_a_features = np.array([1, 1, 0, 1, 0, 1])
version_b_features = np.array([1, 0, 1, 1, 0, 0])
# Features that changed (added or removed)
feature_diff = np.logical_xor(version_a_features, version_b_features)
print(f"Features changed: {np.sum(feature_diff)}") # Features changed: 3
# Identify specifically added vs removed
added = np.logical_and(feature_diff, version_b_features)
removed = np.logical_and(feature_diff, version_a_features)
print(f"Added features at indices: {np.where(added)[0]}") # [2]
print(f"Removed features at indices: {np.where(removed)[0]}") # [1 5]
Performance Optimization with out and where Parameters
NumPy logical functions support out for in-place operations and where for conditional execution.
# Memory-efficient in-place operations
large_array = np.random.rand(1000000) > 0.5
another_array = np.random.rand(1000000) > 0.5
# Preallocate output
result = np.empty(1000000, dtype=bool)
# Compute in-place
np.logical_and(large_array, another_array, out=result)
# Conditional computation with 'where'
mask = np.random.rand(1000000) > 0.3
output = np.zeros(1000000, dtype=bool)
np.logical_and(large_array, another_array, out=output, where=mask)
# Only elements where mask=True are computed
Combining Logical Operations for Complex Filters
Chain logical functions for sophisticated filtering:
# Multi-condition data filtering
data = {
'temperature': np.array([15, 22, 28, 35, 18, 25, 30]),
'pressure': np.array([1010, 1013, 1020, 995, 1015, 1018, 990]),
'humidity': np.array([45, 60, 75, 80, 50, 65, 85])
}
# Complex condition: (temp 20-30 AND humidity < 70) OR pressure < 1000
temp_range = np.logical_and(
data['temperature'] >= 20,
data['temperature'] <= 30
)
low_humidity = data['humidity'] < 70
low_pressure = data['pressure'] < 1000
condition1 = np.logical_and(temp_range, low_humidity)
final_filter = np.logical_or(condition1, low_pressure)
print(f"Matching indices: {np.where(final_filter)[0]}")
# Matching indices: [1 3 5 6]
Broadcasting with Logical Operations
Leverage broadcasting for dimension-agnostic comparisons:
# 3D data: (samples, features, time)
measurements = np.random.randn(5, 3, 10)
# Check if all features exceed threshold at any time point
threshold_low = -1.0
threshold_high = 1.0
within_range = np.logical_and(
measurements > threshold_low,
measurements < threshold_high
)
# Check per sample if all features stay within range for entire duration
all_valid = np.all(within_range, axis=(1, 2))
print(f"Valid samples: {np.where(all_valid)[0]}")
NumPy’s logical functions provide the foundation for efficient boolean operations on arrays. Their element-wise nature, broadcasting support, and optimization parameters make them indispensable for data filtering, validation, and complex conditional logic in numerical computing pipelines.