简体   繁体   中英

Assigning values to discontinued slices in a ndarray

I have a base array that contains data. Some indices in that array need to be re-assigned a new value, and the indices which do are discontinued. I'd like to avoid for-looping over all of that and using the slice notation as it's likely to be faster.

For instance:

arr = np.zeros(100)
sl_obj_1 = slice(2,5)
arr[sl_obj_1] = 42

Works for a single slice. But I have another discontinued slice to apply to that same array, say

sl_obj_2 = slice(12,29)
arr[sl_obj_1] = 55

I would like to accomplish something along the lines of:

arr[sl_obj_1, sl_obj_2] = 42, 55

Any ideas?

EDIT: changed example to emphasis that sequences are or varying lenghts.

There isn't a good way to directly extract multiple slices from a NumPy array, much less different-sized slices. But you can cheat by converting your slices into indices, and using an index array.

In the case of 1-dimensional arrays, this is relatively simple using index arrays .

import numpy as np

def slice_indices(some_list, some_slice):
    """Convert a slice into indices of a list"""
    return np.arange(len(some_list))[some_slice]
    # For a non-NumPy solution, use this:
    # return range(*some_slice.indices(len(some_list)))

arr = np.arange(10)

# We'll make [1, 2, 3] and [8, 7] negative.
slice1, new1 = np.s_[1:4],    [-1, -2, -3]
slice2, new2 = np.s_[8:6:-1], [-8, -7]
# (Here, np.s_ is just a nicer notation for slices.[1])

# Get indices to replace
idx1 = slice_indices(arr, slice1)
idx2 = slice_indices(arr, slice2)

# Use index arrays to assign to all of the indices
arr[[*idx1, *idx2]] = *new1, *new2

# That line expands to this:
arr[[1, 2, 3, 8, 7]] = -1, -2, -3, -8, -7

Note that this doesn't entirely avoid Python iteration—the star operators still create iterators and the index array is a regular python list. In a case with large amounts of data, this could be noticeably slower than the manual approach, because it calculates each index that will be assigned.

You will also need to make sure the replacement data is already the right shape, or you can use NumPy's manual broadcasting functions (eg np.broadcast_to ) to fix the shapes. This introduces additional overhead—if you were rely on automatic broadcasting, you're probably better off doing the assignments in a loop.

arr = np.zeros(100)

idx1 = slice_indices(arr, slice(2, 5))
idx2 = slice_indices(arr, slice(12, 29))

new1 = np.broadcast_to(42, len(idx1))
new2 = np.broadcast_to(55, len(idx2))

arr[*idx1, *idx2] = *new1, *new2

To generalize to more dimensions, slice_indices will need to take care of shape, and you'll have to be more careful about joining multiple sets of indices (rather than arr[[i1, i2, i3]] , you'll need arr[[i1, i2, i3], [j1, j2, j3]] , which can't be concatenated directly).

In practice, if you need to do this a lot, you'd probably be better off using a simple function to encapsulate the loop you're trying to avoid.

def set_slices(arr, *indices_and_values):
    """Set multiple locations in an array to their corresponding values.

    indices_and_values should be a list of index-value pairs.
    """
    for idx, val in indices_and_values:
        arr[idx] = val

# Your example:
arr = np.zeros(100)
set_slices(arr, (np.s_[2:5], 42), (np.s_[12:29], 55))

(If your only goal is making it look like you are using multiple indices simultaneously, here are two functions that try to do everything for you, including broadcasting and handling multidimensional arrays.)


1 np.s_

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM