【问题标题】:Pythonic inverse dict non-unique mappingsPythonic 逆字典非唯一映射
【发布时间】:2014-02-17 22:54:12
【问题描述】:

我有一本这样的字典:

dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}

并且想要这样的逆:

dict2 = dict({1:['a','b','c'], 2:['a','b','c'], 3:['a','b'], 4:['b']})

喜欢这些问题:

Inverse Dict in Python \\ In-place dictionary inversion in Python

但我想用非唯一键来做,我不想就地转换。我有一些代码在工作,但我想知道是否有一种字典理解方式可以做到这一点。

from collections import defaultdict
dict2 = defaultdict(list)
for i in dict1:
    for j in dict1[i]:
        dict2[j].append(i)

我试过这个,但它只适用于独特的映射。唯一我的意思是“对于每个值,只有一个键在其下列出该值”。所以唯一映射:'1:[a],2:[b],3:[c] -> a:[1],b:[2],c:[3]'VS非唯一映射'1: [a], 2: [a, b], 3: [b, c] -> a: [1, 2], b: [2, 3], c: [3]'

dict2 = {j: i for i in dict1 for j in dict1[i]}

我想一定是这样的:

dict2 = {j: [i for i in dict1 if j in dict1[i]] for j in dict1[i]} # I know this doesn't work

除了它不起作用之外,这样的理解似乎效率低下。有没有一种有效的、单一的方式来做到这一点?

【问题讨论】:

  • 它不适用于非唯一值,根据定义,字典或哈希表中的键是唯一的
  • Python 字典不支持重复键 --> http://stackoverflow.com/questions/10664856/make-dictionary-with-duplicate-keys-in-python
  • 我猜我对“独特”的使用是模棱两可的。我所说的“唯一”的意思是,如果原始字典具有从键-> 值的 1-1 映射。唯一我的意思是“对于每个值,只有一个键列出了该值”。如此独特的映射:'1: [a], 2: [b], 3: [c] -> a: [1], b: [2], c: [3]' vs '1: [a], 2: [a, b], 3: [b, c] -> a: [1, 2], b: [2, 3], c: [3]'
  • "...这样的理解似乎效率低下。"似乎您正试图过早地优化您的代码。使用显式 for 循环可能不会对代码的性能产生重大影响。
  • 您到底想对重复项做什么?丢弃它们?将它们分组到列表中?如果丢弃,你如何定义哪些?

标签: python dictionary dictionary-comprehension


【解决方案1】:

标准dict:

>>> dict2 = {}
>>> for key, values in dict1.items():
...     for value in values:
...             dict2.setdefault(value, []).append(key)
... 
>>> dict2
{1: ['a', 'c', 'b'], 2: ['a', 'c', 'b'], 3: ['a', 'b'], 4: ['b']}

defaultdict:

>>> dict2 = defaultdict(list)
>>> for key, values in dict1.items():
...     for value in values:
...             dict2[value].append(key)
... 
>>> dict2
{1: ['a', 'c', 'b'], 2: ['a', 'c', 'b'], 3: ['a', 'b'], 4: ['b']}

【讨论】:

  • 这与我发布的代码基本相同,并没有解决我的任何问题。
  • 这正是你说你想要的输出。如果您想要不同的输出,请模拟一下!
【解决方案2】:

作为单线(感谢 mhlesters 的输入),但可读性一般(并且仅因为 dict2 中的值是可变的,因此 setdefault 返回对它们的引用):

import itertools
[dict2.setdefault(k,[]).append(v) for k,v in itertools.chain.from_iterable([itertools.product(vals,[key]) for key,vals in dict1.items()])]

或者使用 for 循环:

import collections
import itertools
dict2=collections.defaultdict(list)
for k,v in itertools.chain.from_iterable([itertools.product(vals,[key]) for key,vals in dict1.items()]):
    dict2[k].append(v)

【讨论】:

    【解决方案3】:

    我根据 Vroomfondel 的回答想出了一个答案:

    dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = {item: [key for key in dict1 if item in dict1[key]] for value in dict1.values() for item in value}
    

    这不是最快的,但它是单一的,并且不是所提供的选项中最慢的!

    from timeit import timeit
    
    methods = [['Vroomfondel1', '''from collections import defaultdict
    import itertools
    dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = defaultdict(list)
    for k,v in itertools.chain.from_iterable([itertools.product(vals,key) for key,vals in dict1.items()]):
        dict2[k].append(v)'''],
    
    ['Vroomfondel2', '''from collections import defaultdict
    import itertools
    dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = defaultdict(list)
    [dict2[k].append(v) for k,v in itertools.chain.from_iterable([itertools.product(vals,key) for key,vals in dict1.items()])]'''],
    
    
    ['***Vroomfondel2 mod', '''dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = {item: [key for key in dict1 if item in dict1[key]] for value in dict1.values() for item in value}'''],
    
    ['mhlester1', '''dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = {}
    for key, values in dict1.items():
        for value in values:
            dict2.setdefault(value, []).append(key)'''],
    
    ['mhlester1 mod', '''from collections import defaultdict
    dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = defaultdict(list)
    for key, values in dict1.items():
        for value in values:
            dict2[value].append(key)'''],
    
    ['mhlester2', '''from collections import defaultdict
    dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = defaultdict(list)
    for key, values in dict1.items():
        for value in values:
            dict2[value].append(key)'''],
    
    ['initial', '''from collections import defaultdict
    dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
    dict2 = defaultdict(list)
    for i in dict1:
        for j in dict1[i]:
            dict2[j].append(i)''']
    
    ]
    
    for method in methods:
        print "% 15s" % (method[0]), '\t', timeit(method[1], number=10000)
    

    打印出来:

       Vroomfondel1     0.202519893646
       Vroomfondel2     0.164724111557
    ***Vroomfondel2 mod     0.114083051682
          mhlester1     0.0599339008331
      mhlester1 mod     0.091933965683
          mhlester2     0.0900268554688
            initial     0.0953099727631
    

    【讨论】:

    • 如果第一个字典是:A={1:('a','b'), 2:('b','e','c'), 3:('a ','f'), 4:('c','d'), 5:('d','e','f')} with dict2 = {item: [key for key in A if item in A[key]] for value in A.values() for item in value} print dict2 最后它产生: {'a': [1, 3], 'c': [2, 4], 'b': [ 1, 2], 'e': [2, 5], 'd': [4, 5], 'f': [3, 5]}
    猜你喜欢
    • 2015-12-27
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多