2014-01-27 1 views
0

У меня есть словарь, как это:Pythonic обратного ДИКТ неуникальных отображений

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: [а], 2: [а, Ь], 3: [Ь, с] -> а: [1, 2], б: [2, 3], с: [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 

Кроме того, он не работает, похоже, постижению, как это было бы неэффективно. Есть ли эффективный способ с одним лайнером?

+1

Он не будет работать без уникальных значений, по определению, ключи в словарях или хэш-таблиц __unique__ – Oz123

+1

Python словари не поддерживают дубликаты ключей - > 'http: // stackoverflow.com/ questions/10664856/make-dictionary-with-duplicate-keys-in-python' –

+0

Я предполагаю, что мое использование« уникального »неоднозначно. То, что я подразумеваю под «уникальным», заключается в том, что если исходный словарь имеет 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] '' – dantiston

ответ

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 
+0

, если первый dict: A = {1 :('a', 'b'), 2 :('b', 'e', ​​'c'), 3 :('a', 'f'), 4 :('c', 'd'), 5 :('d', 'e', ​​'f')} with dict2 = {item: [ключ для ключа в элементе A if в A [ключ]] для значения в A.values ​​() для элемента в значении} print dict2 Наконец, он дает: {'a': [1, 3], 'c': [2, 4], 'b': [1, 2], ' e ': [2, 5],' d ': [4, 5],' f ': [3, 5]} –

4

Стандартный 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']} 
+0

Это по существу то же самое, что и код, который я опубликовал, и он не касается ни одного из моих вопросов. – dantiston

+0

Это дает именно то, что вы сказали. Если вы хотите получить другой результат, сделайте это! – mhlester

2

В однострочника (благодаря входу mhlesters), но с так себе читабельности (и работает только потому, что значения в dict2 являются изменчивыми и, таким образом, устанавливают, что возвращает ссылку на них):

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

Или с цикл:

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)