Удаление повторяющихся столбцов и строк из двумерного массива NumPy

Я использую массив 2D-форм для хранения пар долготы + широты. В какой-то момент мне нужно объединить два из этих 2D-массивов, а затем удалить все повторяющиеся записи. Я искал функцию, похожую на numpy.unique, но мне не повезло. Любая реализация, о которой я думал, выглядит очень «неоптимизированной». Например, я пытаюсь преобразовать массив в список кортежей, удалить дубликаты с помощью набора, а затем снова преобразовать в массив:

coordskeys = np.array(list(set([tuple(x) for x in coordskeys])))

Есть ли какие-то существующие решения, чтобы не изобретать велосипед?

Чтобы было понятно, я ищу:

>>> a = np.array([[1, 1], [2, 3], [1, 1], [5, 4], [2, 3]])
>>> unique_rows(a)
array([[1, 1], [2, 3],[5, 4]])

Кстати, я хотел использовать для этого только список кортежей, но списки были настолько большими, что они потребляли мои 4 ГБ ОЗУ + 4 ГБ подкачки (массивы numpy более эффективны с точки зрения памяти).


person Sergi    schedule 19.12.2011    source источник


Ответы (6)


Вот одна идея, это займет немного работы, но может быть довольно быстро. Я дам вам случай 1d и дам вам понять, как расширить его до 2d. Следующая функция находит уникальные элементы массива 1d:

import numpy as np
def unique(a):
    a = np.sort(a)
    b = np.diff(a)
    b = np.r_[1, b]
    return a[b != 0]

Теперь, чтобы расширить его до 2D, вам нужно изменить две вещи. Вам нужно будет выяснить, как сделать сортировку самостоятельно, важная вещь в сортировке будет заключаться в том, что две идентичные записи окажутся рядом друг с другом. Во-вторых, вам нужно будет сделать что-то вроде (b != 0).all(axis), потому что вы хотите сравнить всю строку/столбец. Дайте мне знать, достаточно ли этого для начала.

обновлено: с некоторой помощью Дуга, я думаю, это должно работать для случая 2d.

import numpy as np
def unique(a):
    order = np.lexsort(a.T)
    a = a[order]
    diff = np.diff(a, axis=0)
    ui = np.ones(len(a), 'bool')
    ui[1:] = (diff != 0).any(axis=1) 
    return a[ui]
person Bi Rico    schedule 19.12.2011
comment
+1 только что опубликовал мой ответ, затем прочитайте ваш - похоже, что мой является точной 2D-реализацией вашей - та же последовательность идентичных функций (сначала у меня даже был шаг конкатенации строк, но я удалил его и отрезал первую строку) вместо этого исходный массив. - person doug; 20.12.2011
comment
этот ответ в основном использует numpy, поэтому python2/3 не имеет значения. Если это не работает для вас, возможно, что-то еще происходит. - person Bi Rico; 05.03.2016
comment
Работал для меня в Python3. Обратите внимание, что это не сохраняет порядок. - person Ghostkeeper; 24.05.2016
comment
Обратите внимание, что решение lexsort ограничено количеством поддерживаемых столбцов. - person Eelco Hoogendoorn; 07.09.2016

Это должно помочь:

def unique_rows(a):
    a = np.ascontiguousarray(a)
    unique_a = np.unique(a.view([('', a.dtype)]*a.shape[1]))
    return unique_a.view(a.dtype).reshape((unique_a.shape[0], a.shape[1]))

Пример:

>>> a = np.array([[1, 1], [2, 3], [1, 1], [5, 4], [2, 3]])
>>> unique_rows(a)
array([[1, 1],
       [2, 3],
       [5, 4]])
person user545424    schedule 19.12.2011
comment
@ user100464, отредактировано для работы с транспонированными массивами. - person user545424; 05.09.2013

Мой метод заключается в превращении массива 2d в комплексный массив 1d, где реальная часть — это 1-й столбец, а мнимая часть — это 2-й столбец. Затем используйте np.unique. Хотя это будет работать только с 2 столбцами.

import numpy as np 
def unique2d(a):
    x, y = a.T
    b = x + y*1.0j 
    idx = np.unique(b,return_index=True)[1]
    return a[idx] 

Пример -

a = np.array([[1, 1], [2, 3], [1, 1], [5, 4], [2, 3]])
unique2d(a)
array([[1, 1],
       [2, 3],
       [5, 4]])
person kidnakyo    schedule 28.11.2013

Пакет numpy_indexed (отказ от ответственности: я являюсь его автором) заключает решение, опубликованное пользователем 545424, в приятный и проверенный интерфейс. , а также множество связанных функций:

import numpy_indexed as npi
npi.unique(coordskeys)
person Eelco Hoogendoorn    schedule 02.04.2016

поскольку вы ссылаетесь на numpy.unique, вам не нужно поддерживать первоначальный порядок, верно? преобразование в набор, который удаляет дубликаты, а затем обратно в список часто используется идиома:

>>> x = [(1, 1), (2, 3), (1, 1), (5, 4), (2, 3)]
>>> y = list(set(x))
>>> y
[(5, 4), (2, 3), (1, 1)]
>>> 
person yosukesabai    schedule 19.12.2011
comment
Да, порядок не важен. Решение объединения списка + набора - это то, что я использую в качестве примера на ОП (которое, я признаю, довольно запутано). Проблема с ним в том, что он использует списки, и поэтому используемая память огромна, и возникает та же проблема, как если бы я с самого начала работал только со списками, а не с массивами. - person Sergi; 19.12.2011

person    schedule
comment
Дуг, я думаю, вы близки, но у вас возникнут проблемы, потому что NP.sort(A, axis=0) сортирует каждый столбец независимо. Попробуйте запустить свой метод на двух следующих массивах: [[0, 0], [1, 1], [2,2]] и [[0, 1], [1, 0], [2,2]]. Я добавил функцию сортировки в свой ответ, которая сохраняет строки нетронутыми при сортировке. - person Bi Rico; 20.12.2011
comment
Я не знал о lexsort, я включу его в свой ответ, если все в порядке. - person Bi Rico; 20.12.2011
comment
@Bago: абсолютно - вы все равно первыми решили суть проблемы, поэтому я проголосовал за ваш ответ и оставил комментарий, чтобы люди знали, что мой ответ - это просто модифицированная версия вашего, опубликованная несколько часов спустя. . - person doug; 20.12.2011