Как сгруппировать значения словаря Python, которые сами являются словарями - PullRequest
0 голосов
/ 02 апреля 2019

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

my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
           "Q3": {1: "animal", 2: "vehicle"}, Q4: {1: "animal", 2: "vehicle"}}

Результат должен выглядеть следующим образом:

result = {("Q1", "Q2"): {0: "no", 1: "yes"}, 
          ("Q3", "Q4"): {1: "animal", 2: "vehicle"}}

Я пробовал решения, перечисленные здесь: Группировка ключей словаря Python в виде списка и создание нового словаря с этим списком в качестве значения

Использование collection.defaultdict не работает, потому что в результате будет подразумеваться, что словари, которые я использую какключи для группировки в конечном итоге становятся ключами словаря результатов:

result = {{0: "no", 1: "yes"}: ["Q1", "Q2"] , 
          {1: "animal", 2: "vehicle"}: ["Q3", "Q4"]}

Конечно, это не работает, потому что ключи словарей должны быть неизменяемыми.Поэтому я бы хотел что-то вроде frozendict, которого нет в стандартной библиотеке python.

Использование itertools.groupby также не работает, поскольку требует сортировки данных.Но operator.itemgetter не может сортировать словари.Там написано:

TypeError: '<' not supported between instances of 'dict' and 'dict'

Поэтому я хотел бы узнать Pythonic способ решения этой проблемы!Спасибо за вашу помощь:)

Ответы [ 4 ]

4 голосов
/ 02 апреля 2019

Вместо использования frozendict вы можете использовать frozenset элементов словарей:

intermediate_dict = defaultdict(list)
for k, v in my_dict.items():
    intermediate_dict[frozenset(v.items())].append(k)

result = {tuple(v): dict(k) for k, v in intermediate_dict.items()}

Выход:

{('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}

Вышеуказанное не предполагает или требует сортированный ввод, делая его O(n) для всех случаев, в то время как сортировка O(n log n).

3 голосов
/ 02 апреля 2019

Принимая отсортированный словарь по значению, вы можете использовать itertools.groupby:

{tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)}

Код

from itertools import groupby

my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
           "Q3": {1: "animal", 2: "vehicle"}, "Q4": {1: "animal", 2: "vehicle"}}

print({tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)})
# {('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}
0 голосов
/ 02 апреля 2019

Вот еще один способ использования frozenset и groupby

from operator import itemgetter
from itertools import groupby

first = itemgetter(0)
second = itemgetter(1)

my_hashes = sorted([(k, hash(frozenset(v))) for k, v in my_dict.items()], key=second)

d = dict()

for k, v in groupby(my_hashes, key=second):
    items = list(v)
    d[tuple(map(first, items))] = my_dict.get(first(first(items)))

print(d)

{('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}, ('Q1', 'Q2'): {0: 'no', 1: 'yes'}}
0 голосов
/ 02 апреля 2019

Итак, я бы потребовал что-то наподобие Frozendict, которого нет в стандартной библиотеке Python.

Не могли бы вы уточнить это?Хотя frozendict не соответствует языковому стандарту, доступно расширение, которое вы можете установить: https://pypi.org/project/frozendict/

В качестве альтернативы, вы можете превратить словари в набор (отсортированных по ключам) (key, value) элементов дляполучить неизменяемое, каноническое и обратимое представление, которое можно использовать в качестве ключа словаря.

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

Редактировать: Или используйте frozenset () для предметов, как указывает другой ответ.Обратите внимание, что это также требует рекурсивного обеспечения неизменности значений внутреннего словаря.

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