Xóa tất cả các lần xuất hiện của một giá trị khỏi danh sách?


378

Trong Python remove()sẽ loại bỏ sự xuất hiện đầu tiên của giá trị trong danh sách.

Làm thế nào để loại bỏ tất cả các lần xuất hiện của một giá trị khỏi danh sách?

Đây là những gì tôi có trong tâm trí:

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

Câu trả lời:


506

Phương pháp chức năng:

Python 3.x

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

hoặc là

>>> 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
Sử dụng danh sách hiểu qua bộ lọc + lambda; cái trước dễ đọc hơn ngoài hiệu quả cao hơn.
thói quen

17
s / nói chung / nói chung là /
habnabit

99
Mã cho đề xuất của habnabit trông như thế này:[y for y in x if y != 2]
coredumperror

8
Tôi sẽ không gọi giải pháp này là tốt nhất. Việc hiểu danh sách nhanh hơn và dễ hiểu hơn trong khi lướt qua mã. Điều này thà là một cách Perl hơn Python.
Peter Nimroot

3
-1 để gọi trực tiếp __ne__. So sánh hai giá trị là một quá trình phức tạp hơn nhiều so với chỉ gọi __eq__hoặc __ne__trên một trong số chúng. Nó có thể hoạt động chính xác ở đây vì bạn chỉ so sánh các số, nhưng trong trường hợp chung đó không chính xác và là một lỗi.
Aran-Fey

212

Bạn có thể sử dụng một danh sách hiểu:

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
Làm thế nào bạn sẽ loại bỏ các mục mà không kiểm tra chúng?
Alexander Ljungberg

18
Điều này không sửa đổi danh sách ban đầu nhưng trả về một danh sách mới.
John Y

6
@Selinap: Không, điều này là tối ưu vì nó chỉ quét danh sách một lần. Trong mã gốc của bạn, cả intoán tử và removephương thức đều quét toàn bộ danh sách (cho đến khi chúng tìm thấy kết quả khớp) để cuối cùng bạn quét danh sách nhiều lần theo cách đó.
John Kugelman

4
@mhawke, @John Y: chỉ cần sử dụng x [:] = ... thay vì x = và nó sẽ là "tại chỗ" thay vì chỉ đảo ngược tên 'x' (tốc độ về cơ bản là giống nhau và NHIỀU nhanh hơn x .remove có thể được !!!).
Alex Martelli

10
Tôi bỏ phiếu này vì sau 6 năm Python tôi vẫn không hiểu Lambdas :)
Benjamin

107

Bạn có thể sử dụng phép gán lát nếu danh sách gốc phải được sửa đổi, trong khi vẫn sử dụng cách hiểu danh sách hiệu quả (hoặc biểu thức trình tạo).

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

1
@Selinap: bộ lọc không sửa đổi danh sách, nó trả về một danh sách mới.
EM

bộ lọc và danh sách hiểu không sửa đổi một danh sách. phân công lát nào. và ví dụ ban đầu nào.
A. Coady

7
Tôi thích điều này bởi vì nó sửa đổi danh sách mà x đề cập đến. Nếu có bất kỳ tài liệu tham khảo nào khác vào danh sách đó, chúng cũng sẽ bị ảnh hưởng. Điều này trái ngược với các x = [ v for v in x if x != 2 ]đề xuất, tạo ra một danh sách mới và thay đổi x để tham chiếu đến nó, khiến cho danh sách ban đầu không bị ảnh hưởng.
Hannes

40

Lặp lại giải pháp của bài đầu tiên theo cách trừu tượng hơn:

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

19
Đó là O (n * n), mặc dù.
Hannes

@Hannes sẽ không phải là O (n) vì nó sẽ đi qua vòng lặp chỉ một lần & đồng thời xóa mục đó?
penta

1
Hãy xem xét x = [1] * 10000 + [2] * 1000. Phần thân vòng lặp thực thi 1000 lần và .remove () phải bỏ qua 10000 phần tử mỗi lần nó được gọi. Điều đó có mùi giống như O (n * n) đối với tôi nhưng không có bằng chứng. Tôi nghĩ bằng chứng sẽ là giả định rằng số lượng 2 trong danh sách tỷ lệ thuận với chiều dài của nó. Yếu tố tỷ lệ đó sau đó biến mất trong ký hiệu big-O. Tuy nhiên, trường hợp tốt nhất chỉ có số lượng 2 giây không đổi trong danh sách, không phải là O (n ^ 2), chỉ là O (2n) là O (n).
Hannes

23

Xem giải pháp đơn giản

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

Điều này sẽ trả về một danh sách có tất cả các yếu tố xmà không có2


11

Tất cả các câu trả lời ở trên (ngoài Martin Andersson) tạo ra một danh sách mới mà không có các mục mong muốn, thay vì xóa các mục khỏi danh sách ban đầu.

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

Điều này có thể quan trọng nếu bạn có các tài liệu tham khảo khác cho danh sách treo xung quanh.

Để sửa đổi danh sách tại chỗ, sử dụng một phương pháp như thế này

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

Liên quan đến tốc độ, kết quả trên máy tính xách tay của tôi là (tất cả trong danh sách 5000 mục với 1000 mục đã bị xóa)

  • Nghe hiểu - ~ 400us
  • Bộ lọc - ~ 900us
  • vòng lặp .remove () - 50ms

Vì vậy, vòng lặp .remove chậm hơn khoảng 100 lần ........ Hmmm, có thể cần một cách tiếp cận khác. Cách nhanh nhất tôi tìm thấy là sử dụng khả năng hiểu danh sách, nhưng sau đó thay thế nội dung của danh sách ban đầu.

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

Tại sao không chỉ định lại danh sách mới theo địa chỉ cũ? def remove_all(x, l): return [y for y in l if y != x]sau đól = remove_all(3,l)
Dannid

@Dannid Đó là phương pháp thứ hai trong hộp mã đầu tiên. Nó tạo ra một danh sách mới và bạn không sửa đổi danh sách cũ. Bất kỳ tài liệu tham khảo nào khác vào danh sách sẽ vẫn chưa được lọc.
Paul S

À, đúng rồi. Tôi đã bị cuốn vào việc định nghĩa một phương thức, tôi bỏ qua nhiệm vụ đơn giản mà bạn đã thực hiện.
Dannid

7

bạn có thể làm được việc này

while 2 in x:   
    x.remove(2)

3
Đó là một giải pháp tồi, vì danh sách phải được duyệt 2 * n lần cho n lần xuất hiện của 2.
cxxl

Không nên thêm hoặc xóa khỏi danh sách bạn đang duyệt. Thực hành xấu IMHO.
Aman Mathur

5

Với chi phí dễ đọc, tôi nghĩ phiên bản này nhanh hơn một chút vì nó không bắt buộc phải xem lại danh sách, do đó thực hiện chính xác cùng một công việc loại bỏ phải thực hiện:

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)

Đối với danh sách bạn hiển thị trong mã của mình, cách tiếp cận này chậm hơn khoảng 36% so với phương pháp hiểu danh sách (trả về một bản sao), theo phép đo của tôi.
djsmith

Tốt bạn nhận thấy rằng. Tuy nhiên, vì tôi nghĩ rằng nó có thể đánh trượt phán đoán của bạn, tôi đã so sánh phiên bản của tôi với đề xuất đầu tiên của tác giả câu hỏi.
Martin Andersson

4

Cách tiếp cận và thời gian gọn gàng đối với một danh sách / mảng với 1.000.000 phần tử:

Thời gian:

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

Kết luận: numpy nhanh hơn 27 lần (trên máy tính xách tay của tôi) so với phương pháp hiểu danh sách

PS nếu bạn muốn chuyển đổi danh sách Python thông thường của mình lstsang mảng numpy:

arr = np.array(lst)

Thiết lập:

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

Kiểm tra:

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)

Có lẽ không phải là pythonic nhất nhưng vẫn dễ nhất đối với tôi haha


3

Để xóa tất cả các lần xuất hiện trùng lặp và để lại một trong danh sách:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

Đây là chức năng tôi đã sử dụng cho Project Euler:

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

2
Tôi cần phải làm điều này trên một vectơ với các giá trị 250k, và nó hoạt động như một bùa mê.
rschwieb

1
Câu trả lời là: có! Và tôi hoàn toàn hiểu nếu có một vectơ dài nghe có vẻ hoàn toàn điên rồ đối với một lập trình viên có năng lực. Tôi tiếp cận các vấn đề ở đó với tư cách là một nhà toán học, không lo lắng về việc tối ưu hóa các giải pháp và điều đó có thể dẫn đến các giải pháp lâu hơn mệnh giá. (Mặc dù tôi không có kiên nhẫn cho các giải pháp dài hơn 5 phút.)
rschwieb

6
Điều này sẽ loại bỏ bất kỳ thứ tự từ danh sách.
asmeker

4
@JaredBurrows có lẽ bởi vì nó không trả lời câu hỏi như hiện tại, nhưng một câu hỏi hoàn toàn khác.
drevicko

6
-1, đây không phải là câu trả lời cho câu hỏi của OP. Đó là một giải pháp để loại bỏ trùng lặp, đó là một vấn đề hoàn toàn khác.
Anoyz

2

Tôi tin rằng điều này có thể nhanh hơn bất kỳ cách nào khác nếu bạn không quan tâm đến thứ tự danh sách, nếu bạn quan tâm đến đơn hàng cuối cùng lưu trữ các chỉ mục từ bản gốc và khu nghỉ mát bằng cách đó.

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

2
Tôi hiểu bạn sẽ đi đâu, nhưng mã này sẽ không hoạt động vì bạn cũng cần chỉ số bắt đầu và không chỉ 0.
Shedokan

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

Đơn giản hơn nhiều tôi tin.


2
vui lòng chỉnh sửa câu trả lời của bạn để cải thiện sự rõ ràng. Vui lòng làm rõ chính xác mã đề xuất của bạn làm gì, tại sao nó hoạt động và tại sao đây là đề xuất của bạn. Vui lòng định dạng chính xác câu hỏi của bạn để mã rõ ràng từ phần còn lại của câu trả lời của bạn.
Ortund

2

Để cho

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

Giải pháp đơn giản và hiệu quả nhất như đã đăng trước đó là

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

Một khả năng khác nên sử dụng ít bộ nhớ hơn nhưng chậm hơn là

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

Kết quả thời gian cho danh sách có độ dài 1000 và 100000 với 10% mục khớp: 0,16 so với 0,25 ms và 23 so với 123 ms.

Thời gian với độ dài 1000

Thời gian với chiều dài 100000


1

Xóa tất cả các lần xuất hiện của một giá trị khỏi danh sách 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()

Kết quả: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11

Ngoài ra,

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)

Kết quả: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11


"Python 'lồng nhau cho mỗi vòng lặp if' trong một hàm hoạt động với độ chính xác 100%!"
rafiqul786

Bạn không sửa đổi danh sách bạn chỉ cần in các yếu tố. Ngoài ra, việc đặt tên một danh sách là danh sách là khó hiểu
kon psych

0

Nếu bạn không tích hợp sẵn filterhoặc không muốn sử dụng thêm dung lượng và bạn cần một giải pháp tuyến tính ...

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

['Chào thế giới']


hãy cố gắng giải thích câu trả lời của bạn với lời giải thích.
parlad

0

Tôi chỉ làm điều này cho một danh sách. Tôi chỉ là người mới bắt đầu. Một lập trình viên cao cấp hơn một chút chắc chắn có thể viết một hàm như thế này.

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

0

Chúng tôi cũng có thể xóa tại chỗ bằng cách sử dụng một trong hai delhoặc 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))

Bây giờ cho hiệu quả:

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

Như chúng ta thấy phiên bản tại chỗ remove_values_from_list() không cần thêm bộ nhớ, nhưng sẽ mất nhiều thời gian hơn để chạy:

  • 11 giây cho các giá trị loại bỏ tại chỗ
  • 710 mili giây để hiểu danh sách, phân bổ một danh sách mới trong bộ nhớ

0

Không ai đã đăng một câu trả lời tối ưu cho sự phức tạp về thời gian và không gian, vì vậy tôi nghĩ rằng tôi sẽ cho nó một shot. Dưới đây là một giải pháp loại bỏ tất cả các lần xuất hiện của một giá trị cụ thể mà không tạo ra một mảng mới và ở độ phức tạp thời gian hiệu quả. Hạn chế là các yếu tố không duy trì trật tự .

Độ phức tạp thời gian: O (n)
Độ phức tạp không gian bổ sung: 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

Về tốc độ!

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

Chỉ với Python 3


2
Vui vẻ, điều này là trong phạm vi của câu hỏi và là chính xác.
Erich

Tôi không thấy nó đúng như thế nào. Điều này sẽ xóa tất cả các mục khỏi danh sách, không phải tất cả các lần xuất hiện của một giá trị .
Georgy

-3

Có chuyện gì với:

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

Sử dụng anaconda


2
Vui lòng giải thích các dòng mã của bạn để người dùng khác có thể hiểu chức năng của nó. Cảm ơn!
Ignacio Ara

Mã này sẽ không xóa bất cứ thứ gì khỏi danh sách.
Georgy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.