2017-02-17 3 views
10

У меня есть список списков, который выглядит какКак получить количество списков с определенным элементом?

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

Я хочу, чтобы подсчитать количество списков, которые имеют определенный элемент. Например, мой вывод должен быть

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

Как вы можете видеть, мне не нужен общий счет элемента. В случае "c", хотя его общий счет равен 5, выход равен 3, поскольку он встречается только в 3 списках.

Я использую счетчик, чтобы получить счет. То же самое можно увидеть ниже.

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

Так что, когда я печатаю счет, я получаю

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

Я хочу знать, если есть гораздо лучший способ для достижения своей цели.

ответ

11

Используйте Counter и преобразуйте каждый список в набор. set будет удалить все дубликаты из каждого списка, так что вы не считать повторяющиеся значения в одном списке:

>>> from collections import Counter 

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

Если вы как функционального программирования вы также можете кормить chain из set - map PED listOfLists к 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}) 

Это абсолютно эквивалентно (за исключением, может быть, немного быстрее) первого подхода.

9

Я хотел бы преобразовать каждый список в качестве набора, перед подсчетом в генераторе понимания передается Counter:

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

результат:

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

(это практически то, что вы сделали, но выше код шорты много циклов и временные списки)

7

Вы можете сделать это без Counter тоже:

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} 

Не самый элегантный, но должна быть значительно быстрее.

5

Немного стилистической разницы на Counter подходе с itertools.chain.from_iterable может выглядеть

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

Грубый тест

%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

Я получаю намного более быстрое выполнение, используя 'itertools.chain' (~ 40%!) На CPython 2.7.11. Тем не менее, 'Counter' +' itertools.chain' выполняется в 4 раза медленнее, чем метод 'raw', который я представил. – zwer

+1

@zwer Eh, зависит от размера ввода, который мы обсуждаем. У моего решения больше накладных расходов, но если вы увеличиваете размер ввода, он будет быстрее. Вот почему бенчмаркинг не так уж и важен :) – miradulo

+0

Правда, я был просто удивлен резкой разницей в скорости на моем месте, я не привык к тому, что «itertools» действительно превосходит, ну, почти что угодно - они обычно более медленный, но более удобный для чтения выбор: D – zwer

3

Просто конвертируйте в set, сгладьте с помощью itertools.chain.from_iterable, а затем подайте в 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

Этот подход вычисляет уникальные записи в listOfLists с помощью набора понимания, а затем подсчитывает вхождения в каждый список с помощью словаря понимания

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} 

Я признаю, что это немного некрасиво, но это возможное решение (и прохладное использование понимания словаря). Вы также можете сделать это один-лайнер, перемещая расчет A прямо в словаре понимания

+0

нет необходимости снова накладывать свой набор 'A' в список или кормить набор с пониманием списка, выражение генерации лучше ... на самом деле вы можете построить' A 'как установленное понимание слишком – Copperfield

+0

@Copperfield Спасибо за ваше предложение. Я внес изменения. – nbryans

2

Вот еще один вариант с использованием циклов:

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 

Так создать пустой словарь под названием окончательным. Затем пропустите каждую букву каждого списка. Создайте новый ключ и значение = 1, если письмо еще не существует в финале в качестве ключа. В противном случае добавьте 1 к значению для этого ключа.