Wie subtrahiere ich eine Liste von einer anderen?

Lesezeit: 9 Minuten

Benutzeravatar von daydreamer
Tagträumer

Ich möchte die nehmen Unterschied zwischen Listen x Und y:

>>> x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> y = [1, 3, 5, 7, 9]  
>>> x - y
# should return [0, 2, 4, 6, 8]

  • Was sollte [2, 2] – [2] zurückkehren? []? [2]?

    – McKay

    24. Januar 2017 um 20:08 Uhr

  • Was sollte [2, 1, 2, 3, 2, 4, 2] – [2, 3, 2] zurück, und warum? Sollte es die 232 in der Mitte finden und 2142 zurückgeben? oder sollte es jedes Mal das erste finden und 1242 zurückgeben? Oder etwas anderes? Was ich sagen will ist, dass dies keine offensichtlichen Antworten sind und vom Bedarf abhängen.

    – McKay

    5. Juli 2017 um 15:07 Uhr

Benutzeravatar von aaronasterling
aaronasterling

Verwenden Sie ein Listenverständnis, um den Unterschied zu berechnen und dabei das Original beizubehalten Befehl aus x:

[item for item in x if item not in y]

Wenn Sie Listeneigenschaften (z. B. Sortierung) nicht benötigen, verwenden Sie a Unterschied einstellenwie die anderen Antworten vermuten lassen:

list(set(x) - set(y))

Erlauben x - y Infix-Syntax, überschreiben __sub__ auf eine Klasse erben von list:

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(args)

    def __sub__(self, other):
        return self.__class__(*[item for item in self if item not in other])

Verwendung:

x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y   

  • Wenn Sie tun [1,1,2,2] - [1,2] Sie erhalten eine leere Liste. [1,1,2,2] - [2] gibt [1,1] Es ist also nicht wirklich Listensubtraktion, es ist eher wie „Liste von Liste X ohne Elemente aus Menge Y.

    – Alfred Zien

    6. Februar 2016 um 10:25 Uhr

  • Die List-Comprehension-Methode ist viel langsamer (in meinem Beispiel) als die Set-Difference-Methode.

    – redfiloux

    26. Februar 2019 um 10:22 Uhr

  • für größere y: [item for item in x if item not in set(y)]

    – Barney Szabolcs

    4. September 2019 um 12:58 Uhr

  • @BarnabasSzabolcs: Das wird nichts speichern, weil es konvertieren wird y zu einem set Vor jeden Scheck (der ähnlich teuer ist wie die ursprüngliche Arbeit). Sie müssten beides tun yset = set(y) außerhalb der listcomp, dann testen if item not in ysetoder als ungeheuerlicher Hack, tun [item for yset in [set(y)] for item in x if item not in yset] die verschachtelte listcomps missbraucht, um die zu cachen yset als Einzeiler. Eine etwas weniger hässliche Einzeilerlösung, die eine angemessene Leistung erbringt, wäre die Verwendung list(itertools.filterfalse(set(y).__contains__, x)) weil das argument zu filterfalse wird nur einmal aufgebaut.

    – ShadowRanger

    5. September 2019 um 22:03 Uhr


  • Dies funktioniert, aber denken Sie daran, dass Iterationen langsam sind. Ich bevorzuge die nächste Lösung mit dem Subtrahieren von Set von Set, was super schnell zu funktionieren scheint.

    – R3qUi3M

    3. Februar 2022 um 15:24 Uhr

Verwenden Unterschied einstellen

>>> z = list(set(x) - set(y))
>>> z
[0, 8, 2, 4, 6]

Oder Sie haben vielleicht nur x und y gesetzt, damit Sie keine Konvertierungen durchführen müssen.

  • Dadurch geht jede Bestellung verloren. Das kann je nach Kontext eine Rolle spielen oder auch nicht.

    – Aaronasterling

    7. August 2010 um 0:19 Uhr

  • Dadurch gehen auch alle möglichen Duplikate verloren, die möglicherweise gewartet werden müssen/müssen.

    – Opale

    24. Juni 2011 um 5:31 Uhr

  • Ich bekomme TypeError: unhashable type: 'dict'

    – Havnar

    2. August 2017 um 22:26 Uhr

  • Dies ist viel schneller, wenn die zu vergleichenden Listen groß sind

    – JqueryToAddNumbers

    6. Oktober 2018 um 2:57 Uhr

  • Wenn die Reihenfolge und Duplikate von Elementen in der Liste für den Kontext nicht wichtig sind, ist dies eine großartige Antwort und sehr gut lesbar.

    – Watt Iamsuri

    25. April 2019 um 5:36 Uhr

Wenn Duplikate und Bestellartikel ein Problem sind:

[i for i in a if not i in b or b.remove(i)]

a = [1,2,3,3,3,3,4]
b = [1,3]
result: [2, 3, 3, 3, 4]

  • Das funktioniert, obwohl es ist O(m * n) Laufzeit (und ich zucke zusammen, wenn eine Listenkompilierung Nebenwirkungen enthält); Sie können es verbessern, indem Sie es verwenden collections.Counter zu bekommen O(m + n) Laufzeit.

    – ShadowRanger

    6. September 2019 um 18:50 Uhr

  • Ich kann das schwer nachvollziehen, kann mir das jemand erklären?

    – Anuschka

    23. Oktober 2019 um 7:28 Uhr

  • @anushka Eher als [item for item in a if not item in b] (was eher wie eine Mengensubtraktion funktioniert), hat dies ... if not item in b or b.remove(item). b.remove(item) kehrt zurück false Wenn item ist nicht dabei b und entfernt item aus b ansonsten. Dadurch wird verhindert, dass Elemente in der zweiten Liste (a - b, in diesem Fall) nicht mehr als einmal für jedes Auftreten subtrahiert werden. Dies verhindert das Deduplizieren, was passiert, wenn Sie einigen der anderen Antworten folgen. Es ist nicht supereffizient (befolgen Sie unbedingt den Vorschlag von @ShaworRangers zur Effizienz), aber ich denke, dies ist wahrscheinlich die richtigste Antwort.

    – jmorganmartin

    1. Juli 2022 um 0:38 Uhr


Benutzeravatar des Weihnachtsmanns
Weihnachtsmann

Das ist eine “Satz-Subtraktions”-Operation. Verwenden Sie dazu die eingestellte Datenstruktur.

In Python 2.7:

x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y

Ausgang:

>>> print x - y
set([0, 8, 2, 4, 6])

abarnerts Benutzeravatar
Abart

Für viele Anwendungsfälle lautet die gewünschte Antwort:

ys = set(y)
[item for item in x if item not in ys]

Dies ist eine Mischung aus der Antwort von Aaronasterling und der Antwort von Quantensoup.

Aaronasterlings Version tut es len(y) Artikelvergleiche für jedes Element in x, also dauert es quadratisch. Die Version von quantumSoup verwendet Sets, also führt es eine einzelne Set-Suche mit konstanter Zeit für jedes Element darin durch x– aber, weil es konvertiert beide x Und y in Mengen, verliert es die Reihenfolge Ihrer Elemente.

Nur durch Konvertieren y in eine Menge und Iteration x In der Reihenfolge erhalten Sie das Beste aus beiden Welten – lineare Zeit und Ordnungserhaltung.*


Dies hat jedoch immer noch ein Problem von der Version von quantumSoup: Es erfordert, dass Ihre Elemente hashbar sind. Das ist so ziemlich in die Natur von Mengen eingebaut.** Wenn Sie zB versuchen, eine Liste von Diktaten von einer anderen Liste von Diktaten zu subtrahieren, aber die zu subtrahierende Liste groß ist, was tun Sie dann?

Wenn Sie Ihre Werte so dekorieren können, dass sie hashbar sind, wird das Problem gelöst. Zum Beispiel mit einem flachen Wörterbuch, dessen Werte selbst hashbar sind:

ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]

Wenn Ihre Typen etwas komplizierter sind (z. B. wenn Sie es häufig mit JSON-kompatiblen Werten zu tun haben, die hashbar sind, oder mit Listen oder Diktaten, deren Werte rekursiv denselben Typ haben), können Sie diese Lösung dennoch verwenden. Aber einige Typen können einfach nicht in etwas Hashbares umgewandelt werden.


Wenn Ihre Artikel nicht hashbar sind und auch nicht erstellt werden können, aber vergleichbar sind, können Sie zumindest log-lineare Zeit (O(N*log M)das ist viel besser als die O(N*M) Zeit der Listenlösung, aber nicht so gut wie die O(N+M) Zeit der ausgehärteten Lösung) durch Sortieren und Verwenden bisect:

ys = sorted(y)
def bisect_contains(seq, item):
    index = bisect.bisect(seq, item)
    return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]

Wenn Ihre Artikel weder hashbar noch vergleichbar sind, bleiben Sie bei der quadratischen Lösung hängen.


* Beachten Sie, dass Sie dies auch tun könnten, indem Sie ein Paar verwenden OrderedSet Objekte, für die Sie Rezepte und Module von Drittanbietern finden können. Aber ich denke, das ist einfacher.

** Der Grund, warum Set-Lookups konstant sind, ist, dass alles, was es tun muss, ist, den Wert zu hashen und zu sehen, ob es einen Eintrag für diesen Hash gibt. Wenn der Wert nicht gehasht werden kann, funktioniert dies nicht.

Wenn die Listen doppelte Elemente zulassen, können Sie Counter from collections verwenden:

from collections import Counter
result = list((Counter(x)-Counter(y)).elements())

Wenn Sie die Reihenfolge der Elemente von x beibehalten müssen:

result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]

Die anderen Lösungen haben eines von wenigen Problemen:

  1. Sie wahren keine Ordnung, oder
  2. Sie entfernen keine genaue Anzahl von Elementen, z x = [1, 2, 2, 2] Und y = [2, 2] sie konvertieren y zu einem setund entfernen Sie entweder alle übereinstimmenden Elemente (wobei Sie [1] nur) oder eines von jedem einzigartigen Element entfernen (wobei [1, 2, 2]), wenn das richtige Verhalten das Entfernen wäre 2 zweimal, verlassen [1, 2]oder
  3. Tun sie O(m * n) arbeiten, wo eine optimale Lösung ausreichen kann O(m + n) arbeiten

Alain war mit auf dem richtigen Weg Counter um Nr. 2 und Nr. 3 zu lösen, aber diese Lösung verliert die Ordnung. Die Lösung, die Ordnung bewahrt (Entfernen der ersten n Kopien von jedem Wert für n Wiederholungen in der list der zu entfernenden Werte) ist:

from collections import Counter

x = [1,2,3,4,3,2,1]  
y = [1,2,2]  
remaining = Counter(y)

out = []
for val in x:
    if remaining[val]:
        remaining[val] -= 1
    else:
        out.append(val)
# out is now [3, 4, 3, 1], having removed the first 1 and both 2s.

Probieren Sie es online aus!

Um es zu entfernen zuletzt Kopien jedes Elements, ändern Sie einfach die for Schleife zu for val in reversed(x): und hinzufügen out.reverse() unmittelbar nach dem Verlassen der for Schleife.

Aufbau der Counter Ist O(n) bezüglich y‘s Länge, Iteration x Ist O(n) bezüglich x‘s Länge und Counter Mitgliedschaftstests und Mutationen sind O(1)während list.append ist amortisiert O(1) (ein gegebenes append kann sein O(n)aber für viele appends, die gesamten Big-O-Durchschnitte O(1) da immer weniger von ihnen eine Neuzuweisung erfordern), so ist die geleistete Gesamtarbeit O(m + n).

Sie können auch testen, ob Elemente darin enthalten sind y die nicht entfernt wurden x durch testen:

remaining = +remaining  # Removes all keys with zero counts from Counter
if remaining:
    # remaining contained elements with non-zero counts

  • Hinweis: Dies tut erfordern, dass die Werte hashfähig sind, aber jede Lösung, die keine hashfähigen Objekte erfordert, ist auch nicht für allgemeine Zwecke geeignet (z. B. kann zählen ints in ein Array mit fester Länge) oder muss mehr als tun O(m + n) Arbeit (z. B. das nächstbeste große O wäre, eine sortierte zu machen list von eindeutigen Wert/Anzahl-Paaren, ändernd O(1) dict Nachschlagen in O(log n) binäre Suchen; Sie benötigen eindeutige Werte mit ihren Zählwerten, nicht nur sortierte nicht eindeutige Werte, da Sie sonst bezahlen würden O(n) Kosten, um die Elemente aus dem Sortiergut zu entfernen list).

    – ShadowRanger

    6. September 2019 um 18:47 Uhr

  • Ich denke, das ist bisher die beste Antwort, aber für Referenzzwecke wäre es meiner Meinung nach besser, wenn es in eine Funktion umgestaltet würde, da ich davon ausgehe, dass es etwas umständlich ist, jedes Mal, wenn man zwei Listen subtrahieren möchte, mehr als 5 Codezeilen einzugeben .

    – Wladimir Wilimaitis

    6. Mai 2022 um 14:31 Uhr

  • Dies sollte die ausgewählte Antwort sein, wenn auch nur für Punkt 2!

    – Brian Risiko

    23. Juni 2022 um 1:21 Uhr

1443390cookie-checkWie subtrahiere ich eine Liste von einer anderen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy