Найти уникальные строки в numpy.array


200

Мне нужно найти уникальные строки в numpy.array.

Например:

>>> a # I have
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
>>> new_a # I want to get to
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

Я знаю, что могу создать набор и цикл по массиву, но я ищу эффективный чистый numpy решение. Я считаю, что есть способ установить тип данных void, и тогда я мог бы просто использовать numpy.unique, но я не мог понять, как заставить это работать.


11
У панд есть метод dataframe.drop_duplicates (). См stackoverflow.com/questions/12322779/pandas-unique-dataframe и pandas.pydata.org/pandas-docs/dev/generated/...
codeape

Спасибо, но я не могу использовать панд.
Akavall


1
@ Энди Хейден, несмотря на заголовок, не является дубликатом этого вопроса. Ссылка Codeape является дубликатом, хотя.
Вай Ип Тунг

5
Эта функция изначально появилась в версии 1.13: github.com/numpy/numpy/pull/7742
Эрик,

Ответы:


115

Начиная с NumPy 1.13, можно просто выбрать ось для выбора уникальных значений в любом массиве N-dim. Чтобы получить уникальные строки, можно сделать:

unique_rows = np.unique(original_array, axis=0)


12
Осторожнее с этой функцией. np.unique(list_cor, axis=0)возвращает массив с удаленными дублирующимися строками ; он не фильтрует массив по элементам, которые являются уникальными в исходном массиве . Смотрите здесь , например ..
Брэд Соломон

Обратите внимание, что если вы хотите, чтобы уникальные строки игнорировали порядок значений в строке, вы можете сначала отсортировать исходный массив в столбцах:original_array.sort(axis=1)
mangecoeur

140

Еще одно возможное решение

np.vstack({tuple(row) for row in a})

20
+1 Это понятно, коротко и питонно. Если скорость не является реальной проблемой, решения такого типа должны иметь преимущество перед сложными, более высоко оцененными ответами на этот вопрос IMO.
Билл Читам

3
Превосходно! Фигурные скобки или функция set () делают свое дело.
Тиан Хе

2
@ Грег фон Винкель Можете ли вы предложить что-то, что не меняет то, что не меняет порядок.
Laschet Jain

Да, но не в одной команде: x = []; [x.append (tuple (r)) для r в if tuple (r) не в x]; a_unique = array (x);
Грег фон Винкель

1
Чтобы избежать FutureWarning, преобразуйте набор в список, например: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: массивы в стек должны передаваться как тип «последовательности», такой как список или кортеж. Поддержка непоследовательных итераций, таких как генераторы, устарела с NumPy 1.16 и в будущем приведет к ошибке.
Leermeester

111

Другой вариант использования структурированных массивов - это использование voidтипа, который объединяет всю строку в один элемент:

a = np.array([[1, 1, 1, 0, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [1, 1, 1, 0, 0, 0],
              [1, 1, 1, 1, 1, 0]])

b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
_, idx = np.unique(b, return_index=True)

unique_a = a[idx]

>>> unique_a
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

РЕДАКТИРОВАТЬ Добавлено np.ascontiguousarrayпосле рекомендации @ Seberg. Это замедлит метод, если массив еще не является смежным.

РЕДАКТИРОВАТЬ Выше можно немного ускорить, возможно за счет ясности, выполнив:

unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])

Кроме того, по крайней мере, в моей системе производительность выше или ниже, чем у метода lexsort:

a = np.random.randint(2, size=(10000, 6))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
100 loops, best of 3: 3.17 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
100 loops, best of 3: 5.93 ms per loop

a = np.random.randint(2, size=(10000, 100))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
10 loops, best of 3: 29.9 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
10 loops, best of 3: 116 ms per loop

3
Большое спасибо. Это ответ , который я искал, вы можете объяснить , что происходит в этом шаге: b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))?
Akavall

3
@Akavall Создает представление ваших данных с np.voidтипом данных размером в число байтов в полной строке. Это похоже на два, что вы получите, если у вас есть массив np.uint8s и вы видите его как np.uint16s, который объединяет каждые два столбца в один, но более гибкий.
Хайме

3
@Jaime, можете ли вы добавить np.ascontiguousarrayили аналогичный, чтобы быть в целом безопасным (я знаю, что это немного более строгим, чем необходимо, но ...). Строки должны быть смежными, чтобы представление работало должным образом.
Себерг

2
@ConstantineEvans Это недавнее добавление: в numpy 1.6 при попытке запуска np.uniqueмассива np.voidвозвращается ошибка, связанная с сортировкой слиянием, не реализованной для этого типа. Он работает нормально в 1,7, хотя.
Хайме

9
Стоит отметить, что, если этот метод используется для чисел с плавающей запятой, есть уловка, -0.которая не будет сравниваться как равная +0., тогда как сравнение по элементам будет иметь -0.==+0.(как указано в стандарте ieee float). См stackoverflow.com/questions/26782038/...
tom10

29

Если вы хотите избежать затрат памяти на преобразование в серию кортежей или другую подобную структуру данных, вы можете использовать структурированные массивы numpy.

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

В качестве быстрого примера:

import numpy as np

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])

ncols = data.shape[1]
dtype = data.dtype.descr * ncols
struct = data.view(dtype)

uniq = np.unique(struct)
uniq = uniq.view(data.dtype).reshape(-1, ncols)
print uniq

Чтобы понять, что происходит, взгляните на промежуточные результаты.

Как только мы рассматриваем вещи как структурированный массив, каждый элемент в массиве является строкой в ​​вашем исходном массиве. (По сути, это структура данных, аналогичная списку кортежей.)

In [71]: struct
Out[71]:
array([[(1, 1, 1, 0, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(1, 1, 1, 0, 0, 0)],
       [(1, 1, 1, 1, 1, 0)]],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

In [72]: struct[0]
Out[72]:
array([(1, 1, 1, 0, 0, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

После запуска numpy.uniqueмы вернем структурированный массив:

In [73]: np.unique(struct)
Out[73]:
array([(0, 1, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0), (1, 1, 1, 1, 1, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

Затем мы должны рассматривать его как «нормальный» массив (в котором _хранится результат последнего вычисления ipython, поэтому вы видите это _.view...):

In [74]: _.view(data.dtype)
Out[74]: array([0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0])

А затем преобразуйте обратно в 2D-массив ( -1это заполнитель, который говорит numpy вычислить правильное количество строк, дать количество столбцов):

In [75]: _.reshape(-1, ncols)
Out[75]:
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

Очевидно, что если вы хотите быть более кратким, вы можете написать это так:

import numpy as np

def unique_rows(data):
    uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
    return uniq.view(data.dtype).reshape(-1, data.shape[1])

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])
print unique_rows(data)

Что приводит к:

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

Это на самом деле кажется очень медленным, почти таким же медленным, как использование кортежей. Видимо, сортировка структурированного массива происходит медленно.
cge

3
@cge - попробуйте с массивами большего размера. Да, сортировка пустого массива выполняется медленнее, чем сортировка списка. Скорость не является основным фактором в большинстве случаев, когда вы используете ndarrays. Это использование памяти. Список кортежей будет использовать гораздо больше памяти, чем это решение. Даже если у вас достаточно памяти с достаточно большим массивом, преобразование его в список кортежей приводит к большим издержкам, чем преимущество в скорости.
Джо Кингтон

@cge - Ах, я не заметил, что вы используете lexsort. Я думал, что вы имели в виду использование списка кортежей. Да, lexsortвозможно, лучший вариант в этом случае. Я забыл об этом и вскочил на слишком сложное решение.
Джо Кингтон

20

np.uniqueкогда я запускаю его, np.random.random(100).reshape(10,10)возвращаются все уникальные отдельные элементы, но вы хотите уникальные строки, поэтому сначала вам нужно поместить их в кортежи:

array = #your numpy array of lists
new_array = [tuple(row) for row in array]
uniques = np.unique(new_array)

Это единственный способ, которым я вижу, как вы меняете типы, чтобы делать то, что вы хотите, и я не уверен, что итерация списка, чтобы изменить на кортежи, в порядке с вашим "не цикл"


5
+1 Это понятно, коротко и питонно. Если скорость не является реальной проблемой, решения такого типа должны иметь преимущество перед сложными, более высоко оцененными ответами на этот вопрос IMO.
Билл Читам

Я предпочитаю это перед принятым решением. Скорость не проблема для меня, потому что у меня есть только < 100строки на вызов. Это точно описывает, как выполняется выполнение уникальных над строками.
Rayryeng

4
Это на самом деле не работает для моих данных, uniquesсодержит уникальные элементы. Потенциально я неправильно понимаю ожидаемую форму array- не могли бы вы быть более точным здесь?
FooBar

@ ryan-saxe Мне нравится, что это pythonic, но это не очень хорошее решение, потому что возвращаемая строка uniquesотсортирована (и, следовательно, отличается от строк в array). B = np.array([[1,2],[2,1]]); A = np.unique([tuple(row) for row in B]); print(A) = array([[1, 2],[1, 2]])
jmlarson

16

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

ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]

Этот метод не использует кортежи и должен быть намного быстрее и проще, чем другие методы, приведенные здесь.

ПРИМЕЧАНИЕ: предыдущая версия этого не имела ind сразу после [, что означает, что были использованы неправильные индексы. Кроме того, Джо Кингтон отмечает, что это делает различные промежуточные копии. Следующий метод делает меньше, делая отсортированную копию и затем используя ее представления:

b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]

Это быстрее и использует меньше памяти.

Кроме того, если вы хотите найти уникальные строки в ndarray независимо от того, сколько измерений в массиве, будет работать следующее:

b = a[lexsort(a.reshape((a.shape[0],-1)).T)];
b[np.concatenate(([True], np.any(b[1:]!=b[:-1],axis=tuple(range(1,a.ndim)))))]

Интересная оставшаяся проблема будет, если вы захотите отсортировать / уникально вдоль произвольной оси массива произвольной размерности, что будет более трудным.

Редактировать:

Чтобы продемонстрировать разницу в скорости, я провел несколько тестов в ipython из трех разных методов, описанных в ответах. С вашим точным знаком a нет большой разницы, хотя эта версия немного быстрее:

In [87]: %timeit unique(a.view(dtype)).view('<i8')
10000 loops, best of 3: 48.4 us per loop

In [88]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True], np.any(a[ind[1:]]!= a[ind[:-1]], axis=1)))]
10000 loops, best of 3: 37.6 us per loop

In [89]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10000 loops, best of 3: 41.6 us per loop

Однако при увеличении a эта версия оказывается намного, намного быстрее:

In [96]: a = np.random.randint(0,2,size=(10000,6))

In [97]: %timeit unique(a.view(dtype)).view('<i8')
10 loops, best of 3: 24.4 ms per loop

In [98]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10 loops, best of 3: 28.2 ms per loop

In [99]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!= a[ind[:-1]],axis=1)))]
100 loops, best of 3: 3.25 ms per loop

Очень хорошо! С другой стороны, он делает несколько промежуточных копий. (например, a[ind[1:]]копия и т. д.) С другой стороны, ваше решение, как правило, в 2-3 раза быстрее, чем мое, до тех пор, пока у вас не закончится оперативная память.
Джо Кингтон

Хорошая точка зрения. Как оказалось, моя попытка извлечь промежуточные копии, используя только индексы, заставила мой метод использовать больше памяти и в конечном итоге медленнее, чем просто создание отсортированной копии массива, поскольку a_sorted [1:] не является копией a_sorted ,
cge

Что dtypeу тебя на времени? Я думаю, что вы ошиблись. В моей системе вызов, np.uniqueкак описано в моем ответе, немного быстрее, чем при использовании любого из двух вариантов np.lexsort. И это примерно в 5 раз быстрее, если массив для поиска уникальных объектов имеет форму (10000, 100). Даже если вы решите переопределить, что np.uniqueнужно для сокращения некоторого (незначительного) времени выполнения, свертывание каждой строки в один объект выполняет более быстрое сравнение, чем необходимость вызывать np.anyсравнение столбцов, особенно для большего числа столбцов.
Хайме

@cge: вы, вероятно, имели в виду «np.any» вместо стандартного «any», которое не принимает аргумент ключевого слова.
М. Тоя

@Jaime - я верю, что dtypeэто просто a.dtype, то есть тип данных просматриваемых данных, как это сделал Джо Кингтон в своем ответе. Если столбцов много, другой (несовершенный!) Способ сохранить скорость - использовать lexsortсортировку только по нескольким столбцам. Это зависит от данных, так как необходимо знать, какие столбцы обеспечивают достаточную дисперсию для идеальной сортировки. Например a.shape = (60000, 500)- сортировка на первых 3 -х колонок: ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0])). Экономия времени довольно существенная, но опять же отказ от ответственности: он может не охватить все случаи - это зависит от данных.
n1k31t4

9

Вот еще один вариант @Greg pythonic ответа

np.vstack(set(map(tuple, a)))

9

Я сравнил предложенную альтернативу для скорости и обнаружил, что, к удивлению, решение void view uniqueдаже немного быстрее, чем родной numpy uniqueс axisаргументом. Если вы ищете скорость, вы захотите

numpy.unique(
    a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
    ).view(a.dtype).reshape(-1, a.shape[1])

введите описание изображения здесь


Код для воспроизведения сюжета:

import numpy
import perfplot


def unique_void_view(a):
    return numpy.unique(
        a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
        ).view(a.dtype).reshape(-1, a.shape[1])


def lexsort(a):
    ind = numpy.lexsort(a.T)
    return a[ind[
        numpy.concatenate((
            [True], numpy.any(a[ind[1:]] != a[ind[:-1]], axis=1)
            ))
        ]]


def vstack(a):
    return numpy.vstack({tuple(row) for row in a})


def unique_axis(a):
    return numpy.unique(a, axis=0)


perfplot.show(
    setup=lambda n: numpy.random.randint(2, size=(n, 20)),
    kernels=[unique_void_view, lexsort, vstack, unique_axis],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    xlabel='len(a)',
    equality_check=None
    )

1
Очень хороший ответ, один незначительный момент: vstack_dictникогда не использует диктовку, фигурные скобки - это комплексное понимание, и поэтому его поведение почти идентично vstatck_set. Поскольку vstack_dictграфик производительности отсутствует для графика, похоже, он просто покрывается vstack_setграфиком производительности, поскольку они очень похожи!
Akavall

Спасибо за ответ. Я улучшил сюжет, чтобы включить только один vstackвариант.
Нико Шлёмер

8

Мне не понравился ни один из этих ответов, потому что ни один из них не обрабатывает массивы с плавающей точкой в ​​линейной алгебре или в смысле векторного пространства, где две строки, «равные», означают «в некотором 𝜀». В одном ответе, который имеет пороговое значение допуска, https://stackoverflow.com/a/26867764/500207 , был выбран порог как поэлементной, так и десятичной точности, который работает в некоторых случаях, но не так математически обобщен, как истинное векторное расстояние.

Вот моя версия:

from scipy.spatial.distance import squareform, pdist

def uniqueRows(arr, thresh=0.0, metric='euclidean'):
    "Returns subset of rows that are unique, in terms of Euclidean distance"
    distances = squareform(pdist(arr, metric=metric))
    idxset = {tuple(np.nonzero(v)[0]) for v in distances <= thresh}
    return arr[[x[0] for x in idxset]]

# With this, unique columns are super-easy:
def uniqueColumns(arr, *args, **kwargs):
    return uniqueRows(arr.T, *args, **kwargs)

Вышеуказанная функция общественного достояния используется scipy.spatial.distance.pdistдля нахождения евклидова (настраиваемого) расстояния между каждой парой строк. Затем он сравнивает каждое расстояние со threshстарым, чтобы найти строки, которые находятся внутри threshдруг друга, и возвращает только одну строку из каждого thresh-кластера.

Как указывалось, расстояние metricне обязательно должно быть евклидовым - pdistможет вычислять различные расстояния, включая cityblock(норма Манхэттена) и cosine(угол между векторами).

Если thresh=0(по умолчанию), то строки должны быть точными, чтобы считаться «уникальными». Другие хорошие значения для threshиспользования масштабируются с машинной точностью, т thresh=np.spacing(1)*1e3. Е.


Лучший ответ. Спасибо. Это самый (математически) обобщенный ответ, написанный до сих пор. Он рассматривает матрицу как набор точек данных или выборок в N-мерном пространстве и находит набор одинаковых или похожих точек (сходство определяется либо евклидовым расстоянием, либо любыми другими методами). Эти точки могут быть перекрывающимися точками данных или очень близкими окрестностями. В конце набор одинаковых или похожих точек заменяется любой точкой (в приведенном выше ответе первой точкой), принадлежащей одному и тому же набору. Это помогает уменьшить избыточность из облака точек.
Санчит

@Sanchit, ага, это хорошая точка, вместо того, чтобы выбирать «первую» точку (на самом деле она может быть фактически случайной, так как это зависит от того, как Python хранит точки в a set) в качестве представителя каждой threshокрестности определенного размера, функция может позволить пользователь может указать, как выбрать эту точку, например, использовать «медиану» или точку, ближайшую к центроиду, и т. д.
Ахмед Фасих,

Конечно. Без сомнений. Я только что упомянул первый пункт, так как это то, что делает ваша программа, что вполне нормально.
Санчит

Просто исправление - я неправильно сказал выше, что строка, которая будет выбрана для каждого threshкластера, будет случайной из-за неупорядоченной природы set. Конечно , это brainfart на моей части, setхранит наборы индексов , которые находятся в thresh-окрестности, так что это findRows делает на самом деле возвращения, для каждого - threshкластера, первая строка в нем.
Ахмед Фасих

3

Почему бы не использовать drop_duplicatesот панд:

>>> timeit pd.DataFrame(image.reshape(-1,3)).drop_duplicates().values
1 loops, best of 3: 3.08 s per loop

>>> timeit np.vstack({tuple(r) for r in image.reshape(-1,3)})
1 loops, best of 3: 51 s per loop

Я действительно люблю этот ответ. Конечно, он не использует numpy напрямую, но для меня это тот, который легче понять, будучи быстрым.
noctilux

3

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

import numpy_indexed as npi
new_a = npi.unique(a)  # unique elements over axis=0 (rows) by default

1

np.unique работает с учетом списка кортежей:

>>> np.unique([(1, 1), (2, 2), (3, 3), (4, 4), (2, 2)])
Out[9]: 
array([[1, 1],
       [2, 2],
       [3, 3],
       [4, 4]])

Со списком списков это поднимает TypeError: unhashable type: 'list'


кажется, не работает на моем. Каждый кортеж состоит из двух строк вместо двух чисел с плавающей точкой
mjp

не работает, возвращает список элементов, а не кортежи
Моханад Калейа

1

Основываясь на ответе на этой странице, я написал функцию, которая копирует возможности функции MATLAB unique(input,'rows'), с дополнительной функцией для принятия допуска для проверки уникальности. Он также возвращает такие индексы, чтоc = data[ia,:] и data = c[ic,:]. Пожалуйста, сообщите, если вы видите какие-либо расхождения или ошибки.

def unique_rows(data, prec=5):
    import numpy as np
    d_r = np.fix(data * 10 ** prec) / 10 ** prec + 0.0
    b = np.ascontiguousarray(d_r).view(np.dtype((np.void, d_r.dtype.itemsize * d_r.shape[1])))
    _, ia = np.unique(b, return_index=True)
    _, ic = np.unique(b, return_inverse=True)
    return np.unique(b).view(d_r.dtype).reshape(-1, d_r.shape[1]), ia, ic

1

Помимо превосходного ответа @Jaime, другой способ свернуть строку - использовать a.strides[0](при условии, что он aявляется C-смежным), что равно a.dtype.itemsize*a.shape[0]. Кроме того void(n), это ярлык для dtype((void,n)). мы наконец приходим к этой самой короткой версии:

a[unique(a.view(void(a.strides[0])),1)[1]]

Для

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

0

Для общих целей, таких как трехмерные или более крупные многомерные вложенные массивы, попробуйте следующее:

import numpy as np

def unique_nested_arrays(ar):
    origin_shape = ar.shape
    origin_dtype = ar.dtype
    ar = ar.reshape(origin_shape[0], np.prod(origin_shape[1:]))
    ar = np.ascontiguousarray(ar)
    unique_ar = np.unique(ar.view([('', origin_dtype)]*np.prod(origin_shape[1:])))
    return unique_ar.view(origin_dtype).reshape((unique_ar.shape[0], ) + origin_shape[1:])

который удовлетворяет вашему 2D-набору данных:

a = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
unique_nested_arrays(a)

дает:

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

Но также и 3D-массивы, такие как:

b = np.array([[[1, 1, 1], [0, 1, 1]],
              [[0, 1, 1], [1, 1, 1]],
              [[1, 1, 1], [0, 1, 1]],
              [[1, 1, 1], [1, 1, 1]]])
unique_nested_arrays(b)

дает:

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

Использование unique return_indexфункции Jaime должно упростить эту последнюю returnстроку. Просто индексируйте оригинал arна правой оси.
hpaulj

0

Ни один из этих ответов не работал для меня. Я предполагаю, что мои уникальные строки содержали строки, а не числа. Однако этот ответ из другого потока работал:

Источник: https://stackoverflow.com/a/38461043/5402386

Вы можете использовать методы списка .count () и .index ()

coor = np.array([[10, 10], [12, 9], [10, 5], [12, 9]])
coor_tuple = [tuple(x) for x in coor]
unique_coor = sorted(set(coor_tuple), key=lambda x: coor_tuple.index(x))
unique_count = [coor_tuple.count(x) for x in unique_coor]
unique_index = [coor_tuple.index(x) for x in unique_coor]

0

На самом деле мы можем превратить числовой массив mxn в числовой массив mx 1, попробуйте использовать следующую функцию, она предоставляет count , inverse_idx и т. Д., Как и numpy.unique:

import numpy as np

def uniqueRow(a):
    #This function turn m x n numpy array into m x 1 numpy array storing 
    #string, and so the np.unique can be used

    #Input: an m x n numpy array (a)
    #Output unique m' x n numpy array (unique), inverse_indx, and counts 

    s = np.chararray((a.shape[0],1))
    s[:] = '-'

    b = (a).astype(np.str)

    s2 = np.expand_dims(b[:,0],axis=1) + s + np.expand_dims(b[:,1],axis=1)

    n = a.shape[1] - 2    

    for i in range(0,n):
         s2 = s2 + s + np.expand_dims(b[:,i+2],axis=1)

    s3, idx, inv_, c = np.unique(s2,return_index = True,  return_inverse = True, return_counts = True)

    return a[idx], inv_, c

Пример:

A = np.array([[ 3.17   9.502  3.291],
  [ 9.984  2.773  6.852],
  [ 1.172  8.885  4.258],
  [ 9.73   7.518  3.227],
  [ 8.113  9.563  9.117],
  [ 9.984  2.773  6.852],
  [ 9.73   7.518  3.227]])

B, inv_, c = uniqueRow(A)

Results:

B:
[[ 1.172  8.885  4.258]
[ 3.17   9.502  3.291]
[ 8.113  9.563  9.117]
[ 9.73   7.518  3.227]
[ 9.984  2.773  6.852]]

inv_:
[3 4 1 0 2 4 0]

c:
[2 1 1 1 2]

-1

Давайте возьмем всю матрицу в виде списка, затем удалим дубликаты из этого списка и, наконец, вернем наш уникальный список обратно в матрицу:

matrix_as_list=data.tolist() 
matrix_as_list:
[[1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0], [1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 0]]

uniq_list=list()
uniq_list.append(matrix_as_list[0])

[uniq_list.append(item) for item in matrix_as_list if item not in uniq_list]

unique_matrix=np.array(uniq_list)
unique_matrix:
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

-3

Самое простое решение - сделать строки одним элементом, сделав их строками. Затем каждый ряд можно сравнить как единое целое по уникальности с помощью numpy. Это решение обобщенно, вам просто нужно изменить форму и транспонировать массив для других комбинаций. Вот решение для предоставленной проблемы.

import numpy as np

original = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

uniques, index = np.unique([str(i) for i in original], return_index=True)
cleaned = original[index]
print(cleaned)    

Дам:

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

Отправить мой Нобелевский приз по почте


Очень неэффективно и подвержено ошибкам, например, с различными вариантами печати. Другие варианты явно предпочтительнее.
Майкл

-3
import numpy as np
original = np.array([[1, 1, 1, 0, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [1, 1, 1, 0, 0, 0],
                     [1, 1, 1, 1, 1, 0]])
# create a view that the subarray as tuple and return unique indeies.
_, unique_index = np.unique(original.view(original.dtype.descr * original.shape[1]),
                            return_index=True)
# get unique set
print(original[unique_index])
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.