как присвоить значения массиву numpy с учетом индексов строк и индексов начальных столбцов?

#python #numpy #numpy-ndarray #numpy-slicing

#python #numpy #numpy-ndarray #numpy-нарезка

Вопрос:

 a = np.array([2,3,1,4])
b = np.array([2,3,7,1])
c = np.zeros((4, 10))

  

Я хочу присвоить значение 1 некоторым элементам в c . a и b определите позиции таких элементов. a являются ли индексы начальных столбцов значениями 1 в каждой строке. И b представляет, сколько последовательных 1 есть в строке. Результат, который я ожидаю, это:

 array([[ 0.,  0.,  1.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  1.,  1.,  0.,  0.,  0.,  0.],
       [ 0.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.]])
  

Я могу использовать простой цикл for, как показано ниже:

 for i in range(c.shape[0]):
    for k in range(a[i], a[i] b[i]):
        c[i,k]=1
  

Но это было бы медленно для больших массивов, есть ли более быстрая индексация numpy для этого? Спасибо.

Комментарии:

1. c[i, a[i]:a[i] b[i]] = 1 должно работать вместо k цикла.

2. да, моя главная проблема — это самый внешний цикл for .

3. Вы не можете справиться с внешним циклом, не имея сначала дело с внутренним.

4. Какова типичная форма c в вашем реальном варианте использования?

Ответ №1:

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

Попробуйте онлайн!

 import numpy as np

def set_val_2d(a, val, starts, lens):
    begs = starts   np.arange(a.shape[0]) * a.shape[1]
    ends = begs   lens
    clens = lens.cumsum()
    ix = np.ones((clens[-1],), dtype = np.int64)
    ix[0] = begs[0]
    ix[clens[:-1]] = begs[1:] - ends[:-1]   1
    ix = ix.cumsum()
    a.ravel()[ix] = val

a = np.array([2,3,1,4])
b = np.array([2,3,7,1])
c = np.zeros((4, 10))

set_val_2d(c, 1, a, b)
print(c)
  

Вывод:

 [[0. 0. 1. 1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 1. 1. 1. 0. 0. 0. 0.]
 [0. 1. 1. 1. 1. 1. 1. 1. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]
  

Ответ №2:

Вы можете преобразовать его в одномерную задачу

 def convert_inds(a,b,array_shape):
    
    nrows,ncols = array_shape
    to_take = np.zeros(sum(b))
    count = 0
    for ind,item in enumerate(b):
        start_ind = ind*ncols a[ind]
        to_take[count:count item] = np.arange(start_ind,start_ind item)
        count  = item
        
    return to_take.astype(np.int)

to_take = convert_inds(a,b,c.shape)

c.ravel()[to_take] = 1
  

В приведенном выше коде convert_inds будет преобразовано a и b в

 array([ 2,  3, 13, 14, 15, 21, 22, 23, 24, 25, 26, 27, 34])
  

какие индексы 1 s в сглаженном c . Делая это, вам нужно только выполнить итерацию b в функции convert_inds .

Ответ №3:

Если вы выберете причудливый подход, основанный на индексации, самой сложной частью будет поиск индексов оси 1. Это очень похоже на:

 >>> np.repeat(a, b)
array([2, 2, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 4])
  

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

 def accumulative_count(counts, initial):
    counter = np.ones(np.sum(counts), dtype=int)
    marker_idx = np.r_[0, np.cumsum(counts)[:-1]]
    subtract_vals = np.r_[1, counts[:-1]]
    initial_vals = np.r_[initial[0], np.diff(initial)]
    counter[marker_idx] = counter[marker_idx] - subtract_vals   initial_vals
    return np.cumsum(counter)

>>> accumulative_count(counts, initial)
array([2, 3, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 4], dtype=int32)
  

В конце концов, вы способны его завершить:

 c[np.repeat(np.arange(len(c)), b), accumulative_count(b, a)] = 1
  

c:

 array([[0., 0., 1., 1., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 1., 1., 0., 0., 0., 0.],
       [0., 1., 1., 1., 1., 1., 1., 1., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])