Alle Vorkommen eines Wertes aus einer Liste entfernen?


377

In Python remove()wird das erste Vorkommen von Wert in einer Liste entfernt.

Wie entferne ich alle Vorkommen eines Wertes aus einer Liste?

Das habe ich vor:

>>> remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
[1, 3, 4, 3]

Antworten:


505

Funktionsansatz:

Python 3.x.

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter((2).__ne__, x))
[1, 3, 3, 4]

oder

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter(lambda a: a != 2, x))
[1, 3, 3, 4]

Python 2.x.

>>> x = [1,2,3,2,2,2,3,4]
>>> filter(lambda a: a != 2, x)
[1, 3, 3, 4]

120
Verwenden Sie das Listenverständnis über dem Filter + Lambda; Ersteres ist besser lesbar und im Allgemeinen effizienter.
Hablabit

17
s / allgemein / allgemein sein /
habnabit

99
Der Code für Habnabits Vorschlag sieht folgendermaßen aus:[y for y in x if y != 2]
Coredumperror

8
Ich würde diese Lösung nicht als die beste bezeichnen. Das Verständnis von Listen ist beim Durchblättern von Code schneller und einfacher zu verstehen. Dies wäre eher ein Perl-Weg als Python.
Peter Nimroot

3
-1 zum direkten Aufrufen __ne__. Das Vergleichen zweier Werte ist weitaus komplexer als nur das Aufrufen __eq__oder Aufrufen __ne__eines dieser Werte . Hier funktioniert es möglicherweise richtig, weil Sie nur Zahlen vergleichen, aber im Allgemeinen ist das falsch und ein Fehler.
Aran-Fey

211

Sie können ein Listenverständnis verwenden:

def remove_values_from_list(the_list, val):
   return [value for value in the_list if value != val]

x = [1, 2, 3, 4, 2, 2, 3]
x = remove_values_from_list(x, 2)
print x
# [1, 3, 4, 3]

7
Wie würden Sie Elemente entfernen, ohne sie zu überprüfen?
Alexander Ljungberg

18
Dadurch wird die ursprüngliche Liste nicht geändert, sondern eine neue Liste zurückgegeben.
John Y

6
@Selinap: Nein, dies ist optimal, da die Liste nur einmal gescannt wird. In Ihrem ursprünglichen Code scannen sowohl der inOperator als auch die removeMethode die gesamte Liste (bis sie eine Übereinstimmung finden), sodass Sie die Liste auf diese Weise mehrmals scannen.
John Kugelman

4
@mhawke, @John Y: benutze einfach x [:] = ... anstelle von x = und es wird "an Ort und Stelle" sein, anstatt nur den Namen 'x' neu zu binden (Geschwindigkeit ist im Wesentlichen gleich und VIEL schneller als x .entfernen kann sein !!!).
Alex Martelli

10
Ich stimme dafür, weil ich nach 6 Jahren Python Lambdas immer noch nicht verstehe :)
Benjamin

107

Sie können die Slice-Zuweisung verwenden, wenn die ursprüngliche Liste geändert werden muss, während Sie dennoch ein effizientes Listenverständnis (oder einen Generatorausdruck) verwenden.

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> x[:] = (value for value in x if value != 2)
>>> x
[1, 3, 4, 3]

1
@Selinap: Der Filter ändert die Liste nicht, sondern gibt eine neue Liste zurück.
EM

Filter- und Listenverständnis ändert keine Liste. Slice-Zuweisung tut. und das ursprüngliche Beispiel tut es.
A. Coady

7
Ich mag das, weil es die Liste ändert, auf die sich x bezieht. Wenn andere Verweise auf diese Liste vorhanden sind, sind sie ebenfalls betroffen. Dies steht im Gegensatz zu den x = [ v for v in x if x != 2 ]Vorschlägen, bei denen eine neue Liste erstellt und x geändert wird, um darauf zu verweisen, wobei die ursprüngliche Liste unberührt bleibt.
Hannes

40

Wiederholen Sie die Lösung des ersten Beitrags abstrakter:

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> while 2 in x: x.remove(2)
>>> x
[1, 3, 4, 3]

19
Es ist jedoch O (n * n).
Hannes

@Hannes wäre es nicht O (n), da es nur einmal durch die Schleife geht und gleichzeitig das Objekt entfernt?
Penta

1
Überlegen Sie x = [1] * 10000 + [2] * 1000. Der Schleifenkörper wird 1000 Mal ausgeführt und .remove () muss bei jedem Aufruf 10000 Elemente überspringen. Das riecht für mich nach O (n * n), ist aber kein Beweis. Ich denke, der Beweis wäre anzunehmen, dass die Anzahl der 2s in der Liste proportional zu ihrer Länge ist. Dieser Proportionalitätsfaktor verschwindet dann in der Big-O-Notation. Der beste Fall von nur einer konstanten Anzahl von 2s in der Liste ist jedoch nicht O (n ^ 2), sondern nur O (2n), was O (n) ist.
Hannes

23

Siehe die einfache Lösung

>>> [i for i in x if i != 2]

Dies gibt eine Liste mit allen Elementen von xohne zurück2


11

Alle obigen Antworten (mit Ausnahme von Martin Anderssons) erstellen eine neue Liste ohne die gewünschten Elemente, anstatt die Elemente aus der ursprünglichen Liste zu entfernen.

>>> import random, timeit
>>> a = list(range(5)) * 1000
>>> random.shuffle(a)

>>> b = a
>>> print(b is a)
True

>>> b = [x for x in b if x != 0]
>>> print(b is a)
False
>>> b.count(0)
0
>>> a.count(0)
1000

>>> b = a
>>> b = filter(lambda a: a != 2, x)
>>> print(b is a)
False

Dies kann wichtig sein, wenn Sie andere Verweise auf die Liste haben.

Verwenden Sie eine solche Methode, um die Liste zu ändern

>>> def removeall_inplace(x, l):
...     for _ in xrange(l.count(x)):
...         l.remove(x)
...
>>> removeall_inplace(0, b)
>>> b is a
True
>>> a.count(0)
0

Was die Geschwindigkeit betrifft, sind die Ergebnisse auf meinem Laptop (alle auf einer 5000-Eintragsliste mit 1000 entfernten Einträgen).

  • Listenverständnis - ~ 400us
  • Filter - ~ 900us
  • .remove () Schleife - 50ms

Die .remove-Schleife ist also ungefähr 100x langsamer ........ Hmmm, vielleicht ist ein anderer Ansatz erforderlich. Am schnellsten habe ich das Listenverständnis verwendet, aber dann den Inhalt der ursprünglichen Liste ersetzt.

>>> def removeall_replace(x, l):
....    t = [y for y in l if y != x]
....    del l[:]
....    l.extend(t)
  • removeeall_replace () - 450us

Warum nicht einfach die neue Liste unter der alten Adresse neu zuweisen? def remove_all(x, l): return [y for y in l if y != x]dannl = remove_all(3,l)
Dannid

@Dannid Das ist die zweite Methode im ersten Codefeld. Es wird eine neue Liste erstellt, und Sie ändern die alte Liste nicht. Alle anderen Verweise auf die Liste bleiben ungefiltert.
Paul S

Ah richtig. Ich war so sehr mit der Definition einer Methode beschäftigt, dass ich die einfache Aufgabe übersah, die Sie bereits erledigt hatten.
Dannid

7

du kannst das

while 2 in x:   
    x.remove(2)

3
Das ist eine schlechte Lösung, da die Liste 2 * n Mal für n Vorkommen von 2
durchlaufen werden muss

Es wird nicht empfohlen, der Liste, die Sie durchlaufen, etwas hinzuzufügen oder daraus zu entfernen. Schlechte Praxis IMHO.
Aman Mathur

5

Auf Kosten der Lesbarkeit denke ich, dass diese Version etwas schneller ist, da sie die Zeit nicht zwingt, die Liste erneut zu überprüfen, und daher genau die gleiche Arbeit erledigt, die das Entfernen sowieso tun muss:

x = [1, 2, 3, 4, 2, 2, 3]
def remove_values_from_list(the_list, val):
    for i in range(the_list.count(val)):
        the_list.remove(val)

remove_values_from_list(x, 2)

print(x)

Für die Liste, die Sie in Ihrem Code anzeigen, ist dieser Ansatz nach meiner Messung etwa 36% langsamer als die Listenverständnismethode (die eine Kopie zurückgibt).
DJsmith

Gut, dass du das bemerkt hast. Da ich jedoch der Meinung bin, dass dies Ihrem Urteil entgangen sein könnte, habe ich meine Version mit dem allerersten Vorschlag des Fragestellers verglichen.
Martin Andersson

4

Numpy Ansatz und Timings gegen eine Liste / ein Array mit 1.000.000 Elementen:

Timings:

In [10]: a.shape
Out[10]: (1000000,)

In [13]: len(lst)
Out[13]: 1000000

In [18]: %timeit a[a != 2]
100 loops, best of 3: 2.94 ms per loop

In [19]: %timeit [x for x in lst if x != 2]
10 loops, best of 3: 79.7 ms per loop

Fazit: Numpy ist 27-mal schneller (auf meinem Notebook) als der Ansatz des Listenverständnisses

PS, wenn Sie Ihre reguläre Python-Liste lstin ein Numpy-Array konvertieren möchten:

arr = np.array(lst)

Installieren:

import numpy as np
a = np.random.randint(0, 1000, 10**6)

In [10]: a.shape
Out[10]: (1000000,)

In [12]: lst = a.tolist()

In [13]: len(lst)
Out[13]: 1000000

Prüfen:

In [14]: a[a != 2].shape
Out[14]: (998949,)

In [15]: len([x for x in lst if x != 2])
Out[15]: 998949

4
a = [1, 2, 2, 3, 1]
to_remove = 1
a = [i for i in a if i != to_remove]
print(a)

Vielleicht nicht die pythonischste, aber immer noch die einfachste für mich, haha


3

So entfernen Sie alle doppelten Vorkommen und lassen eines in der Liste:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

Hier ist die Funktion, die ich für Project Euler verwendet habe:

def removeOccurrences(e):
  return list(set(e))

2
Ich musste dies auf einem Vektor mit 250k-Werten tun, und es funktioniert wie ein Zauber.
Rschwieb

1
Die Antwort ist ja! Und ich verstehe vollkommen, ob ein Vektor, der lange dauert, für einen kompetenten Programmierer völlig verrückt klingt. Ich gehe die Probleme dort als Mathematiker an, ohne mir Gedanken über die Optimierung der Lösungen zu machen, und das kann zu Lösungen führen, die länger als das Nennwert sind. (Obwohl ich keine Geduld für Lösungen habe, die länger als 5 Minuten dauern.)
rschwieb

6
Dadurch wird jede Bestellung aus der Liste entfernt.
Asmeurer

4
@JaredBurrows vielleicht, weil es die aktuelle Frage nicht beantwortet, sondern eine ganz andere Frage.
Drevicko

6
-1, dies ist keine Antwort auf die Frage des OP. Es ist eine Lösung, um Duplikate zu entfernen, was eine ganz andere Sache ist.
Anoyz

2

Ich glaube, dies ist wahrscheinlich schneller als jede andere Möglichkeit, wenn Sie sich nicht um die Reihenfolge der Listen kümmern. Wenn Sie sich um die endgültige Reihenfolge kümmern, speichern Sie die Indizes des Originals und greifen Sie darauf zurück.

category_ids.sort()
ones_last_index = category_ids.count('1')
del category_ids[0:ones_last_index]

2
Ich verstehe, wohin du gehst, aber dieser Code funktioniert nicht, da du auch den
Startindex

2
for i in range(a.count(' ')):
    a.remove(' ')

Viel einfacher glaube ich.


2
Bitte bearbeiten Sie Ihre Antwort, um die Übersichtlichkeit zu verbessern. Bitte machen Sie deutlich, was genau Ihr empfohlener Code bewirkt, warum er funktioniert und warum dies Ihre Empfehlung ist. Bitte formatieren Sie Ihre Frage auch richtig, damit der Code vom Rest Ihrer Antwort deutlich erkennbar ist.
Ortund

2

Lassen

>>> x = [1, 2, 3, 4, 2, 2, 3]

Die einfachste und effizienteste Lösung, wie bereits erwähnt, ist

>>> x[:] = [v for v in x if v != 2]
>>> x
[1, 3, 4, 3]

Eine andere Möglichkeit, die weniger Speicher verbrauchen sollte, aber langsamer ist, ist

>>> for i in range(len(x) - 1, -1, -1):
        if x[i] == 2:
            x.pop(i)  # takes time ~ len(x) - i
>>> x
[1, 3, 4, 3]

Timing-Ergebnisse für Listen mit einer Länge von 1000 und 100000 mit 10% übereinstimmenden Einträgen: 0,16 vs 0,25 ms und 23 vs 123 ms.

Timing mit Länge 1000

Timing mit Länge 100000


1

Entfernen Sie alle Vorkommen eines Werts aus einer Python-Liste

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list():
    for list in lists:
      if(list!=7):
         print(list)
remove_values_from_list()

Ergebnis: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11

Alternative,

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list(remove):
    for list in lists:
      if(list!=remove):
        print(list)
remove_values_from_list(7)

Ergebnis: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11


"Python 'verschachtelt für jede if-Schleife' innerhalb einer Funktion, die mit 100% iger Genauigkeit arbeitet!"
Rafiqul786

Sie ändern die Liste nicht, sondern drucken nur die Elemente. Auch das Benennen einer Liste als Listen ist verwirrend
kon psych

0

Wenn Sie nicht eingebaut waren filteroder keinen zusätzlichen Platz benötigen und eine lineare Lösung benötigen ...

def remove_all(A, v):
    k = 0
    n = len(A)
    for i in range(n):
        if A[i] !=  v:
            A[k] = A[i]
            k += 1

    A = A[:k]

0
hello =  ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
#chech every item for a match
for item in range(len(hello)-1):
     if hello[item] == ' ': 
#if there is a match, rebuild the list with the list before the item + the list after the item
         hello = hello[:item] + hello [item + 1:]
print hello

['Hallo Welt']


Bitte versuchen Sie, Ihre Antwort mit einer Erklärung zu erläutern.
Parlad

0

Ich habe das nur für eine Liste gemacht. Ich bin nur ein Anfänger. Ein etwas fortgeschrittener Programmierer kann sicherlich eine solche Funktion schreiben.

for i in range(len(spam)):
    spam.remove('cat')
    if 'cat' not in spam:
         print('All instances of ' + 'cat ' + 'have been removed')
         break

0

Wir können auch alle vor Ort entfernen, indem wir entweder deloder pop:

import random

def remove_values_from_list(lst, target):
    if type(lst) != list:
        return lst

    i = 0
    while i < len(lst):
        if lst[i] == target:
            lst.pop(i)  # length decreased by 1 already
        else:
            i += 1

    return lst

remove_values_from_list(None, 2)
remove_values_from_list([], 2)
remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)], 2)
print(len(lst))

Nun zur Effizienz:

In [21]: %timeit -n1 -r1 x = random.randrange(0,10)
1 loop, best of 1: 43.5 us per loop

In [22]: %timeit -n1 -r1 lst = [random.randrange(0, 10) for x in range(1000000)]
g1 loop, best of 1: 660 ms per loop

In [23]: %timeit -n1 -r1 lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)]
    ...: , random.randrange(0,10))
1 loop, best of 1: 11.5 s per loop

In [27]: %timeit -n1 -r1 x = random.randrange(0,10); lst = [a for a in [random.randrange(0, 10) for x in
    ...:  range(1000000)] if x != a]
1 loop, best of 1: 710 ms per loop

Wie wir sehen, benötigt die In-Place-Version remove_values_from_list()keinen zusätzlichen Speicher, aber die Ausführung dauert viel länger:

  • 11 Sekunden für Inplace-Entfernungswerte
  • 710 Millisekunden für das Listenverständnis, wodurch eine neue Liste im Speicher zugewiesen wird

0

Niemand hat eine optimale Antwort für die zeitliche und räumliche Komplexität veröffentlicht, daher dachte ich, ich würde es versuchen. Hier ist eine Lösung, die alle Vorkommen eines bestimmten Werts entfernt, ohne ein neues Array zu erstellen, und dies zu einem effizienten Zeitpunkt. Der Nachteil ist, dass die Elemente die Reihenfolge nicht aufrechterhalten .

Zeitliche Komplexität: O (n)
Zusätzliche räumliche Komplexität: O (1)

def main():
    test_case([1, 2, 3, 4, 2, 2, 3], 2)     # [1, 3, 3, 4]
    test_case([3, 3, 3], 3)                 # []
    test_case([1, 1, 1], 3)                 # [1, 1, 1]


def test_case(test_val, remove_val):
    remove_element_in_place(test_val, remove_val)
    print(test_val)


def remove_element_in_place(my_list, remove_value):
    length_my_list = len(my_list)
    swap_idx = length_my_list - 1

    for idx in range(length_my_list - 1, -1, -1):
        if my_list[idx] == remove_value:
            my_list[idx], my_list[swap_idx] = my_list[swap_idx], my_list[idx]
            swap_idx -= 1

    for pop_idx in range(length_my_list - swap_idx - 1):
        my_list.pop() # O(1) operation


if __name__ == '__main__':
    main()

-1

Über die Geschwindigkeit!

import time
s_time = time.time()

print 'start'
a = range(100000000)
del a[:]
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 3.25

s_time = time.time()
print 'start'
a = range(100000000)
a = []
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 2.11

-3
p=[2,3,4,4,4]
p.clear()
print(p)
[]

Nur mit Python 3


2
Komischerweise liegt dies im Rahmen der gestellten Frage und ist richtig.
Erich

Ich sehe nicht, wie es richtig ist. Dadurch werden alle Elemente aus der Liste entfernt, nicht alle Vorkommen eines Werts .
Georgy

-3

Was ist falsch mit:

Motor=['1','2','2']
For i in Motor:
       If i  != '2':
       Print(i)
Print(motor)

Mit Anakonda


2
Bitte erläutern Sie Ihre Codezeilen, damit andere Benutzer die Funktionalität verstehen können. Vielen Dank!
Ignacio Ara

Dieser Code entfernt nichts aus der Liste.
Georgy
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.