In dieser Antwort werden zwei Abschnitte aufgeführt: Zwei einzigartige Lösungen und ein Diagramm der Geschwindigkeit für bestimmte Lösungen.
Doppelte Elemente entfernen
Die meisten dieser Antworten entfernen nur doppelte Elemente, die hashbar sind. Diese Frage bedeutet jedoch nicht, dass nicht nur hashbare Elemente benötigt werden. Dies bedeutet, dass ich einige Lösungen anbieten werde, für die keine hashbaren Elemente erforderlich sind.
Sammlungen.Counter ist ein leistungsfähiges Werkzeug in der Standardbibliothek, das dafür perfekt sein könnte. Es gibt nur eine andere Lösung, die sogar Counter enthält. Diese Lösung ist jedoch auch auf Hash- Schlüssel beschränkt.
Um nicht zerlegbare Schlüssel in Counter zuzulassen, habe ich eine Container-Klasse erstellt, die versucht, die Standard-Hash-Funktion des Objekts abzurufen. Wenn dies jedoch fehlschlägt, wird die Identitätsfunktion ausprobiert. Es definiert auch eine Gleichung und eine Hash- Methode. Dies sollte ausreichen, um nicht zerlegbare Elemente in unserer Lösung zuzulassen . Nicht hashbare Objekte werden so behandelt, als wären sie hashbar. Diese Hash-Funktion verwendet jedoch die Identität für nicht verwertbare Objekte, was bedeutet, dass zwei gleiche Objekte, die beide nicht verwertbar sind, nicht funktionieren. Ich schlage vor, dass Sie dies überschreiben und ändern, um den Hash eines äquivalenten veränderlichen Typs zu verwenden (wie hash(tuple(my_list))
wenn Sie if my_list
als Liste verwenden).
Ich habe auch zwei Lösungen gemacht. Eine andere Lösung, die die Reihenfolge der Elemente beibehält und eine Unterklasse von OrderedDict und Counter mit dem Namen 'OrderedCounter' verwendet. Hier sind die Funktionen:
from collections import OrderedDict, Counter
class Container:
def __init__(self, obj):
self.obj = obj
def __eq__(self, obj):
return self.obj == obj
def __hash__(self):
try:
return hash(self.obj)
except:
return id(self.obj)
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first encountered'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
def remd(sequence):
cnt = Counter()
for x in sequence:
cnt[Container(x)] += 1
return [item.obj for item in cnt]
def oremd(sequence):
cnt = OrderedCounter()
for x in sequence:
cnt[Container(x)] += 1
return [item.obj for item in cnt]
remd ist eine nicht geordnete Sortierung, oremd ist eine geordnete Sortierung. Sie können klar erkennen, welches schneller ist, aber ich werde es trotzdem erklären. Die nicht geordnete Sortierung ist etwas schneller. Es speichert weniger Daten, da keine Bestellung erforderlich ist.
Jetzt wollte ich auch die Geschwindigkeitsvergleiche jeder Antwort zeigen. Also mache ich das jetzt.
Welche Funktion ist die schnellste?
Zum Entfernen von Duplikaten habe ich aus einigen Antworten 10 Funktionen zusammengestellt. Ich habe die Geschwindigkeit jeder Funktion berechnet und sie mit matplotlib.pyplot in ein Diagramm eingefügt .
Ich habe dies in drei Grafikrunden unterteilt. Ein Hashable ist ein Objekt, das gehasht werden kann, ein Nicht-Hashable ist ein Objekt, das nicht gehasht werden kann. Eine geordnete Sequenz ist eine Sequenz, die die Ordnung beibehält, eine ungeordnete Sequenz bewahrt die Ordnung nicht. Hier noch ein paar Begriffe:
Ungeordnetes Hashable war für jede Methode geeignet, bei der Duplikate entfernt wurden, ohne dass die Reihenfolge eingehalten werden musste. Es musste nicht für Unashashables funktionieren, aber es konnte.
Bestelltes Hashable war für jede Methode geeignet, bei der die Reihenfolge der Elemente in der Liste beibehalten wurde, aber es musste nicht für nicht zerlegbare Elemente funktionieren, aber es konnte.
Ordered Unhashable war eine Methode, die die Reihenfolge der Elemente in der Liste beibehielt und für Unhashables funktionierte.
Auf der y-Achse ist die Anzahl der Sekunden angegeben.
Auf der x-Achse befindet sich die Nummer, auf die die Funktion angewendet wurde.
Wir haben Sequenzen für ungeordnete Hashables und geordnete Hashables mit folgendem Verständnis generiert: [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]
Für bestellte nicht zerlegbare Gegenstände: [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]
Beachten Sie, dass es einen "Schritt" im Bereich gibt, da dies ohne ihn 10x so lange gedauert hätte. Auch weil ich meiner persönlichen Meinung nach dachte, es hätte ein bisschen leichter zu lesen ausgesehen.
Beachten Sie auch, dass die Tasten in der Legende das sind, was ich als die wichtigsten Teile der Funktion erraten wollte. Welche Funktion hat das Schlimmste oder Beste? Die Grafik spricht für sich.
Nachdem dies erledigt ist, sind hier die Grafiken.
Ungeordnete Hashables
(Vergrößert)
Bestellte Hashables
(Vergrößert)
Bestellte Unhashables
(Vergrößert)