2012-08-14 11 views
8

Ich möchte die Schnittmenge zwischen verschachtelten Listen finden, während die Reihenfolge beibehalten wird.Python: Kreuzung von verschachtelten Listen, in denen Reihenfolge wichtig ist

taxa = [['E_pyrifoliae_Ep1_96', 'Bacteria', 'Proteobacteria', 'Gammaproteobacteria', 'Enterobacteriales', 'Enterobacteriaceae', 'Erwinia'], 
['E_amylovora_CFBP1430', 'Bacteria', 'Proteobacteria', 'Gammaproteobacteria', 'Enterobacteriales', 'Enterobacteriaceae', 'Erwinia'], 
['E_amylovora_ATCC49946', 'Bacteria', 'Proteobacteria', 'Gammaproteobacteria', 'Enterobacteriales', 'Enterobacteriaceae', 'Erwinia']] 

die Kreuzung finden Ich habe:

set.intersection(*map(set, taxa)) 

oder

set(taxa[0]).intersection(*taxa) 

aber die ursprüngliche Reihenfolge nicht eingehalten wird.

set(['Erwinia', 'Gammaproteobacteria', 'Enterobacteriaceae', 'Enterobacteriales', 'Proteobacteria', 'Bacteria']) 

Im Grunde, was ich tun muss, ist das letzte gemeinsame Element zwischen den verschachtelten Listen finden (sie sind taxanomic Klassifikationen). Ich muss also nicht alle Schnittpunkte finden, nur den letzten oder alle, wenn ich nur den letzten Eintrag aufrufen kann.

intersection_lst[-1] 

In diesem Fall möchte ich den Ausgang "Erwinia" sein.

Danke für Ihre Hilfe.

+0

, welche Version von Python sind mit Ihnen zu arbeiten? –

+0

die Version ist Python 2.7.3 – Binnie

Antwort

7

Finden Sie die Kreuzung einzigartig sind, dann bestellen wieder einzuführen.

intersection_set = set.intersection(*map(set, taxa)) 
intersection_lst = [t for t in taxa[0] if t in intersection_set] 

Oder, wenn Sie auf Einzeiler unmäßig lieb sind:

sorted(set.intersection(*map(set, taxa)), key=lambda x: taxa[0].index(x)) 
+0

Das ist perfekt! Vielen Dank! – Binnie

0

Sie können dies mit bekommen:

[t for t in taxa[0] if all(t in l for l in taxa)] 
# ['Bacteria', 'Proteobacteria', 'Gammaproteobacteria', 'Enterobacteriales', 'Enterobacteriaceae', 'Erwinia'] 

Wenn die Listen groß sind, wäre es effizienter sein, zu tun:

taxa_set = map(set, taxa)  
[t for t in taxa[0] if all(t in l for l in taxa_set)] 
0
from collections import OrderedDict 
from itertools import chain 

d=OrderedDict() 
for elem in chain(*taxa): 
    if elem in d: 
     d[elem] += 1 
    else: 
     d[elem] = 1 

intersection_lst = [ k for k,v in d.items() if v == len(taxa) ] 

Beachten Sie, dass dies nur funktioniert, wenn die inneren Listen sind einzigartig.

Und hier ist ein Beispiel einer geordneten Zähler mit:

from collections import OrderedDict,Counter 
from itertools import chain 

class OrderedCounter(Counter,OrderedDict): pass 

d = OrderedCounter(chain(*taxa)) 
intersection_lst = [ k for k,v in d.items() if v == len(taxa) ] 

noch funktioniert nur, wenn Elemente in jeder sublist

+0

Dies wird nicht funktionieren, wenn ein Element mehrmals in einer inneren Liste erscheint. –

+0

@DavidRobinson - Guter Punkt, ich hatte nicht darüber nachgedacht. – mgilson

0

hatte ich ein ähnliches Problem heute. In meinen Benchmarks war die Verwendung von set.intersection der schnellste Weg, dies in CPython zu erreichen, wobei ~ 170us mit meinem Dataset verwendet wurde.

In PyPy jedoch nahm eine Hand-Roll-Funktion, die den Vorteil der Bestellung nahm nur ~ 80us, fast doppelt so schnell wie CPython! Die gleiche Funktion in CPython hat ~ 6200us.

Hier ist die Funktion für die Nachwelt:

def intersect_ordered(a, b): 
    matches = [] 
    ia, ib = 0, 0 
    la, lb = len(a), len(b) 
    while ia < la and ib < lb: 
     va, vb = a[ia], b[ib] 
     if va < vb: 
      ia += 1 
     elif vb < va: 
      ib += 1 
     else: 
      matches.append(va) 
      ia += 1 
      ib += 1 
    return matches