Вычислить количество всех элементов во вложенном списке

у меня есть список списков и я хотел бы создать фрейм данных с количеством всех уникальных элементов. Вот мои тестовые данные:

test = [["P1", "P1", "P1", "P2", "P2", "P1", "P1", "P3"],
        ["P1", "P1", "P1"],
        ["P1", "P1", "P1", "P2"],
        ["P4"],
        ["P1", "P4", "P2"],
        ["P1", "P1", "P1"]]

Я могу сделать что-то вроде этого, используя Counter С for цикл as:

from collections import Counter
for item in test:
     print(Counter(item))

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

ожидаемый вывод в виде фрейма данных:

P1 P2 P3 P4
15 4  1  2

4 ответов


вот один из способов.

from collections import Counter
from itertools import chain

test = [["P1", "P1", "P1", "P2", "P2", "P1", "P1", "P3"],
        ["P1", "P1", "P1"],
        ["P1", "P1", "P1", "P2"],
        ["P4"],
        ["P1", "P4", "P2"],
        ["P1", "P1", "P1"]]

c = Counter(chain.from_iterable(test))

for k, v in c.items():
    print(k, v)

# P1 15
# P2 4
# P3 1
# P4 2    

для вывода в виде фрейма данных:

df = pd.DataFrame.from_dict(c, orient='index').transpose()

#    P1 P2 P3 P4
# 0  15  4  1  2

С точки зрения лучшей производительности, вы должны использовать:

  • collections.Counter С itertools.chain.from_iterable as:

    >>> from collections import Counter
    >>> from itertools import chain
    
    >>> Counter(chain.from_iterable(test))
    Counter({'P1': 15, 'P2': 4, 'P4': 2, 'P3': 1})
    
  • или, yo должен использовать collections.Counter С понимание (требует меньше импорта itertools С такой же производительностью) as:

    >>> from collections import Counter
    
    >>> Counter([x for a in test for x in a])
    Counter({'P1': 15, 'P2': 4, 'P4': 2, 'P3': 1})
    

продолжайте читать для более альтернативных решений и сравнение производительности. (скип иное)


подход 1: объедините свои подсписки, чтобы создать единый list и найти счет, используя collections.Counter.

  • Решение 1: конкатенация списка с помощью itertools.chain.from_iterable и найти счет, используя collections.Counter as:

    test = [
        ["P1", "P1", "P1", "P2", "P2", "P1", "P1", "P3"],
        ["P1", "P1", "P1"],
        ["P1", "P1", "P1", "P2"],
        ["P4"],
        ["P1", "P4", "P2"],
        ["P1", "P1", "P1"]
    ]
    
    from itertools import chain 
    from collections import Counter
    
    my_counter = Counter(chain.from_iterable(test)) 
    
  • решение 2: объединить список с помощью понимание as:

    from collections import Counter
    
    my_counter = Counter([x for a in my_list for x in a])
    
  • решение 3: конкатенация списка с помощью sum

    from collections import Counter
    
    my_counter = Counter(sum(test, []))
    

подход 2: вычислить количество элементов в каждом подсписке с помощью collections.Counter и затем sum the Counter объекты в списке.

  • решение 4: подсчет объектов каждого подсписка с помощью collections.Counter и map as:

    from collections import Counter
    
    my_counter = sum(map(Counter, test), Counter())
    
  • решение 5: подсчет объектов каждого подсписка с помощью понимание as:

    from collections import Counter
    
    my_counter = sum([Counter(t) for t in test], Counter())
    

во всех решениях выше,my_counter проведет значение:

>>> my_counter
Counter({'P1': 15, 'P2': 4, 'P4': 2, 'P3': 1})

Сравнение Производительности

ниже timeit сравнение на Python 3 для списка 1000 подсписок и 100 элементов в каждом подсписке:

  1. быстрый, используя chain.from_iterable (17.1 мсек)

    mquadri$ python3 -m timeit "from collections import Counter; from itertools import chain; my_list = [list(range(100)) for i in range(1000)]" "Counter(chain.from_iterable(my_list))"
    100 loops, best of 3: 17.1 msec per loop 
    
  2. второй в списке использует понимание чтобы объединить список, а затем сделать Count (аналогичный результат, как указано выше, но без дополнительный импорт itertools) (18.36 msec)

    mquadri$ python3 -m timeit "from collections import Counter; my_list = [list(range(100)) for i in range(1000)]" "Counter([x for a in my_list for x in a])"
    100 loops, best of 3: 18.36 msec per loop
    
  3. третий с точки зрения производительности является использование Counter по подспискам внутри понимание : (162 МС)

    mquadri$ python3 -m timeit "from collections import Counter; my_list = [list(range(100)) for i in range(1000)]" "sum([Counter(t) for t in my_list], Counter())"
    10 loops, best of 3: 162 msec per loop
    
  4. четвертым в списке является использование Counter С map (результаты очень похожи на тот, который использует понимание выше) (176 msec)

    mquadri$ python3 -m timeit "from collections import Counter; my_list = [list(range(100)) for i in range(1000)]" "sum(map(Counter, my_list), Counter())"
    10 loops, best of 3: 176 msec per loop
    
  5. решение с помощью sum для объединения списка слишком медленно (526 МС)

    mquadri$ python3 -m timeit "from collections import Counter; my_list = [list(range(100)) for i in range(1000)]" "Counter(sum(my_list, []))"
    10 loops, best of 3: 526 msec per loop
    

вот еще один способ сделать это, используя itertools.groupby

>>> from itertools import groupby, chain

>>> out = [(k,len(list(g))) for k,g in groupby(sorted(chain(*test)))]
>>> out
>>> [('P1', 15), ('P2', 4), ('P3', 1), ('P4', 2)]

преобразовать его в dict, как:

>>> dict(out)
>>> {'P2': 4, 'P3': 1, 'P1': 15, 'P4': 2}

чтобы преобразовать его в dataframe используйте

>>> import pandas as pd

>>> pd.DataFrame(dict(out), index=[0])
   P1  P2  P3  P4
0  15   4   1   2

функция " set " сохраняет только уникальные элементы в списке. Таким образом, используя "len(set(mylinst))", вы получаете количество уникальных элементов в своем списке. Тогда вам нужно только повторить это.

dict_nb_item = {}
i = 0
for test_item in test:
    dict_nb_item[i] = len(set(test_item))
    i += 1
print(dict_nb_item)