Разбить список записей на их количество в python - PullRequest
0 голосов
/ 03 апреля 2020

Допустим, у меня есть две категории (A и B) со следующими возможными параметрами:

As = ['A1', 'A2']
Bs = ['B1', 'B2', 'B3']

Предположим следующие входные данные в виде списка словарей:

input_data = [
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B2'},
    {'A': 'A2', 'B': 'B3'},
    {'A': 'A2', 'B': 'B2'},
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B2'},
    {'A': 'A1', 'B': 'B1'}
]

Я хочу разбить вышеприведенное число на составляющие в следующем формате:

expected_output = {
    'A': {
        'A1': {'B1': 5, 'B2': 2, 'B3': 0},
        'A2': {'B1': 0, 'B2': 1, 'B3': 1}
    },
    'B': {
        'B1': {'A1': 5, 'A2': 0},
        'B2': {'A1': 2, 'A2': 1},
        'B3': {'A1': 0, 'A2': 1}
    }
}

Каков наилучший метод для решения этой проблемы?

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Вы можете использовать вложенный defaultdict из Counter для подсчета:

from collections import defaultdict, Counter
from pprint import pprint

input_data = [
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B2'},
    {'A': 'A2', 'B': 'B3'},
    {'A': 'A2', 'B': 'B2'},
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B1'},
    {'A': 'A1', 'B': 'B2'},
    {'A': 'A1', 'B': 'B1'}
]

d = defaultdict(lambda : defaultdict(Counter))

mapping = {'A': 'B', 'B': 'A'}

for data in input_data:
    for k, v in data.items():
        reverse_key = data[mapping[k]]
        d[k][v][reverse_key] += 1

pprint(d)

Выход:

defaultdict(<function <lambda> at 0x0000021CB4665160>,
            {'A': defaultdict(<class 'collections.Counter'>,
                              {'A1': Counter({'B1': 5, 'B2': 2}),
                               'A2': Counter({'B3': 1, 'B2': 1})}),
             'B': defaultdict(<class 'collections.Counter'>,
                              {'B1': Counter({'A1': 5}),
                               'B2': Counter({'A1': 2, 'A2': 1}),
                               'B3': Counter({'A2': 1})})})

Вы также можете использовать вложенный defaultdict без Counter:

d = defaultdict(lambda : defaultdict(lambda : defaultdict(int)))

, который даст почти то же самое:

defaultdict(<function <lambda> at 0x000001D544AD5160>,
            {'A': defaultdict(<function <lambda>.<locals>.<lambda> at 0x000001D546AB89D0>,
                              {'A1': defaultdict(<class 'int'>,
                                                 {'B1': 5,
                                                  'B2': 2}),
                               'A2': defaultdict(<class 'int'>,
                                                 {'B2': 1,
                                                  'B3': 1})}),
             'B': defaultdict(<function <lambda>.<locals>.<lambda> at 0x000001D546AB8A60>,
                              {'B1': defaultdict(<class 'int'>, {'A1': 5}),
                               'B2': defaultdict(<class 'int'>,
                                                 {'A1': 2,
                                                  'A2': 1}),
                               'B3': defaultdict(<class 'int'>, {'A2': 1})})})

Примечание: Вышеуказанное не включает 0 значений. Это не имеет значения, потому что даже если вы сделаете d['A']['A1']['B3'], вы получите 0.

Также defaultdict и Counter являются подклассами dict, поэтому они могут рассматриваться как обычные словари ,

0 голосов
/ 03 апреля 2020

Я придумал следующее решение:

import itertools

grouping_func = lambda x: (x['A'], x['B'])
sorted_data = sorted(input_data, key=grouping_func)
groups = itertools.groupby(sorted_data, grouping_func)

A_results = {a: {b: 0 for b in Bs} for a in As}
B_results = {b: {a: 0 for a in As} for b in Bs}

for group, group_data in groups:
    a, b = group
    count = len(list(group_data))
    A_results[a][b] = count
    B_results[b][a] = count
output = {'A': A_results, 'B': B_results}
print(output)

Мне интересно, есть ли лучший и более оптимизированный метод для достижения этой цели?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...