Was ist eine gute pythonische Art, doppelte Objekte zu finden?

8

Ich verwende häufig sorted und groupby , um doppelte Elemente in einem iterablen Objekt zu finden. Jetzt sehe ich, dass es unzuverlässig ist:

%Vor%

Der Grund, warum der obige Code in Python 2.x fehlschlägt, ist hier erklärt .

Was ist eine zuverlässige pythonische Methode, um Duplikate zu finden?

Ich habe nach ähnlichen Fragen / Antworten zu SO gesucht. Das beste von ihnen ist " Wie nehme ich in Python eine Liste und reduziere sie auf eine Liste von Duplikaten? ", aber die akzeptierte Lösung ist nicht pythonisch (es ist prozedurale Multilinie für ... wenn ... add ... else ... add ... return result) und andere Lösungen sind unzuverlässig (hängt von der nicht erfüllten Transitivität des Operators "& lt;" ab) oder sind langsam (O n * n).

[BEARBEITEN] Geschlossen. Die angenommene Antwort hat mir geholfen, Schlussfolgerungen in meiner Antwort unten allgemeiner zusammenzufassen.

Ich verwende gerne eingebaute Typen, um z.B. Baumstrukturen. Deshalb habe ich jetzt Angst vor der Mischung.

    
hynekcer 20.04.2012, 14:03
quelle

5 Antworten

11

Hinweis: Nimmt an, dass Einträge hashbar sind

%Vor%     
jamylak 20.04.2012, 14:06
quelle
1

Fazit:

  • Wenn alle Elemente hashbar sind und mindestens Python 2.7 haben, ist die beste Lösung oben mit collections.Counter .
  • Wenn einige Elemente nicht hashbar sind oder Python 2.7+ nicht vorhanden ist, dann ist die Lösung groupby(sorted(..)) unter Bedingungen sehr gut
    • kombiniere str und unicode oder
    • nicht
    • verwende keinen Typ, bei dem der Name aphabetisch zwischen "str" ​​und "unicode" steht. (typischerweise "Tupel" oder "Typ")
  • Wenn Daten nicht hashbar und gemischt sind, kann nichts weiter oben verwendet werden.
    • Counter(map(pickled.dumps, data)) anstelle von Counter(data) und entpacke es schließlich oder
    • groupby(sorted(data, key=pickled.dumps)) wenn das unpickling unerwünscht ist oder kein Python 2.7
  • Eine naive Lösung ", die unten besprochen wird, kann besser sein als das Beizen für eine sehr kleine Anzahl von Gegenständen mit weniger als 300 Stück.

Alle anderen Lösungen in anderen Fragen sind derzeit schlechter .

Anmerkungen:

  • Es sieht so aus, dass die Klasse Counter kopiert und in niedrigere Python-Versionen eingefügt werden kann.
  • Eine " naive Lösung ", die jedes Element in ganzen Daten durchsucht, kann für eine sehr kleine Anzahl von Elementen verwendet werden. Es hat den Vorteil, dass keine hashbaren Daten benötigt werden und nicht von der Transitivität des Standards "& lt;" Betreiber, aber für mehr als 25 verschiedene Artikel ist besser die Theke und für mehr als 300 nicht waschbare "wilde" Gegenstände ist besser die Theke auf eingelegte Gegenstände.

Ich dachte darüber nach, Artikel nach Typ vorzusortieren oder sie durch Hash für hashable Elemente zu erweitern, es hilft derzeit, aber es ist keine sichere Lösung, weil das gleiche Problem mit "& lt;" Operator Insite Listen, Tupel etc.

    
hynekcer 21.04.2012 10:16
quelle
1

Dieses Thema ist für mich interessant, also habe ich die obige Lösung gegen die im anderen Thread akzeptierte Lösung zeitlich abgestimmt.

Die Counter-Methode ist dieser Thread ist sehr elegant; akzeptierte Antwort in diesem Thread Wie nehme ich in Python eine Liste und reduziere sie auf eine Liste von Duplikaten? scheint etwa 2 mal schneller zu sein.

%Vor%

Ergebnisse:

%Vor%

Ich habe das unter verschiedenen Spezifikationen versucht und das Ergebnis immer das selbe.

    
Akavall 21.04.2012 19:03
quelle
0

Allerdings gibt es in beiden Lösungen eine Falle. Der Grund dafür ist, dass die Werte mit demselben Hashwert zusammengeführt werden. Es kommt also darauf an, ob die verwendeten Werte den gleichen Hashwert haben. Es ist nicht dieser verrückte Kommentar, wie Sie vielleicht denken (ich war auch früher überrascht), weil Python einige Werte auf die besondere Art und Weise hasht. Probieren Sie:

%Vor%

Die 1, 1.0 und True haben per Definition den gleichen Hash und ähnlich die 0, 0.0 und False. Es druckt Folgendes auf meiner Konsole (denke an die letzten beiden Zeilen - alle Werte sollten eigentlich Duplikate sein):

%Vor%     
pepr 23.04.2012 07:40
quelle
0

Nur weil ich neugierig war, hier ist die Lösung, die einen Unterschied zwischen 0, False, 0.0 usw. macht. Es basiert auf dem Sortieren der Sequenz mit my_cmp , die auch den Typ des Gegenstandes berücksichtigt. Es ist natürlich sehr langsam im Vergleich zu den oben genannten Lösungen. Dies liegt an der Sortierung. Aber vergleichen Sie die Ergebnisse!

%Vor%

Es druckt auf meiner Konsole:

%Vor%     
pepr 24.04.2012 07:52
quelle

Tags und Links