NumPy - np.put() - Replace Elements by Index
import numpy as np
Key Insights
np.put()modifies arrays in-place by replacing elements at specified indices with new values, offering better performance than traditional indexing for bulk replacements- The function automatically flattens the array view during indexing, treating multidimensional arrays as 1D sequences while preserving the original shape
- Understanding
np.put()’s mode parameter (‘raise’, ‘wrap’, ‘clip’) is critical for handling out-of-bounds indices in production code
Understanding np.put() Basics
np.put() replaces elements in an array at specified indices with new values. Unlike standard indexing operations, it modifies the array in-place and treats the array as a flattened 1D sequence regardless of its actual dimensions.
import numpy as np
# Basic usage
arr = np.array([10, 20, 30, 40, 50])
np.put(arr, [1, 3], [99, 88])
print(arr) # [10 99 30 88 50]
The function signature is straightforward: np.put(a, ind, v, mode='raise') where a is the target array, ind contains indices, v holds replacement values, and mode controls out-of-bounds behavior.
Working with Multidimensional Arrays
When working with multidimensional arrays, np.put() uses C-order (row-major) flattening to determine indices. This means elements are indexed as if the array were reshaped to 1D using ravel().
# 2D array indexing
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Replace elements at flat indices 0, 4, and 8
np.put(matrix, [0, 4, 8], [100, 500, 900])
print(matrix)
# [[100 2 3]
# [ 4 500 6]
# [ 7 8 900]]
The flattened index calculation follows this pattern: for a 2D array with shape (m, n), the flat index for position (i, j) equals i * n + j.
# Understanding flat indexing
arr_3d = np.arange(24).reshape(2, 3, 4)
print(f"Shape: {arr_3d.shape}")
print(f"Element at [1, 2, 3]: {arr_3d[1, 2, 3]}")
# Calculate flat index: 1*12 + 2*4 + 3 = 23
np.put(arr_3d, [23], [999])
print(f"Modified element at [1, 2, 3]: {arr_3d[1, 2, 3]}") # 999
Handling Multiple Replacements
np.put() efficiently handles bulk replacements. When the values array is shorter than the indices array, NumPy cycles through the values.
# Broadcasting values
arr = np.zeros(10, dtype=int)
indices = [0, 2, 4, 6, 8]
values = [1, 2]
np.put(arr, indices, values)
print(arr) # [1 0 2 0 1 0 2 0 1 0]
For single-value replacements across multiple indices, pass a scalar or single-element array:
# Replace multiple positions with same value
arr = np.arange(10)
np.put(arr, [1, 3, 5, 7], -1)
print(arr) # [ 0 -1 2 -1 4 -1 6 -1 8 9]
Mode Parameter: Controlling Index Bounds
The mode parameter determines how np.put() handles out-of-bounds indices. This becomes critical when working with dynamic index arrays or untrusted input.
arr = np.array([1, 2, 3, 4, 5])
# mode='raise' (default): raises IndexError
try:
np.put(arr, [0, 10], [99, 88], mode='raise')
except IndexError as e:
print(f"Error: {e}")
# mode='wrap': wraps indices using modulo
arr_wrap = np.array([1, 2, 3, 4, 5])
np.put(arr_wrap, [0, 7], [99, 88], mode='wrap')
print(arr_wrap) # [99 2 88 4 5] (index 7 wraps to 2)
# mode='clip': clips indices to valid range
arr_clip = np.array([1, 2, 3, 4, 5])
np.put(arr_clip, [0, 10], [99, 88], mode='clip')
print(arr_clip) # [99 2 3 4 88] (index 10 clips to 4)
Performance Considerations
np.put() provides performance advantages over fancy indexing for certain operations, particularly when modifying elements in-place without creating intermediate arrays.
import time
# Benchmark comparison
large_arr = np.arange(1_000_000)
indices = np.random.randint(0, 1_000_000, size=100_000)
values = np.random.randint(0, 100, size=100_000)
# Using np.put()
arr1 = large_arr.copy()
start = time.perf_counter()
np.put(arr1, indices, values)
put_time = time.perf_counter() - start
# Using fancy indexing
arr2 = large_arr.copy()
start = time.perf_counter()
arr2[indices] = values
fancy_time = time.perf_counter() - start
print(f"np.put(): {put_time:.4f}s")
print(f"Fancy indexing: {fancy_time:.4f}s")
Practical Use Cases
Masking and Conditional Replacement
Combine np.put() with np.where() to perform conditional replacements:
# Replace negative values with zero
arr = np.array([-5, 3, -2, 8, -1, 6])
negative_indices = np.where(arr < 0)[0]
np.put(arr, negative_indices, 0)
print(arr) # [0 3 0 8 0 6]
Image Processing
Modify specific pixels in image arrays efficiently:
# Simulate grayscale image (100x100)
image = np.random.randint(0, 256, size=(100, 100), dtype=np.uint8)
# Set specific pixels to white (255)
pixel_coords = [(10, 20), (30, 40), (50, 60)]
flat_indices = [i * 100 + j for i, j in pixel_coords]
np.put(image, flat_indices, 255)
print(f"Modified pixels: {len(flat_indices)}")
Sparse Updates
Update sparse positions in large arrays without creating boolean masks:
# Large data array with sparse updates
data = np.zeros(10000)
update_positions = [100, 500, 1500, 3000, 7500]
update_values = [1.5, 2.3, 4.1, 3.7, 5.9]
np.put(data, update_positions, update_values)
print(f"Non-zero elements: {np.count_nonzero(data)}")
Common Pitfalls
Index Ordering Matters
Unlike some array operations, np.put() processes indices sequentially. Duplicate indices result in the last value being assigned:
arr = np.zeros(5)
np.put(arr, [2, 2, 2], [10, 20, 30])
print(arr) # [0. 0. 30. 0. 0.] - last value wins
In-Place Modification
Remember that np.put() modifies the original array. If you need to preserve the original, create a copy first:
original = np.array([1, 2, 3, 4, 5])
modified = original.copy()
np.put(modified, [0, 2], [99, 88])
print(f"Original: {original}") # [1 2 3 4 5]
print(f"Modified: {modified}") # [99 2 88 4 5]
Type Compatibility
Ensure value types are compatible with the array dtype to avoid unexpected conversions:
int_arr = np.array([1, 2, 3, 4, 5], dtype=np.int32)
np.put(int_arr, [0, 2], [1.7, 3.9])
print(int_arr) # [1 2 3 4 5] - floats truncated to integers
float_arr = np.array([1, 2, 3, 4, 5], dtype=np.float64)
np.put(float_arr, [0, 2], [1.7, 3.9])
print(float_arr) # [1.7 2. 3.9 4. 5. ] - preserves precision
Integration with Other NumPy Functions
Combine np.put() with other NumPy operations for complex array manipulations:
# Find and replace outliers
data = np.array([1, 2, 100, 4, 5, 200, 7, 8])
mean = np.mean(data)
std = np.std(data)
threshold = mean + 2 * std
outlier_indices = np.where(data > threshold)[0]
np.put(data, outlier_indices, mean)
print(data) # Outliers replaced with mean
np.put() excels in scenarios requiring direct index-based modifications without the overhead of boolean masking or fancy indexing. Its in-place operation and mode flexibility make it particularly valuable for performance-critical array manipulation tasks.