Удаление дубликатов из списка массивов numPy


У меня есть обычный список Python, который содержит (многомерные) массивы numPy, все одинаковой формы и с одинаковым количеством значений. Некоторые массивы в списке являются дубликатами более ранних.

У меня есть проблема, что я хочу удалить все дубликаты, но тот факт, что тип данных-массивы numPy, немного усложняет это...

• я не могу использовать set() , так как массивы numPy не хешируются.
• Я не могу проверить наличие дубликатов во время вставки, так как массивы являются генерируется пакетами функцией и добавляется в список с помощью .расширять().
* массивы numPy напрямую не сопоставимы, не прибегая к одной из собственных функций numPy, поэтому я не могу просто взять что-то, что использует "if x in list"...
* Содержимое списка должно оставаться массивами numPy в конце процесса; я мог бы сравнить копии массивов, преобразованных во вложенные списки, но я не могу преобразовать массивы в прямые списки python постоянно.

Любые предложения о том, как я могу удалить дублирует эффективно здесь?

3 2

3 ответа:

Используя решения здесь: наиболее эффективное свойство хэширования для массива numpy мы видим, что хэширование лучше всего работает с A.tostring (), если a-массив numpy. Итак:

import numpy as np
arraylist = [np.array([1,2,3,4]), np.array([1,2,3,4]), np.array([1,3,2,4])]
L = {array.tostring(): array for array in arraylist}
L.values() # [array([1, 3, 2, 4]), array([1, 2, 3, 4])]

В зависимости от структуры ваших данных, возможно, будет быстрее напрямую сравнить все массивы, а не искать какой-то способ хэширования массивов. Алгоритм O (n^2), но каждое отдельное сравнение будет намного быстрее, чем создание строк или списков python ваших массивов. Так что это зависит от того, сколько массивов вы должны проверить.

Напр.

uniques = []
for arr in possible_duplicates:
    if not any(numpy.array_equal(arr, unique_arr) for unique_arr in uniques):
        uniques.append(arr)

Вот один из способов использования tuple:

>>> import numpy as np
>>> t = [np.asarray([1, 2, 3, 4]), 
         np.asarray([1, 2, 3, 4]), 
         np.asarray([1, 1, 3, 4])]

>>> map(np.asarray, set(map(tuple, t)))
[array([1, 1, 3, 4]), array([1, 2, 3, 4])]

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

def to_tuple(arr):
    return tuple(arr.reshape((arr.size,)))

def from_tuple(tup, original_shape):
    np.asarray(tup).reshape(original_shape)

Пример:

In [64]: t = np.asarray([[[1,2,3],[4,5,6]],
                         [[1,1,3],[4,5,6]],
                         [[1,2,3],[4,5,6]]])

In [65]: map(lambda x: from_tuple(x, t[0].shape), set(map(to_tuple, t)))
Out[65]: 
[array([[1, 2, 3],
        [4, 5, 6]]), 
 array([[1, 1, 3],
        [4, 5, 6]])]

Другой вариант-создать pandas.DataFrame из ваших ndarrays (обрабатывая их как строки путем изменения формы, если это необходимо) и использовать встроенные модули pandas для унификации строк.

In [34]: t
Out[34]: [array([1, 2, 3, 4]), array([1, 2, 3, 4]), array([1, 1, 3, 4])]

In [35]: pandas.DataFrame(t).drop_duplicates().values
Out[35]: 
array([[1, 2, 3, 4],
       [1, 1, 3, 4]])

В целом, я думаю, что это плохая идея, чтобы попытаться использовать tostring() в качестве квази-хэша функция, потому что вам понадобится больше кода котельной плиты, чем в моем подходе, просто чтобы защитить от возможности того, что некоторые из содержимого мутируют после того, как им был назначен их "хэш" ключ в некотором dict.

Если изменение формы и преобразование в tuple происходит слишком медленно, учитывая размер данных, я чувствую, что это выявляет более фундаментальную проблему: приложение не разработано хорошо вокруг потребностей (например, de-duping) и пытается втиснуть их в какой-то процесс Python, запущенный в память, вероятно, не тот путь. На этом этапе я бы остановился, чтобы рассмотреть, не является ли что-то вроде Cassandra, которая может легко строить индексы базы данных поверх больших столбцов (или многомерных массивов) данных с плавающей запятой (или других) более разумным подходом.