Sözlük eşlemesini tersine / tersine çevir


Yanıtlar:


924

Python 2.7.x için

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

Python 3+ için:

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

4
Son zamanlarda Python 2.7.x sürümleri de my_map.items()çalışıyor
valentin

29
Bu değerlerde teklik yoksa işe yaramayacağı dışında çalışır. Bu durumda bazı girişleri
kaybedeceksiniz


2
Evet, bir uygulama detayı olarak. The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon. Bu şekilde kalacağının garantisi yoktur, bu nedenle Dictaynı davranışa sahip kod yazmayın OrderedDict.
Mattias

9
@Mattias, bu Python 3.6 için geçerlidir. 3.7 sürümü için sipariş koruma resmi: mail.python.org/pipermail/python-dev/2017-December/151283.html . BDFL böyle söyledi.
interDist

174

Diksiyondaki değerlerin benzersiz olduğunu varsayarsak:

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

22
Değerlerin de yıkanabilir olması gerekiyor
John La Rooy

30
@ Buttons840: Değerler benzersiz değilse, sözlüğün benzersiz bir şekilde ters çevrilmesi yoktur veya başka bir deyişle, tersine çevirme anlamlı değildir.
Wrzlprmft

2
@ Buttons840 Değer için yalnızca son anahtar görünecektir. Muhtemelen iteritems()çıkacak sipariş konusunda hiçbir garanti yoktur , bu nedenle, benzersiz olmayan bir değer için bazı koşullar altında görünüşte çoğaltılabilecek şekilde rastgele bir anahtarın atanacağı varsayılabilir, ancak genel olarak böyle değildir.
Evgeni Sergeev

2
Elbette, Python 3'te artık bir iteritems()yöntem olmadığını ve bu yaklaşımın işe yaramayacağını unutmayın; items()orada kabul edilen cevapta gösterildiği gibi kullanın . Ayrıca, bir sözlük anlama bunu aramaktan daha güzel hale getirecektir dict.
Mark Amery

5
@Wrzlprmft Benzersiz olmayan değerler durumunda tersin doğal bir tanımı vardır. Her değer, ona giden anahtarlar kümesiyle eşlenir.
Leo

135

İçindeki değerler my_mapbenzersiz değilse:

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

56
... veya sadece inv_map.setdefault (v, []) .ekleyin (k). Eskiden varsayılan bir fanboy gibiydim, ama sonra çok fazla kez vidalandım ve aslında açık olanın örtükten daha iyi olduğu sonucuna vardım.
alsuren

Bu cevap çoklu harita için yanlıştır, buraya ekleyin işe yaramaz çünkü değer her seferinde boş listeye sıfırlanır, set_default kullanmalıdır
Yaroslav Bulatov

1
@YaroslavBulatov hayır, burada gösterilen kod bozuk değil - inv_map.get(v, [])varsa eklenmiş listeyi döndürür, bu nedenle atama boş bir listeye sıfırlanmaz. setdefaultyine de daha güzel olurdu.
Mark Amery

10
Burada bir set daha anlamlı olur. Anahtarlar (muhtemelen) yıkanabilir ve sipariş yoktur. inv_map.setdefault(v, set()).add(k).
Artyer

1
Python3 içinde, my_map.items()yerine kullanın my_map.iteritems().
apitsch

42

Eşlemenizin türünü korurken bunu yapmak için (bunun bir dictveya dictalt sınıf olduğunu varsayarak ):

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

4
Akıllıca olabilir, ancak orijinal sözlükte birden fazla anahtar aynı değere sahip olduğunda çalışmaz.
Rafael_Espericueta

1
@Rafael_Espericueta Bu soruya verilebilecek olası herhangi bir cevap için geçerlidir, çünkü tekrarlanan değerleri olan bir harita ters çevrilemez.
Mark Amery

2
@Mark_Amery Bir anlamda daha genel olarak tersine çevrilebilir. Örneğin: D = {1: [1, 2], 2: [2, 3], 3: [1]}, Dinv = {1: [1, 3], 2: [1, 2], 3: [2]}. D, örneğin {parent: children} sözlüğünde, Dinv ise {child: ebeveyn} sözlüğündedir.
Rafael_Espericueta

36

Bunu dene:

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

( Sözlük görünümlerindeki Python belgelerinin aşağıdakileri açıkça garanti ettiğini .keys()ve.values() işe yukarıdaki yaklaşım verir aynı sırayla, kendi unsurlar var.)

Alternatif:

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

veya python 3.0'ın diksiyon anlamalarını kullanma

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

1
Bunun yalnızca anahtarlar benzersizse işe yaradığına dikkat edin (bu anahtarları ters çevirmek istiyorsanız neredeyse hiç olmaz).
2018'de

Python.org/dev/peps/pep-0274'e göre diksiyon kavrayışları 2.7+ sürümünde de mevcuttur.
Kawu

24

Başka, daha işlevsel bir yol:

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

3
Gönderdiğiniz için teşekkürler. Bunun tercih edileceğinden emin değilim - PEP 279'da Guido Van Rossum'u alıntılamak için: " filterve mapölmeli ve daha fazla varyantı büyütmek yerine liste anlayışlarına katılmalı".
Brian M. Hunt

2
Evet, bu adil bir nokta Brian. Sadece bir konuşma noktası olarak ekliyordum. Diksiyon anlama yolu hayal edebileceğim çoğu kişi için daha okunabilir. (Ve muhtemelen daha hızlı tahmin ediyorum)
Brendan Maguire

3
Diğerlerinden daha az okunabilir olabilir, ancak bu şekilde veya dictgibi diğer haritalama türleri ile takas edebilme faydası vardırcollections.OrderedDictcollections.defaultdict
Will S

10

Bu, Robert'ın cevabı üzerine genişleyerek , diksiyondaki değerlerin benzersiz olmadığı zamanlara başvuruyor.

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

Uygulama, reversediki kez kullanamayacağınız ve orijinali geri alamayacağınız için sınırlıdır . Bu şekilde simetrik değildir. Python 2.6 ile test edilmiştir. Buraya ben çıkan dicti yazdırmak için kullanıyorum nasıl kullanıldığı durumdur.

Bunun yerine bir kullanmayı tercih ediyorsanız setbir daha listve bu yerine, mantıklı olan sırasız uygulamalar var var olabileceği setdefault(v, []).append(k), kullanım setdefault(v, set()).add(k).


bu aynı zamanda listeler yerine kümeleri kullanmak için iyi bir yer olurdu, yanirevdict.setdefault(v, set()).add(k)
mueslo

Tabii ki, ama kullanmak için iyi bir neden olmasının nedeni bu set. Burada geçerli olan içsel tiptir. Hangi değerleri olmayan nerede tüm anahtarları bulmak istiyorsanız 1veya 2? Sonra sadece yapabilirim d.keys() - inv_d[1] - inv_d[2](Python 3'te)
mueslo

9

Ayrıca, yinelenen tuşlarla sözlüğü tersine çevirebiliriz 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']}  

Buraya bakın :

Bu teknik, eşdeğer bir teknikten daha basit ve daha hızlıdır dict.setdefault().


6

Örneğin, şu sözlüğe sahipsiniz:

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

Ve bunu tersine çevrilmiş bir biçimde elde etmek istersiniz:

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

İlk Çözüm . Sözlüğünüzdeki anahtar / değer çiftlerini ters forçevirmek için bir -loop yaklaşımı kullanın :

# 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)

İkinci Çözüm . Ters çevirme için sözlük anlama yaklaşımı kullanın :

# Use this code to invert dictionaries that have unique values

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

Üçüncü Çözüm . Ters çevirme yaklaşımını geri döndürmeyi kullanın (ikinci çözüme dayanır):

# 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
dictayrılmış ve değişken isimleri için kullanılmamalıdır
crypdick

2
bize ne my_mapolduğunu söylemeyi unuttum
crypdick

dictio()? Bunu mu demek istediniz dict()?
Georgy

5

Liste ve sözlük anlama kombinasyonu. Yinelenen tuşları işleyebilir

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

1
Gibi stackoverflow.com/a/41861007/1709587 , bu kolayca kod fazladan satır bir çift ile O (n) çözülmüştür bir soruna O (n²) çözümüdür.
Mark Amery

2

Değerler benzersiz değilse ve biraz sert iseniz:

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

Özellikle büyük bir dikte için, bu çözümün Python'un bir haritayı tersine çevirme / tersine çevirme cevabından çok daha az verimli olduğuna dikkat edin, çünkü items()birden fazla kez döngü yapar .


7
Bu sadece okunamayan ve bakımı kolay kod yazmamaya iyi bir örnek. Yapmayacağım -1çünkü hala soruyu cevaplıyor, sadece benim düşüncem.
Russ Bradberry

1

Yukarıda önerilen diğer işlevlere ek olarak, lambdasları seviyorsanız:

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

Veya bunu şu şekilde yapabilirsiniz:

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

2
1; yaptığınız tek şey sayfadan başka cevaplar alıp lambda'ya koymak. Ayrıca, bir değişkene bir lambda atamak bir PEP 8 ihlalidir.
Mark Amery

1

Bunu yapmanın en iyi yolunun bir sınıf tanımlamak olduğunu düşünüyorum. İşte bir "simetrik sözlük" uygulaması:

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

Gerekirse silme ve yineleme yöntemleri uygulanacak kadar kolaydır.

Bu uygulama, tüm bir sözlüğü tersine çevirmekten çok daha etkilidir (bu sayfadaki en popüler çözüm gibi görünüyor). Bahsetmemek gerekirse, SymDict'inize istediğiniz kadar değer ekleyebilir veya kaldırabilirsiniz ve ters sözlüğünüz her zaman geçerli kalır - tüm sözlüğü bir kez ters çevirirseniz bu doğru değildir.


Bu fikri beğendim, ancak gelişmiş hesaplama elde etmek için ek hafızadan işlem yapıldığını belirtmek iyi olur. Daha mutlu bir ortam aynayı önbelleğe alabilir veya tembel bir şekilde hesaplayabilir. Ayrıca, örneğin sözlük görünümleri ve özel işleçlerle sözdizimsel olarak daha çekici hale getirilebileceğini belirtmek gerekir.
Brian M. Hunt

@ BrianM.Hunt Hafızadan işlem yapıyor, ama çok değil. Her nesneye yalnızca iki işaretçi kümesi kaydedersiniz. Nesneleriniz tek tam sayılardan çok daha büyükse, bu çok fazla fark yaratmaz. Öte yandan, büyük nesneler masanız varsa, bu önerileri dikkate almanız gerekebilir ...
NcAdams

Ve katılıyorum, burada yapılacak daha çok şey var - bunu daha sonra tam olarak çalışan bir veri
tipine

2
"Bu uygulama tüm bir sözlüğü tersine çevirmekten çok daha etkilidir" - hım, neden? Bu yaklaşımın önemli bir performans avantajına sahip olabileceğine dair hiçbir makul yol görmüyorum; hala bu şekilde iki sözlük var. Bir şey varsa, bunun dict'i bir kavrayışla ters çevirmekten daha yavaş olmasını beklerim, çünkü dikteyi ters çevirirseniz Python, temelde C veri yapısına kaç kova tahsis edeceğini ve ters haritayı oluşturabileceğini önceden biliyor olabilir. hiç uğramadan dictresize, ancak bu yaklaşım olasılığı olduğunu Python inkar eder.
Mark Amery

1

Bu, benzersiz olmayan değerleri işler ve benzersiz durumun görünümünü korur.

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

Python 3.x için, yerini itervaluesile values.


3
Bu çözüm, bir astar olarak oldukça zariftir ve benzersiz olmayan değerler durumunu yönetir. Bununla birlikte, O (n2) 'de bir karmaşıklığa sahiptir, bu da birkaç düzine eleman için uygun olması gerektiği anlamına gelir, ancak ilk sözlüğünüzde yüz binlerce öğe varsa pratik kullanım için çok yavaş olacaktır. Varsayılan bir dikteye dayalı çözümler bundan daha hızlıdır.
gabuzo

Gabuzo oldukça haklı. Bu sürüm (tartışmasız) bazılarından daha net, ancak büyük veriler için uygun değil.
Ersatz Kwisatz

0

İşlev, tür listesinin değerleri için simetriktir; Reverse_dict (reverse_dict (sözlük)) yaparken tuples listelere dahil edilir

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

Sözlükler, değerlerin aksine sözlükte benzersiz bir anahtar gerektirdiğinden, ters çevrilmiş değerleri yeni belirli anahtarların içine dahil edilecek bir sıralama listesine eklememiz gerekir.

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

İki yönlü olmayan haritalar için hızlı fonksiyonel çözüm (değerler benzersiz değil):

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

Teorik olarak, bu, zorunlu çözümdeki gibi kümeye (veya listeye eklenerek) tek tek eklenmekten daha hızlı olmalıdır .

Ne yazık ki değerler sıralanabilir olmalıdır, sıralama groupby tarafından gereklidir.


1
"Teorik olarak, bu sete tek tek eklemek (veya listeye eklemek) daha hızlı olmalıdır" - hayır. nOrijinal diksiyondaki unsurlar göz önüne alındığında , yaklaşımınız O(n log n)zorun maddelerini sıralama ihtiyacı nedeniyle zaman karmaşıklığına sahipken, saf emir yaklaşımının O(n)zaman karmaşıklığı vardır. Tüm ı yaklaşımınız daha hızlı saçma sapan büyük dek olabilir biliyoruz dictiçinde s pratikte , ama kesinlikle teoride değil hızlıdır.
Mark Amery

0

Bunu python 2.7 / 3.x için deneyin

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

-1

Bunu python 2'de yapardım.

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

Anahtar / değer çiftlerini eşzamanlı olarak dict.items(veya iteritemsPython 2'de) yinelemek, anahtarları yinelerken her bir değeri ayrı ayrı ayıklamaktan daha etkilidir.
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})

Bu, çıktıyı şu şekilde sağlayacaktır: {1: ['a', 'd'], 2: ['b'], 3: ['c']}


Anahtar / değer çiftlerini eşzamanlı olarak dict.items(veya iteritemsPython 2'de) yinelemek, anahtarları yinelerken her bir değeri ayrı ayrı ayıklamaktan daha etkilidir. Ayrıca, başkalarını çoğaltan bir cevaba açıklama eklemediniz.
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

bu kod şöyle:

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
Genellikle, kodun ne yapmak istediği ve başkalarını tanıtmadan sorunu neden çözdüğüne dair bir açıklama içeriyorsa, cevaplar çok daha yararlıdır.
Tom Aranda

1
Çok güzel, ama açıklanamayan birçok karar var (örneğin, anahtarlar için neden küçük harf?)
Liudvikas Akelis

-2

Tamamen farklı bir şey değil, sadece Cookbook'un biraz yeniden yazılmış tarifi. Dahası setdefault, her seferinde örnek üzerinden almak yerine, tutma yöntemi ile optimize edilir :

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)

2.x için değiştirin CPython 3.x altında çalıştırılmak üzere tasarlanan mapping.items()ilemapping.iteritems()

Makinemdeki diğer örneklerden biraz daha hızlı çalışıyor


1
Sonucu bir olarak oluşturmak dictve daha sonra (doğru tipte bir sınıfla başlamak yerine) sonunda istenen sınıfa dönüştürmek, burada tamamen önlenebilir bir performans vuruşu yapmış gibi görünüyor.
Mark Amery

-2

Bunu 'for' ve method '.get ()' döngüsünün yardımıyla yazdım ve sözlüğün 'map' adını 'map1' olarak değiştirdim çünkü 'map' bir işlev.

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

-2

Değerler benzersiz değilse VE bir karma (bir boyut) olabilir:

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)

Ve bir özyineleme ile daha derin kazmanız gerekiyorsa, sadece bir boyut:

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)

Varsayılan bir çözüm kullanarak çözümlerinizi geliştirebilirsiniz: tüm invDict [item] = invDict.get (item, []) satırlarını
kaldıracaktır

İlk yaklaşım burada dönüştürür {"foo": "bar"}için {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}ve herhangi bir değer varsa bir istisna yükseltir myDictdeğil bir iterable olduğunu. Burada hangi davranışı uygulamaya çalıştığınızdan emin değilim, ama aslında uyguladığınız şey kimsenin istemeyeceği bir şey.
Mark Amery
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.