Избегайте for-loop для разделения массива на несколько массивов по значениям индекса с помощью numpy

#python #arrays #numpy #for-loop

#питон #массивы #numpy #for-цикл

Вопрос:

Ввод: существует два входных массива:

 value_array = [56, 10, 65, 37, 29, 14, 97, 46]
index_array = [ 0,  0,  1,  0,  3,  0,  1,  1]
 

Вывод: я хочу разделить value_array использование index_array без использования for-loop. Таким образом, выходной массив будет:

 split_array = [[56, 10, 37, 14],  # index 0
               [65, 97, 46],      # index 1
               [],                # index 2
               [29]]              # index 3
 

Есть ли какой-либо способ сделать это, используя numpy без использования какого-либо for-цикла? Я посмотрел numpy.where , но не могу понять, как это сделать.

For-loop: Вот способ сделать это с помощью for-loop. Я хочу избежать цикла for.

 split_array = []
for i in range(max(index_array)   1):
    split_array.append([])
           
for i in range(len(value_array)):
    split_array[index_array[i]].append(value_array[i])
 

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

1. У вас есть рабочая версия с циклом ?

2. @ivan добавил код с for-loop.

3. Поскольку split_array это список с массивами / списками различной длины, вы не можете избежать циклов уровня Python (даже если он скрыт в np.split функции). no-loop Операции fabelled ограничены целым массивом (или фрагментами) и возвращают целые (числовые) массивы, а не списки списков.

Ответ №1:

Достаточно ли этого?

Решение 1 (Примечание: цикл for выполняется не по всему индексному массиву)

 import numpy as np

value_array = np.array([56, 10, 65, 37, 29, 14, 97, 46])
index_array = np.array([ 0,  0,  1,  0,  3,  0,  1,  1])

max_idx = np.max(index_array)
split_array = []

for idx in range(max_idx   1):
    split_array.append([])
    split_array[-1].extend(list(value_array[np.where(index_array == idx)]))
print(split_array)
 
 [[56, 10, 37, 14], [65, 97, 46], [], [29]]
 

Решение 2

 import numpy as np

value_array = np.array([56, 10, 65, 37, 29, 14, 97, 46])
index_array = np.array([ 0,  0,  1,  0,  3,  0,  1,  1])

value_array = value_array[index_array.argsort()]
split_idxs = np.squeeze(np.argwhere(np.diff(np.sort(index_array)) != 0)   1)
print(np.array_split(value_array, split_idxs))
 
 [array([56, 10, 37, 14]), array([65, 97, 46]), array([29])]
 

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

1. Обновил мой вопрос. Я просто хочу избежать for-loop, воспользовавшись numpy . Редактировать: я заметил, что в вашем коде есть for-loop с max_idx итерацией, вместо len(value_array) чего это хорошо.

2. Результатом второго решения является отсутствие пустого массива, не так ли?

Ответ №2:

Действительно, вы можете использовать numpy, используя массивы :

 import numpy as np
value_array=np.array(value_array)
index_array=np.array(index_array)
split_array=[value_array[np.where(index_array==j)[0]] for j in set(index_array)]
 

Ответ №3:

Вы могли бы сделать:

 import numpy as np

value_array = np.array([56, 10, 65, 37, 29, 14, 97, 46])
index_array = np.array([ 0,  0,  1,  0,  3,  0,  1,  1])

# find the unique values in index array and the corresponding counts
unique, counts = np.unique(index_array, return_counts=True)

# create an array with 0 for the missing indices
zeros = np.zeros(index_array.max()   1, dtype=np.int32)
zeros[unique] = counts  # zeros = [4 3 0 1] 0 -> 4, 1 -> 3, 2 -> 0, 3 -> 1

# group by index array
so = value_array[np.argsort(index_array)]  # so = [56 10 37 14 65 97 46 29]

# finally split using the counts 
res = np.split(so, zeros.cumsum()[:-1])

print(res)
 

Вывод

 [array([56, 10, 37, 14]), array([65, 97, 46]), array([], dtype=int64), array([29])]
 

Временная сложность этого подхода составляет O (N logN).

Кроме того, если вас не волнуют отсутствующие индексы, вы можете использовать следующее:

 _, counts = np.unique(index_array, return_counts=True)
res = np.split(value_array[np.argsort(index_array)], counts.cumsum()[:-1])

print(res)
 

Вывод

 [array([56, 10, 37, 14]), array([65, 97, 46]), array([29])]
 

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

1. np.split циклические циклы — один фрагмент для каждого возвращаемого массива в списке.

2. @hpaulj Полезно знать!