Знайдіть унікальні рядки в numpy.array


199

Мені потрібно знайти унікальні рядки в 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рішення. Я вважаю, що є спосіб встановити тип даних недійсним, і тоді я можу просто використовувати numpy.unique, але я не міг зрозуміти, як змусити його працювати.



Дякую, але я не можу використовувати панди.
Акавал


1
@Andy Hayden, незважаючи на назву, не є дублікатом цього питання. Хоча посилання codeape є дублікатом.
Вай Іп Тунг

5
Ця функція наближається до 1.13: github.com/numpy/numpy/pull/7742
Ерік

Відповіді:


115

Станом на NumPy 1.13, можна просто вибрати вісь для вибору унікальних значень у будь-якому N-затемненому масиві. Щоб отримати унікальні рядки, можна зробити:

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
@Greg von Winckel Чи можете ви запропонувати щось, що не те, що не змінює порядок.
Laschet Jain

Так, але не в одній команді: x = []; [x.append (tuple (r)) для r in a, якщо tuple (r) не в x]; a_unique = масив (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]])

EDIT Додано np.ascontiguousarrayза рекомендацією @ seberg. Це уповільнить метод, якщо масив ще не є суміжним.

EDIT Наведене можна трохи прискорити, можливо, ціною ясності, виконавши:

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

Крім того, принаймні в моїй системі, ефективність - це нарівні, а то й краще, ніж метод лексорта:

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]))):?
Акавал

3
@Akavall Це створює перегляд ваших даних з np.voidтипом даних розміром кількість байтів у повному рядку. Це схоже два, що ви отримуєте, якщо у вас є масив np.uint8s і розглядаєте його як np.uint16s, який об'єднує всі два стовпці в один, але більш гнучкий.
Хайме

3
@Jaime, чи можете ви додати те np.ascontiguousarrayабо інше, щоб бути в цілому безпечним (я знаю, це трохи більш обмежувально, ніж це необхідно, але ...). Рядки повинні бути суміжними, щоб огляд працював так, як очікувалося.
seberg

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])

Потім переформатуйте назад у двовимірний масив ( -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 Мені подобається, що це пітонічно, але це не є гарним рішенням, тому що рядок, який повернувся 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)))]]

Цей метод не використовує кортежі, і повинен бути набагато швидшим і простішим, ніж інші методи, наведені тут.

ПРИМІТКА. У попередній версії цієї версії не було вказівки відразу після [, що означає, що використовувались неправильні індекси. Також Джо Кінгтон добре підкреслює, що це робить різні проміжні копії. Наступний метод робить менше, роблячи відсортовану копію та використовуючи представлення на неї:

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)))))]

Цікавою проблемою було б, якби ви хотіли сортувати / унікально вздовж довільної осі масиву довільних розмірів, що було б складніше.

Редагувати:

Щоб продемонструвати різницю швидкостей, я провів кілька тестів на іпітон трьох різних методів, описаних у відповідях. У вашому точному значенні різниці не так вже й багато, хоча ця версія трохи швидша:

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" замість стандартного "будь-якого", який не бере аргумент ключового слова.
М. Тоя

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

9

Ось ще одна варіантна відповідь на пітонічну відповідь @Greg

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

9

Я порівняв запропоновану альтернативу за швидкістю і виявив, що, на диво, рішення недійсного перегляду 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графік продуктивності, оскільки вони такі схожі!
Акавал

Дякую за відповідь. Я вдосконалив сюжет, щоб включити лише один 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 зберігає точки в а 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]])

Із списком списків він піднімає a TypeError: unhashable type: 'list'


не працює на моєму. Кожен кортеж - це два рядки замість двох плаваючих чисел
mjp

не працює, він повертає список елементів не кортежів
Mohanad Kaleia

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](якщо вважати aC-суміжним), яке дорівнює 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

Для загальних цілей, таких як 3D або більш багатовимірні вкладені масиви, спробуйте:

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 numpy у масив mx 1 numpy string, будь ласка, спробуйте скористатись такою функцією, вона надає 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.