Python уменьшить, чтобы найти объединение множеств

Я пытаюсь найти объединение множеств множеств. В частности, я хочу объединение списка узлов для каждого ключа в словаре графиков networkx, называемых periodic_gs. Я хотел бы использовать функцию reduce, поскольку разумно брать объединение всех periodic_gs[x].nodes(), где x является ключом periodic_gs.

Вот моя попытка:

reduce(lambda x,y: set(periodic_gs[x].nodes()).union(set(periodic_gs[y].nodes())), periodic_gs.keys(), {})

Для меня это говорит о соединении узлов по каждому графику в словаре. По какой-то причине python говорит мне: TypeError: unhashable type: 'dict' Я не вижу этого TypeError, потому что periodic_gs.keys() - это список ключей (они являются строками, но я не вижу, как это имеет значение), а при замене в аргументы функции лямбда будут работать.

Что вызывает ошибку типа и как ее исправить?

Ответ 1

Вы можете использовать set.union следующим образом:

>>> lis = [{1, 2, 3, 4}, {3, 4, 5}, {7, 3, 6}]
>>> set().union(*lis)
set([1, 2, 3, 4, 5, 6, 7])

Использование reduce:

>>> reduce(set.union, lis)
set([1, 2, 3, 4, 5, 6, 7])

Для вашего кода это должно сделать это:

set().union(*(x.nodes() for x in periodic_gs.values()))
reduce(set.union, (x.nodes() for x in periodic_gs.values()))

Ответ 2

{} - пустой словарь, а не набор. Используйте set() для создания пустого набора.

Однако, я думаю, вы неверно истолковываете, как reduce() работает здесь; x - это предыдущее возвращаемое значение lambda, а y - следующее значение из последовательности. Поскольку вы возвращаете набор, x всегда присутствует здесь, и вы не можете использовать его как ключ к periodic_gs.

Если вы хотите объединение всех узлов в графе, используйте itertools.chain.from_iterable() и set():

from itertools import chain

set(chain.from_iterable(periodic_gs[key].nodes() for key in periodic_gs))

Это создает один набор из каждого из вызовов nodes().

Чтобы использовать reduce(), вам нужно будет учитывать, что первым аргументом всегда является набор:

reduce(lambda res, key: res.union(periodic_gs[key].nodes()),  periodic_gs, set())

Я предполагаю, что periodic_gs является итерируемым (дающим ключи), как обычный словарь; Если нет, используйте periodic_gs.keys().

Быстрая демонстрация с обычным словарем:

>>> example = {'foo': [1,2,3], 'bar': [3, 4, 1]}
>>> reduce(lambda res, key: res.union(example[key]), example, set())
set([1, 2, 3, 4])

Ответ 3

Есть пара проблем с вашим кодом. Инициализатор, который вы указали в reduce, {}, представляет собой пустой dict, а не set, как вы, кажется, предполагаете, что приводит к ошибке первого типа. Тем не менее, даже после того, как вы исправите это, еще большая проблема: lambda оценивает объект set, содержащий узлы, и этот set затем используется как значение x для следующего вызова lambda, который пытается использовать его так, как если бы он был ключом periodic_gs, тем самым вызывая ошибку второго типа. Прежде всего, нет смысла перебирать ключи dict, если вы собираетесь использовать их только для доступа к значениям; просто перебирайте значения в первую очередь! Также нет смысла конвертировать множество списков в группы, а затем принимать их объединение, когда вы можете просто объединить их все вместе и принять объединение результата.

Я бы рекомендовал полностью переписать код как:

set(n for val in periodic_gs.values() for n in val.nodes())

Ответ 4

Вы можете использовать сокращение, чтобы получить объединение нескольких множеств, как показано ниже:

>>> import operator
>>> a = set([1, 3, 5])
>>> b = set([2, 4, 6])
>>> c = set([0, 7, 8])
>>> reduce(operator.or_, [a, b, c])
set([0, 1, 2, 3, 4, 5, 6, 7, 8])

Ответ 5

У вас не может быть set set, потому что элементы set должны быть хешируемыми. Вы можете сделать set frozenset.

{frozenset(n) for val in periodic_gs.values() for n in val.nodes()}