在Python中,如何反转一个二维字典?

5
我有一个以下形式的字典:

dict = {
   "a" : {"a1" : 1},
   "b" : {"a2" : 1, "a3" : 2},
   "c" : {"a2" : 3, "a4" : 3}
}

我需要反向索引字典,格式如下:

inverseDict = {
    "a1" : {"a" : 1},
    "a2" : {"b" : 1, "c" : 3},
    "a3" : {"b" : 2},
    "a4" : {"c" : 3}
}

基本上,
inverseDict = {dict.value.key : { dict.key : dict.value.value}}

因此,基本上我需要将值的键作为键,将键作为值的键,同时合并重复的新键的结果等。

我尝试过

ks = dict.keys()
vals = dict.values()

ks2 = vals.keys()
vals2 = vals.values()

如果这有任何意义的话

但是我遇到了一个错误

'dict_values' object has no attribute 'keys'

据我所知,这是因为dict.values() .keys() .items()返回的是“视图”,而不是实际元素本身,但我不知道如何解决这个问题。

另外,是否有更有效的解决方案?因为我的实际字典相当大(约10k个键),生成的反向字典也会很大(> 3k个键)。


1
相关链接:https://dev59.com/_VcP5IYBdhLWcg3wVYeh - pault
1
只是一个快速的评论,您将想要避免使用内置的关键字 dict 进行赋值。这样会导致遮盖dict内置对象。 - r.ook
@Idlehands 嗯,这不是我的dict的实际名称,只是一个“虚拟”的名称。但是,我明白你的意思。 - George
3个回答

8

使用collections.defaultdict(dict)和双重循环相当容易:

d = {
    "a" : {"a1" : 1},
    "b" : {"a2" : 1, "a3" : 2},
    "c" : {"a2" : 3, "a4" : 3},
}

import collections

inverted = collections.defaultdict(dict)

for key,subd in d.items():
    for k,v in subd.items():  # no inspiration for key/value names...
        inverted[k][key] = v

「inverted」是
{'a1': {'a': 1},
 'a2': {'b': 1, 'c': 3},
 'a3': {'b': 2},
 'a4': {'c': 3}}

使用defaultdict可以避免测试条目是否已存在并在不存在时创建一个字典值。因此,只需按预期顺序毫不费力地添加键/值即可。
请注意,那些需要将项目分配到多个对象中的问题很难使用推导式解决。

它绝对有效,但我不明白为什么使用inverted=collections.defaultdict(dict)比仅仅使用inverted = {}更好。 - George
1
因为使用一个空字典需要测试字典是否已经存在并在不存在时创建它:这会增加代码行数并降低速度。如果需要,您可以将其转换回dictinverted = dict(inverted) - Jean-François Fabre
但是在这两种情况下,您都创建了一个空的“inverted”,然后继续填充它的值?我是否理解有误? - George
使用默认字典,只需执行 inverted["a_key"] 就可以在 a_key 键下创建一个字典(如果不存在)。您无需担心字典的初始化。 - Jean-François Fabre

1

还有一种无需标准库的解决方案...但是Jean-Francois Fabre的答案更加简洁,可能更易于模块化。如果不确定,请使用标准库。

OriginalDict = ... (Original dict items)
InvertedDict = {}
for k, v in OriginalDict.items():
    for k_, v_ in v.items():
        if InvertedDict.get(k_) is None:
            InvertedDict[k_] = {}
        InvertedDict[k_][k] = v_

1
您可以使用setdefault
d = {
    'a': {'a1': 1},
    'b': {'a2': 1, 'a3': 2},
    'c': {'a2': 3, 'a4': 3}
}

result = {}
for ok, vs in d.items():
    for ik, v in vs.items():
        result.setdefault(ik, {})[ok] = v

print(result)

输出

{'a4': {'c': 3}, 'a1': {'a': 1}, 'a2': {'c': 3, 'b': 1}, 'a3': {'b': 2}}

setdefault函数的效果类似于使用defaultdict


好的解决方案。唯一的缺点是,即使找到了键,它也会创建一个空字典对象。当与更可能被内部化或仅创建一次并存储在setdefault可以引用的变量中的不可变对象一起使用时,setdefault更好。 - Jean-François Fabre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接