Can I use list comprehension syntax to create a dictionary?
For example, by iterating over pairs of keys and values:
d = {... for k, v in zip(keys, values)}
Use a dict comprehension (Python 2.7 and later):
{key: value for (key, value) in iterable}
Alternatively for simpler cases or earlier version of Python, use the dict constructor, e.g.:
pairs = [('a', 1), ('b', 2)]
dict(pairs) #=> {'a': 1, 'b': 2}
dict([(k, v+1) for k, v in pairs]) #=> {'a': 2, 'b': 3}
Given separate arrays of keys and values, use the dict constructor with zip:
keys = ['a', 'b']
values = [1, 2]
dict(zip(keys, values)) #=> {'a': 1, 'b': 2}
2) "zip'ped" from two separate iterables of keys/vals
dict(zip(list_of_keys, list_of_values))
In Python 3 and Python 2.7+, dictionary comprehensions look like the below:
d = {k:v for k, v in iterable}
For Python 2.6 or earlier, see fortran's answer.
In fact, you don't even need to iterate over the iterable if it already comprehends some kind of mapping, the dict constructor doing it graciously for you:
>>> ts = [(1, 2), (3, 4), (5, 6)]
>>> dict(ts)
{1: 2, 3: 4, 5: 6}
>>> gen = ((i, i+1) for i in range(1, 6, 2))
>>> gen
<generator object <genexpr> at 0xb7201c5c>
>>> dict(gen)
{1: 2, 3: 4, 5: 6}
Create a dictionary with list comprehension in Python
I like the Python list comprehension syntax.
Can it be used to create dictionaries too? For example, by iterating
over pairs of keys and values:
mydict = {(k,v) for (k,v) in blah blah blah}
You're looking for the phrase "dict comprehension" - it's actually:
mydict = {k: v for k, v in iterable}
Assuming blah blah blah is an iterable of two-tuples - you're so close. Let's create some "blahs" like that:
blahs = [('blah0', 'blah'), ('blah1', 'blah'), ('blah2', 'blah'), ('blah3', 'blah')]
Dict comprehension syntax:
Now the syntax here is the mapping part. What makes this a dict comprehension instead of a set comprehension (which is what your pseudo-code approximates) is the colon, : like below:
mydict = {k: v for k, v in blahs}
And we see that it worked, and should retain insertion order as-of Python 3.7:
>>> mydict
{'blah0': 'blah', 'blah1': 'blah', 'blah2': 'blah', 'blah3': 'blah'}
In Python 2 and up to 3.6, order was not guaranteed:
>>> mydict
{'blah0': 'blah', 'blah1': 'blah', 'blah3': 'blah', 'blah2': 'blah'}
Adding a Filter:
All comprehensions feature a mapping component and a filtering component that you can provide with arbitrary expressions.
So you can add a filter part to the end:
>>> mydict = {k: v for k, v in blahs if not int(k[-1]) % 2}
>>> mydict
{'blah0': 'blah', 'blah2': 'blah'}
Here we are just testing for if the last character is divisible by 2 to filter out data before mapping the keys and values.
In Python 2.7, it goes like:
>>> list1, list2 = ['a', 'b', 'c'], [1,2,3]
>>> dict( zip( list1, list2))
{'a': 1, 'c': 3, 'b': 2}
Zip them!
Python version >= 2.7, do the below:
d = {i: True for i in [1,2,3]}
Python version < 2.7(RIP, 3 July 2010 - 31 December 2019), do the below:
d = dict((i,True) for i in [1,2,3])
To add onto #fortran's answer, if you want to iterate over a list of keys key_list as well as a list of values value_list:
d = dict((key, value) for (key, value) in zip(key_list, value_list))
or
d = {(key, value) for (key, value) in zip(key_list, value_list)}
Just to throw in another example. Imagine you have the following list:
nums = [4,2,2,1,3]
and you want to turn it into a dict where the key is the index and value is the element in the list. You can do so with the following line of code:
{index:nums[index] for index in range(0,len(nums))}
Here is another example of dictionary creation using dict comprehension:
What i am tring to do here is to create a alphabet dictionary where each pair; is the english letter and its corresponding position in english alphabet
>>> import string
>>> dict1 = {value: (int(key) + 1) for key, value in
enumerate(list(string.ascii_lowercase))}
>>> dict1
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'g': 7, 'f': 6, 'i': 9, 'h': 8,
'k': 11, 'j': 10, 'm': 13, 'l': 12, 'o': 15, 'n': 14, 'q': 17, 'p': 16, 's':
19, 'r': 18, 'u': 21, 't': 20, 'w': 23, 'v': 22, 'y': 25, 'x': 24, 'z': 26}
>>>
Notice the use of enumerate here to get a list of alphabets and their indexes in the list and swapping the alphabets and indices to generate the key value pair for dictionary
Hope it gives a good idea of dictionary comp to you and encourages you to use it more often to make your code compact
This code will create dictionary using list comprehension for multiple lists with different values that can be used for pd.DataFrame()
#Multiple lists
model=['A', 'B', 'C', 'D']
launched=[1983,1984,1984,1984]
discontinued=[1986, 1985, 1984, 1986]
#Dictionary with list comprehension
keys=['model','launched','discontinued']
vals=[model, launched,discontinued]
data = {key:vals[n] for n, key in enumerate(keys)}
#Convert dict to dataframe
df=pd.DataFrame(data)
display(df)
enumerate will pass n to vals to match each key with its list
Try this,
def get_dic_from_two_lists(keys, values):
return { keys[i] : values[i] for i in range(len(keys)) }
Assume we have two lists country and capital
country = ['India', 'Pakistan', 'China']
capital = ['New Delhi', 'Islamabad', 'Beijing']
Then create dictionary from the two lists:
print get_dic_from_two_lists(country, capital)
The output is like this,
{'Pakistan': 'Islamabad', 'China': 'Beijing', 'India': 'New Delhi'}
Adding to #Ekhtiar answer, if you want to make look up dict from list, you can use this:
names = ['a', 'b', 'd', 'f', 'c']
names_to_id = {v:k for k, v in enumerate(names)}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'f': 4}
Or in rare case that you want to filter duplicate, use set first (best in list of number):
names = ['a', 'b', 'd', 'f', 'd', 'c']
sorted_list = list(set(names))
sorted_list.sort()
names_to_id = {v:k for k, v in enumerate(sorted_list)}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'f': 4}
names = [1,2,5,5,6,2,1]
names_to_id = {v:k for k, v in enumerate(set(names))}
# {1: 0, 2: 1, 5: 2, 6: 3}
>>> {k: v**3 for (k, v) in zip(string.ascii_lowercase, range(26))}
Python supports dict comprehensions, which allow you to express the creation of dictionaries at runtime using a similarly concise syntax.
A dictionary comprehension takes the form {key: value for (key, value) in iterable}. This syntax was introduced in Python 3 and backported as far as Python 2.7, so you should be able to use it regardless of which version of Python you have installed.
A canonical example is taking two lists and creating a dictionary where the item at each position in the first list becomes a key and the item at the corresponding position in the second list becomes the value.
The zip function used inside this comprehension returns an iterator of tuples, where each element in the tuple is taken from the same position in each of the input iterables. In the example above, the returned iterator contains the tuples (“a”, 1), (“b”, 2), etc.
Output:
{'i': 512, 'e': 64, 'o': 2744, 'h': 343, 'l': 1331, 's': 5832, 'b': 1, 'w': 10648, 'c': 8, 'x': 12167, 'y': 13824, 't': 6859, 'p': 3375, 'd': 27, 'j': 729, 'a': 0, 'z': 15625, 'f': 125, 'q': 4096, 'u': 8000, 'n': 2197, 'm': 1728, 'r': 4913, 'k': 1000, 'g': 216, 'v': 9261}
Yes, it's possible. In python, Comprehension can be used in List, Set, Dictionary, etc.
You can write it this way
mydict = {k:v for (k,v) in blah}
Another detailed example of Dictionary Comprehension with the Conditional Statement and Loop:
parents = [father, mother]
parents = {parent:1 - P["mutation"] if parent in two_genes else 0.5 if parent in one_gene else P["mutation"] for parent in parents}
You can create a new dict for each pair and merge it with the previous dict:
reduce(lambda p, q: {**p, **{q[0]: q[1]}}, bla bla bla, {})
Obviously this approaches requires reduce from functools.
Assuming blah blah blah is a two-tuples list:
Let's see two methods:
# method 1
>>> lst = [('a', 2), ('b', 4), ('c', 6)]
>>> dict(lst)
{'a': 2, 'b': 4, 'c': 6}
# method 2
>>> lst = [('a', 2), ('b', 4), ('c', 6)]
>>> d = {k:v for k, v in lst}
>>> d
{'a': 2, 'b': 4, 'c': 6}
this approach uses iteration over the given date using a for loop.
Syntax: {key: value for (key, value) in data}
Eg:
# create a list comprehension with country and code:
Country_code = [('China', 86), ('USA', 1),
('Ghana', 233), ('Uk', 44)]
# use iterable method to show results
{key: value for (key, value) in Country_code}
I want to write a code which takes the following inputs:
list (list of maps)
request_keys (list of strings)
operation (add,substract,multiply,concat)
The code would look at the list for the maps having the same value for all keys except the keys given in request_keys. Upon finding two maps for which the value in the search keys match, the code would do the operation (add,multiple,substract,concat) on the two maps and combine them into one map. This combination map would basically replace the other two maps.
i have written the following peice of code to do this. The code only does add operation. It can be extended to make the other operations
In [83]: list
Out[83]:
[{'a': 2, 'b': 3, 'c': 10},
{'a': 2, 'b': 3, 'c': 3},
{'a': 2, 'b': 4, 'c': 4},
{'a': 2, 'b': 3, 'c': 2},
{'a': 2, 'b': 3, 'c': 3}]
In [84]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:def func(list,request_keys):
: new_list = []
: found_indexes = []
: for i in range(0,len(list)):
: new_item = list[i]
: if i in found_indexes:
: continue
: for j in range(0,len(list)):
: if i != j and {k: v for k,v in list[i].iteritems() if k not in request_keys} == {k: v for k,v in list[j].iteritems() if k not in request_keys}:
: found_indexes.append(j)
: for request_key in request_keys:
: new_item[request_key] += list[j][request_key]
: new_list.append(new_item)
: return new_list
:--
In [85]: func(list,['c'])
Out[85]: [{'a': 2, 'b': 3, 'c': 18}, {'a': 2, 'b': 4, 'c': 4}]
In [86]:
What i want to know is, is there a faster, more memory efficient, cleaner and a more pythonic way of doing the same?
Thank you
You manually generate all the combinations and then compare each of those combinations. This is pretty wasteful. Instead, I suggest grouping the dictionaries in another dictionary by their matching keys, then adding the "same" dictionaries. Also, you forgot the operator parameter.
import collections, operator, functools
def func(lst, request_keys, op=operator.add):
matching_dicts = collections.defaultdict(list)
for d in lst:
key = tuple(sorted(((k, d[k]) for k in d if k not in request_keys)))
matching_dicts[key].append(d)
for group in matching_dicts.values():
merged = dict(group[0])
merged.update({key: functools.reduce(op, (g[key] for g in group))
for key in request_keys})
yield merged
What this does: First, it creates a dictionary, mapping the key-value pairs that have to be equal for two dictionaries to match to all those dictionaries that have those key-value pairs. Then it iterates the dicts from those groups, using one of that group as a prototype and updating it with the sum (or product, or whatever, depending on the operator) of the all the dicts in that group for the required_keys.
Note that this returns a generator. If you want a list, just call it like list(func(...)), or accumulate the merged dicts in a list and return that list.
from itertools import groupby
from operator import itemgetter
def mergeDic(inputData, request_keys):
keys = inputData[0].keys()
comparedKeys = [item for item in keys if item not in request_keys]
grouper = itemgetter(*comparedKeys)
result = []
for key, grp in groupby(sorted(inputData, key = grouper), grouper):
temp_dict = dict(zip(comparedKeys, key))
for request_key in request_keys:
temp_dict[request_key] = sum(item[request_key] for item in grp)
result.append(temp_dict)
return result
inputData = [{'a': 2, 'b': 3, 'c': 10},
{'a': 2, 'b': 3, 'c': 3},
{'a': 2, 'b': 4, 'c': 4},
{'a': 2, 'b': 3, 'c': 2},
{'a': 2, 'b': 3, 'c': 3}]
from pprint import pprint
pprint(mergeDic(inputData,['c']))
I have data which consists of a series of categories, each with two amounts. For example, {'cat':'red', 'a':1, 'b':2}, {'cat':'red', 'a':3, 'b':3}, {'cat':'blue', 'a':1, 'b':3}
I want to keep a running total of the two amounts, by category. Result would be {'cat':'red', 'a':4, 'b':5}, {'cat':'blue', 'a':1, 'b':3}
Is there a more pythonic method than:
totals = {}
for item in data:
if item['cat'] in totals:
totals[item['cat']]['a'] += item['a']
totals[item['cat']]['b'] += item['b']
else:
totals[item['cat']] = {'a':item['a'], 'b':item['b']}
Your data structure should really be moved to a dictionary, keyed on the cat value. Use collections.defaultdict() and collections.Counter() to keep track of the values and make summing easier:
from collections import defaultdict, Counter
totals = defaultdict(Counter)
for item in data:
cat = item.pop('cat')
totals[cat] += Counter(item)
Demo:
>>> from collections import defaultdict, Counter
>>> data = {'cat':'red', 'a':1, 'b':2}, {'cat':'red', 'a':3, 'b':3}, {'cat':'blue', 'a':1, 'b':3}
>>> totals = defaultdict(Counter)
>>> for item in data:
... cat = item.pop('cat')
... totals[cat] += Counter(item)
...
>>> totals
defaultdict(<class 'collections.Counter'>, {'blue': Counter({'b': 3, 'a': 1}), 'red': Counter({'b': 5, 'a': 4})})
>>> totals['blue']
Counter({'b': 3, 'a': 1})
>>> totals['red']
Counter({'b': 5, 'a': 4})
If you still require a sequence of dictionaries in the same format, you can then turn the above dictionary of counters back into 'plain' dictionaries again:
output = []
for cat, counts in totals.iteritems():
item = {'cat': cat}
item.update(counts)
output.append(item)
resulting in:
>>> output
[{'a': 1, 'b': 3, 'cat': 'blue'}, {'a': 4, 'b': 5, 'cat': 'red'}]
Have a look at dict.setdefault and collections.counter.
Possible solution using setdefault:
totals = {}
for item in data:
d = totals.setdefault(item['cat'], {'a':0, 'b':0})
d['a'] += item['a']
d['b'] += item['b']
with result total = {'blue': {'a': 1, 'b': 3}, 'red': {'a': 4, 'b': 5}}. Note that this does not have the 'cat' entries like in your expected answer. Instead, the colors are used directly as the key for the resulting dictionary.
See Martijn's answer for an example using Counter.
I would collect your data into a temporary composite data structure based on a combination of the collections.Counter and collections.defaultdict classes. The keys of this data structure would be the cat's color and associated with each will be a Counterto hold the totals for each color cat. Making it adefaultdictmeans not having to worry about whether it's first time the color has been encountered and or not.
This will perform the summing of values needed as it is created and is fairly easy to turn into the output sequence you want afterwards:
from collections import Counter, defaultdict
data = ({'cat':'red', 'a':1, 'b':2},
{'cat':'red', 'a':3, 'b':3},
{'cat':'blue', 'a':1, 'b':3})
cat_totals = defaultdict(Counter) # hybrid data structure
for entry in data:
cat_totals[entry['cat']].update({k:v for k,v in entry.iteritems()
if k != 'cat'})
results = tuple(dict([('cat', color)] + cat_totals[color].items())
for color in cat_totals)
print results # ({'a': 1, 'b': 3, 'cat': 'blue'}, {'a': 4, 'b': 5, 'cat': 'red'})
I'm trying to use the solution provided here
Instead of getting a dictionary, how can I get a string with the same output i.e. character followed by the number of occurrences
Example:d2m2e2s3
To convert from the dict to the string in the format you want:
''.join('{}{}'.format(key, val) for key, val in adict.items())
if you want them alphabetically ordered by key:
''.join('{}{}'.format(key, val) for key, val in sorted(adict.items()))
Is this what you are looking for?
#!/usr/bin/python
dt={'d': 2, 'f': 2, 'g': 2, 'q': 5, 'w': 3}
st=""
for key,val in dt.iteritems():
st = st + key + str(val)
print st
output: q5w3d2g2f2
Or this?
#!/usr/bin/python
dt={'d': 2, 'f': 2, 'g': 2, 'q': 5, 'w': 3}
dt=sorted(dt.iteritems())
st=""
for key,val in dt:
st = st + key + str(val)
print st
output: d2f2g2q5w3
Example with join:
#!/usr/bin/python
adict=dt={'d': 2, 'f': 2, 'g': 2, 'q': 5, 'w': 3}
' '.join('{0}{1}'.format(key, val) for key, val in sorted(adict.items()))
output: 'd2 f2 g2 q5 w3'
>>> result = {'d': 2, 'f': 2, 'g': 2, 'q': 5, 'w': 3}
>>> ''.join('%s%d' % (k,v) for k,v in result.iteritems())
'q5w3d2g2f2'
or if you want them alphabetically...
>>> ''.join('%s%d' % (k,v) for k,v in sorted(result.iteritems()))
'd2f2g2q5w3'
or if you want them in increasing order of count...
>>> ''.join('%s%d' % (k,v) for k,v in sorted(result.iteritems(),key=lambda x:x[1]))
'd2g2f2w3q5'
Once you have the dict solution, just use join to join them into a string:
''.join([k+str(v) for k,v in result.iteritems()])
You can replace the '' with whatever separater (including none) you want between numbers
Another approach, avoiding the % interpolation (or format()) by using join() only:
''.join(''.join((k, str(v))) for k,v in mydict.items())
I have a dictionary with almost 100,000 (key, value) pairs and the majority of the keys map to the same values. For example:
mydict = {'a': 1, 'c': 2, 'b': 1, 'e': 2, 'd': 3, 'h': 1, 'j': 3}
What I want to do, is to reverse the dictionary so that each value in mydict is going to be a key at the reverse_dict and is going to map to a list of all the mydict.keys() that used to map to that value in mydict. So based on the example above I would get:
reversed_dict = {1: ['a', 'b', 'h'], 2: ['c', 'e'] , 3: ['d', 'j']}
I came up with a solution that is very expensive and I want to hear any ideas for doing this more efficiently than this:
reversed_dict = {}
for value in mydict.values():
reversed_dict[value] = []
for key in mydict.keys():
if mydict[key] == value:
if key not in reversed_dict[value]:
reversed_dict[value].append(key)
Using collections.defaultdict:
from collections import defaultdict
reversed_dict = defaultdict(list)
for key, value in mydict.items():
reversed_dict[value].append(key)
reversed_dict = {}
for key, value in mydict.items():
reversed_dict.setdefault(value, [])
reversed_dict[value].append(key)
for k,v in dict.iteritems():
try:
reversed_dict[v].append(k)
except KeyError:
reversed_dict[v]=[k]
I think you're wasting a few cycles by replacing a key with the same key again and again...
reversed_dict = {}
for value in mydict.values():
if value not in reversed_dict.keys(): #checking to be sure it hasn't been done.
reversed_dict[value] = []
for key in mydict.keys():
if mydict[key] == value:
if key not in reversed_dict[value]: reversed_dict[value].append(key)
Using itertools.groupby:
from operator import itemgetter
from itertools import groupby
snd = itemgetter(1)
def sort_and_group(itr, f):
return groupby(sorted(itr, key=f), f)
mydict = {'a': 1, 'c': 2, 'b': 1, 'e': 2, 'd': 3, 'h': 1, 'j': 3}
reversed_dict = {number: [char for char,_ in v]
for number, v in sort_and_group(mydict.items(), snd)}
reversed_dict = collections.defaultdict(list)
for key, value in dict_.iteritems():
reversed_dict[value].append(key)
def reverse_dict(mydict):
v={}
for x,y in mydict.items():
if y not in v:
v[y]=[x]
else:
v[y].append(x)
return v
print(reverse_dict(mydict))