it-swarm.com.de

Python-Listen-Subtraktionsoperation

Ich möchte etwas Ähnliches machen:

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

Dies wird jedoch nicht von Python-Listen unterstützt. Was ist der beste Weg, dies zu tun?

169
daydreamer

Verwenden Sie ein Listenverständnis:

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

Wenn Sie die --Infix-Syntax verwenden möchten, können Sie Folgendes tun:

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

sie können es dann wie folgt verwenden: 

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

Wenn Sie jedoch nicht unbedingt Listeneigenschaften (z. B. Reihenfolge) benötigen, verwenden Sie einfach Sets, wie die anderen Antworten empfehlen.

241
aaronasterling

Differenz setzen

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

Oder Sie haben nur x und y Sets, so dass Sie keine Konvertierungen vornehmen müssen.

190
quantumSoup

Das ist eine Operation "Subtraktion einstellen". 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

Ausgabe:

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

wenn doppelte und bestellende Artikel problematisch 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]
31
nguyên

Für viele Anwendungsfälle lautet die 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 quantumSoup .

die Version von aaronasterling führt len(y)-Elementvergleiche für jedes Element in x durch, sodass quadratische Zeit benötigt wird. Die Version von QuantumSoup verwendet Mengen, dh, es wird für jedes Element in x— eine einzige konstante Zeitsatzsuche ausgeführt. Da sie jedoch bothx und y in Mengen konvertiert, verliert sie die Reihenfolge Ihrer Elemente.

Wenn Sie nur y in eine Menge konvertieren und x in der Reihenfolge iterieren, erhalten Sie das Beste aus beiden Welten - lineare Zeit und die Erhaltung der Reihenfolge. *


Dies hat jedoch immer noch ein Problem aus der Version von QuantumSoup: Es erfordert, dass Ihre Elemente hashierbar sind. Dies ist in der Art von Mengen ziemlich eingebettet. ** Wenn Sie versuchen, eine Liste von Diktaten von einer anderen Liste von Diktaten abzuziehen, aber die zu subtrahierende Liste ist groß, was tun Sie dann?

Wenn Sie Ihre Werte so gestalten können, dass sie hashbar sind, wird das Problem gelöst. Zum Beispiel mit einem flachen Wörterbuch, dessen Werte selbst hashable 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. handelt es sich häufig um JSON-kompatible Werte, die hashable sind, oder um Listen oder Dictes, deren Werte rekursiv vom gleichen Typ sind), können Sie diese Lösung dennoch verwenden. Einige Typen können jedoch nicht in etwas Hashbares konvertiert werden.


Wenn Ihre Elemente nicht hashierbar sind und nicht erstellt werden können, sie jedoch vergleichbar sind, können Sie zumindest eine logarithmische Zeit (O(N*log M)) erhalten, die viel besser ist als die O(N*M)-Zeit der Listenlösung, jedoch nicht als gut wie die O(N+M)-Zeit der eingestellten Lösung) durch Sortieren und Verwenden von 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.


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

** Der Grund, warum Suchvorgänge konstant sind, besteht darin, dass der Wert nur durch einen Hashwert überprüft und überprüft werden muss, ob für diesen Hash ein Eintrag vorhanden ist. Wenn der Wert nicht gehasht werden kann, funktioniert dies nicht.

13
abarnert

Das Nachschlagen von Werten in Sätzen ist schneller als das Nachschlagen in Listen:

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

Ich glaube, das wird etwas besser skalieren als:

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

Beide behalten die Reihenfolge der Listen bei.

7
rudolfbyker

Versuche dies.

def subtract_lists(a, b):
    """ Subtracts two lists. Throws ValueError if b contains items not in a """
    # Terminate if b is empty, otherwise remove b[0] from a and recurse
    return a if len(b) == 0 else [a[:i] + subtract_lists(a[i+1:], b[1:]) 
                                  for i in [a.index(b[0])]][0]

>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0]
>>> x = [1,2,3,4,5,6,7,8,9,0,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0, 9]     #9 is only deleted once
>>>
2
user3435376

Ich denke das ist schneller:

In [1]: a = [1,2,3,4,5]

In [2]: b = [2,3,4,5]

In [3]: c = set(a) ^ set(b)

In [4]: c
Out[4]: {1}
1
Eds_k

Dieses Beispiel zieht zwei Listen ab:

# List of pairs of points
list = []
list.append([(602, 336), (624, 365)])
list.append([(635, 336), (654, 365)])
list.append([(642, 342), (648, 358)])
list.append([(644, 344), (646, 356)])
list.append([(653, 337), (671, 365)])
list.append([(728, 13), (739, 32)])
list.append([(756, 59), (767, 79)])

itens_to_remove = []
itens_to_remove.append([(642, 342), (648, 358)])
itens_to_remove.append([(644, 344), (646, 356)])

print("Initial List Size: ", len(list))

for a in itens_to_remove:
    for b in list:
        if a == b :
            list.remove(b)

print("Final List Size: ", len(list))
1
Joao Nicolau

Die Antwort von @aaronasterling sieht gut aus, ist jedoch nicht mit der Standardoberfläche von list kompatibel: x = MyList(1, 2, 3, 4) vs x = MyList([1, 2, 3, 4]). Daher kann der folgende Code als Python-Liste verwendet werden:

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

Beispiel: 

x = MyList([1, 2, 3, 4])
y = MyList([2, 5, 2])
z = x - y
1
Hamid Zafar

Ich denke, der einfachste Weg, dies zu erreichen, ist die Verwendung von set ().

>>> x = [1,2,3,4,5,6,7,8,9,0]  
>>> y = [1,3,5,7,9]  
>>> list(set(x)- set(y))
[0, 2, 4, 6, 8]
0
Loochie

Sie können Counter aus Sammlungen verwenden:

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