Temukan persimpangan dua daftar bersarang?

468

Saya tahu cara mendapatkan persimpangan dua daftar datar:

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]

atau

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

print intersect(b1, b2)

Tetapi ketika saya harus menemukan persimpangan untuk daftar bersarang maka masalah saya mulai:

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

Pada akhirnya saya ingin menerima:

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

Bisakah kalian bantu saya dengan ini?

Terkait

elfuego1
sumber
Berapakah persimpangan Anda untuk c1 intersect c2? Apakah Anda hanya ingin mencari apakah c1 ada di c2? Atau Anda ingin menemukan semua elemen di c1 yang muncul di mana saja di c2?
Brian R. Bondy
Baca ini dan mainkan dengan penerjemah.
Pithikos

Jawaban:

177

Jika kamu mau:

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

Maka di sini adalah solusi Anda untuk Python 2:

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

Di Python 3 filtermengembalikan iterable, bukan list, jadi Anda harus membungkus filterpanggilan dengan list():

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

Penjelasan:

Bagian filter mengambil setiap item sublist dan memeriksa untuk melihat apakah ada dalam daftar sumber c1. Pemahaman daftar dieksekusi untuk setiap sublist di c2.

Brian R. Bondy
sumber
35
Anda dapat menggunakan filter(set(c1).__contains__, sublist)untuk efisiensi. Namun, keuntungan dari solusi ini adalah filter()mempertahankan tipe string dan tuple.
jfs
3
Saya suka metode ini, tetapi saya mendapatkan '' kosong di daftar hasil saya
Jonathan Ong
Saya menambahkan Python 3 compat di sini, karena saya menggunakan ini sebagai target dupe untuk dupe dari pertanyaan Python 3
Antti Haapala
9
Ini membaca IMO yang lebih baik dengan pemahaman bersarang:c3 = [[x for x in sublist if x in c1] for sublist in c2]
Eric
894

Anda tidak perlu mendefinisikan persimpangan. Ini sudah menjadi bagian dari kelas set.

>>> b1 = [1,2,3,4,5,9,11,15]
>>> b2 = [4,5,6,7,8]
>>> set(b1).intersection(b2)
set([4, 5])
S.Lott
sumber
3
Apakah ini lebih lambat dari lambda karena konversi untuk ditetapkan?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
32
@ S.Lott, ada yang salah dengan apa set(b1) & set(b2)? IMO pembersihnya untuk menggunakan operator.
gwg
4
Plus, menggunakan setakan menyebabkan kode yang urutan besarnya lebih cepat. Inilah contoh benchmark®: gist.github.com/andersonvom/4d7e551b4c0418de3160
andersonvom
5
Hanya berfungsi jika hasilnya tidak harus dipesan.
Borbag
7
Jadi ... jawaban ini sama sekali tidak menjawab pertanyaan, kan? Karena ini sekarang berfungsi dengan daftar bersarang .
Mayou36
60

Bagi orang-orang yang hanya ingin menemukan persimpangan dua daftar, Penanya menyediakan dua metode:

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]

dan

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

print intersect(b1, b2)

Tetapi ada metode hybrid yang lebih efisien, karena Anda hanya perlu melakukan satu konversi antara daftar / set, berbeda dengan tiga:

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

Ini akan berjalan di O (n), sedangkan metode aslinya yang melibatkan pemahaman daftar akan berjalan di O (n ^ 2)

Zack Burt
sumber
Sebagai "jika val dalam s2" berjalan dalam O (N), kompleksitas cuplikan kode yang diusulkan juga O (n ^ 2)
Romeno
8
Kasus rata-rata "val in s2" adalah O (1) menurut wiki.python.org/moin/TimeComplexity#set - dengan demikian atas operasi n waktu yang diharapkan adalah O (n) (apakah waktu kasus terburuk adalah O ( n) atau O (n ^ 2) tergantung pada apakah kasus rata-rata ini mewakili waktu diamortisasi atau tidak, tetapi ini tidak terlalu penting dalam praktiknya).
D Coetzee
2
Runtime adalah O (N) bukan karena diamortisasi tetapi karena keanggotaan yang ditetapkan rata-rata O (1) (misalnya ketika menggunakan tabel hash), itu adalah perbedaan besar, misalnya karena waktu diamortisasi dijamin.
miroB
28

Pendekatan fungsional:

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

dan itu dapat diterapkan pada kasus 1+ daftar yang lebih umum

ikan buntal
sumber
untuk memungkinkan daftar masukan kosong: set(*input_list[:1]).intersection(*input_list[1:]). Versi Iterator ( it = iter(input_list)): reduce(set.intersection, it, set(next(it, []))). Kedua versi tidak perlu mengonversi semua daftar input untuk ditetapkan. Yang terakhir ini lebih hemat memori.
jfs
Gunakan from functools import reduceuntuk menggunakannya dalam Python 3. Atau lebih baik lagi, gunakan forloop eksplisit .
TrigonaMinima
27

Versi pemahaman daftar murni

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

Varian rata:

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

Varian bersarang:

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

Operator & mengambil persimpangan dua set.

{1, 2, 3} & {2, 3, 4}
Out[1]: {2, 3}
aflaisler
sumber
Baik, tapi topik ini untuk daftar!
Rafa0809
3
Hasil persimpangan dua daftar adalah himpunan sehingga jawaban ini benar-benar valid.
shrewmouse
Daftar dapat berisi nilai duplikat tetapi set tidak.
diewland
13

Cara pythonic mengambil persimpangan 2 daftar adalah:

[x for x in list1 if x in list2]
Flying_ostrich
sumber
2
Pertanyaan ini tentang daftar bersarang. Jawaban Anda tidak menjawab pertanyaan itu.
Thomas
8

Anda harus meratakan menggunakan kode ini (diambil dari http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks ), kodenya belum teruji, tapi saya cukup yakin itu berhasil:


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

Setelah Anda meratakan daftar, Anda melakukan persimpangan dengan cara yang biasa:


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))
Geo
sumber
2
Itu sedikit kode mendatarkan Geo, tetapi tidak menjawab pertanyaan. Penanya secara khusus mengharapkan hasilnya dalam bentuk [[13,32], [7,13,28], [1,6]].
Rob Young
8

Sejak intersectdidefinisikan, pemahaman daftar dasar sudah cukup:

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

Peningkatan berkat ucapan S. Lott dan komentar terkait TM:

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

Diberikan:

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

Saya menemukan kode berikut berfungsi dengan baik dan mungkin lebih ringkas jika menggunakan operasi yang ditetapkan:

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

Itu mendapat:

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

Jika pesanan diperlukan:

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

kita punya:

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

Omong-omong, untuk gaya python yang lebih banyak, yang ini juga baik-baik saja:

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

Saya tidak tahu apakah saya terlambat menjawab pertanyaan Anda. Setelah membaca pertanyaan Anda, saya datang dengan fungsi intersect () yang dapat bekerja pada daftar dan daftar bersarang. Saya menggunakan rekursi untuk mendefinisikan fungsi ini, sangat intuitif. Semoga ini yang Anda cari:

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

Contoh:

>>> 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]
Mrsky Boatin
sumber
2

Apakah Anda mempertimbangkan [1,2]untuk bersinggungan [1, [2]]? Artinya, apakah hanya angka yang Anda pedulikan, atau struktur daftar juga?

Jika hanya angka-angka, selidiki bagaimana cara "meratakan" daftar, kemudian gunakan set()metode.

beristirahat
sumber
Saya ingin membiarkan struktur daftar tidak berubah.
elfuego1
1

Saya juga mencari cara untuk melakukannya, dan akhirnya berakhir seperti ini:

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]
Remco van Zuijlen
sumber
Jika tidak menggunakan set.intersection maka yang sederhana ini adalah apa yang saya juga akan lakukan.
slaughter98
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]]
pengguna3105897
sumber
0

Kita dapat menggunakan metode yang ditetapkan untuk ini:

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
Birendra Kumar
sumber
0

Untuk mendefinisikan persimpangan yang memperhitungkan kardinalitas elemen dengan benar, gunakan 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]
James Hirschorn
sumber
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]] ?

Berikut ini satu cara untuk menyetel c3yang tidak melibatkan set:

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

Tetapi jika Anda lebih suka menggunakan satu baris saja, Anda bisa melakukan ini:

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

Ini adalah pemahaman daftar di dalam pemahaman daftar, yang agak tidak biasa, tapi saya pikir Anda seharusnya tidak mengalami terlalu banyak kesulitan untuk mengikutinya.

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

Bagi saya ini adalah cara yang sangat elegan dan cepat untuk itu :)

Michal
sumber
0

daftar datar dapat dibuat dengan reducemudah.

Yang Anda butuhkan untuk menggunakan initializer - argumen ketiga dalam reducefungsi.

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

Kode di atas berfungsi untuk python2 dan python3, tetapi Anda perlu mengimpor mengurangi modul sebagai from functools import reduce. Lihat tautan di bawah untuk detailnya.

Raja Sakthiyan
sumber
-1

Cara sederhana untuk menemukan perbedaan dan persimpangan antara iterables

Gunakan metode ini jika pengulangan penting

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)
Connor
sumber