Python tìm các phần tử trong một danh sách không nằm trong [trùng lặp] khác


135

Tôi cần so sánh hai danh sách để tạo một danh sách mới các yếu tố cụ thể được tìm thấy trong một danh sách nhưng không có trong danh sách khác. Ví dụ:

main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 

Tôi muốn lặp qua list_1 và nối vào main_list tất cả các thành phần từ list_2 không có trong list_1.

Kết quả sẽ là:

main_list=["f", "m"]

Làm thế nào tôi có thể làm điều đó với python?


2
Bạn đang tìm kiếm các yếu tố trong list_2đó không xuất hiện ở đâu list_1hoặc các yếu tố list_2không có cùng chỉ mục trong list_1?
Patrick Haugh

Câu trả lời:


95

TL; DR:
GIẢI PHÁP (1)

import numpy as np
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`

GIẢI PHÁP (2) Bạn muốn một danh sách được sắp xếp

def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans
main_list = setdiff_sorted(list_2,list_1)




GIẢI THÍCH:
(1) Bạn có thể sử dụng NumPy của setdiff1d( array1, array2, assume_unique= False).

assume_uniquehỏi người dùng NẾU các mảng C ALNG KHÔNG ĐỘC ĐÁO.
Nếu False, sau đó các yếu tố duy nhất được xác định đầu tiên.
Nếu True, hàm sẽ cho rằng các phần tử đã là duy nhất Hàm AND sẽ bỏ qua việc xác định các phần tử duy nhất.

Sản lượng này các giá trị duy nhất trong array1đó là không trong array2. assume_uniqueFalsetheo mặc định.

Nếu bạn quan tâm đến các yếu tố duy nhất (dựa trên phản hồi của Chinny84 ), thì chỉ cần sử dụng (trong đó assume_unique=False=> giá trị mặc định):

import numpy as np
list_1 = ["a", "b", "c", "d", "e"]
list_2 = ["a", "f", "c", "m"] 
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`


(2) Đối với những người muốn sắp xếp câu trả lời, tôi đã tạo một chức năng tùy chỉnh:

import numpy as np
def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans

Để có câu trả lời, hãy chạy:

main_list = setdiff_sorted(list_2,list_1)

GHI CHÚ:
(a) Giải pháp 2 (chức năng tùy chỉnh setdiff_sorted) trả về một danh sách (so với một mảng trong giải pháp 1).

(b) Nếu bạn không chắc chắn các phần tử có phải là duy nhất hay không, chỉ cần sử dụng cài đặt mặc định của NumPy setdiff1dtrong cả hai giải pháp A và B. Điều gì có thể là một ví dụ về biến chứng? Xem ghi chú (c).

(c) Mọi thứ sẽ khác nếu một trong hai danh sách không phải là duy nhất.
Nói list_2không phải là duy nhất : list2 = ["a", "f", "c", "m", "m"]. Giữ nguyên trạng list1: list_1 = ["a", "b", "c", "d", "e"]
Đặt giá trị mặc định của assume_uniquesản lượng ["f", "m"](trong cả hai giải pháp). TUY NHIÊN, nếu bạn đặt assume_unique=True, cả hai giải pháp đều cho ["f", "m", "m"]. Tại sao? Điều này là do người dùng ĐÁNH GIÁ rằng các yếu tố là duy nhất). Do đó, CNTT TỐT HƠN ĐỂ GIỮassume_uniqueđến giá trị mặc định của nó. Lưu ý rằng cả hai câu trả lời được sắp xếp.


Nếu danh sách của bạn đã được đặt hàng, điều này cũng sẽ trả về một danh sách được sắp xếp. Giải pháp gốc chuyển đổi thành tập hợp và sau đó nhận được sự khác biệt (các giải pháp được hiển thị bên dưới) trả về một danh sách không theo thứ tự có thể khiến việc kiểm tra trực quan kết quả của bạn trở nên khó khăn hơn.
Nhân đôi

1
Xin chào, @Doubledown! Mối quan tâm của bạn đã được giải quyết trong bài chỉnh sửa. Hi vọng điêu nay co ich!
jcoderepo

181

Bạn có thể sử dụng bộ:

main_list = list(set(list_2) - set(list_1))

Đầu ra:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> set(list_2) - set(list_1)
set(['m', 'f'])
>>> list(set(list_2) - set(list_1))
['m', 'f']

Theo nhận xét của @JonClements, đây là phiên bản gọn gàng hơn:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> list(set(list_2).difference(list_1))
['m', 'f']

2
Điều này là tốt nếu chúng ta chỉ quan tâm đến uniquecác yếu tố nhưng nếu chúng ta có nhiều m'sví dụ thì điều này sẽ không nhận được nó.
Chinny84

Đúng. Tôi cho rằng các poster đang tìm kiếm các yếu tố độc đáo. Tôi cho rằng nó phụ thuộc vào những gì anh ấy có nghĩa là "cụ thể".
nrlakin

Thật vậy, tôi đã không bỏ phiếu cho câu trả lời của bạn, đặc biệt là cho một câu hỏi ban đầu không rõ ràng.
Chinny84

13
Bạn có thể viết điều này list(set(list_2).difference(list_1))để tránh setchuyển đổi rõ ràng ...
Jon Clements

Đừng lo lắng! Cảm ơn @leaf đã hỗ trợ định dạng.
nrlakin

59

Không chắc chắn tại sao các giải thích trên quá phức tạp khi bạn có sẵn các phương thức gốc:

main_list = list(set(list_2)-set(list_1))

6
Bảo quản trật tự có thể là lý do
Keith

57

Sử dụng một danh sách hiểu như thế này:

main_list = [item for item in list_2 if item not in list_1]

Đầu ra:

>>> list_1 = ["a", "b", "c", "d", "e"]
>>> list_2 = ["a", "f", "c", "m"] 
>>> 
>>> main_list = [item for item in list_2 if item not in list_1]
>>> main_list
['f', 'm']

Biên tập:

Giống như được đề cập trong các ý kiến ​​dưới đây, với danh sách lớn, ở trên không phải là giải pháp lý tưởng. Khi đó, một lựa chọn tốt hơn sẽ được chuyển đổi list_1thành setđầu tiên:

set_1 = set(list_1)  # this reduces the lookup time from O(n) to O(1)
main_list = [item for item in list_2 if item not in set_1]

3
Lưu ý: Đối với lớn hơn list_1, bạn muốn chuyển sang một set/ frozenset, ví dụ set_1 = frozenset(list_1), sau đó main_list = [item for item in list_2 if item not in set_1], giảm thời gian kiểm tra từ O(n)mỗi mục xuống (khoảng) O(1).
ShadowRanger

@ettanany Hãy cẩn thận nếu bạn thử giải pháp như ettanany đã đăng. Tôi đã thử giải pháp của ettanany và nó thực sự siêu chậm cho một danh sách lớn hơn. Bạn có thể cập nhật câu trả lời để kết hợp đề xuất của Shadowranger không?
Nhân đôi

Nó sẽ có thể nhận được chỉ mục, thay vì chuỗi?
JareBear

@JareBear Bạn có thể sử dụng enumerate()cho điều đó:[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany

@ ettanany cảm ơn bạn rất nhiều !! Tôi sẽ thực hiện càng sớm càng tốt, tôi đã làm nó. Nhưng mã của bạn trông sạch sẽ hơn rất nhiều.
JareBear

5

Nếu bạn muốn có một giải pháp một liner (bỏ qua nhập khẩu) mà chỉ yêu cầu O(max(n, m))làm việc cho các đầu vào có độ dài nm, không O(n * m)làm việc, bạn có thể làm như vậy với các itertoolsmô-đun :

from itertools import filterfalse

main_list = list(filterfalse(set(list_1).__contains__, list_2))

Điều này tận dụng các chức năng chức năng có chức năng gọi lại khi xây dựng, cho phép nó tạo ra cuộc gọi lại một lần và sử dụng lại cho mọi phần tử mà không cần lưu trữ ở đâu đó (vì filterfalselưu trữ bên trong); danh sách hiểu và biểu thức trình tạo có thể làm điều này, nhưng nó xấu.

Điều đó nhận được kết quả tương tự trong một dòng như:

main_list = [x for x in list_2 if x not in list_1]

với tốc độ của:

set_1 = set(list_1)
main_list = [x for x in list_2 if x not in set_1]

Tất nhiên, nếu các so sánh được dự định là vị trí, vì vậy:

list_1 = [1, 2, 3]
list_2 = [2, 3, 4]

nên sản xuất:

main_list = [2, 3, 4]

(vì giá trị trong list_2có một trận đấu ở các chỉ số tương tự ở list_1), bạn chắc chắn nên đi với câu trả lời Patrick , trong đó bao gồm không tạm lists hoặc sets (ngay cả với sets là xấp xỉ O(1), họ có một "hằng số" nhân tố cao hơn cho mỗi séc hơn kiểm tra bình đẳng đơn giản ) và liên quan đến O(min(n, m))công việc, ít hơn bất kỳ câu trả lời nào khác, và nếu vấn đề của bạn là nhạy cảm về vị trí, là giải pháp chính xác duy nhất khi các yếu tố phù hợp xuất hiện ở độ lệch không khớp.

: Cách để thực hiện điều tương tự với việc hiểu danh sách như một lớp lót sẽ là lạm dụng vòng lặp lồng nhau để tạo và (các) giá trị bộ đệm trong vòng lặp "ngoài cùng", ví dụ:

main_list = [x for set_1 in (set(list_1),) for x in list_2 if x not in set_1]

điều này cũng mang lại lợi ích hiệu suất nhỏ trên Python 3 (vì hiện tại set_1được phân vùng cục bộ trong mã hiểu, thay vì tra cứu từ phạm vi lồng nhau cho mỗi kiểm tra; trên Python 2 không thành vấn đề, vì Python 2 không sử dụng các bao đóng cho danh sách hiểu, chúng hoạt động trong cùng một phạm vi chúng được sử dụng).


4
main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"]

for i in list_2:
    if i not in list_1:
        main_list.append(i)

print(main_list)

đầu ra:

['f', 'm']

Giống như các giải pháp tương đương danh sách hiểu dựa , điều này sẽ được làm chậm nếu list_1là lớn, và list_2có kích thước không tầm thường, bởi vì nó liên quan đến len(list_2) O(n)quét của list_1, làm cho nó O(n * m)(nơi nmlà độ dài của list_2list_1tương ứng). Nếu bạn chuyển đổi list_1sang phía trước set/ frozensetlên, kiểm tra có chứa có thể được thực hiện O(1), làm cho tổng số công việc O(n)theo độ dài list_2(về mặt kỹ thuật O(max(n, m)), vì bạn làm O(m)việc để thực hiện set).
ShadowRanger

1

Tôi sẽ zipliệt kê các danh sách với nhau để so sánh chúng theo từng yếu tố.

main_list = [b for a, b in zip(list1, list2) if a!= b]

Nếu OP muốn so sánh phần tử bằng phần tử (đó là chưa rõ ràng, ví dụ có thể đi một trong hai cách), đây là nhiều hiệu quả hơn các câu trả lời khác, vì đó là một giá rẻ duy nhất vượt qua hơn cả lists với một single mới listđược xây dựng, không có temporaries thêm , không có kiểm tra ngăn chặn đắt tiền, v.v.
ShadowRanger

1
@ShadowRanger điều này sẽ chỉ hoạt động cho sự khác biệt về yếu tố thông minh, đó là một điểm quan trọng
ford hoàn thành

@fordpreinf: Yup. Câu trả lời của riêng tôi bao gồm sự khác biệt vị trí độc lập.
ShadowRanger

1

Tôi đã sử dụng hai phương pháp và tôi thấy một phương pháp hữu ích hơn phương pháp khác. Đây là câu trả lời của tôi:

Dữ liệu đầu vào của tôi:

crkmod_mpp = ['M13','M18','M19','M24']
testmod_mpp = ['M13','M14','M15','M16','M17','M18','M19','M20','M21','M22','M23','M24']

np.setdiff1dPhương pháp 1 : Tôi thích cách tiếp cận này hơn phương pháp khác vì nó giữ nguyên vị trí

test= list(np.setdiff1d(testmod_mpp,crkmod_mpp))
print(test)
['M15', 'M16', 'M22', 'M23', 'M20', 'M14', 'M17', 'M21']

Phương thức 2: Mặc dù nó đưa ra câu trả lời giống như trong Phương thức 1 nhưng làm xáo trộn trật tự

test = list(set(testmod_mpp).difference(set(crkmod_mpp)))
print(test)
['POA23', 'POA15', 'POA17', 'POA16', 'POA22', 'POA18', 'POA24', 'POA21']

Phương thức 1 np.setdiff1dđáp ứng hoàn hảo yêu cầu của tôi. Câu trả lời này cho thông tin.


0

Nếu số lần xuất hiện nên được tính đến, có lẽ bạn cần phải sử dụng một cái gì đó như collections.Counter:

list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 
from collections import Counter
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['f', 'm']

Như đã hứa, điều này cũng có thể xử lý số lần xuất hiện khác nhau là "sự khác biệt":

list_1=["a", "b", "c", "d", "e", 'a']
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['a', 'f', 'm']

-1

Từ ser1 loại bỏ các mục có trong ser2.

Đầu vào

ser1 = pd.Series ([1, 2, 3, 4, 5]) ser2 = pd.Series ([4, 5, 6, 7, 8])

Giải pháp

ser1 [~ ser1.isin (ser2)]


Chào mừng bạn đến với Stack Overflow. Câu hỏi này có tám câu trả lời khác, một trong số đó đã được chấp nhận bởi người đăng ban đầu. Vui lòng mô tả cách câu trả lời của bạn cải thiện theo những gì đã được trình bày.
chb
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.