Как назначить разное количество элементов разным строкам в 3D-массиве numpy?

#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чтобы увидеть промежуточные объекты.

Подробные сведения:

  1. ind является кортежем индексных массивов (для каждого измерения). Первый массив для первого измерения и так далее. Это «составной индекс» элементов, который нужно установить.
  2. tVal содержит значения, которые должны быть установлены в последовательных элементах данных.
  3. 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, :]