Rimuovere tutte le occorrenze di un valore da un elenco?


378

In Python remove()rimuoverà la prima occorrenza di valore in un elenco.

Come rimuovere tutte le occorrenze di un valore da un elenco?

Questo è ciò che ho in mente:

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

Risposte:


506

Approccio funzionale:

Python 3.x

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

o

>>> 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
Usa la comprensione dell'elenco sul filtro + lambda; il primo è più leggibile oltre che generalmente più efficiente.
habnabit,

17
s / generalmente / generalmente essendo /
habnabit il

99
Il codice per il suggerimento di habnabit è simile al seguente:[y for y in x if y != 2]
coredumperror

8
Non definirei questa soluzione la migliore. Le comprensioni dell'elenco sono più veloci e più facili da comprendere mentre si scorre il codice. Preferirebbe essere più un modo Perl che Python.
Peter Nimroot,

3
-1 per invocare direttamente __ne__. Il confronto di due valori è un processo molto più complesso di una semplice chiamata __eq__o __ne__su uno di essi. Potrebbe funzionare correttamente qui perché stai solo confrontando i numeri, ma nel caso generale questo è errato e un bug.
Aran-Fey,

212

È possibile utilizzare una comprensione dell'elenco:

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
Come rimuoveresti gli articoli senza controllarli?
Alexander Ljungberg,

18
Ciò non modifica l'elenco originale ma restituisce un nuovo elenco.
John Y,

6
@Selinap: No, questo è ottimale in quanto scansiona l'elenco una sola volta. Nel tuo codice originale sia l' inoperatore che il removemetodo scansionano l'intero elenco (fino a quando non trovano una corrispondenza) in modo da finire la scansione dell'elenco più volte in quel modo.
John Kugelman,

4
@mhawke, @John Y: basta usare x [:] = ... invece di x = e sarà "sul posto" piuttosto che semplicemente ribaltare il nome 'x' (la velocità è essenzialmente la stessa e MOLTO più veloce di x .remove può essere !!!).
Alex Martelli,

10
Lo voto perché dopo 6 anni di Python non capisco ancora Lambdas :)
Benjamin,

107

È possibile utilizzare l'assegnazione delle sezioni se l'elenco originale deve essere modificato, pur utilizzando una comprensione dell'elenco efficiente (o espressione del generatore).

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

1
@Selinap: il filtro non modifica l'elenco, restituisce un nuovo elenco.
EM,

la comprensione del filtro e dell'elenco non modifica un elenco. l'assegnazione della fetta fa. e l'esempio originale fa.
A. Coady,

7
Mi piace perché modifica l'elenco a cui x fa riferimento. Se ci sono altri riferimenti a quell'elenco, anche questi saranno interessati. Ciò è in contrasto con le x = [ v for v in x if x != 2 ]proposte, che creano un nuovo elenco e modificano x per fare riferimento ad esso, lasciando intatto l'elenco originale.
Hannes,

40

Ripetendo la soluzione del primo post in modo più astratto:

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

19
È O (n * n), comunque.
Hannes,

@Hannes non sarebbe O (n) poiché sta attraversando il ciclo solo una volta e allo stesso tempo rimuove l'elemento?
penta,

1
Prendere in considerazione x = [1] * 10000 + [2] * 1000. Il corpo del loop viene eseguito 1000 volte e .remove () deve saltare 10000 elementi ogni volta che viene invocato. Per me puzza di O (n * n) ma non è una prova. Penso che la prova sarebbe quella di presumere che il numero di 2 nell'elenco sia proporzionale alla sua lunghezza. Tale fattore di proporzionalità scompare quindi nella notazione big-O. Il caso migliore, tuttavia, di solo un numero costante di 2s nell'elenco, non è O (n ^ 2), solo O (2n) che è O (n).
Hannes,

23

Vedi la soluzione semplice

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

Questo restituirà un elenco con tutti gli elementi di xsenza2


11

Tutte le risposte sopra (eccetto quelle di Martin Andersson) creano un nuovo elenco senza gli elementi desiderati, anziché rimuoverli dall'elenco originale.

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

Questo può essere importante se hai altri riferimenti all'elenco in giro.

Per modificare l'elenco in atto, utilizzare un metodo come questo

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

Per quanto riguarda la velocità, i risultati sul mio laptop sono (tutti su un elenco di 5000 voci con 1000 voci rimosse)

  • Comprensione lista - ~ 400us
  • Filtro - ~ 900us
  • .remove () loop - 50ms

Quindi il ciclo .remove è circa 100 volte più lento ........ Hmmm, forse è necessario un approccio diverso. Il più veloce che ho trovato è usare la comprensione dell'elenco, ma poi sostituire il contenuto dell'elenco originale.

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

Perché non riassegnare la nuova lista sotto il vecchio indirizzo allora? def remove_all(x, l): return [y for y in l if y != x]poil = remove_all(3,l)
Dannid

@Dannid Questo è il secondo metodo nella prima casella di codice. Crea un nuovo elenco e non stai modificando il vecchio elenco. Qualsiasi altro riferimento all'elenco rimarrà non filtrato.
Paul S

Ah giusto. Sono stato così preso dalla definizione di un metodo, ho trascurato il semplice compito che avevi già fatto.
Dannid,

7

Puoi farlo

while 2 in x:   
    x.remove(2)

3
Questa è una cattiva soluzione, poiché l'elenco deve essere attraversato 2 * n volte per n occorrenze di 2.
cxxl

Non è consigliabile aggiungere o rimuovere dall'elenco che si sta attraversando. Cattiva pratica IMHO.
Aman Mathur,

5

A costo di leggibilità, penso che questa versione sia leggermente più veloce in quanto non costringe il tempo a riesaminare l'elenco, quindi facendo esattamente lo stesso lavoro di rimozione deve comunque fare:

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)

Per l'elenco che mostri nel tuo codice, questo approccio è circa il 36% più lento rispetto al metodo di comprensione dell'elenco (che restituisce una copia), secondo la mia misurazione.
djsmith,

Bene, l'hai notato. Tuttavia, poiché penso che avrebbe potuto sfuggire al tuo giudizio, stavo confrontando la mia versione con la prima proposta fatta dall'autore della domanda.
Martin Andersson,

4

Approccio e tempismo numpy rispetto a un elenco / array con 1.000.000 di elementi:

Tempi:

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

Conclusione: numpy è 27 volte più veloce (sul mio taccuino) rispetto all'approccio alla comprensione dell'elenco

PS se vuoi convertire il tuo normale elenco Python lstin numpy array:

arr = np.array(lst)

Impostare:

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

Dai un'occhiata:

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)

Forse non il più pitonico ma comunque il più semplice per me haha


3

Per rimuovere tutte le occorrenze duplicate e lasciarne una nell'elenco:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

Ecco la funzione che ho usato per Project Euler:

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

2
Avevo bisogno di farlo su un vettore con valori di 250k e funziona come un incantesimo.
rschwieb,

1
La risposta è si! E capisco perfettamente se avere un vettore che a lungo sembra completamente pazzo per un programmatore competente. Mi affaccio ai problemi lì come matematico, non preoccupandomi di ottimizzare le soluzioni, e ciò può portare a soluzioni più lunghe della pari. (Anche se non ho alcuna pazienza per soluzioni più lunghe di 5 minuti.)
rschwieb

6
Ciò rimuoverà qualsiasi ordine dall'elenco.
asmeurer,

4
@JaredBurrows forse perché non risponde alla domanda così com'è attualmente, ma una domanda abbastanza diversa.
drevicko,

6
-1, questa non è una risposta alla domanda del PO. È una soluzione per rimuovere i duplicati, che è una questione completamente diversa.
Anoyz,

2

Credo che questo sia probabilmente più veloce di qualsiasi altro modo se non ti interessa l'ordine delle liste, se ti preoccupi dell'ordine finale memorizza gli indici dall'originale e ricorri a quello.

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

2
Capisco dove stai andando, ma questo codice non funzionerà poiché è necessario anche l'indice iniziale e non solo 0.
Shedokan,

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

Molto più semplice credo.


2
si prega di modificare la risposta in modo da migliorare la chiarezza. Per favore chiarisci cosa fa esattamente il tuo codice raccomandato, perché funziona e perché questa è la tua raccomandazione. Si prega inoltre di formattare correttamente la domanda in modo che il codice sia chiaramente distinguibile dal resto della risposta.
Ortund,

2

Permettere

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

La soluzione più semplice ed efficiente come già pubblicata in precedenza è

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

Un'altra possibilità che dovrebbe usare meno memoria ma essere più lenta è

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

Risultati di temporizzazione per elenchi di lunghezza 1000 e 100000 con voci di corrispondenza del 10%: 0,16 vs 0,25 ms e 23 vs 123 ms.

Tempismo con lunghezza 1000

Tempismo con lunghezza 100000


1

Rimuovi tutte le occorrenze di un valore da un elenco Python

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

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

In alternativa,

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)

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


"Python 'annidato per ogni ciclo if' all'interno di una funzione che funziona con precisione al 100%!"
rafiqul786,

Non si modifica l'elenco, si stampano solo gli elementi. Anche nominare un elenco come elenchi è
fonte di

0

Se non hai integrato filtero non desideri utilizzare spazio aggiuntivo e hai bisogno di una soluzione lineare ...

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

['Ciao mondo']


prova a elaborare la tua risposta con una spiegazione.
parlad

0

L'ho appena fatto per un elenco. Sono solo un principiante. Un programmatore leggermente più avanzato può sicuramente scrivere una funzione come questa.

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

0

Possiamo anche rimuovere sul posto tutti usando delo 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))

Ora per l'efficienza:

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

Come vediamo che la versione sul posto remove_values_from_list()non richiede memoria aggiuntiva, ma richiede molto più tempo per essere eseguita:

  • 11 secondi per i valori di rimozione sul posto
  • 710 milli secondi per la comprensione dell'elenco, che alloca un nuovo elenco in memoria

0

Nessuno ha pubblicato una risposta ottimale per la complessità del tempo e dello spazio, quindi ho pensato di provarlo. Ecco una soluzione che rimuove tutte le occorrenze di un valore specifico senza creare un nuovo array e una complessità temporale efficiente. Lo svantaggio è che gli elementi non mantengono l' ordine .

Complessità temporale: O (n)
Complessità spaziale aggiuntiva: 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

A proposito della velocità!

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

Solo con Python 3


2
Esilarante, questo rientra nell'ambito della domanda posta ed è corretto.
Erich,

Non vedo come sia corretto. Ciò rimuoverà tutti gli elementi dall'elenco, non tutte le occorrenze di un valore .
Georgy,

-3

Cosa c'è di sbagliato in:

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

Usando l'anaconda


2
Spiega le tue righe di codice in modo che altri utenti possano comprenderne le funzionalità. Grazie!
Ignacio Ara,

Questo codice non rimuoverà nulla dall'elenco.
Georgy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.