Xóa tất cả các thành phần xảy ra trong danh sách này khỏi danh sách khác


365

Hãy nói rằng tôi có hai danh sách, l1l2. Tôi muốn thực hiện l1 - l2, trong đó trả về tất cả các yếu tố l1không trong l2.

Tôi có thể nghĩ ra một cách tiếp cận vòng lặp ngây thơ để làm điều này, nhưng điều đó sẽ thực sự không hiệu quả. Một cách pythonic và hiệu quả để làm điều này là gì?

Ví dụ, nếu tôi có l1 = [1,2,6,8] and l2 = [2,3,5,8], l1 - l2nên trả lại[1,6]


12
Chỉ là một mẹo: PEP8 nói rằng không nên sử dụng chữ thường "L" vì nó trông quá giống chữ 1.
puzzekek

2
Tôi đồng ý. Tôi đọc toàn bộ câu hỏi này và câu trả lời tự hỏi tại sao mọi người cứ sử dụng mười một và mười hai. Chỉ đến khi tôi đọc bình luận của @spelchekr thì nó mới có ý nghĩa.
cướp


@JimG. Dataframe và danh sách không giống nhau.
giảm hoạt động

Câu trả lời:


491

Python có một tính năng ngôn ngữ gọi là Danh sách toàn diện hoàn toàn phù hợp để làm cho loại điều này cực kỳ dễ dàng. Câu lệnh sau đây thực hiện chính xác những gì bạn muốn và lưu trữ kết quả vào l3:

l3 = [x for x in l1 if x not in l2]

l3sẽ chứa [1, 6].


8
Rất trăn; Tôi thích nó! Làm thế nào là hiệu quả?
fandom

2
Tôi tin rằng khá hiệu quả, và nó có lợi ích là cực kỳ dễ đọc và rõ ràng như những gì bạn đang cố gắng thực hiện. Tôi đã xem qua một bài đăng trên blog mà bạn có thể thấy thú vị liên quan đến hiệu quả: blog.cdleary.com/2010/04/effic-of-list-comprehensions
Donut

6
@fandom: bản thân việc hiểu danh sách khá hiệu quả (mặc dù việc hiểu trình tạo có thể hiệu quả hơn bằng cách không sao chép các phần tử trong bộ nhớ), nhưng intoán tử không hiệu quả trong danh sách. intrên một danh sách là O (n), trong khi intrên một tập hợp là O (1). Tuy nhiên, cho đến khi bạn nhận được hàng ngàn yếu tố trở lên, bạn sẽ không nhận thấy sự khác biệt.
Daniel Pryden

1
l3 = [x for x in l1 if x not in set(l2)]? Tôi chắc chắn nếu set(l2)được gọi nhiều hơn một lần.
Danosaure

5
Bạn cũng có thể chỉ cần đặt l2s = set(l2)và sau đó nói l3 = [x for x in l1 if x not in l2s]. Hơi dễ hơn một chút.
gamechekr

149

Một cách là sử dụng bộ:

>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])

58
Điều này cũng sẽ loại bỏ các bản sao khỏi l1, có thể là một tác dụng phụ không mong muốn.
loại

37
..và mất thứ tự phần tử (nếu thứ tự là quan trọng).
Danosaure

3
Tôi chỉ muốn thêm rằng tôi đã tính thời gian này so với câu trả lời được chấp nhận và nó được thực hiện nhiều hơn bởi hệ số khoảng 3 : timeit.timeit('a = [1,2,3,4]; b = [1,3]; c = [i for i in a if a not in b]', number=100000) -> 0.12061533199999985 timeit.timeit('a = {1,2,3,4}; b = {1,3}; c = a - b', number=100000) -> 0.04106225999998969. Vì vậy, nếu hiệu suất là một yếu tố quan trọng, câu trả lời này có thể phù hợp hơn (và cũng có thể nếu bạn không quan tâm đến các bản sao hoặc đơn đặt hàng)
wfgeo 15/07/19

38

Thay vào đó, bạn cũng có thể sử dụng filtervới biểu thức lambda để có được kết quả mong muốn. Ví dụ:

>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])

#     v  `filter` returns the a iterator object. Here I'm type-casting 
#     v  it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]

So sánh hiệu suất

Ở đây tôi đang so sánh hiệu suất của tất cả các câu trả lời được đề cập ở đây. Như mong đợi, hoạt động dựa trên Arkku set là nhanh nhất.

PS: set không duy trì thứ tự và loại bỏ các yếu tố trùng lặp khỏi danh sách. Do đó, không sử dụng sự khác biệt thiết lập nếu bạn cần bất kỳ trong số này.


32

Mở rộng câu trả lời của Donut và các câu trả lời khác ở đây, bạn có thể nhận được kết quả thậm chí tốt hơn bằng cách sử dụng trình hiểu của trình tạo thay vì hiểu danh sách và bằng cách sử dụng setcấu trúc dữ liệu (vì intoán tử là O (n) trong danh sách nhưng O (1) trên một bộ).

Vì vậy, đây là một chức năng sẽ làm việc cho bạn:

def filter_list(full_list, excludes):
    s = set(excludes)
    return (x for x in full_list if x not in s)

Kết quả sẽ là một lần lặp sẽ tìm nạp danh sách được lọc một cách lười biếng. Nếu bạn cần một đối tượng danh sách thực (ví dụ: nếu bạn cần thực hiện len()kết quả), thì bạn có thể dễ dàng xây dựng một danh sách như vậy:

filtered_list = list(filter_list(full_list, excludes))

29

Sử dụng loại thiết lập Python. Đó sẽ là Pythonic nhất. :)

Ngoài ra, vì nó là bản địa, nó cũng là phương pháp được tối ưu hóa nhất.

Xem:

http://docs.python.org/l Library / stdtypes.html # set

http://docs.python.org/l Library / sets.htm (đối với trăn già)

# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2

5
Khi sử dụng các tập hợp cần lưu ý rằng đầu ra của được đặt hàng, nghĩa là {1,3,2} trở thành {1,2,3} và {"A", "C", "B"} trở thành {"A", "B", "C"} và bạn có thể không muốn có điều đó.
Pablo Reyes

2
phương pháp này sẽ không hoạt động nếu danh sách l1bao gồm các yếu tố lặp đi lặp lại.
jdhao

10

sử dụng Đặt mức độ hiểu {x cho x trong l2} hoặc đặt (l2) để được đặt, sau đó sử dụng Danh sách tổng hợp để nhận danh sách

l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]

mã kiểm tra điểm chuẩn:

import time

l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))

l2set = {x for x in l2}

tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)

tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)

print("speedup %fx"%(difflist/diffset))

kết quả kiểm tra điểm chuẩn:

0.0015058517456054688
3.968189239501953
speedup 2635.179227x    

1
l2set = set( l2 )thay vìl2set = { x for x in l2 }
cz

1
Tâm hồn tốt đẹp! Nhưng nó phải được ghi nhớ, rằng nó chỉ hoạt động với các đối tượng có thể băm.
Eerik Sven Puudist

7

Giải pháp thay thế:

reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])

2
Có bất kỳ lợi thế để sử dụng phương pháp này? Có vẻ như nó phức tạp hơn và khó đọc hơn mà không có nhiều lợi ích.
skrrgwasme 7/11/2015

Điều đó có vẻ phức tạp. Giảm rất linh hoạt và có thể được sử dụng cho nhiều mục đích. Nó được gọi là gấp. giảm là thực sự gấp. Giả sử bạn muốn thêm các nội dung phức tạp hơn vào đó thì có thể trong hàm này nhưng việc hiểu danh sách là câu trả lời tốt nhất được chọn sẽ chỉ giúp bạn có đầu ra cùng loại tức là danh sách và có thể có cùng độ dài trong khi với các nếp gấp bạn có thể thay đổi loại đầu ra là tốt. vi.wikipedia.org/wiki/Fold_%28higher-order_feft%29 . Giải pháp này là n * m hoặc ít phức tạp hơn. Những người khác có thể hoặc không thể tốt hơn mặc dù.
Akshay Hazari

1
giảm (chức năng, danh sách, bộ tích lũy ban đầu (có thể thuộc bất kỳ loại nào))
Akshay Hazari
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.