Search code examples
pythonarraysnumpyfallback

N-D indexing with defaults in NumPy


Can I index NumPy N-D array with fallback to default values for out-of-bounds indexes? Example code below for some imaginary np.get_with_default(a, indexes, default):

import numpy as np
print(np.get_with_default(
    np.array([[1,2,3],[4,5,6]]), # N-D array
    [(np.array([0, 0, 1, 1, 2, 2]), np.array([1, 2, 2, 3, 3, 5]))], # N-tuple of indexes along each axis
    13, # Default for out-of-bounds fallback
))

should print

[2 3 6 13 13 13]

I'm looking for some built-in function for this. If such not exists then at least some short and efficient implementation to do that.


Solution

  • I don't know if there is anything in NumPy to do that directly, but you can always implement it yourself. This is not particularly smart or efficient, as it requires multiple advanced indexing operations, but does what you need:

    import numpy as np
    
    def get_with_default(a, indices, default=0):
        # Ensure inputs are arrays
        a = np.asarray(a)
        indices = tuple(np.broadcast_arrays(*indices))
        if len(indices) <= 0 or len(indices) > a.ndim:
            raise ValueError('invalid number of indices.')
        # Make mask of indices out of bounds
        mask = np.zeros(indices[0].shape, np.bool)
        for ind, s in zip(indices, a.shape):
            mask |= (ind < 0) | (ind >= s)
        # Only do masking if necessary
        n_mask = np.count_nonzero(mask)
        # Shortcut for the case where all is masked
        if n_mask == mask.size:
            return np.full_like(a, default)
        if n_mask > 0:
            # Ensure index arrays are contiguous so masking works right
            indices = tuple(map(np.ascontiguousarray, indices))
            for ind in indices:
                # Replace masked indices with zeros
                ind[mask] = 0
        # Get values
        res = a[indices]
        if n_mask > 0:
            # Replace values of masked indices with default value
            res[mask] = default
        return res
    
    # Test
    print(get_with_default(
        np.array([[1,2,3],[4,5,6]]),
        (np.array([0, 0, 1, 1, 2, 2]), np.array([1, 2, 2, 3, 3, 5])),
        13
    ))
    # [ 2  3  6 13 13 13]