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.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.