2009-09-29 2 views
10

У меня есть очень большая коллекция (p, q) кортежей, которые я бы хотел преобразовать в словарь списков, где первый элемент в каждом кортеже является ключом, который индексирует список, содержащий q ,Сочетание словарей списков в Python

Пример:

Original List: (1, 2), (1, 3), (2, 3) 
Resultant Dictionary: {1:[2, 3], 2:[3]} 

Кроме того, я хотел бы эффективно сочетать эти словари.

Пример:

Original Dictionaries: {1:[2, 3], 2:[3]}, {1:[4], 3:[1]} 
Resultant Dictionary: {1:[2, 3, 4], 2:[3], 3:[1]} 

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

Заранее спасибо

ответ

14

Если список кортежей сортируется, itertools.groupby, как это было предложено @gnibbler, не плохо альтернативой defaultdict, но он должен быть использован иначе, чем он предложил:

import itertools 
import operator 

def lot_to_dict(lot): 
    key = operator.itemgetter(0) 
    # if lot's not sorted, you also need...: 
    # lot = sorted(lot, key=key) 
    # NOT in-place lot.sort to avoid changing it! 
    grob = itertools.groupby(lot, key) 
    return dict((k, [v[1] for v in itr]) for k, itr in grob) 

для «слияния» dicts списков в новый d.o.l ...:

def merge_dols(dol1, dol2): 
    keys = set(dol1).union(dol2) 
    no = [] 
    return dict((k, dol1.get(k, no) + dol2.get(k, no)) for k in keys) 

Я даю [] прозвище no, чтобы избежать бесцельно построения много пустых списков, учитывая, что производительность имеет важное значение. Если наборы ключей можно DOLS' перекрываться лишь незначительно, быстрее будет:

def merge_dols(dol1, dol2): 
    result = dict(dol1, **dol2) 
    result.update((k, dol1[k] + dol2[k]) 
       for k in set(dol1).intersection(dol2)) 
    return result 

, так как это использует список-катенация только для перекрывающихся ключей - так, если те немногие, это будет быстрее.

+0

2016 и '' 'merge_dols''' v.1 звучит фантастично и элегантно, спасибо! v.2, однако, использует [неприемлемый метод] (http://stackoverflow.com/questions/38987/how-to-merge-two-python-dictionaries-in-a-single-expression/39858#39858) : '' 'dict (dol1, ** dol2)' '' – raratiru

3

defaltdict на помощь (как обычно)

from collections import defaultdict 
my_dict = defaultdict(list) 

for key,value in original_list: 
    my_dict[key].append(value) 

Объединение двух dicts можно сделать так (обратите внимание, что будут сохранены дубликатами):

for key,value in orig_dict: 
    new_dict[key].extend(value) 
+0

Я думаю, вам нужно .append() элементы из кортежей. – Noah

+0

Ваш второй цикл не работает – SilentGhost

3

collections.defaultdict работает следующим образом:

from collections import defaultdict 
dic = defaultdict(list) 
for i, j in tuples: 
    dic[i].append(j) 

похож на dicts:

a, b = {1:[2, 3], 2:[3]}, {1:[4], 3:[1]} 
de = defaultdict(list, a) 
for i, j in b.items(): 
    de[i].extend(j) 
0

Вот итератор стиль делает его

 
>>> mylist=[(1, 2), (1, 3), (2, 3)] 
>>> from itertools import groupby 
>>> from operator import itemgetter 
>>> mylist=[(1, 2), (1, 3), (2, 3)] 
>>> groupby(mylist,itemgetter(0)) 

>>> list(_) 
[(1, <itertools._grouper object at 0xb7d402ec>), (2, <itertools._grouper object at 0xb7c716ec>)] 
+0

Это не то, о чем попросил ОП, не так ли? – SilentGhost

0

Я хотел это сделать в одна линия просто для удовольствия:

>>> from itertools import groupby 
>>> t=(1, 2), (1, 3), (2, 3) 
>>> [(i,[x for _,x in list(f)]) for i,f in groupby(sorted(t),lambda t: t[0])] 
[(1, [2, 3]), (2, [3])] 
>>> b={1:[2, 3], 2:[3]}, {1:[4], 3:[1]} 
>>> dict([(key,sum([i[1::][0] for i in elements],[])) for key,elements in groupby(sorted(b[0].items()+b[1].items()),lambda t: t[0])]) 
{1: [2, 3, 4], 2: [3], 3: [1]} 
-1

Это, как я делаю это в Python 2.7:

combined = {} 
combined.update(d1) 
combined.update(d2) 

Это хорошо, чтобы определить функцию полезности для сделайте это:

def merge(d1, d2): 
    ''' Merge two dictionaries. ''' 
    merged = {} 
    merged.update(d1) 
    merged.update(d2) 
    return merged 
Смежные вопросы