Suchen Sie eindeutige Zeilen in numpy.array

199

Ich muss eindeutige Zeilen in a finden numpy.array.

Beispielsweise:

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

Ich weiß, dass ich ein Set und eine Schleife über das Array erstellen kann, aber ich suche nach einer effizienten reinen numpyLösung. Ich glaube, dass es eine Möglichkeit gibt, den Datentyp auf void zu setzen, und dann könnte ich ihn einfach verwenden numpy.unique, aber ich konnte nicht herausfinden, wie er funktioniert.

Akavall
quelle
11
pandas hat eine dataframe.drop_duplicates () -Methode. Siehe stackoverflow.com/questions/12322779/pandas-unique-dataframe und pandas.pydata.org/pandas-docs/dev/generated/…
codeape
Danke, aber ich kann keine Pandas benutzen.
Akavall
2
Mögliches Duplikat von Entfernen von Duplikaten in jeder Zeile eines numpy-Arrays
Andy Hayden
1
@Andy Hayden, trotz des Titels ist es kein Duplikat zu dieser Frage. Der Link von codeape ist jedoch ein Duplikat.
Wai Yip Tung
5
Diese Funktion kommt nativ zu 1.13: github.com/numpy/numpy/pull/7742
Eric

Antworten:

114

Ab NumPy 1.13 kann einfach die Achse für die Auswahl eindeutiger Werte in einem beliebigen N-Dim-Array ausgewählt werden. Um eindeutige Zeilen zu erhalten, kann man Folgendes tun:

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

aiwabdn
quelle
12
Vorsicht bei dieser Funktion. np.unique(list_cor, axis=0)Sie erhalten das Array mit entfernten doppelten Zeilen . Das Array wird nicht nach Elementen gefiltert, die im ursprünglichen Array eindeutig sind . Siehe hier zum Beispiel ..
Brad Solomon
Beachten Sie, dass Sie, wenn Sie möchten, dass eindeutige Zeilen die Reihenfolge der Werte in der Zeile ignorieren, das ursprüngliche Array zuerst direkt in den Spalten sortieren können:original_array.sort(axis=1)
mangecoeur
139

Noch eine mögliche Lösung

np.vstack({tuple(row) for row in a})
Greg von Winckel
quelle
20
+1 Das ist klar, kurz und pythonisch. Sofern Geschwindigkeit kein echtes Problem ist, sollten diese Arten von Lösungen den komplexen, höher bewerteten Antworten auf diese Frage IMO den Vorzug geben.
Bill Cheatham
3
Ausgezeichnet! Geschweifte Klammern oder die Funktion set () machen den Trick.
Tian He
2
@ Greg von Winckel Kannst du etwas vorschlagen, das nichts tut, was die Reihenfolge nicht ändert?
Laschet Jain
Ja, aber nicht in einem einzigen Befehl: x = []; [x.append (Tupel (r)) für r in einem if-Tupel (r) nicht in x]; a_unique = Array (x);
Greg von Winckel
1
Um eine FutureWarning zu vermeiden, konvertieren Sie die Menge in eine Liste wie: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: Arrays zum Stapeln müssen als "Sequenz" -Typ wie Liste oder Tupel übergeben werden. Die Unterstützung für nicht sequenzierte Iterables wie Generatoren ist ab NumPy 1.16 veraltet und wird in Zukunft einen Fehler auslösen.
Leermeester
111

Eine weitere Option für die Verwendung strukturierter Arrays ist die Verwendung einer Ansicht eines voidTyps, der die gesamte Zeile zu einem einzigen Element zusammenfügt:

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 hinzugefügt np.ascontiguousarrayfolgende @ seberg Empfehlung. Dies verlangsamt die Methode, wenn das Array noch nicht zusammenhängend ist.

BEARBEITEN Das oben Genannte kann leicht beschleunigt werden, möglicherweise auf Kosten der Klarheit, indem Sie Folgendes tun:

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

Zumindest auf meinem System ist es in Bezug auf die Leistung gleich oder sogar besser als die Lexsort-Methode:

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
quelle
3
Vielen Dank. Dies ist die Antwort, nach der ich gesucht habe. Können Sie erklären, was in diesem Schritt vor sich geht b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))?
Akavall
3
@Akavall Es wird eine Ansicht Ihrer Daten mit einem np.voidDatentyp erstellt, der der Anzahl der Bytes in einer vollständigen Zeile entspricht. Es sind zwei ähnliche, die Sie erhalten, wenn Sie ein Array von np.uint8s haben und es als np.uint16s anzeigen , das alle zwei Spalten zu einer einzigen kombiniert, aber flexibler ist.
Jaime
3
@Jaime, kannst du ein np.ascontiguousarrayoder ähnliches hinzufügen , um allgemein sicher zu sein (ich weiß, es ist etwas restriktiver als nötig, aber ...). Die Zeilen müssen zusammenhängend sein, damit die Ansicht wie erwartet funktioniert.
Seberg
2
@ConstantineEvans Es handelt sich um eine neue Ergänzung: In Numpy 1.6 wird beim Versuch, np.uniqueauf einem Array von ausgeführt zu werden, np.voidein Fehler zurückgegeben, der darauf zurückzuführen ist, dass Mergesort für diesen Typ nicht implementiert wurde. In 1.7 funktioniert es jedoch einwandfrei.
Jaime
9
Es ist erwähnenswert, dass es bei Verwendung dieser Methode für Gleitkommazahlen einen Haken gibt, -0.der nicht gleich ist +0., wohingegen ein Element-für-Element-Vergleich dies hätte -0.==+0.(wie im ieee-Gleitkomma-Standard angegeben). Siehe stackoverflow.com/questions/26782038/…
tom10
29

Wenn Sie die Speicherkosten für die Konvertierung in eine Reihe von Tupeln oder eine andere ähnliche Datenstruktur vermeiden möchten, können Sie die strukturierten Arrays von numpy nutzen.

Der Trick besteht darin, Ihr ursprüngliches Array als strukturiertes Array anzuzeigen, wobei jedes Element einer Zeile des ursprünglichen Arrays entspricht. Dies macht keine Kopie und ist sehr effizient.

Als schnelles Beispiel:

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

Schauen Sie sich die Zwischenergebnisse an, um zu verstehen, was los ist.

Sobald wir die Dinge als strukturiertes Array betrachten, ist jedes Element im Array eine Zeile in Ihrem ursprünglichen Array. (Grundsätzlich ist es eine ähnliche Datenstruktur wie eine Liste von Tupeln.)

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

Sobald wir ausgeführt werden numpy.unique, erhalten wir ein strukturiertes Array zurück:

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

Das müssen wir dann als "normales" Array anzeigen ( _speichert das Ergebnis der letzten Berechnung in ipython, weshalb Sie sehen _.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])

Und dann wieder in ein 2D-Array umformen ( -1ist ein Platzhalter, der numpy anweist, die richtige Anzahl von Zeilen zu berechnen und die Anzahl der Spalten anzugeben):

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

Wenn Sie präziser sein möchten, können Sie dies natürlich wie folgt schreiben:

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)

Was in ... endet:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
Joe Kington
quelle
Dies scheint tatsächlich sehr langsam zu sein, fast so langsam wie die Verwendung von Tupeln. Das Sortieren eines solchen strukturierten Arrays ist anscheinend langsam.
Cge
3
@cge - Probieren Sie es mit größeren Arrays aus. Ja, das Sortieren eines Numpy-Arrays ist langsamer als das Sortieren einer Liste. Geschwindigkeit ist jedoch in den meisten Fällen, in denen Sie ndarrays verwenden, nicht das Hauptkriterium. Es ist die Speichernutzung. Eine Liste von Tupeln benötigt erheblich mehr Speicher als diese Lösung. Selbst wenn Sie mit einem relativ großen Array über genügend Speicher verfügen, ist das Konvertieren in eine Liste von Tupeln mit einem höheren Overhead verbunden als der Geschwindigkeitsvorteil.
Joe Kington
@cge - Ah, ich habe nicht bemerkt, dass du benutzt lexsort. Ich dachte, Sie beziehen sich auf die Verwendung einer Liste von Tupeln. Ja, lexsortist in diesem Fall wahrscheinlich die bessere Option. Ich hatte es vergessen und war zu einer übermäßig komplexen Lösung gesprungen.
Joe Kington
20

np.uniqueWenn ich es ausführe, werden np.random.random(100).reshape(10,10)alle eindeutigen Einzelelemente zurückgegeben, aber Sie möchten die eindeutigen Zeilen, also müssen Sie sie zuerst in Tupel setzen:

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

Nur so sehe ich, dass Sie die Typen ändern, um das zu tun, was Sie wollen, und ich bin mir nicht sicher, ob die Listeniteration, die in Tupel geändert werden soll, in Ordnung ist, wenn Sie nicht durchlaufen.

Ryan Saxe
quelle
5
+1 Das ist klar, kurz und pythonisch. Sofern Geschwindigkeit kein echtes Problem ist, sollten diese Arten von Lösungen den komplexen, höher bewerteten Antworten auf diese Frage IMO den Vorzug geben.
Bill Cheatham
Ich ziehe dies der akzeptierten Lösung vor. Geschwindigkeit ist für mich kein Problem, da ich möglicherweise nur < 100Zeilen pro Aufruf habe. Dies beschreibt genau, wie das Ausführen von eindeutigen Zeilen ausgeführt wird.
Rayryeng
4
Dies funktioniert eigentlich nicht für meine Daten, uniquesenthält eindeutige Elemente. Möglicherweise missverstehe ich die erwartete Form von array- könnten Sie hier genauer sein?
FooBar
@ ryan-saxe Ich mag, dass dies pythonisch ist, aber dies ist keine gute Lösung, da die zurückgegebenen Zeilen uniquessortiert sind (und sich daher von den Zeilen in unterscheiden 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 sortiert ein abgeflachtes Array und prüft dann, ob jedes Element dem vorherigen entspricht. Dies kann manuell ohne Abflachung erfolgen:

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

Diese Methode verwendet keine Tupel und sollte viel schneller und einfacher sein als andere hier angegebene Methoden.

HINWEIS: Eine frühere Version davon hatte nicht das Ind direkt nach einem [, was bedeutet, dass die falschen Indizes verwendet wurden. Außerdem macht Joe Kington einen guten Punkt , dass dies tut eine Vielzahl von Zwischenkopien machen. Die folgende Methode macht weniger, indem sie eine sortierte Kopie erstellt und dann Ansichten davon verwendet:

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

Dies ist schneller und benötigt weniger Speicher.

Wenn Sie eindeutige Zeilen in einem ndarray suchen möchten, unabhängig davon, wie viele Dimensionen sich im Array befinden, funktioniert Folgendes:

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

Ein interessantes verbleibendes Problem wäre, wenn Sie entlang einer beliebigen Achse eines Arrays mit beliebigen Dimensionen sortieren / eindeutig sein möchten, was schwieriger wäre.

Bearbeiten:

Um die Geschwindigkeitsunterschiede zu demonstrieren, habe ich einige Tests in ipython der drei verschiedenen Methoden durchgeführt, die in den Antworten beschrieben sind. Mit Ihrem genauen a gibt es keinen allzu großen Unterschied, obwohl diese Version etwas schneller ist:

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

Mit einem größeren a ist diese Version jedoch viel, viel schneller:

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
quelle
Sehr schön! Nebenbei bemerkt, es werden jedoch mehrere Zwischenkopien erstellt. (z. B. a[ind[1:]]eine Kopie usw.) Andererseits ist Ihre Lösung im Allgemeinen 2-3x schneller als meine, bis Ihnen der RAM ausgeht.
Joe Kington
Guter Punkt. Wie sich herausstellte, hat mein Versuch, Zwischenkopien nur mit den Indizes zu erstellen, dazu geführt, dass meine Methode mehr Speicher benötigt und langsamer ist als nur eine sortierte Kopie des Arrays, da a_sorted [1:] keine Kopie von a_sorted ist .
cge
Was ist dtypein deinen Timings? Ich denke, du hast das falsch verstanden. Auf meinem System ist das Anrufen np.uniquewie in meiner Antwort beschrieben etwas schneller als das Verwenden einer Ihrer beiden Varianten von np.lexsort. Und es ist ungefähr 5x schneller, wenn das Array, um Unikate zu finden, Form hat (10000, 100). Selbst wenn Sie sich für eine Neuimplementierung entscheiden, np.uniqueum eine (geringfügige) Ausführungszeit zu verkürzen, führt das Reduzieren jeder Zeile zu einem einzelnen Objekt schnellere Vergleiche aus, als wenn Sie np.anyden Vergleich der Spalten aufrufen müssen , insbesondere bei höheren Spaltenzahlen.
Jaime
@cge: Sie meinten wahrscheinlich "np.any" anstelle von "any", das kein Schlüsselwortargument verwendet.
M. Toya
@Jaime - Ich glaube, es dtypeist nur a.dtypeder Datentyp der angezeigten Daten, wie es Joe Kington in seiner Antwort getan hat. Wenn es viele Spalten gibt, besteht eine andere (unvollständige!) Möglichkeit, die Dinge schnell zu halten, lexsortdarin, nur einige Spalten zu sortieren. Dies ist datenspezifisch, da man wissen muss, welche Spalten genügend Varianz bieten, um perfekt zu sortieren. ZB a.shape = (60000, 500)- sortiere nach den ersten 3 Spalten : ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0])). Die Zeitersparnis ist ziemlich beträchtlich, aber der Haftungsausschluss noch einmal: Es werden möglicherweise nicht alle Fälle erfasst - es hängt von den Daten ab.
n1k31t4
9

Hier ist eine weitere Variante für die @ Greg-Python-Antwort

np.vstack(set(map(tuple, a)))
Divenex
quelle
9

Ich habe die vorgeschlagene Alternative auf Geschwindigkeit verglichen und festgestellt, dass die Void-View- uniqueLösung überraschenderweise uniquemit dem axisArgument sogar etwas schneller ist als Numpys native . Wenn Sie Geschwindigkeit suchen, werden Sie wollen

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

Geben Sie hier die Bildbeschreibung ein


Code zur Reproduktion der Handlung:

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
quelle
1
Sehr schöne Antwort, ein kleiner Punkt: vstack_dictVerwenden Sie niemals ein Diktat, geschweiften Klammern ist ein festes Verständnis, und daher ist sein Verhalten fast identisch mit vstatck_set. Da die vstack_dictLeistungslinie für das Diagramm fehlt, sieht es so aus, als würde sie nur vom vstack_setLeistungsdiagramm abgedeckt , da sie so ähnlich sind!
Akavall
Danke für die Antwort. Ich habe die Handlung so verbessert, dass sie nur eine vstackVariante enthält.
Nico Schlömer
8

Ich mochte keine dieser Antworten, weil keine Gleitkomma-Arrays im Sinne einer linearen Algebra oder eines Vektorraums handhabt, wobei zwei Zeilen, die „gleich“ sind, „innerhalb eines 𝜀“ bedeuten. Bei der einen Antwort mit einem Toleranzschwellenwert, https://stackoverflow.com/a/26867764/500207 , wurde der Schwellenwert sowohl als element- als auch als dezimalgenau angenommen. Dies funktioniert in einigen Fällen, ist jedoch mathematisch nicht so allgemein wie ein wahre Vektorentfernung.

Hier ist meine Version:

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)

Die obige Public-Domain-Funktion verwendet scipy.spatial.distance.pdist, um den euklidischen (anpassbaren) Abstand zwischen jedem Zeilenpaar zu ermitteln. Dann vergleicht es jede Entfernung mit einer threshalten, um die Zeilen zu finden, die threshineinander liegen, und gibt nur eine Zeile von jedem threshCluster zurück.

Wie angedeutet, muss die Entfernung metricnicht euklidisch sein - pdistkann verschiedene Entfernungen berechnen, einschließlich cityblock(Manhattan-Norm) und cosine(Winkel zwischen Vektoren).

Wenn thresh=0(Standardeinstellung), müssen die Zeilen bitgenau sein, um als "eindeutig" zu gelten. Andere gute Werte für die threshVerwendung skalierter Maschinengenauigkeit, dh thresh=np.spacing(1)*1e3.

Ahmed Fasih
quelle
Beste Antwort. Vielen Dank. Es ist die (mathematisch) verallgemeinerteste Antwort, die bisher geschrieben wurde. Es betrachtet eine Matrix als einen Satz von Datenpunkten oder Stichproben im N-dimensionalen Raum und findet eine Sammlung gleicher oder ähnlicher Punkte (Ähnlichkeit wird entweder durch den euklidischen Abstand oder durch andere Methoden definiert). Diese Punkte können überlappende Datenpunkte oder sehr nahe Nachbarschaften sein. Am Ende wird eine Sammlung gleicher oder ähnlicher Punkte durch einen Punkt (in der obigen Antwort durch einen ersten Punkt) ersetzt, der zu derselben Menge gehört. Dies hilft, die Redundanz einer Punktwolke zu reduzieren.
Sanchit
@Sanchit aha, das ist ein guter Punkt, anstatt den „ersten“ Punkt (tatsächlich könnte er effektiv zufällig sein, da es davon abhängt, wie Python die Punkte in a speichert set) als Vertreter jeder threshNachbarschaft mit der Größe auszuwählen, könnte die Funktion das zulassen Benutzer, um anzugeben, wie dieser Punkt ausgewählt werden soll, z. B. den „Median“ oder den Punkt, der dem Schwerpunkt am nächsten liegt, usw.
Ahmed Fasih
Sicher. Kein Zweifel. Ich habe gerade den ersten Punkt erwähnt, da dies das ist, was Ihr Programm tut, was völlig in Ordnung ist.
Sanchit
Nur eine Korrektur - ich habe oben fälschlicherweise gesagt, dass die Zeile, die für jeden threshCluster ausgewählt wird, aufgrund der ungeordneten Natur von zufällig ist set. Natürlich ist das ein Brainfart auf meiner Seite ist, die setGeschäfte Tupeln von Indizes , die in denen sind thresh-neighborhood, so dass dies findRows tut in der Tat Gegenzug für jeden thresh-Cluster, die erste Zeile darin.
Ahmed Fasih
3

Warum nicht drop_duplicatesvon Pandas verwenden:

>>> 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
quelle
Ich liebe diese Antwort wirklich. Sicher, es wird nicht direkt numpy verwendet, aber für mich ist es das, was am einfachsten zu verstehen ist, wenn man schnell ist.
Noctilux
3

Das numpy_indexed- Paket (Haftungsausschluss: Ich bin sein Autor) verpackt die von Jaime veröffentlichte Lösung in eine schöne und getestete Oberfläche sowie viele weitere Funktionen:

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

np.unique arbeitet mit einer Liste von Tupeln:

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

Bei einer Liste von Listen wird a TypeError: unhashable type: 'list'

Codeape
quelle
scheint bei mir nicht zu funktionieren. Jedes Tupel besteht aus zwei Zeichenfolgen anstelle von zwei
Gleitkommazahlen
funktioniert nicht, es gibt eine Liste von Elementen zurück, die keine Tupel sind
Mohanad Kaleia
1

Basierend auf der Antwort auf dieser Seite habe ich eine Funktion geschrieben, die die Fähigkeit der MATLAB- unique(input,'rows')Funktion nachbildet , mit der zusätzlichen Funktion, Toleranz für die Überprüfung der Eindeutigkeit zu akzeptieren. Es gibt auch die Indizes zurück, so dass c = data[ia,:]und data = c[ic,:]. Bitte melden Sie, wenn Sie Unstimmigkeiten oder Fehler feststellen.

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
quelle
1

Abgesehen von der hervorragenden Antwort von @Jaime besteht eine andere Möglichkeit, eine Zeile zu reduzieren, darin, a.strides[0](vorausgesetzt, sie aist C-zusammenhängend) zu verwenden, was gleich ist a.dtype.itemsize*a.shape[0]. Weiterhin void(n)ist eine Abkürzung für dtype((void,n)). Wir kommen endlich zu dieser kürzesten Version:

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

Zum

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
BM
quelle
0

Versuchen Sie Folgendes für allgemeine Zwecke wie 3D oder höher mehrdimensionale verschachtelte Arrays:

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

welches Ihren 2D-Datensatz erfüllt:

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)

gibt:

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

Aber auch 3D-Arrays wie:

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)

gibt:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])
Tara
quelle
Die Verwendung von unique return_indexwie Jaime sollte die letzte returnZeile einfacher machen. Indizieren Sie einfach das Original arauf der rechten Achse.
Hpaulj
0

Keine dieser Antworten hat bei mir funktioniert. Ich gehe davon aus, dass meine eindeutigen Zeilen Zeichenfolgen und keine Zahlen enthielten. Diese Antwort aus einem anderen Thread hat jedoch funktioniert:

Quelle: https://stackoverflow.com/a/38461043/5402386

Sie können die Methoden der Listen .count () und .index () verwenden

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
quelle
0

Wir können das numerische numpy-Array mxn tatsächlich in ein numpy-String-Array mx 1 umwandeln. Versuchen Sie es mit der folgenden Funktion. Es bietet count , inverse_idx und usw., genau wie 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

Beispiel:

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
quelle
-1

Lassen Sie uns die gesamte Numpy-Matrix als Liste abrufen, dann Duplikate aus dieser Liste löschen und schließlich unsere eindeutige Liste wieder in eine Numpy-Matrix zurückgeben:

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
quelle
-3

Die einfachste Lösung besteht darin, die Zeilen zu einem einzelnen Element zu machen, indem Sie sie zu Zeichenfolgen machen. Jede Zeile kann dann mit numpy als Ganzes auf ihre Einzigartigkeit hin verglichen werden. Diese Lösung ist verallgemeinerbar. Sie müssen lediglich Ihr Array umformen und für andere Kombinationen transponieren. Hier ist die Lösung für das bereitgestellte Problem.

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)    

Wird geben:

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

Schicken Sie meinen Nobelpreis per Post

Dave Pena
quelle
Sehr ineffizient und fehleranfällig, z. B. mit unterschiedlichen Druckoptionen. Die anderen Optionen sind eindeutig vorzuziehen.
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
quelle