Tìm giao điểm của hai danh sách lồng nhau?


468

Tôi biết làm thế nào để có một giao điểm của hai danh sách phẳng:

b1 = [1,2,3,4,5,9,11,15]
b2 = [4,5,6,7,8]
b3 = [val for val in b1 if val in b2]

hoặc là

def intersect(a, b):
    return list(set(a) & set(b))

print intersect(b1, b2)

Nhưng khi tôi phải tìm giao điểm cho các danh sách lồng nhau thì vấn đề của tôi bắt đầu:

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

Cuối cùng, tôi muốn nhận được:

c3 = [[13,32],[7,13,28],[1,6]]

Các bạn có thể giúp tôi một tay với điều này?

Liên quan


Giao điểm của bạn sẽ là gì cho c1 giao nhau c2? Bạn có muốn chỉ đơn giản là tìm c1 trong c2 không? Hay bạn muốn tìm tất cả các yếu tố trong c1 xuất hiện ở bất cứ đâu trong c2?
Brian R. Bondy

Đọc này và chơi trong trình thông dịch.
Pithikos

Câu trả lời:


177

Nếu bạn muốn:

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]
c3 = [[13, 32], [7, 13, 28], [1,6]]

Đây là giải pháp của bạn cho Python 2:

c3 = [filter(lambda x: x in c1, sublist) for sublist in c2]

Trong Python 3 filtertrả về một lần lặp thay vì list, vì vậy bạn cần phải thực hiện filtercác cuộc gọi bằng list():

c3 = [list(filter(lambda x: x in c1, sublist)) for sublist in c2]

Giải trình:

Phần bộ lọc lấy từng mục của danh sách con và kiểm tra xem nó có trong danh sách nguồn c1 không. Việc hiểu danh sách được thực hiện cho từng danh sách con trong c2.


35
Bạn có thể sử dụng filter(set(c1).__contains__, sublist)cho hiệu quả. btw, lợi thế của giải pháp này là filter()bảo tồn các loại dây và tuples.
jfs

3
Tôi thích phương pháp này, nhưng tôi đang bị trống '' trong danh sách kết quả của mình
Jonathan Ong

Tôi đã thêm trình soạn thảo Python 3 ở đây, vì tôi đang sử dụng mục tiêu này làm mục tiêu lừa đảo cho bản sao của câu hỏi Python 3
Antti Haapala

9
Điều này đọc IMO tốt hơn với sự hiểu biết lồng nhau:c3 = [[x for x in sublist if x in c1] for sublist in c2]
Eric

894

Bạn không cần xác định giao lộ. Nó đã là một phần hạng nhất của bộ.

>>> b1 = [1,2,3,4,5,9,11,15]
>>> b2 = [4,5,6,7,8]
>>> set(b1).intersection(b2)
set([4, 5])

3
Điều này sẽ chậm hơn lambda vì chuyển đổi để thiết lập?
Ciro Santilli 郝海东 冠状 病 事件 法轮功

32
@ S.Lott, có gì sai với set(b1) & set(b2)? IMO sạch hơn để sử dụng các nhà điều hành.
gwg

4
Thêm vào đó, việc sử dụng setsẽ dẫn đến mã đó là các đơn đặt hàng có cường độ nhanh hơn. Đây là một điểm chuẩn mẫu®
andersonvom

5
Chỉ hoạt động nếu kết quả không phải được đặt hàng.
Borbag

7
Vì vậy, ... câu trả lời này không có cách nào trả lời câu hỏi, phải không? Bởi vì điều này bây giờ làm việc với các danh sách lồng nhau .
Mayou36

60

Đối với những người chỉ muốn tìm giao điểm của hai danh sách, Asker đã cung cấp hai phương thức:

b1 = [1,2,3,4,5,9,11,15]
b2 = [4,5,6,7,8]
b3 = [val for val in b1 if val in b2]

def intersect(a, b):
     return list(set(a) & set(b))

print intersect(b1, b2)

Nhưng có một phương pháp lai hiệu quả hơn, bởi vì bạn chỉ phải thực hiện một chuyển đổi giữa danh sách / bộ, trái ngược với ba:

b1 = [1,2,3,4,5]
b2 = [3,4,5,6]
s2 = set(b2)
b3 = [val for val in b1 if val in s2]

Điều này sẽ chạy trong O (n), trong khi phương thức ban đầu của anh ấy liên quan đến việc hiểu danh sách sẽ chạy trong O (n ^ 2)


Vì "if val in s2" chạy trong O (N), độ phức tạp của đoạn mã được đề xuất cũng là O (n ^ 2)
Romeno

8
Trường hợp trung bình của "val in s2" là O (1) theo wiki.python.org/moin/TimeComplexity#set - do đó, qua n hoạt động, thời gian dự kiến ​​là O (n) (cho dù thời gian trong trường hợp xấu nhất là O (n n) hoặc O (n ^ 2) tùy thuộc vào trường hợp trung bình này có đại diện cho thời gian khấu hao hay không, nhưng điều này không quan trọng lắm trong thực tế).
D Coetzee

2
Thời gian chạy là O (N) không phải vì nó được khấu hao mà vì thành viên được đặt ở mức trung bình O (1) (ví dụ: khi sử dụng bảng băm), đó là sự khác biệt lớn, ví dụ vì thời gian khấu hao được đảm bảo.
miroB

28

Cách tiếp cận chức năng:

input_list = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]]

result = reduce(set.intersection, map(set, input_list))

và nó có thể được áp dụng cho trường hợp tổng quát hơn của danh sách 1+


để cho phép danh sách đầu vào trống : set(*input_list[:1]).intersection(*input_list[1:]). Phiên bản lặp ( it = iter(input_list)) : reduce(set.intersection, it, set(next(it, []))). Cả hai phiên bản không yêu cầu chuyển đổi tất cả các danh sách đầu vào để thiết lập. Thứ hai là bộ nhớ hiệu quả hơn.
jfs

Sử dụng from functools import reduceđể sử dụng nó trong Python 3. Hoặc tốt hơn nữa, sử dụng một forvòng lặp rõ ràng .
TrigonaMinima

27

Phiên bản hiểu danh sách thuần túy

>>> c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
>>> c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]
>>> c1set = frozenset(c1)

Biến thể phẳng:

>>> [n for lst in c2 for n in lst if n in c1set]
[13, 32, 7, 13, 28, 1, 6]

Biến thể lồng nhau:

>>> [[n for n in lst if n in c1set] for lst in c2]
[[13, 32], [7, 13, 28], [1, 6]]

20

Toán tử & lấy giao điểm của hai bộ.

{1, 2, 3} & {2, 3, 4}
Out[1]: {2, 3}

Tốt, nhưng chủ đề này là dành cho danh sách!
Rafa0809

3
Kết quả của giao điểm của hai danh sách là một tập hợp vì vậy câu trả lời này là hoàn toàn hợp lệ.
shrewmouse

Danh sách có thể chứa giá trị trùng lặp nhưng bộ thì không.
diewland

13

Một cách pythonic để lấy giao điểm của 2 danh sách là:

[x for x in list1 if x in list2]

2
Câu hỏi này là về danh sách lồng nhau. Câu trả lời của bạn không trả lời câu hỏi.
Thomas

8

Bạn nên làm phẳng bằng cách sử dụng mã này (lấy từ http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks ), mã này chưa được kiểm tra, nhưng tôi khá chắc chắn rằng nó hoạt động:


def flatten(x):
    """flatten(sequence) -> list

    Returns a single, flat list which contains all elements retrieved
    from the sequence and all recursively contained sub-sequences
    (iterables).

    Examples:
    >>> [1, 2, [3,4], (5,6)]
    [1, 2, [3, 4], (5, 6)]
    >>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, MyVector(8,9,10)])
    [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]"""

    result = []
    for el in x:
        #if isinstance(el, (list, tuple)):
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

Sau khi bạn đã san phẳng trong danh sách, bạn thực hiện các giao điểm theo cách thông thường:


c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

def intersect(a, b):
     return list(set(a) & set(b))

print intersect(flatten(c1), flatten(c2))

2
Đó là một chút tốt đẹp của mã phẳng Geo, nhưng nó không trả lời câu hỏi. Người hỏi đặc biệt hy vọng kết quả dưới dạng [[13,32], [7,13,28], [1,6]].
Rob Young

8

Kể từ khi intersectđược xác định, một sự hiểu biết danh sách cơ bản là đủ:

>>> c3 = [intersect(c1, i) for i in c2]
>>> c3
[[32, 13], [28, 13, 7], [1, 6]]

Cải thiện nhờ vào xét S. Lott và TM của nhận xét liên quan.:

>>> c3 = [list(set(c1).intersection(i)) for i in c2]
>>> c3
[[32, 13], [28, 13, 7], [1, 6]]

5

Được:

> c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]

> c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

Tôi thấy đoạn mã sau hoạt động tốt và chính xác có thể nhiều hơn nếu sử dụng hoạt động thiết lập:

> c3 = [list(set(f)&set(c1)) for f in c2] 

Nó có:

> [[32, 13], [28, 13, 7], [1, 6]]

Nếu cần đặt hàng:

> c3 = [sorted(list(set(f)&set(c1))) for f in c2] 

chúng tôi có:

> [[13, 32], [7, 13, 28], [1, 6]]

Nhân tiện, đối với một phong cách python hơn, cái này cũng tốt:

> c3 = [ [i for i in set(f) if i in c1] for f in c2]

3

Tôi không biết nếu tôi trễ trả lời câu hỏi của bạn. Sau khi đọc câu hỏi của bạn tôi đã đưa ra một hàm intersect () có thể làm việc trên cả hai danh sách và danh sách lồng nhau. Tôi đã sử dụng đệ quy để định nghĩa hàm này, nó rất trực quan. Hy vọng nó là những gì bạn đang tìm kiếm:

def intersect(a, b):
    result=[]
    for i in b:
        if isinstance(i,list):
            result.append(intersect(a,i))
        else:
            if i in a:
                 result.append(i)
    return result

Thí dụ:

>>> c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
>>> c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]
>>> print intersect(c1,c2)
[[13, 32], [7, 13, 28], [1, 6]]

>>> b1 = [1,2,3,4,5,9,11,15]
>>> b2 = [4,5,6,7,8]
>>> print intersect(b1,b2)
[4, 5]

2

Bạn có cân nhắc [1,2]để giao nhau với [1, [2]]? Đó là, chỉ có những con số bạn quan tâm, hoặc cấu trúc danh sách là tốt?

Nếu chỉ có các số, hãy điều tra cách "làm phẳng" danh sách, sau đó sử dụng set()phương thức.


Tôi muốn giữ nguyên cấu trúc của danh sách.
elfuego1

1

Tôi cũng đang tìm cách để làm điều đó, và cuối cùng nó đã kết thúc như thế này:

def compareLists(a,b):
    removed = [x for x in a if x not in b]
    added = [x for x in b if x not in a]
    overlap = [x for x in a if x in b]
    return [removed,added,overlap]

Nếu không sử dụng set.intersection thì những lớp lót đơn giản này là điều tôi cũng sẽ làm.
tàn

0
c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]

c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

c3 = [list(set(c2[i]).intersection(set(c1))) for i in xrange(len(c2))]

c3
->[[32, 13], [28, 13, 7], [1, 6]]

0

Chúng ta có thể sử dụng các phương thức thiết lập cho việc này:

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

   result = [] 
   for li in c2:
       res = set(li) & set(c1)
       result.append(list(res))

   print result

0

Để xác định giao lộ có tính đến chính xác tính chính xác của các yếu tố sử dụng Counter:

from collections import Counter

>>> c1 = [1, 2, 2, 3, 4, 4, 4]
>>> c2 = [1, 2, 4, 4, 4, 4, 5]
>>> list((Counter(c1) & Counter(c2)).elements())
[1, 2, 4, 4, 4]

0
# Problem:  Given c1 and c2:
c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]
# how do you get c3 to be [[13, 32], [7, 13, 28], [1, 6]] ?

Đây là một cách để thiết lập c3không liên quan đến các bộ:

c3 = []
for sublist in c2:
    c3.append([val for val in c1 if val in sublist])

Nhưng nếu bạn chỉ muốn sử dụng một dòng, bạn có thể làm điều này:

c3 = [[val for val in c1 if val in sublist]  for sublist in c2]

Đó là một sự hiểu biết danh sách bên trong một sự hiểu biết danh sách, điều này hơi bất thường, nhưng tôi nghĩ bạn không nên gặp quá nhiều khó khăn khi theo dõi nó.


0
c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63]
c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]
c3 = [list(set(i) & set(c1)) for i in c2]
c3
[[32, 13], [28, 13, 7], [1, 6]]

Đối với tôi đây là cách rất thanh lịch và nhanh chóng để làm điều đó :)


0

danh sách phẳng có thể được thực hiện thông qua reducemột cách dễ dàng.

Tất cả bạn cần sử dụng bộ khởi tạo - đối số thứ ba trong reducehàm.

reduce(
   lambda result, _list: result.append(
       list(set(_list)&set(c1)) 
     ) or result, 
   c2, 
   [])

Mã trên hoạt động cho cả python2 và python3, nhưng bạn cần nhập mô-đun giảm như from functools import reduce. Tham khảo liên kết dưới đây để biết chi tiết.


-1

Cách đơn giản để tìm sự khác biệt và giao điểm giữa các lần lặp

Sử dụng phương pháp này nếu sự lặp lại có vấn đề

from collections import Counter

def intersection(a, b):
    """
    Find the intersection of two iterables

    >>> intersection((1,2,3), (2,3,4))
    (2, 3)

    >>> intersection((1,2,3,3), (2,3,3,4))
    (2, 3, 3)

    >>> intersection((1,2,3,3), (2,3,4,4))
    (2, 3)

    >>> intersection((1,2,3,3), (2,3,4,4))
    (2, 3)
    """
    return tuple(n for n, count in (Counter(a) & Counter(b)).items() for _ in range(count))

def difference(a, b):
    """
    Find the symmetric difference of two iterables

    >>> difference((1,2,3), (2,3,4))
    (1, 4)

    >>> difference((1,2,3,3), (2,3,4))
    (1, 3, 4)

    >>> difference((1,2,3,3), (2,3,4,4))
    (1, 3, 4, 4)
    """
    diff = lambda x, y: tuple(n for n, count in (Counter(x) - Counter(y)).items() for _ in range(count))
    return diff(a, b) + diff(b, a)
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.