Znajdź unikalne wiersze w numpy.array

199

Muszę znaleźć unikalne wiersze w numpy.array .

Na przykład:

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

Wiem, że mogę utworzyć zestaw i zapętlić tablicę, ale szukam wydajnego czystego numpyrozwiązania. Uważam, że istnieje sposób, aby ustawić typ danych jako nieważny, a następnie mógłbym po prostu użyć numpy.unique, ale nie mogłem wymyślić, jak to zrobić.

Akavall
źródło
Dziękuję, ale nie mogę używać pand.
Akavall,
1
@Andy Hayden, pomimo tytułu, nie jest duplikatem tego pytania. Link do codeape jest jednak duplikatem.
Wai Yip Tung
5
Ta funkcja będzie dostępna natywnie w wersji 1.13: github.com/numpy/numpy/pull/7742
Eric,

Odpowiedzi:

115

Od NumPy 1.13 można po prostu wybrać oś do wyboru unikalnych wartości w dowolnej tablicy N-dim. Aby uzyskać unikalne wiersze, możesz:

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

aiwabdn
źródło
12
Ostrożnie z tą funkcją. np.unique(list_cor, axis=0)dostaje tablicę ze zduplikowanymi wierszami ; nie filtruje tablicy do elementów unikalnych w oryginalnej tablicy . Zobacz tutaj , na przykład ..
Brad Solomon
Zauważ, że jeśli chcesz, aby unikalne wiersze ignorowały kolejność wartości w wierszu, możesz najpierw posortować oryginalną tablicę w kolumnach:original_array.sort(axis=1)
mangecoeur
140

Jeszcze inne możliwe rozwiązanie

np.vstack({tuple(row) for row in a})
Greg von Winckel
źródło
20
+1 To jest jasne, krótkie i pytoniczne. O ile szybkość nie jest prawdziwym problemem, tego rodzaju rozwiązania powinny mieć pierwszeństwo przed złożonymi, wyżej głosowanymi odpowiedziami na to pytanie IMO.
Bill Cheatham
3
Doskonały! Nawiasy klamrowe lub funkcja set () rozwiązują problem.
Tian He
2
@Greg von Winckel Czy możesz zasugerować coś, co nie jest czymś, co nie zmienia kolejności.
Laschet Jain
Tak, ale nie w jednym poleceniu: x = []; [x.append (krotka (r)) dla rw krotce if (r) nie w x]; a_unique = array (x);
Greg von Winckel
1
Aby uniknąć FutureWarning, przekonwertuj zestaw na listę taką jak: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: tablice na stos muszą być przekazywane jako typ „sekwencji”, taki jak lista lub krotka. Wsparcie dla iteracji niesekwencyjnych, takich jak generatory, jest przestarzałe od NumPy 1.16 i spowoduje błąd w przyszłości.
leermeester,
111

Inną opcją użycia tablic strukturalnych jest użycie widoku voidtypu, który łączy cały wiersz w jeden element:

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

EDYCJA Dodano np.ascontiguousarrayzgodnie z zaleceniem @ seberg. Spowolni to metodę, jeśli tablica nie jest już ciągła.

EDYCJA Powyższe można nieco przyspieszyć, być może kosztem przejrzystości, wykonując:

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

Ponadto, przynajmniej w moim systemie, pod względem wydajności jest na równi, a nawet lepiej, niż metoda 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
Jaime
źródło
3
Wielkie dzięki. Oto odpowiedź, której szukałem. Czy możesz wyjaśnić, co się dzieje na tym etapie b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1]))):?
Akavall,
3
@Akavall Tworzy widok twoich danych o np.voidtypie danych wielkości liczby bajtów w pełnym rzędzie. To dwa podobne, co dostajesz, jeśli masz tablicę np.uint8s i widzisz ją jako np.uint16s, która łączy co dwie kolumny w jedną, ale bardziej elastyczną.
Jaime
3
@Jaime, czy możesz dodać coś np.ascontiguousarraypodobnego, aby być ogólnie bezpiecznym (wiem, że jest to nieco bardziej restrykcyjne niż konieczne, ale ...). Wiersze muszą być ciągłe, aby widok działał zgodnie z oczekiwaniami.
seberg
2
@ ConstantineEvans Jest to najnowszy dodatek: w numpy 1.6 próba uruchomienia np.uniquena tablicy np.voidzwraca błąd związany z brakiem implementacji scalania dla tego typu. Jednak działa dobrze w 1.7.
Jaime
9
Warto zauważyć, że jeśli ta metoda jest używana do liczb zmiennoprzecinkowych, istnieje haczyk, -0.który nie będzie porównywany jako równy +0., podczas gdy porównanie elementu po elemencie miałoby -0.==+0.(zgodnie z normą ieee float). Zobacz stackoverflow.com/questions/26782038/…
tom10
29

Jeśli chcesz uniknąć kosztów pamięci związanych z konwersją do serii krotek lub innej podobnej struktury danych, możesz wykorzystać tablice strukturalne numpy.

Sztuką jest wyświetlenie oryginalnej tablicy jako tablicy strukturalnej, w której każdy element odpowiada rzędowi oryginalnej tablicy. To nie tworzy kopii i jest dość wydajne.

Jako szybki przykład:

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

Aby zrozumieć, co się dzieje, spójrz na wyniki pośrednie.

Gdy zobaczymy rzeczy jako tablicę strukturalną, każdy element w tablicy jest wierszem w oryginalnej tablicy. (Zasadniczo jest to struktura danych podobna do listy krotek).

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

Po uruchomieniu numpy.uniqueotrzymamy tablicę strukturalną:

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

To, co musimy następnie zobaczyć jako „normalną” tablicę ( _przechowuje wynik ostatniego obliczenia ipython, dlatego właśnie widzisz _.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])

A następnie przekształć z powrotem w tablicę 2D ( -1jest symbolem zastępczym, który każe numpy obliczyć prawidłową liczbę wierszy, podać liczbę kolumn):

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

Oczywiście, jeśli chcesz być bardziej zwięzły, możesz napisać to jako:

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)

Co skutkuje w:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
Joe Kington
źródło
To wydaje się bardzo wolne, prawie tak wolne jak używanie krotek. Najwyraźniej sortowanie takiej tablicy strukturalnej jest powolne.
cge
3
@cge - Wypróbuj z większymi tablicami. Tak, sortowanie tablicy numpy jest wolniejsze niż sortowanie listy. Szybkość nie jest głównym czynnikiem w większości przypadków, gdy używasz ndarrays. To użycie pamięci. Lista krotek zajmie znacznie więcej pamięci niż to rozwiązanie. Nawet jeśli masz wystarczającą ilość pamięci, przy dość dużej tablicy, przekształcenie jej w listę krotek ma większe obciążenie niż przewaga szybkości.
Joe Kington,
@cge - Ach, nie zauważyłem, że używasz lexsort. Myślałem, że masz na myśli listę krotek. Tak, lexsortjest prawdopodobnie lepszą opcją w tym przypadku. Zapomniałem o tym i przeskoczyłem do zbyt złożonego rozwiązania.
Joe Kington,
20

np.uniquekiedy go uruchomię, np.random.random(100).reshape(10,10)zwraca wszystkie unikalne pojedyncze elementy, ale chcesz unikalnych wierszy, więc najpierw musisz je umieścić w krotki:

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

To jedyny sposób, w jaki widzę, jak zmieniasz typy, aby robić to, co chcesz, i nie jestem pewien, czy iteracja listy do zamiany na krotki jest w porządku z twoim „nie powtarzaniem”

Ryan Saxe
źródło
5
+1 To jest jasne, krótkie i pytoniczne. O ile szybkość nie jest prawdziwym problemem, tego rodzaju rozwiązania powinny mieć pierwszeństwo przed złożonymi, wyżej głosowanymi odpowiedziami na to pytanie IMO.
Bill Cheatham
Wolę to niż zaakceptowane rozwiązanie. Szybkość nie jest dla mnie problemem, ponieważ mogę mieć tylko < 100wiersze na wywołanie. To dokładnie opisuje, w jaki sposób przeprowadzane jest wykonywanie unikatowych wierszy.
rayryeng
4
To faktycznie nie działa na moje dane, uniqueszawiera unikalne elementy. Potencjalnie nie rozumiem oczekiwanego kształtu array- czy mógłbyś być tutaj bardziej precyzyjny?
FooBar,
@ ryan-saxe Podoba mi się, że jest to pytoniczne, ale nie jest to dobre rozwiązanie, ponieważ zwracany wiersz uniquesjest sortowany (a zatem różni się od wierszy w 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 działa poprzez sortowanie spłaszczonej tablicy, a następnie sprawdzenie, czy każdy element jest równy poprzedniemu. Można to zrobić ręcznie bez spłaszczania:

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

Ta metoda nie używa krotek i powinna być znacznie szybsza i prostsza niż inne metody tu podane.

UWAGA: poprzednia wersja tego nie miała ind bezpośrednio po [, co oznacza, że ​​użyto niewłaściwych indeksów. Poza tym Joe Kington ma rację, że robi to wiele pośrednich kopii. Poniższa metoda zmniejsza, tworząc posortowaną kopię, a następnie używając jej widoków:

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

Jest to szybsze i zużywa mniej pamięci.

Ponadto, jeśli chcesz znaleźć unikalne wiersze w tablicy ndarray niezależnie od liczby wymiarów w tablicy, będą działać następujące czynności:

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

Ciekawym pozostałym problemem byłoby, gdybyś chciał posortować / unikalny wzdłuż dowolnej osi tablicy o dowolnym wymiarze, co byłoby trudniejsze.

Edytować:

Aby zademonstrować różnice prędkości, przeprowadziłem kilka testów w ipython trzech różnych metod opisanych w odpowiedziach. Z twoim dokładnym a nie ma zbyt dużej różnicy, chociaż ta wersja jest nieco szybsza:

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

Z większym a ta wersja kończy się jednak znacznie, znacznie szybciej:

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
cge
źródło
Bardzo dobrze! Z drugiej strony, robi kilka kopii pośrednich. (np. a[ind[1:]]jest kopią itp.) Z drugiej strony twoje rozwiązanie jest zazwyczaj 2-3 razy szybsze niż moje, dopóki nie zabraknie pamięci RAM.
Joe Kington,
Słuszna uwaga. Jak się okazuje, moja próba pobrania kopii pośrednich przy użyciu samych indeksów sprawiła, że ​​moja metoda zużywa więcej pamięci i kończy się wolniej niż po prostu posortowana kopia tablicy, ponieważ a_sorted [1:] nie jest kopią a_sortowanej .
cge
Co dtypemasz na myśli? Myślę, że źle to zrozumiałeś. W moim systemie wywołanie np.uniquezgodnie z opisem w mojej odpowiedzi jest nieco szybsze niż użycie jednego z dwóch smaków np.lexsort. I jest około 5 razy szybsza, jeśli tablica do wyszukiwania unikalnych ma kształt (10000, 100). Nawet jeśli zdecydujesz się na ponowne zaimplementowanie tego, co skraca np.uniqueniektóre (niewielkie) czasy wykonania, zwinięcie każdego wiersza w pojedynczy obiekt powoduje szybsze porównania niż konieczność wywoływania np.anyporównania kolumn, szczególnie w przypadku większej liczby kolumn.
Jaime
@ cge: prawdopodobnie miałeś na myśli „np.any” zamiast standardowego „any”, który nie przyjmuje argumentu słowa kluczowego.
M. Toya,
@Jaime - uważam, że dtypejest to tylko a.dtypetyp danych oglądanych danych, tak jak to zrobił Joe Kington w swojej odpowiedzi. Jeśli jest wiele kolumn, innym (niedoskonałym!) Sposobem na szybkie utrzymanie lexsortjest sortowanie tylko na kilku kolumnach. Jest to specyficzne dla danych, ponieważ trzeba wiedzieć, które kolumny zapewniają wystarczającą wariancję, aby idealnie sortować. Eg a.shape = (60000, 500)- sort na pierwszych 3 kolumny: ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0])). Oszczędności czasu są dość znaczne, ale ponownie zastrzeżenie: może nie obejmować wszystkich przypadków - zależy to od danych.
n1k31t4
9

Oto kolejna odmiana pythonicznej odpowiedzi @Greg

np.vstack(set(map(tuple, a)))
divenex
źródło
9

Porównałem sugerowaną alternatywę dla szybkości i odkryłem, że, co zaskakujące, uniquerozwiązanie widoku pustki jest nawet nieco szybsze niż natywny numpy uniquez axisargumentem. Jeśli szukasz prędkości, będziesz chciał

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

wprowadź opis zdjęcia tutaj


Kod do odtworzenia fabuły:

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
    )
Nico Schlömer
źródło
1
Bardzo ładna odpowiedź, jedna drobna uwaga: vstack_dictnigdy nie używa dykta, nawiasy klamrowe są ustalonym rozumieniem, a zatem jego zachowanie jest prawie identyczne vstatck_set. Ponieważ vstack_dictbrakuje linii wydajności dla wykresu fro, wygląda na to, że jest ona objęta przez vstack_setwykres wydajności, ponieważ są one bardzo podobne!
Akavall,
Dziękuję za odpowiedź. Udoskonaliłem fabułę, aby zawierała tylko jeden vstackwariant.
Nico Schlömer,
8

Nie podobało mi się żadna z tych odpowiedzi, ponieważ żadna nie obsługuje tablic zmiennoprzecinkowych w algebrze liniowej lub w przestrzeni wektorowej, w której dwa wiersze będące „równe” oznaczają „w pewnym 𝜀”. Jedna odpowiedź, która ma próg tolerancji, https://stackoverflow.com/a/26867764/500207 , przyjęła próg zarówno pod względem elementarnym, jak i precyzji dziesiętnej , co działa w niektórych przypadkach, ale nie jest tak matematycznie ogólne jak prawdziwa odległość wektorowa.

Oto moja wersja:

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)

Powyższa funkcja domeny publicznej służy scipy.spatial.distance.pdistdo znajdowania odległości euklidesowej (konfigurowalnej) między każdą parą wierszy. Następnie porównuje każdą odległość ze threshstarą, aby znaleźć wiersze, które są threshmiędzy sobą, i zwraca tylko jeden wiersz z każdej grupy thresh.

Jak wskazano, odległość metricnie musi być euklidesowa - pdistmoże obliczyć różne odległości, w tym cityblock(normę Manhattanu) icosine (kąt między wektorami).

Jeśli thresh=0(domyślnie), wiersze muszą być bitowe, aby można je było uznać za „unikalne”. Inne dobre wartości do threshzastosowania skalowane precyzja maszyny, tj thresh=np.spacing(1)*1e3.

Ahmed Fasih
źródło
Najlepsza odpowiedź. Dzięki. Jest to najbardziej (matematycznie) uogólniona odpowiedź napisana do tej pory. Traktuje macierz jako zbiór punktów danych lub próbek w przestrzeni N-wymiarowej i znajduje zbiór takich samych lub podobnych punktów (podobieństwo jest definiowane albo przez odległość euklidesową, albo dowolną inną metodą). Punkty te mogą nakładać się na punkty danych lub bardzo bliskie sąsiedztwa. Na koniec zbiór takich samych lub podobnych punktów jest zastępowany przez dowolny punkt (w powyższej odpowiedzi pierwszym punktem) należący do tego samego zestawu. Pomaga to zmniejszyć nadmiarowość z chmury punktów.
Sanchit
@ Sanchit aha, to dobra uwaga, zamiast wybierać „pierwszy” punkt (w rzeczywistości może być efektywnie losowy, ponieważ zależy to od tego, w jaki sposób Python przechowuje punkty w a set) jako reprezentatywne dla każdej threshwielkości sąsiedztwa, funkcja może pozwolić użytkownik, aby określić, jak wybrać ten punkt, np. użyć „mediany” lub punktu najbliższego środka ciężkości itp.
Ahmed Fasih
Pewnie. Bez wątpienia. Właśnie wspomniałem o pierwszej kwestii, ponieważ to właśnie robi twój program, co jest całkowicie w porządku.
Sanchit
Tylko korekta - błędnie powiedziałem powyżej, że wiersz, który zostanie wybrany dla każdej grupy thresh, będzie losowy z powodu nieuporządkowanej natury set. Oczywiście, że to brainfart na mojej części, setprzechowuje krotki indeksów, które są w thresh-neighborhood, więc to findRows robi w rzeczywistości zamian za każdym thresh-cluster, pierwszy wiersz w nim.
Ahmed Fasih
3

Dlaczego nie skorzystać drop_duplicatesz pand:

>>> 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
kalu
źródło
Naprawdę uwielbiam tę odpowiedź. Jasne, nie używa bezpośrednio numpy, ale dla mnie jest to ten, który jest najłatwiejszy do zrozumienia, będąc szybki.
noctilux
3

Numpy_indexed pakiet (disclaimer: Jestem jego autorem) owija rozwiązanie zamieszczonych przez Jaime w miłej i sprawdzony interfejs, a także wiele innych funkcji:

import numpy_indexed as npi
new_a = npi.unique(a)  # unique elements over axis=0 (rows) by default
Eelco Hoogendoorn
źródło
1

np.unique działa, mając listę krotek:

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

Z listą list podnosi TypeError: unhashable type: 'list'

Codeape
źródło
wydaje się nie działać na mój. Każda krotka ma dwa ciągi zamiast dwóch liczb zmiennoprzecinkowych
mjp
nie działa, zwraca listę elementów, które nie są krotkami
Mohanad Kaleia,
1

W oparciu o odpowiedź na tej stronie napisałem funkcję, która replikuje zdolność funkcji MATLAB unique(input,'rows'), z dodatkową funkcją akceptowania tolerancji dla sprawdzania unikalności. Zwraca również takie wskaźniki, że c = data[ia,:]i data = c[ic,:]. Zgłoś, jeśli zauważysz jakiekolwiek rozbieżności lub błędy.

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
Arash_D_B
źródło
1

Oprócz doskonałej odpowiedzi @Jaime, innym sposobem zwinięcia wiersza jest użycie a.strides[0](zakładając, że ajest to ciągły C), który jest równy a.dtype.itemsize*a.shape[0]. Ponadto void(n)jest skrót do dtype((void,n)). docieramy w końcu do tej najkrótszej wersji:

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

Dla

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
BM
źródło
0

W przypadku ogólnych celów, takich jak wielowymiarowe tablice zagnieżdżone 3D lub wyższe, wypróbuj to:

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

który odpowiada Twojemu zestawowi danych 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)

daje:

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

Ale także tablice 3D, takie jak:

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)

daje:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])
Tara
źródło
Używanie tak unique return_indexjak Jaime powinno uprościć tę ostatnią returnlinię. Po prostu zindeksuj orginal arna prawej osi.
hpaulj
0

Żadna z tych odpowiedzi nie działała dla mnie. Zakładam, że moje unikalne wiersze zawierały ciągi, a nie liczby. Jednak ta odpowiedź z innego wątku działała:

Źródło: https://stackoverflow.com/a/38461043/5402386

Możesz użyć metod z listy .count () i .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]
mjp
źródło
0

Możemy faktycznie zamienić tablicę liczbową mxn na tablicę ciągów liczbowych mx 1, spróbuj użyć następującej funkcji, która zapewnia count , inverse_idx i etc, podobnie jak 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

Przykład:

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]
Ting On Chan
źródło
-1

Pobierzmy całą macierz liczbową jako listę, a następnie upuść duplikaty z tej listy, a na koniec zwróć naszą unikalną listę z powrotem do macierzy liczbowej:

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]])
Mahdi Ghelichi
źródło
-3

Najprostszym rozwiązaniem jest uczynienie wierszy pojedynczym elementem poprzez utworzenie ciągów. Każdy wiersz może być następnie porównany jako całość za pomocą numpy. To rozwiązanie jest możliwe do uogólnienia, wystarczy przekształcić i przetransponować tablicę dla innych kombinacji. Oto rozwiązanie podanego problemu.

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)    

Da:

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

Wyślij moją nagrodę Nobla pocztą

Dave Pena
źródło
Bardzo nieefektywny i podatny na błędy, np. Z różnymi opcjami drukowania. Inne opcje są wyraźnie preferowane.
Michael
-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])
YoungLearnsToCoding
źródło