DICTIONARY COMPREHENSION
[xv if c else yv for (c,xv,yv) in zip(condition,x,y)]
zip
Suppose we have two lists, we want to make them a list making pairs with . How can we do that?
>>> a = [1,2,3]
>>> b = [4,5,6]
For Python 2.x:
>>> z = zip(a,b)
>>> z
[(1, 4), (2, 5), (3, 6)]
For Python 3.x:
>>> z = list(zip(a,b))
>>> z
[(1, 4), (2, 5), (3, 6)]
Then, we reverse the course, and want to get each list back. How?
>>> c, d = zip(*z)
>>> c, d
((1, 2, 3), (4, 5, 6))
Dictionary construction with zip
We can use zip to generate dictionaries when the keys and values must be computed at runtime.
In general, we can make a dictionary by typing in the dictionary literals:
>>> D1 = {'a':1, 'b':2, 'c':3}
>>> D1
{'a': 1, 'c': 3, 'b': 2}
>>>
Or we can make it by assigning values to keys:
>>> D1 = {}
>>> D1['a'] = 1
>>> D1['b'] = 2
>>> D1['c'] = 3
>>> D1
{'a': 1, 'c': 3, 'b': 2}
However, there are cases when we get the keys and values in lists at runtime. For instance like this:
>>> keys = ['a', 'b', 'c']
>>> values = [1, 2, 3]
How can we construct a dictionary from those lists of keys and values?
Now it's time to use zip. First, we zip the lists and loop through them in parallel like this:
>>> list(zip(keys,values))
[('a', 1), ('b', 2), ('c', 3)]
>>> D2 = {}
>>> for (k,v) in zip(keys, values):
... D2[k] = v
...
>>> D2
{'a': 1, 'b': 2, 'c': 3}
Note that making a dictionary like that only works for Python 3.x.
There is another way of constructing a dictionary via zip that's working for both Python 2.x and 3.x. We make a dict from zip result:
>>> D3 = dict(zip(keys, values))
>>> D3
{'a': 1, 'b': 2, 'c': 3}
Python 3.x introduced dictionary comprehension, and we'll see how it handles the similar case. Let's move to the next section.
Dictionary Comprehension
As in the previous section, we have two lists:
>>> keys = ['a', 'b', 'c']
>>> values = [1, 2, 3]
Now we use dictionary comprehension (Python 3.x) to make dictionary from those two lists:
>>> D = { k:v for (k,v) in zip(keys, values)}
>>> D
{'a': 1, 'b': 2, 'c': 3}
It seems require more code than just doing this:
>>> D = dict(zip(keys, values))
>>> D
{'a': 1, 'b': 2, 'c': 3}
However, there are more cases when we can utilize the dictionary comprehension. For instance, we can construct dictionary from one list using comprehension:
>>> D = {x: x**2 for x in [1,2,3,4,5]}
>>> D
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
>>> D = {x.upper(): x*3 for x in 'abcd'}
>>> D
{'A': 'aaa', 'C': 'ccc', 'B': 'bbb', 'D': 'ddd'}
When we want initialize a dict from keys, we do this:
>>> D = dict.fromkeys(['a','b','c'], 0)
>>> D
{'a': 0, 'c': 0, 'b': 0}
We can use dictionary comprehension to do the same thing;
>>> D = {k: 0 for k in ['a','b','c']}
>>> D
{'a': 0, 'c': 0, 'b': 0}
We sometimes generate a dict by iterating each element:
>>> D = dict.fromkeys('dictionary')
>>> D
{'a': None, 'c': None, 'd': None, 'i': None, 'o': None, 'n': None, 'r': None, 't': None, 'y': None}
If we use comprehension:
>>> D = {k:None for k in 'dictionary'}
>>> D
{'a': None, 'c': None, 'd': None, 'i': None, 'o': None, 'n': None, 'r': None, 't': None, 'y': None}
Simple zip()
The following example calculates Hamming distance. Hamming distance is the number of positions at which the corresponding symbols are different. It's defined for two strings of equal length.
def hamming(s1,s2):
if len(s1) != len(s2):
raise ValueError("Not defined - unequal lenght sequences")
return sum(c1 != c2 for c1,c2 in zip(s1,s2))
if __name__ == '__main__':
print(hamming('toned', 'roses')) # 3
print(hamming('2173896', '2233796')) # 3
print(hamming('0100101000', '1101010100')) # 6
Conditional zip()
>>> x = [1,2,3,4,5]
>>> y = [11,12,13,14,15]
>>> condition = [True,False,True,False,True]
>>> [xv if c else yv for (c,xv,yv) in zip(condition,x,y)]
[1, 12, 3, 14, 5]
The same thing can be done using NumPy's where:
>>> import numpy as np
>>> np.where([1,0,1,0,1], np.arange(1,6), np.arange(11,16))
array([ 1, 12, 3, 14, 5])