2017-02-17 47 views
10

Mam listę list, który wygląda jakJak uzyskać liczbę list z określonym elementem?

listOfLists = [ 
    ['a','b','c','d'], 
    ['a','b'], 
    ['a','c'], 
    ['c','c','c','c'] 
] 

Chcę policzyć liczbę list, które mają konkretny element. Na przykład, mój wynik powinien wynosić:

{'a':3,'b':2,'c':3,'d':1} 

Jak widać, nie potrzebuję całkowitej liczby elementów. W przypadku "c", chociaż jego całkowita liczba to 5, wynik wynosi 3, ponieważ występuje tylko na 3 listach.

Używam licznika do liczenia. To samo widać poniżej.

line_count_tags = [] 
for lists in lists_of_lists: 
    s = set() 
    for element in lists: 
     s.add(t) 
    lines_count_tags.append(list(s)) 

count = Counter([count for counts in lines_count_tags for count in counts]) 

Więc, kiedy mogę wydrukować liczyć, mam

{'a':3,'c':3,'b':2,'d':1} 

Chcę wiedzieć, czy jest to znacznie lepszy sposób, aby osiągnąć swój cel.

Odpowiedz

11

Użyj Counter i przekonwertuj każdą listę do zestawu. set będzie usunąć duplikaty z każdej listy, dzięki czemu nie liczą zduplikowanych wartości w tym samym liście:

>>> from collections import Counter 

>>> Counter(item for lst in listOfLists for item in set(lst)) 
Counter({'a': 3, 'b': 2, 'c': 3, 'd': 1}) 

Jeśli podoba Ci się programowania funkcyjnego można także karmić chain z set - map ped listOfLists do Counter :

>>> from collections import Counter 
>>> from itertools import chain 

>>> Counter(chain.from_iterable(map(set, listOfLists))) 
Counter({'a': 3, 'b': 2, 'c': 3, 'd': 1}) 

Który jest całkowicie równoważny (z wyjątkiem może być nieco szybszy) do pierwszego podejścia.

9

Chciałbym przekształcić każdą listy jako zestaw przed licząc na zrozumienie generatora przekazywana do Counter:

import collections 
print(collections.Counter(y for x in listOfLists for y in set(x))) 

wynik:

Counter({'a': 3, 'c': 3, 'b': 2, 'd': 1}) 

(to jest praktycznie to, co zrobiłeś, ale powyższe spodenki kod wiele pętli i tworzenie tymczasowych list)

7

Można to zrobić bez Counter też:

result = {} 
for lis in listOfLists: 
    for element in set(lis): 
     result[element] = result.get(element, 0) + 1 
print result # {'a': 3, 'c': 3, 'b': 2, 'd': 1} 

Nie najbardziej eleganckie, ale powinno być znacznie szybciej.

5

Nieco stylistycznego różnicy na podejściu Counter z itertools.chain.from_iterable może wyglądać

Counter(chain.from_iterable(map(set, listOfLists))) 

Demo

>>> from itertools import chain 
>>> from collections import Counter 
>>> Counter(chain.from_iterable(map(set, listOfLists))) 
Counter({'a': 3, 'b': 2, 'c': 3, 'd': 1}) 

surowca odniesienia

%timeit Counter(item for lst in listOfLists for item in set(lst)) 
100000 loops, best of 3: 13.5 µs per loop 

%timeit Counter(chain.from_iterable(map(set, listOfLists))) 
100000 loops, best of 3: 12.4 µs per loop 
+0

Dostaję znacznie szybszą realizację przy użyciu 'itertools.chain' (~ 40%!) Na CPython 2.7.11. Mimo to 'Counter' +' itertools.chain' wykonuje 4 razy wolniej niż metoda 'raw', którą przedstawiłem. – zwer

+1

@zwer Eh, zależy od rozmiaru wejściowego, który omawiamy. Moje rozwiązanie ma więcej narzutów, ale jeśli zwiększysz rozmiar wejściowy, będzie on szybszy. To dlatego benchmarking nie jest aż tak ważny :) – miradulo

+0

To prawda, byłem zaskoczony, że różnica prędkości w moim miejscu była zaskoczona. Nie jestem przyzwyczajony do tego, że 'itertools' faktycznie przewyższają, no, właściwie wszystko - są zwykle wolniejszy, ale łatwiejszy do odczytania wybór: D – zwer

3

Wystarczy przeliczyć na set, spłaszczyć, używając itertools.chain.from_iterable, a następnie wprowadzić do Counter.

from collections import Counter 
from itertools import chain 

inp = [ 
    ['a','b','c','d'], 
    ['a','b'], 
    ['a','c'], 
    ['c','c','c','c'] 
] 


print(Counter(chain.from_iterable(map(set, inp)))) 
2

Podejście oblicza unikatowe wpisy w listOfLists stosując zestaw ze zrozumieniem, a następnie zlicza wystąpienia w każdej listy używając słownika zrozumieniem

A = {val for s in listOfLists for val in s} 
d = {i: sum(i in j for j in listOfLists) for i in A} 
print(d) # {'a': 3, 'c': 3, 'b': 2, 'd': 1} 

Muszę przyznać, że to trochę brzydki, ale jest to możliwe rozwiązanie (i fajne użycie słownika). Można również zrobić to jedną wkładkę poruszając obliczenie A prawo do słownika pojmowania

+0

nie ma potrzeby ponownego wrzucania zestawu 'A' do listy lub podania zestawu ze zrozumieniem listy, ekspresja generacji jest lepsza ... w rzeczywistości można zbudować' A "jako zrozumienie zestawu także – Copperfield

+0

@Copperfield Dzięki za twoją sugestię. Dokonałem zmiany. – nbryans

2

Oto kolejna wersja za pomocą pętli:

listOfLists = [ 
    ['a','b','c','d'], 
    ['a','b'], 
    ['a','c'], 
    ['c','c','c','c'] 
    ] 

final = {} 
for lst in listOfLists: 
    for letter in lst: 
     if letter in final: 
      final[letter] += 1 
     else: 
      final[letter] = 1 

Więc utworzyć pusty słownik o nazwie ostateczna. Następnie przeprowadź pętlę przez każdą literę każdej listy. Utwórz nowy klucz i wartość = 1, jeśli litera nie istnieje jeszcze jako ostateczna. W przeciwnym razie dodaj 1 do wartości tego klucza.