Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append to a dict of lists with a dict comprehension

Suppose I have a large list of words. For an example:

>>> with open('/usr/share/dict/words') as f:
...     words=[word for word in f.read().split('\n') if word]

If I wanted to build an index by first letter of this word list, this is easy:

d={}
for word in words:
   if word[0].lower() in 'aeiou':
       d.setdefault(word[0].lower(),[]).append(word)
       # You could use defaultdict here too...

Results in something like this:

{'a':[list of 'a' words], 'e':[list of 'e' words], 'i': etc...}

Is there a way to do this with Python 2.7, 3+ dict comprehension? In other words, is it possible with the dict comprehension syntax to append the list represented by the key as the dict is being built?

ie:

  index={k[0].lower():XXX for k in words if k[0].lower() in 'aeiou'}

Where XXX performs an append operation or list creation for the key as index is being created.

Edit

Taking the suggestions and benchmarking:

def f1():   
    d={}
    for word in words:
        c=word[0].lower()
        if c in 'aeiou':
           d.setdefault(c,[]).append(word)

def f2():
   d={}
   {d.setdefault(word[0].lower(),[]).append(word) for word in words 
        if word[0].lower() in 'aeiou'} 

def f3():
    d=defaultdict(list)                       
    {d[word[0].lower()].append(word) for word in words 
            if word[0].lower() in 'aeiou'}         

def f4():
    d=functools.reduce(lambda d, w: d.setdefault(w[0], []).append(w[1]) or d,
       ((w[0].lower(), w) for w in words
        if w[0].lower() in 'aeiou'), {}) 

def f5():   
    d=defaultdict(list)
    for word in words:
        c=word[0].lower() 
        if c in 'aeiou':
            d[c].append(word)       

Produces this benchmark:

   rate/sec    f4     f2     f1     f3     f5
f4       11    -- -21.8% -31.1% -31.2% -41.2%
f2       14 27.8%     -- -11.9% -12.1% -24.8%
f1       16 45.1%  13.5%     --  -0.2% -14.7%
f3       16 45.4%  13.8%   0.2%     -- -14.5%
f5       18 70.0%  33.0%  17.2%  16.9%     --

The straight loop with a default dict is fastest followed by set comprehension and loop with setdefault.

Thanks for the ideas!

like image 936
the wolf Avatar asked Jun 30 '12 18:06

the wolf


People also ask

Can I use list comprehension with dictionary?

Using dict() method we can convert list comprehension to the dictionary. Here we will pass the list_comprehension like a list of tuple values such that the first value act as a key in the dictionary and the second value act as the value in the dictionary.

Can you append a dictionary to a dictionary?

Yes, you can append to a dictionary in Python. It is done using the update() method. The update() method links one dictionary with another, and the method involves inserting key-value pairs from one dictionary into another dictionary.

What is the difference between list comprehension and dict comprehension?

Its syntax is the same as List Comprehension. It returns a generator object. A dict comprehension, in contrast, to list and set comprehensions, needs two expressions separated with a colon. The expression can also be tuple in List comprehension and Set comprehension.

Should I use dict () or {}?

tl;dr. With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.


2 Answers

No - dict comprehensions are designed to generate non-overlapping keys with each iteration; they don't support aggregation. For this particular use case, a loop is the proper way to accomplish the task efficiently (in linear time).

like image 83
Amber Avatar answered Sep 28 '22 00:09

Amber


This is not exactly a dict comprehension, but:

reduce(lambda d, w: d.setdefault(w[0], []).append(w[1]) or d,
       ((w[0].lower(), w) for w in words
        if w[0].lower() in 'aeiou'), {})
like image 31
Aleksei astynax Pirogov Avatar answered Sep 27 '22 23:09

Aleksei astynax Pirogov