Получить список всех индексов повторяющихся элементов в массиве numpy

Я пытаюсь получить индекс всех повторяющихся элементов в массиве numpy, но решение, которое я нашел на данный момент, ДЕЙСТВИТЕЛЬНО неэффективно для большого (> 20000 элементов) входного массива (требуется больше или меньше 9 секунд). Идея проста:

  1. records_array – это массив numpy (timedamp), из которого мы хотим извлечь индексы повторяющихся временных меток

  2. time_array – это массив numpy, содержащий все временные метки, которые повторяются в records_array

  3. records – это QuerySet django (который можно легко преобразовать в список), содержащий некоторые объекты Record. Мы хотим создать список пар, образованных всеми возможными комбинациями атрибутов tagId записи, соответствующих повторяющимся меткам времени, найденным из records_array .

Вот рабочий (но неэффективный) код, который у меня есть на данный момент:

 tag_couples = []; for t in time_array: users_inter = np.nonzero(records_array == t)[0] # Get all repeated timestamps in records_array for time t l = [str(records[i].tagId) for i in users_inter] # Create a temporary list containing all tagIds recorded at time t if l.count(l[0]) != len(l): #remove tuples formed by the first tag repeated tag_couples +=[x for x in itertools.combinations(list(set(l)),2)] # Remove duplicates with list(set(l)) and append all possible couple combinations to tag_couples 

Я уверен, что это можно оптимизировать с помощью Numpy, но я не могу найти способ сравнения records_array с каждым элементом time_array без использования цикла for (это нельзя сравнить, просто используя == , поскольку они оба массива).

4 Solutions collect form web for “Получить список всех индексов повторяющихся элементов в массиве numpy”

Решение, основанное, как обычно, на numpy на магии unique() , без циклов и списков:

 records_array = array([1, 2, 3, 1, 1, 3, 4, 3, 2]) idx_sort = argsort(records_array) sorted_records_array = records_array[idx_sort] vals, idx_start, count = unique(sorted_records_array, return_counts=True, return_index=True) # sets of indices res = split(idx_sort, idx_start[1:]) #filter them with respect to their size, keeping only items occurring more than once vals = vals[count > 1] res = filter(lambda x: x.size > 1, res) 

EDIT: следующий мой предыдущий ответ, который потребовал немного больше памяти, используя numpy вещание и вызов unique дважды:

 records_array = array([1, 2, 3, 1, 1, 3, 4, 3, 2]) vals, inverse, count = unique(records_array, return_inverse=True, return_counts=True) idx_vals_repeated = where(count > 1)[0] vals_repeated = vals[idx_vals_repeated] rows, cols = where(inverse == idx_vals_repeated[:, newaxis]) _, inverse_rows = unique(rows, return_index=True) res = split(cols, inverse_rows[1:]) 

с ожидаемым значением res = [array([0, 3, 4]), array([1, 8]), array([2, 5, 7])]

Вы также можете сделать это:

 a = [1,2,3,1,1,3,4,3,2] index_sets = [np.argwhere(i==a) for i in np.unique(a)] 

это даст вам набор массивов с индексами уникальных элементов.

 [array([[0],[3],[4]], dtype=int64), array([[1],[8]], dtype=int64), array([[2],[5],[7]], dtype=int64), array([[6]], dtype=int64)] 

Добавлено: Дальнейшее изменение в понимании списка также может отбрасывать отдельные уникальные значения и учитывать проблему скорости в случае множества уникальных уникальных элементов:

 new_index_sets = [np.argwhere(i[0]== a) for i in np.array(np.unique(a, return_counts=True)).T if i[1]>=2] 

это дает:

 [array([[0],[3],[4]], dtype=int64), array([[1],[8]], dtype=int64), array([[2],[5],[7]], dtype=int64)] 

поэтому я не смог избавиться от цикла for, но мне удалось list.count() его с минимальным использованием цикла for, используя set тип данных и метод list.count() :

 data = [1,2,3,1,4,5,2,2] indivs = set(data) multi_index = lambda lst, val: [i for i, x in enumerate(lst) if x == val] if data != list(indivs): dupes = [multi_index(data, i) for i in indivs if data.count(i) > 1] 

Если вы зацикливаете свой набор indivs, который содержит значения (без дубликатов), а затем перебирайте полный список, если вы найдете элемент с дубликатом. Я ищу альтернативу numpy, если это недостаточно быстро для вас. Объекты генератора также могут ускорить это, если потребуется.

Изменить: ответ gg349 содержит решение numpy, над которым я работал!

Вы могли бы сделать что-то вроде:

 1. add original index ref so [[1,0],[2,1],[3,2],[1,3],[1,4]... 2. sort on [:,0] 3. use np.where(ra[1:,0] != ra[:-1,0]) 4. use the list of indexes from above to construct your final list of lists 

РЕДАКТИРОВАТЬ – ОК, поэтому после моего быстрого ответа я уехал на некоторое время, и я вижу, что меня отклонили, что справедливо, поскольку numpy.argsort() намного лучше, чем мое предложение. Я проголосовал за numpy.unique() поскольку это интересная функция. Однако, если вы используете timeit, вы обнаружите, что

 idx_start = np.where(sorted_records_array[:-1] != sorted_records_array[1:])[0] + 1 res = np.split(idx_sort, idx_start) 

незначительно быстрее, чем

 vals, idx_start = np.unique(sorted_records_array, return_index=True) res = np.split(idx_sort, idx_start[1:]) 

Далее отредактируйте следующий вопрос @Nicolas

Я не уверен, что ты сможешь. Можно было бы получить два массива индексов в соответствии с точками разрыва, но вы не можете разбить разные «строки» массива на части разного размера, используя np.split, поэтому

 a = np.array([[4,27,42,12, 4 .. 240, 12], [3,65,23...] etc]) idx = np.argsort(a, axis=1) sorted_a = np.diagonal(a[:, idx[:]]).T idx_start = np.where(sorted_a[:,:-1] != sorted_a[:,1:]) # idx_start => (array([0,0,0,..1,1,..]), array([1,4,6,7..99,0,4,5])) 

но это может быть достаточно хорошим в зависимости от того, что вы хотите делать с информацией.

  • Решить систему линейных целочисленных уравнений в Python
  • Создание массива в numpy / scipy путем итерации в Python?
  • побитовые операции между элементами в списке
  • Использование numpy.newaxis
  • Как читать изображение из массива numpy в изображение PIL?
  • понимание функции dstack
  • Изменение размера массивов numpy.memmap
  • numpy 3D-изображение в 2D
  • Python - лучший язык программирования в мире.