Присвоение значений прекращенным слайсам в ndarray - PullRequest
1 голос
/ 22 марта 2020

У меня есть базовый массив, который содержит данные. Некоторым индексам в этом массиве необходимо переназначить новое значение, и индексы, которые делают это, больше не поддерживаются. Я бы хотел избежать зацикливания всего этого и использования нотации среза, так как это, вероятно, будет быстрее.

Например:

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

Работает для одного среза. Но у меня есть еще один прекращенный фрагмент, чтобы применить к этому же массиву, скажем,

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

Я хотел бы достичь 1015 * что-то вроде:

arr[sl_obj_1, sl_obj_2] = 42, 55

Есть идеи?

РЕДАКТИРОВАТЬ: изменен пример с акцентом на то, что последовательности имеют или различную длину.

1 Ответ

0 голосов
/ 22 марта 2020

Не существует хорошего способа прямого извлечения нескольких фрагментов из массива NumPy, а тем более фрагментов разных размеров. Но вы можете обмануть, преобразовав свои срезы в индексы и используя массив индексов.

В случае одномерных массивов это относительно просто, используя индексные массивы .

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

Обратите внимание, что это не совсем исключает итерацию Python: операторы звезды по-прежнему создают итераторы, а индексный массив представляет собой обычный список python. В случае больших объемов данных это может быть заметно медленнее, чем при ручном подходе, поскольку он рассчитывает каждый индекс, который будет присвоен.

Вам также необходимо убедиться, что данные замены уже имеют правильную форму или вы можете использовать функции ручного вещания NumPy (например, np.broadcast_to) для исправления форм. Это приводит к дополнительным накладным расходам - ​​если вы полагались на автоматическое вещание c, вам, вероятно, лучше выполнять назначения в al oop.

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

Чтобы обобщить на большее количество измерений, slice_indices будет нужно позаботиться о форме, и вам нужно быть более осторожным при объединении нескольких наборов индексов (вместо arr[[i1, i2, i3]] вам понадобится arr[[i1, i2, i3], [j1, j2, j3]], который нельзя объединить напрямую).

На практике, если вам нужно много делать, вам, вероятно, лучше использовать простую функцию для инкапсуляции l oop, которого вы пытаетесь избежать.

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))

(Если Ваша единственная цель - сделать так, чтобы выглядело , как будто вы используете несколько индексов одновременно, здесь - это две функции, которые пытаются сделать все для вас, включая широковещательную передачу и обработку многомерных массивов.)


1 np.s_

...