Python findet Elemente in einer Liste, die nicht in der anderen enthalten sind [Duplikat]

135

Ich muss zwei Listen vergleichen, um eine neue Liste spezifischer Elemente zu erstellen, die in einer Liste enthalten sind, in der anderen jedoch nicht. Beispielsweise:

main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 

Ich möchte list_1 durchlaufen und alle Elemente aus list_2 an main_list anhängen, die nicht in list_1 enthalten sind.

Das Ergebnis sollte sein:

main_list=["f", "m"]

Wie kann ich das mit Python machen?

CosimoCD
quelle
2
Suchen Sie nach Elementen list_2, die nirgendwo in erscheinen, list_1oder nach Elementen list_2, die nicht im selben Index vorhanden sind list_1?
Patrick Haugh

Antworten:

95

TL; DR:
LÖSUNG (1)

import numpy as np
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`

LÖSUNG (2) Sie möchten eine sortierte Liste

def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans
main_list = setdiff_sorted(list_2,list_1)




ERKLÄRUNGEN:
(1) Sie können NumPy die verwenden setdiff1d( array1, array2, assume_unique= False).

assume_uniquefragt den Benutzer, WENN die Arrays BEREITS EINZIGARTIG SIND.
Wenn Falseja, werden zuerst die eindeutigen Elemente bestimmt.
Wenn dies Trueder Fall ist , geht die Funktion davon aus, dass die Elemente bereits eindeutig sind. Die UND-Funktion überspringt die Bestimmung der eindeutigen Elemente.

Dies ergibt die eindeutigen Werte array1, die nicht in sind array2. assume_uniqueist Falsestandardmäßig.

Wenn Sie sich mit den eindeutigen Elementen befassen (basierend auf der Antwort von Chinny84 ), verwenden Sie einfach (wobei assume_unique=False=> der Standardwert):

import numpy as np
list_1 = ["a", "b", "c", "d", "e"]
list_2 = ["a", "f", "c", "m"] 
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`


(2) Für diejenigen, die Antworten sortieren möchten, habe ich eine benutzerdefinierte Funktion erstellt:

import numpy as np
def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans

Um die Antwort zu erhalten, führen Sie Folgendes aus:

main_list = setdiff_sorted(list_2,list_1)

SEITLICHE HINWEISE:
(a) Lösung 2 (benutzerdefinierte Funktion setdiff_sorted) gibt eine Liste zurück (im Vergleich zu einem Array in Lösung 1).

(b) Wenn Sie nicht sicher sind, ob die Elemente eindeutig sind, verwenden Sie einfach die Standardeinstellung von NumPy's setdiff1din beiden Lösungen A und B. Was kann ein Beispiel für eine Komplikation sein? Siehe Anmerkung (c).

(c) Die Dinge werden anders sein, wenn eine der beiden Listen nicht eindeutig ist.
Sprich list_2ist nicht eindeutig : list2 = ["a", "f", "c", "m", "m"]. Behalten Sie list1es bei: list_1 = ["a", "b", "c", "d", "e"]
Festlegen des Standardwerts für die assume_uniqueAusbeuten ["f", "m"](in beiden Lösungen). Wenn Sie jedoch einstellen assume_unique=True, geben beide Lösungen ["f", "m", "m"]. Warum? Dies liegt daran, dass der Benutzer davon ausgegangen ist, dass die Elemente eindeutig sind. Daher ist es besser zu haltenassume_uniqueauf den Standardwert. Beachten Sie, dass beide Antworten sortiert sind.

jcoderepo
quelle
Wenn Ihre Listen bereits bestellt sind, wird auch eine geordnete Liste zurückgegeben. Die native Lösung, in Mengen zu konvertieren und dann den Unterschied zu ermitteln (Lösungen siehe unten), gibt eine ungeordnete Liste zurück, die es möglicherweise schwieriger macht, Ihre Ergebnisse visuell zu überprüfen.
Doubledown
1
Hallo, @Doubledown! Ihr Anliegen wurde in dem bearbeiteten Beitrag angesprochen. Hoffe das hilft!
Jcoderepo
181

Sie können Sets verwenden:

main_list = list(set(list_2) - set(list_1))

Ausgabe:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> set(list_2) - set(list_1)
set(['m', 'f'])
>>> list(set(list_2) - set(list_1))
['m', 'f']

Laut @ JonClements 'Kommentar ist hier eine übersichtlichere Version:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> list(set(list_2).difference(list_1))
['m', 'f']
nrlakin
quelle
2
Dies ist gut, wenn wir uns nur um uniqueElemente kümmern , aber was ist, wenn wir m'szum Beispiel mehrere haben, würde dies nicht aufgreifen.
Chinny84
Das ist richtig. Ich nahm an, dass das Poster nach einzigartigen Elementen suchte. Ich nehme an, es hängt davon ab, was er mit "spezifisch" meint.
Nrlakin
In der Tat habe ich Ihre Antwort nicht abgelehnt, insbesondere nicht für eine unklare ursprüngliche Frage.
Chinny84
13
Sie könnten dies so schreiben, list(set(list_2).difference(list_1))dass die explizite setKonvertierung vermieden wird ...
Jon Clements
Keine Sorge! Vielen Dank an @leaf für die Formatierungshilfe.
Nrlakin
59

Ich bin mir nicht sicher, warum die obigen Erklärungen so kompliziert sind, wenn native Methoden verfügbar sind:

main_list = list(set(list_2)-set(list_1))
A. Kot
quelle
6
Die Aufrechterhaltung der Ordnung könnte der Grund sein
Keith
57

Verwenden Sie ein Listenverständnis wie folgt:

main_list = [item for item in list_2 if item not in list_1]

Ausgabe:

>>> list_1 = ["a", "b", "c", "d", "e"]
>>> list_2 = ["a", "f", "c", "m"] 
>>> 
>>> main_list = [item for item in list_2 if item not in list_1]
>>> main_list
['f', 'm']

Bearbeiten:

Wie in den Kommentaren unten erwähnt, ist das Obige bei großen Listen nicht die ideale Lösung. Wenn dies der Fall ist, wäre eine bessere Option die Konvertierung list_1in eine seterste:

set_1 = set(list_1)  # this reduces the lookup time from O(n) to O(1)
main_list = [item for item in list_2 if item not in set_1]
ettanany
quelle
3
Hinweis: Für größere list_1, würden Sie wollen ein preconvert set/ frozenset, zum Beispiel set_1 = frozenset(list_1), dann main_list = [item for item in list_2 if item not in set_1], die Verringerung der Prüfzeit von O(n)pro Artikel bis (etwa) O(1).
ShadowRanger
@ettanany Bitte seien Sie vorsichtig, wenn Sie die Lösung wie von ettanany angegeben ausprobieren. Ich habe die Lösung von ettanany so wie sie ist ausprobiert und es ist in der Tat sehr langsam für eine größere Liste. Können Sie die Antwort aktualisieren, um den Vorschlag von Shadowranger aufzunehmen?
Doubledown
Wäre es möglich, den Index anstelle der Zeichenfolge zu erhalten?
JareBear
@ JareBear Sie können dafür verwenden enumerate():[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany
@ ettanany's vielen Dank !! Ich werde das so schnell wie möglich umsetzen, ich hatte es getan. Aber Ihr Code sieht so viel sauberer aus.
JareBear
5

Wenn Sie eine einzeilige Lösung (Ignorieren von Importen) wünschen, die nur O(max(n, m))Arbeit für Längeneingaben erfordert nund mnicht O(n * m)funktioniert, können Sie dies mit dem itertoolsModul tun :

from itertools import filterfalse

main_list = list(filterfalse(set(list_1).__contains__, list_2))

Dies nutzt die funktionalen Funktionen, die eine Rückruffunktion bei der Erstellung übernehmen, sodass der Rückruf einmal erstellt und für jedes Element wiederverwendet werden kann, ohne dass er irgendwo gespeichert werden muss (da filterfalseer intern gespeichert wird ). Listenverständnisse und Generatorausdrücke können dies, aber es ist hässlich. †

Das ergibt die gleichen Ergebnisse in einer einzigen Zeile wie:

main_list = [x for x in list_2 if x not in list_1]

mit der Geschwindigkeit von:

set_1 = set(list_1)
main_list = [x for x in list_2 if x not in set_1]

Wenn die Vergleiche positionell sein sollen, gilt Folgendes:

list_1 = [1, 2, 3]
list_2 = [2, 3, 4]

sollte produzieren:

main_list = [2, 3, 4]

(weil der Wert in list_2eine Übereinstimmung mit demselben Index in hat list_1), sollten Sie auf jeden Fall mit Patricks Antwort gehen , die keine temporären lists oder sets beinhaltet (selbst wenn sets ungefähr ist O(1), haben sie einen höheren "konstanten" Faktor pro Prüfung als einfache Gleichheitsprüfungen ) und beinhaltet O(min(n, m))Arbeit, weniger als jede andere Antwort, und wenn Ihr Problem positionsempfindlich ist, ist dies die einzig richtige Lösung, wenn übereinstimmende Elemente bei nicht übereinstimmenden Offsets angezeigt werden.

†: Der Weg, dasselbe mit einem Listenverständnis wie ein Einzeiler zu tun, besteht darin, verschachtelte Schleifen zu missbrauchen, um Werte in der "äußersten" Schleife zu erstellen und zwischenzuspeichern, z.

main_list = [x for set_1 in (set(list_1),) for x in list_2 if x not in set_1]

set_1Dies bietet auch einen geringfügigen Leistungsvorteil für Python 3 (da jetzt der Verständniscode lokal festgelegt ist und nicht bei jeder Prüfung aus dem verschachtelten Bereich nachgeschlagen wird). Bei Python 2 spielt dies keine Rolle, da Python 2 keine Verschlüsse für Python 3 verwendet Listenverständnisse (sie arbeiten in demselben Bereich, in dem sie verwendet werden).

ShadowRanger
quelle
4
main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"]

for i in list_2:
    if i not in list_1:
        main_list.append(i)

print(main_list)

Ausgabe:

['f', 'm']
Inconnu
quelle
Wie die äquivalente Liste Verständnis basierten Lösung , wird dieser langsam sein , wenn list_1groß ist, und list_2ist von nicht-trivialer Größe, weil es sich um len(list_2) O(n)Scans list_1, so dass es O(n * m)(wo nund msind die Längen list_2und list_1jeweils). Wenn Sie list_1in ein set/ frozensetim Voraus konvertieren , können die enthaltenen Überprüfungen durchgeführt werden O(1), wodurch die Gesamtarbeit O(n)auf der Länge von list_2(technisch gesehen O(max(n, m)), da Sie daran O(m)arbeiten, das zu erstellen set) ausgeführt wird.
ShadowRanger
1

Ich würde zipdie Listen zusammenfügen, um sie Element für Element zu vergleichen.

main_list = [b for a, b in zip(list1, list2) if a!= b]
Patrick Haugh
quelle
Wenn das OP Element für Element vergleichen möchte (es ist unklar, das Beispiel könnte in beide Richtungen gehen), ist dies viel effizienter als die anderen Antworten, da es sich um einen einzelnen billigen Durchgang über beide lists handelt, wobei ein einzelnes neues erstellt listwird und keine zusätzlichen Provisorien erstellt werden , keine teuren Eindämmungsprüfungen usw.
ShadowRanger
1
@ ShadowRanger Dies würde nur für elementweise Unterschiede funktionieren, was ein wichtiger Punkt ist
Ford Präfekt
@fordprefect: Ja. Meine eigene Antwort bezieht sich auf positionsunabhängige Unterschiede.
ShadowRanger
1

Ich habe zwei Methoden verwendet und fand eine Methode nützlich gegenüber der anderen. Hier ist meine Antwort:

Meine Eingabedaten:

crkmod_mpp = ['M13','M18','M19','M24']
testmod_mpp = ['M13','M14','M15','M16','M17','M18','M19','M20','M21','M22','M23','M24']

Methode 1: np.setdiff1dIch mag diesen Ansatz gegenüber anderen, weil er die Position beibehält

test= list(np.setdiff1d(testmod_mpp,crkmod_mpp))
print(test)
['M15', 'M16', 'M22', 'M23', 'M20', 'M14', 'M17', 'M21']

Methode2: Es gibt zwar die gleiche Antwort wie in Methode1, stört aber die Reihenfolge

test = list(set(testmod_mpp).difference(set(crkmod_mpp)))
print(test)
['POA23', 'POA15', 'POA17', 'POA16', 'POA22', 'POA18', 'POA24', 'POA21']

Methode1 np.setdiff1derfüllt meine Anforderungen perfekt. Diese Antwort zur Information.

Msquare
quelle
0

Wenn die Anzahl der Vorkommen berücksichtigt werden soll, müssen Sie wahrscheinlich Folgendes verwenden collections.Counter:

list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 
from collections import Counter
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['f', 'm']

Wie versprochen kann dies auch eine unterschiedliche Anzahl von Ereignissen als "Unterschied" behandeln:

list_1=["a", "b", "c", "d", "e", 'a']
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['a', 'f', 'm']
MSeifert
quelle
-1

Entfernen Sie aus ser1 die in ser2 vorhandenen Elemente.

Eingang

ser1 = pd.Series ([1, 2, 3, 4, 5]) ser2 = pd.Series ([4, 5, 6, 7, 8])

Lösung

ser1 [~ ser1.isin (ser2)]

adnan
quelle
Willkommen bei Stack Overflow. Diese Frage hat acht weitere Antworten, von denen eine vom Originalplakat akzeptiert wurde. Bitte beschreiben Sie, wie sich Ihre Antwort gegenüber dem, was bereits präsentiert wurde, verbessert.
chb