#python #arrays #numpy #indexing
#python #массивы #numpy #индексирование
Вопрос:
Я пытаюсь использовать массив для индексации другого массива. Цель состоит в том, чтобы поместить значение (val) в первую строку (2-я ось) разное количество раз (num). Я не очень хорошо это объясняю, но результат примера показывает, что я ищу. Я думаю, что я очень близок.
Я получаю эту ошибку:
Ошибка типа: только целочисленные скалярные массивы могут быть преобразованы в скалярный индекс
import numpy as np
# dataset
data = np.zeros((3, 4, 5))
val = np.array([6, 7, 8])
num = np.array([2, 4, 3])
# Do something like this, using an index, not a loop
data[:, 0][:, 0:num] = val
# This is the result I am hoping to get
[[[6. 6. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
[[7. 7. 7. 7. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
[[8. 8. 8. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]]
Комментарии:
1. В
0:num
,num
должно быть скаляром, единственным значением. Вы даете ему массив из 3 элементов. Таким образом, вы не можете создать несколько фрагментов.
Ответ №1:
Используйте следующий код:
ind = (np.repeat(range(3), num),
np.zeros(num.sum(), dtype='int'),
np.hstack([np.arange(i) for i in num]))
tVal = np.repeat(val, num)
data[ind] = tVal
Выведите ind и tvalчтобы увидеть промежуточные объекты.
Подробные сведения:
ind
является кортежем индексных массивов (для каждого измерения). Первый массив для первого измерения и так далее. Это «составной индекс» элементов, который нужно установить.tVal
содержит значения, которые должны быть установлены в последовательных элементах данных.data[ind] = tVal
выполняет фактическую подстановку (каждое значение из tVal в указанный элемент данных).
Прочитайте о каждом используемом методе Numpy, чтобы ознакомиться с другими деталями и предысторией.
Комментарии:
1. Я пытаюсь векторизовать это, чтобы заставить его работать быстрее. Это был самый быстрый цикл, который я нашел: для i в диапазоне (len(num)): пример[:, 0][i, 0:num[i]] = val[i] печать (пример)
Ответ №2:
Предполагая, что data
на самом деле это все нули, вы можете сделать это очень просто, np.cumsum
используя технику, которую я обычно использую для маскировки. Ключ в том, чтобы установить начало и конец выполнения с помощью простого индексирования, а затем суммировать между ними.
data[:, 0, 0] = val # Start the sum
data[np.arange(data.shape[0]), 0, num] = -val
data.cumsum(axis=-1, out=data)
Если num == data.shape[-1]
когда-либо True
, вам нужно будет сначала отфильтровать индекс, чтобы выполнить сумму до конца:
mask = (num < data.shape[-1])
data[np.arange(data.shape[0])[mask], 0, num[mask]] = -val
Если data
не все нули, вы можете так же легко создать маску и использовать ее в сочетании с. np.repeat
Вы делаете маску, используя ту же технику np.cumsum
и тот факт, что np.uint8
и np.bool_
у того же размера элемента:
mask = np.zeros_like(data, dtype=np.uint8)
mask[:, 0, 0] = 1 # Start the sum
m = (num < data.shape[-1])
mask[np.arange(data.shape[0])[m], 0, num[m]] = -1
mask.cumsum(axis=-1, out=mask)
mask = mask.view(bool)
data[mask] = np.repeat(val, num)
Если вас интересует только первая строка каждой плоскости, вы, вероятно, могли бы работать с представлением, которое включает только эти строки, чтобы упростить индексацию:
x = data[:, 0, :]