Группировка ключей словаря Python в виде списка и создание нового словаря с этим списком в качестве значения

#python #list #dictionary

Вопрос:

У меня есть словарь python

 d = {1: 6, 2: 1, 3: 1, 4: 9, 5: 9, 6: 1}
 

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

 v = {6:[1], 1:[2, 3, 6], 9: [4, 5]}
 

Обратите внимание, что ключи нового словаря v должны быть отсортированы. Мне трудно визуализировать и реализовать это создание словаря. Пожалуйста, предложите мне простой и эффективный способ сделать это.

Ответ №1:

Использование collections.defaultdict для удобства:

 from collections import defaultdict

v = defaultdict(list)

for key, value in sorted(d.items()):
    v[value].append(key)
 

но вы можете сделать это и с помощью bog-standard dict , используя dict.setdefault() :

 v = {}

for key, value in sorted(d.items()):
    v.setdefault(value, []).append(key)
 

Приведенное выше правило сначала сортирует ключи; последующая сортировка значений выходного словаря гораздо более громоздка и неэффективна.

Если кому-либо не нужно сортировать выходные данные, вы можете прервать sorted() вызов и использовать наборы (ключи во входном словаре гарантированно уникальны, поэтому информация не теряется):

 v = {}

for key, value in d.items():
    v.setdefault(value, set()).add(key)
 

производить:

 {6: {1}, 1: {2, 3, 6}, 9: {4, 5}}
 

(то, что выходные данные заданных значений сортируются, является совпадением, побочным эффектом того, как реализованы значения хэша для целых чисел; наборы представляют собой неупорядоченные структуры).

Комментарии:

1. Если вы используете defaultdict, но не хотите, чтобы поведение «по умолчанию» продолжалось после инициализации dict, вы можете установить default_factory для атрибута значение None . Тогда ваш defaultdict будет вести себя как обычный dict почти во всех отношениях.

2. В качестве примечания, я большой поклонник того, как вы сортируете предметы, а не значения, как это было бы первым инстинктом большинства людей (или, по крайней мере, моим). 1.

3. Что такое list во 2-й строке? Кажется, это сработает, просто имея v = defaultdict()

4. @clwen: нет, это не сработает. defaultdict() принимает заводскую функцию , которая при вызове создает новый объект для вставки в словарь, когда ключ еще не существует. Передача list означает, что всякий раз, когда ключ еще не существует, v[value] объект defaultdict вызывает list() и вставляет результат в словарь для этого ключа. Если вы опускаете заводскую функцию, defaultdict действует как обычный словарь и вызывает a KeyError для отсутствующих ключей.

5. «Сортировка значений выходного словаря позже намного более громоздка и неэффективна». Я придираюсь, но сортировка сгруппированных списков происходит быстрее, чем сортировка всех ключей сначала, говоря асимптотически…

Ответ №2:

Если вам на самом деле не нужен dict в конце дня, вы можете использовать itertools.groupby :

 from itertools import groupby
from operator import itemgetter

for k, v in groupby(sorted(d.items(), key=itemgetter(1)), itemgetter(1)):
    print(k, list(map(itemgetter(0), v)))
 

Конечно, вы могли бы использовать это для создания dict, если бы вы действительно хотели:

 {
    k: list(map(itemgetter(0), v))
    for k, v in groupby(sorted(d.items(), key=itemgetter(1)), itemgetter(1))
}
 

Но на этом этапе вам, вероятно, лучше использовать решение Martijn defaultdict.