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()}