反转/反转字典映射


Answers:


922

对于Python 2.7.x

inv_map = {v: k for k, v in my_map.iteritems()}

对于Python 3+:

inv_map = {v: k for k, v in my_map.items()}

4
在最近的Python 2.7.x版本my_map.items()的作品,以及
瓦伦丁

29
除了在值不统一的情况下不起作用之外,这将起作用。在这种情况下,您将丢失一些条目
gabuzo


2
是的,作为实施细节。The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon。无法保证会保持这样的状态,因此不要依赖于Dict具有与相同行为的代码OrderedDict
Mattias

9
@Mattias,对于Python 3.6来说是这样。对于3.7版,正式保留订单:mail.python.org/pipermail/python-dev/2017-December/151283.html。BDFL是这样说的。
interDist

174

假设字典中的值是唯一的:

dict((v, k) for k, v in my_map.iteritems())

22
这些值也必须是可哈希的
John La Rooy

30
@ Buttons840:如果值不是唯一的,则无论如何字典都没有唯一的反转,或者换句话说,反转没有意义。
Wrzlprmft 2014年

2
@ Buttons840仅最后一个键将显示该值。可能无法保证iteritems()将要输出的顺序,因此可以假定将为某个非唯一值分配一个任意键,这种方式在某些情况下显然可以重现,但通常不会。
Evgeni Sergeev

2
注意,当然,在Python 3中不再有iteritems()方法,这种方法将行不通。items()如接受的答案所示,在此处使用。同样,字典理解比调用还要漂亮dict
Mark Amery

5
@Wrzlprmft对于非唯一值,有一个自然的反定义。每个值都映射到导致它的键集。
狮子座

135

如果中的值my_map不是唯一的:

inv_map = {}
for k, v in my_map.iteritems():
    inv_map[v] = inv_map.get(v, [])
    inv_map[v].append(k)

56
...或只是inv_map.setdefault(v,[])。append(k)。我曾经是defaultdict的忠实拥boy,但是后来我被搞砸了太多次,并得出结论说,显式实际上比隐式更好。
alsuren 2010年

这个答案对于多地图是不正确的,在这里追加是没有用的,因为每次该值都会重置为空列表,应该使用set_default
Yaroslav Bulatov 16-4-22

1
@YaroslavBulatov不,这里显示的代码没有损坏- inv_map.get(v, [])如果有一个列表,则返回已经添加的列表,因此分配不会重置为空列表。setdefault仍然会更漂亮。
Mark Amery

10
在这里设置一个更有意义。键(可能)是可散列的,并且没有顺序。inv_map.setdefault(v, set()).add(k)
Artyer

1
在python3中,使用my_map.items()代替my_map.iteritems()
apitsch '19

42

为此,同时保留映射类型(假设它是a dictdict子类):

def inverse_mapping(f):
    return f.__class__(map(reversed, f.items()))

4
可能很聪明,但是当原始字典中有多个键具有相同的值时,它将不起作用。
Rafael_Espericueta

1
@Rafael_Espericueta对于这个问题的任何可能答案都是如此,因为重复值的映射是不可逆的。
Mark Amery

2
@Mark_Amery在某种意义上,它可以更一般地是可逆的。例如:D = {1:[1,2],2:[2,3],3:[1]},Dinv = {1:[1,3],2:[1,2],3: [2]}。D是词典,例如{parent:children},而Dinv是词典,{child:parents}。
Rafael_Espericueta,

36

尝试这个:

inv_map = dict(zip(my_map.values(), my_map.keys()))

(请注意,字典视图上的Python文档明确地保证了这一点,.keys()并且.values()其元素具有相同的顺序,这使得上述方法可以工作。)

或者:

inv_map = dict((my_map[k], k) for k in my_map)

或使用python 3.0的dict理解

inv_map = {my_map[k] : k for k in my_map}

1
请注意,这仅在键是唯一的时才有效(如果要反转它们,几乎绝不会这样)。
gented

根据python.org/dev/peps/pep-0274 dict的理解在2.7+中也可用。
卡乌(Kawu)

24

另一种更实用的方法:

my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))

3
感谢您的发布。我不确定这是否是可取的-在PEP 279中引用Guido Van Rossum的话:“ filter并且map应该死掉并纳入列表理解中,而不是增加变体”。
Brian M. Hunt 2014年

2
是的,布莱恩是个公平点。我只是将其添加为对话点。dict理解方法在我想像的大多数情况下更具可读性。(而且我想可能还会更快)
Brendan Maguire 2014年

3
可能比别人少可读,但这种方式确实有能够换出效益dict与其他测绘类型,如collections.OrderedDictcollections.defaultdict
威尔小号

10

这扩展了Robert的答案,适用于字典中的值不是唯一的情况。

class ReversibleDict(dict):

    def reversed(self):
        """
        Return a reversed dict, with common values in the original dict
        grouped into a list in the returned dict.

        Example:
        >>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
        >>> d.reversed()
        {1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
        """

        revdict = {}
        for k, v in self.iteritems():
            revdict.setdefault(v, []).append(k)
        return revdict

实现受到限制,因为您不能使用reversed两次并取回原始文件。因此它不是对称的。已通过Python 2.6测试。是一个我用来打印结果字典的用例。

如果您宁愿使用a而set不是a list,并且可能存在对此有意义的无序应用程序,而不是setdefault(v, []).append(k)use setdefault(v, set()).add(k)


这也是使用集而不是列表的好地方,即revdict.setdefault(v, set()).add(k)
mueslo

当然,但这就是为什么使用它的充分理由set。这是适用于此的固有类型。如果我要查找所有值都不为1或的键2怎么办?然后我可以做d.keys() - inv_d[1] - inv_d[2](在Python 3中)
mueslo

9

我们还可以使用重复键反转字典defaultdict

from collections import Counter, defaultdict

def invert_dict(d):
    d_inv = defaultdict(list)
    for k, v in d.items():
        d_inv[v].append(k)
    return d_inv

text = 'aaa bbb ccc ddd aaa bbb ccc aaa' 
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}  

这里

与使用的等效技术相比,此技术更简单,更快dict.setdefault()


6

例如,您有以下字典:

dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}

而且您想以相反的形式获取它:

inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}

第一个解决方案。要在字典中反转键值对,请使用for-loop方法:

# Use this code to invert dictionaries that have non-unique values

inverted_dict = dict()
for key, value in dict.items():
    inverted_dict.setdefault(value, list()).append(key)

第二解决方案。使用字典理解方法进行反演:

# Use this code to invert dictionaries that have unique values

inverted_dict = {value: key for key, value in dict.items()}

第三解。使用还原反转方法(取决于第二种解决方案):

# Use this code to invert dictionaries that have lists of values

dict = {value: key for key in inverted_dict for value in my_map[key]}

4
dict保留,不应该用于变量名
crypdick

2
忘记告诉我们什么my_map
crypdick

dictio()?你是说dict()
乔治,

5

列表和字典理解的结合。可以处理重复的钥匙

{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}

1
就像stackoverflow.com/a/41861007/1709587一样,这是O(n²)解决方案,可以通过使用几行额外的代码在O(n)中轻松解决。
Mark Amery

2

如果值不是唯一的,并且您有点硬核:

inv_map = dict(
    (v, [k for (k, xx) in filter(lambda (key, value): value == v, my_map.items())]) 
    for v in set(my_map.values())
)

特别是对于大字典,请注意,此解决方案的效率远不及Python反向/反转映射的答案,因为它会循环items()多次。


7
这只是普通的不可读性,也是如何不编写可维护代码的一个很好的例子。我不会,-1因为它仍然可以回答问题,仅是我的意见。
Russ Bradberry,2012年

1

除了上面建议的其他功能之外,如果您喜欢lambdas:

invert = lambda mydict: {v:k for k, v in mydict.items()}

或者,您也可以采用这种方式:

invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )

2
-1; 您所做的全部工作都是从页面中获取其他答案,并将其放入lambda中。同样,将lambda分配给变量也违反了PEP 8
Mark Amery

1

我认为最好的方法是定义一个类。这是“对称字典”的实现:

class SymDict:
    def __init__(self):
        self.aToB = {}
        self.bToA = {}

    def assocAB(self, a, b):
        # Stores and returns a tuple (a,b) of overwritten bindings
        currB = None
        if a in self.aToB: currB = self.bToA[a]
        currA = None
        if b in self.bToA: currA = self.aToB[b]

        self.aToB[a] = b
        self.bToA[b] = a
        return (currA, currB)

    def lookupA(self, a):
        if a in self.aToB:
            return self.aToB[a]
        return None

    def lookupB(self, b):
        if b in self.bToA:
            return self.bToA[b]
        return None

如果需要,删除和迭代方法很容易实现。

与反转整个字典(这似乎是此页面上最流行的解决方案)相比,这种实现方式效率更高。更不用说,您可以根据需要在自己的SymDict中添加或删除值,并且逆字典将始终保持有效-如果仅对整个字典进行一次反向操作,则情况并非如此。


我喜欢这个主意,尽管要注意它会折衷增加内存以提高计算效率。更快乐的媒体可能正在缓存或延迟计算镜像。还值得注意的是,可以通过例如字典视图和自定义运算符使它在语法上更具吸引力。
Brian M. Hunt

@ BrianM.Hunt它权衡了内存,但不是很多。您只存储两组指向每个对象的指针。如果您的对象比单个整数大得多,那么差别不会太大。另一方面,如果您有巨大的微小物体表,则可能需要考虑这些建议……
NcAdams 2014年

我同意,有更多的在这里完成-我可能会在以后充实了这一点成为一个全功能的数据类型
NcAdams

2
“这种实现比倒置整个字典更有效” -嗯,为什么?我认为这种方法没有明显的性能优势。这样您仍然有两个字典。如果有的话,我希望这比例如将dict倒转要,因为如果您将dict倒转,Python可能会提前知道在基础C数据结构中分配多少存储桶并创建逆映射。从来没有调用过dictresize,但是这种方法否认了Python的这种可能性。
Mark Amery

1

这处理非唯一值,并保留了唯一情况的大部分外观。

inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}

对于Python 3.x,请替换itervaluesvalues


3
这种解决方案作为一个衬套非常优雅,并且可以管理非唯一值案例。但是,它在O(n2)中具有复杂性,这意味着它可以用于几十个元素,但是如果您的初始字典中有成千上万个元素,那么对于实际使用来说太慢了。基于默认字典的解决方案比此方法快得多。
gabuzo

加布佐说得很对。此版本(可以说)比某些版本更清晰,但不适用于大数据。
Ersatz Kwisatz

0

函数对于类型列表的值是对称的;执行reverse_dict(reverse_dict(dictionary))时,元组被覆盖到列表中

def reverse_dict(dictionary):
    reverse_dict = {}
    for key, value in dictionary.iteritems():
        if not isinstance(value, (list, tuple)):
            value = [value]
        for val in value:
            reverse_dict[val] = reverse_dict.get(val, [])
            reverse_dict[val].append(key)
    for key, value in reverse_dict.iteritems():
        if len(value) == 1:
            reverse_dict[key] = value[0]
    return reverse_dict

0

由于字典在字典中需要一个与值不同的唯一键,因此我们必须将反转的值附加到要包含在新的特定键中的排序列表中。

def r_maping(dictionary):
    List_z=[]
    Map= {}
    for z, x in dictionary.iteritems(): #iterate through the keys and values
        Map.setdefault(x,List_z).append(z) #Setdefault is the same as dict[key]=default."The method returns the key value available in the dictionary and if given key is not available then it will return provided default value. Afterward, we will append into the default list our new values for the specific key.
    return Map

0

非双射映射的快速功能解决方案(值不是唯一的):

from itertools import imap, groupby

def fst(s):
    return s[0]

def snd(s):
    return s[1]

def inverseDict(d):
    """
    input d: a -> b
    output : b -> set(a)
    """
    return {
        v : set(imap(fst, kv_iter))
        for (v, kv_iter) in groupby(
            sorted(d.iteritems(),
                   key=snd),
            key=snd
        )
    }

从理论上讲,这应该比在命令式解决方案中一个接一个地添加到集合(或追加到列表中)要快。

不幸的是,值必须是可排序的,groupby要求排序。


1
“从理论上讲,这应该比一开始添加到集合(或追加到列表中)要快”。给定n原始字典中的元素,O(n log n)由于需要对字典项进行排序,因此您的方法具有时间复杂性,而幼稚的命令式方法具有O(n)时间复杂性。就我所知,您的方法dict实践中可能要快到令人惊讶的大,但从理论上讲它肯定不会更快。
Mark Amery

0

尝试使用python 2.7 / 3.x

inv_map={};
for i in my_map:
    inv_map[my_map[i]]=i    
print inv_map

-1

我会在python 2中那样做。

inv_map = {my_map[x] : x for x in my_map}

通过dict.items(或iteritems在Python 2中)同时迭代键/值对比在迭代键时分别提取每个值更有效。
jpp

-1
def invertDictionary(d):
    myDict = {}
  for i in d:
     value = d.get(i)
     myDict.setdefault(value,[]).append(i)   
 return myDict
 print invertDictionary({'a':1, 'b':2, 'c':3 , 'd' : 1})

这将提供以下输出:{1:['a','d'],2:['b'],3:['c']}


通过dict.items(或iteritems在Python 2中)同时迭代键/值对比在迭代键时分别提取每个值更有效。此外,您没有为重复的答案添加任何解释。
jpp

-1
  def reverse_dictionary(input_dict):
      out = {}
      for v in input_dict.values():  
          for value in v:
              if value not in out:
                  out[value.lower()] = []

      for i in input_dict:
          for j in out:
              if j in map (lambda x : x.lower(),input_dict[i]):
                  out[j].append(i.lower())
                  out[j].sort()
      return out

这段代码是这样的:

r = reverse_dictionary({'Accurate': ['exact', 'precise'], 'exact': ['precise'], 'astute': ['Smart', 'clever'], 'smart': ['clever', 'bright', 'talented']})

print(r)

{'precise': ['accurate', 'exact'], 'clever': ['astute', 'smart'], 'talented': ['smart'], 'bright': ['smart'], 'exact': ['accurate'], 'smart': ['astute']}

1
通常,如果答案包括对代码意图的解释,以及为什么不引入其他代码就能解决问题的原因,则答案会更有帮助。
汤姆·阿兰达

1
很好,但是有很多无法解释的决定(例如,为什么要使用小写字母?)
Liudvikas Akelis

-2

没有什么完全不同,只是Cookbook重写了一些食谱。还通过保留setdefault方法进行了优化,而不是每次通过实例进行优化:

def inverse(mapping):
    '''
    A function to inverse mapping, collecting keys with simillar values
    in list. Careful to retain original type and to be fast.
    >> d = dict(a=1, b=2, c=1, d=3, e=2, f=1, g=5, h=2)
    >> inverse(d)
    {1: ['f', 'c', 'a'], 2: ['h', 'b', 'e'], 3: ['d'], 5: ['g']}
    '''
    res = {}
    setdef = res.setdefault
    for key, value in mapping.items():
        setdef(value, []).append(key)
    return res if mapping.__class__==dict else mapping.__class__(res)

设计下CPython的3.X中运行,为2.X替换mapping.items()mapping.iteritems()

在我的机器上,运行速度比这里的其他示例更快


1
将结果构建为a dict,然后最后转换为所需的类(而不是从正确类型的类开始),在我看来,这会导致完全可以避免的性能下降。
Mark Amery

-2

我在循环“ for”和方法“ .get()”的帮助下编写了此代码,并将字典的名称“ map”更改为“ map1”,因为“ map”是一个函数。

def dict_invert(map1):
    inv_map = {} # new dictionary
    for key in map1.keys():
        inv_map[map1.get(key)] = key
    return inv_map

-2

如果值不是唯一的,并且可能是哈希(一维):

for k, v in myDict.items():
    if len(v) > 1:
        for item in v:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

如果需要更深层次地进行递归,则只需一个维度即可:

def digList(lst):
    temp = []
    for item in lst:
        if type(item) is list:
            temp.append(digList(item))
        else:
            temp.append(item)
    return set(temp)

for k, v in myDict.items():
    if type(v) is list:
        items = digList(v)
        for item in items:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

您可以使用defaultdict改进解决方案:它将删除所有的invDict [item] = invDict.get(item,[])行
gabuzo

如果in中的任何值都不是可迭代的,则您的第一种方法将转换{"foo": "bar"}{'b': ['foo'], 'a': ['foo'], 'r': ['foo']}并引发异常myDict。我不确定您要在此处实现哪种行为,但实际上实现的是几乎没人想要的东西。
Mark Amery
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.