How do I create a default dictionary of dictionaries - string

I am trying to write some code that involves creating a default dictionary of dictionaries. However, I have no idea how to initialise/create such a thing. My current attempt looks something like this:
from collections import defaultdict
inner_dict = {}
dict_of_dicts = defaultdict(inner_dict(int))
The use of this default dict of dictionaries is to for each pair of words that I produce from a file I open (e.g. [['M UH M', 'm oo m']] ), to set each segment of the first word delimited by empty space as a key in the outer dictionary, and then for each segment in the second word delimited by empty space count the frequency of that segment.
For example
[['M UH M', 'm oo m']]
(<class 'dict'>, {'M': {'m': 2}, 'UH': {'oo': 1}})
Having just run this now it doesn't seem to have output any errors, however I was just wondering if something like this will actually produce a default dictionary of dictionaries.
Apologies if this is a duplicate, however previous answers to these questions have been confusing and in a different context.

To initialise a defaultdict that creates dictionaries as its default value you would use:
d = defaultdict(dict)
For this particular problem, a collections.Counter would be more suitable
>>> from collections import defaultdict, Counter
>>> d = defaultdict(Counter)
>>> for a, b in zip(*[x.split() for x in ['M UH M', 'm oo m']]):
... d[a][b] += 1
>>> print(d)
defaultdict(collections.Counter,
{'M': Counter({'m': 2}), 'UH': Counter({'oo': 1})})
Edit
You expressed interest in a comment about the equivalent without a Counter. Here is the equivalent using a plain dict
>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> for a, b in zip(*[x.split() for x in ['M UH M', 'm oo m']]):
... d[a][b] = d[a].get(b, 0) + 1
>>> print(d)
defaultdict(dict, {'M': {'m': 2}, 'UH': {'oo': 1}})

You also could a use a normal dictionary and its setdefault method.
my_dict.setdefault(key, default) will look up my_dict[key] and ...
... if the key already exists, return its current value without modifying it, or ...
... assign the default value (my_dict[key] = default) and then return that.
So you can call my_dict.setdefault(key, {}) always when you want to get a value from your outer dictionary instead of the normal my_dict[key] to retrieve either the real value assigned with this key if it#s present, or to get a new empty dictionary as default value which gets automatically stored into your outer dictionary as well.
Example:
outer_dict = {"M": {"m": 2}}
inner_dict = d.setdefault("UH", {})
# outer_dict = {"M": {"m": 2}, "UH": {}}
# inner_dict = {}
inner_dict["oo"] = 1
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1}}
# inner_dict = {"oo": 1}
inner_dict = d.setdefault("UH", {})
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1}}
# inner_dict = {"oo": 1}
inner_dict["xy"] = 3
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1, "xy": 3}}
# inner_dict = {"oo": 1, "xy": 3}
This way you always get a valid inner_dict, either an empty default one or the one that's already present for the given key. As dictionaries are mutable data types, modifying the returned inner_dict will also modify the dictionary inside outer_dict.

The other answers propose alternative solutions or show you can make a default dictionary of dictionaries using d = defaultdict(dict)
but the question asked how to make a default dictionary of default dictionaries, my navie first attempt was this:
from collections import defaultdict
my_dict = defaultdict(defaultdict(list))
however this throw an error: *** TypeError: first argument must be callable or None
so my second attempt which works is to make a callable using the lambda key word to make an anonymous function:
from collections import defaultdict
my_dict = defaultdict(lambda: defaultdict(list))
which is more concise than the alternative method using a regular function:
from collections import defaultdict
def default_dict_maker():
return defaultdict(list)
my_dict = defaultdict(default_dict_maker)
you can check it works by assigning:
my_dict[2][3] = 5
my_dict[2][3]
>>> 5
or by trying to return a value:
my_dict[0][0]
>>> []
my_dict[5]
>>> defaultdict(<class 'list'>, {})
tl;dr
this is your oneline answer my_dict = defaultdict(lambda: defaultdict(list))

Related

Erroneous behaviour while updating nested dictionary python3

While working on defaultdict class of collection package in python3.7, I see that new key is generated from the duplicate of last key, instead of initiating dictionary. Is there a way to initiate new element with given dictionary which is init_dict in below example code.
Example code to reproduce error:
from collections import defaultdict
init_dict = {'buy_qty': 0,
'sell_qty': 0}
pnl = defaultdict(lambda: init_dict)
pnl['a']['buy_qty'] += 1
pnl['a']['sell_qty'] += 1
Now when I do
pnl['b']
gives me
{'buy_qty': 1, 'sell_qty': 1}
I am looking for pnl['b'] to be initialized with init_dict. How can I achieve that?
Your copying by reference, not by value. So whatever you do to one dictionary, the other will be affected.
You can check this with the id() function:
print(id(pnl['a']))
print(id(pnl['b']))
print(id(pnl['a']) == id(pnl['b']))
Which will give the same memory addresses:
1817103232768
1817103232768
True
verifying that they are the same objects. You can fix this by assigning a shallow copy of the dictionary using dict.copy(), as mentioned in the comments:
pnl = defaultdict(lambda: init_dict.copy())
Or casting dict():
pnl = defaultdict(lambda: dict(init_dict))
Or using ** from PEP 448 -- Additional Unpacking Generalizations
:
pnl = defaultdict(lambda: {**init_dict})
Additionally, consider using a collections.Counter to do the counting, instead of initializing zero count dictionaries yourself:
from collections import defaultdict, Counter
pnl = defaultdict(Counter)
pnl['a']['buy_qty'] += 1
pnl['a']['sell_qty'] += 1
print(pnl)
# defaultdict(<class 'collections.Counter'>, {'a': Counter({'buy_qty': 1, 'sell_qty': 1})})
print(pnl['b']['buy_qty'])
# 0
print(pnl['b']['buy_qty'])
# 0
pnl['b']['buy_qty'] += 1
pnl['b']['sell_qty'] += 1
print(pnl)
# defaultdict(<class 'collections.Counter'>, {'a': Counter({'buy_qty': 1, 'sell_qty': 1}), 'b': Counter({'buy_qty': 1, 'sell_qty': 1})})
Counter is a subclass of dict, so they will work the same as normal dictionaries.

I append dictionary to a list, while printing i get only the last appended thing in the dictionary of the list

class one:
def __init__(self,id,d):
self.id=id
self.d=d
def printfun(self):
for i in l:
print(i.id,i.d)
l=[]
d={}
for i in range(2):
id=int(input())
d["a"]=int(input())
d["b"]=int(input())
o=one(id,d)
l.append(o)
o.printfun()
and my output is:
100
1
2
101
3
4
100 {'a': 3, 'b': 4}
101 {'a': 3, 'b': 4}
I append dictionary to a list, while printing i get only the last appended thing in the dictionary of the list. How to get all the dictionary i have appended in the list, and why i am not getting first dictionary i had appended in the list.
You need to append a new dictionary to the list, because otherwise you're appending a reference to the old list that has modified values.
Python never implicitly copies objects. When you set dict2 = dict1, you are making them refer to the same exact dict object, so when you mutate it, all references to it keep referring to the object in its current state.
https://stackoverflow.com/a/2465932/4361039
l=[]
for i in range(2):
id=int(input())
a=int(input())
b=int(input())
o=one(id, {"a": a, "b": b})
l.append(o)
o.printfun()

Python Dictionary - Combine Dictionaries

Given a list of Dictionaries, return a new Dictionary of all of their keys combined.
This is what I have done so far:
def combine_dictionaries(dictionary_list):
# your code goes here
my_dictionary = {}
for key in dictionary_list:
my_dictionary.update(key, dictionary_list[key])
return my_dictionary
This is the error it produces:
list indices must be integers or slices, not dict
Can someone let me know, how to get a integer when I have been provided a list of dictionaries?
The expected result should look something like this:
{'a': 3, 'b': 2, 'c': 4, 4: 4, 3: 3}
Your function is almost there.
I believe that you should only pass key in your dictionnary update because the update built-in function accepts either another dictionary object or an iterable of key/value pairs.
def combine_dictionaries(dictionary_list):
my_dictionary = {}
for key in dictionary_list:
my_dictionary.update(key)
return my_dictionary

Bracket for values in dictionary python 3

I am wondering how can I manage the bracket[] for the values of that key. For instance, a dictionary named "diamond" which contains = {'a':'Roger', 'c':'Rafael', 'b':'Roger'}. I am supposed to re-organize the data in diamond so that it would become rediamond = {'Rafael':['c'], 'Roger': '['a','b']'}.
My Code
def group_by_owners(files):
store = dict()
for key,value in files.items():
if value in store:
store[value]=(store[value], [key])
else:
store[value]=[key]
return store
files = {
'a': 'Rafael',
'b': 'Roger',
'c': 'Roger'
}
print(group_by_owners(files))
My Output
{'Rafael': (['a']), 'Roger': ['b'],['c']}
Expected Output
{{'Rafael': (['a']), 'Roger': ['b','c']}
So if there is to be 3 values for Roger, it should organize like ['','',''] .
You should use a defaultdict:
from collections import defaultdict
diamond = {'a':'Roger', 'c':'Rafael', 'b':'Roger'}
rediamond = defaultdict(list)
for k, v in diamond.items():
rediamond[v].append(k)

Counter class extension

I am having a problem finding an elegant way to create a Counter() class that can:
Feed in arbitrary number of keys and return a nested dictionary based on this list of keys.
Increment for this nested dictionary is arbitrary as well.
For example:
counter = Counter()
for line in fin:
if a:
counter.incr(key1, 1)
else:
counter.incr(key2, key3, 2)
print counter
Ideally I am hoping to get the result looks like: {key1 : 20, {key2 : {key3 : 40}}}. But I am stuck in creating this arbitrary nested dictionary from list of keys. Any help is appreciated.
you can subclass dict and create your own nested structure.
here's my attempt at writing such class :
class Counter(dict):
def incr(self, *args):
if len(args) < 2:
raise TypeError, "incr() takes at least 2 arguments (%d given)" %len(args)
curr = self
keys, count = args[:-1], args[-1]
for depth, key in enumerate(keys, 1):
if depth == len(keys):
curr[key] = curr.setdefault(key, 0) + count
else:
curr = curr.setdefault(key, {})
counter = Counter()
counter.incr('key1', 1)
counter.incr('key2', 'key3', 2)
counter.incr('key1', 7)
print counter #{'key2': {'key3': 2}, 'key1': 8}
There are two possibilities.
First, you can always fake the nested-keys thing by using a flat Counter with a "key path" made of tuples:
counter = Counter()
for line in fin:
if a:
counter.incr((key1,), 1)
else:
counter.incr((key2, key3), 2)
But then you'll need to write a str-replacement—or, better, a wrapper class that implements __str__. And while you're at it, you can easily write an incr wrapper that lets you use exactly the API you wanted:
def incr(self, *args):
super().incr(args[:-1], args[-1])
Alternatively, you can build your own Counter-like class on top of a nested dict. The code for Counter is written in pure Python, and the source is pretty simple and readable.
From, your code, it looks like you don't have any need to access things like counter[key2][key3] anywhere, which means the first is probably going to be simpler and more appropriate.
The only type of value that can exist in a Counter object is an int, you will not be able to represent a nested dictionary with a Counter.
Here is one way to do this with a normal dictionary (counter = {}). First, to update increment the value for a single key:
counter[key1] = counter.setdefault(key1, 0) + 1
Or for an arbitrary list of keys to create the nested structure:
tmp = counter
for key in key_list[:-1]:
tmp = tmp.setdefault(key, {})
tmp[key_list[-1]] = tmp.setdefault(key_list[-1], 0) + 1
I would probably turn this into the following function:
def incr(counter, val, *keys):
tmp = counter
for key in keys[:-1]:
tmp = tmp.setdefault(key, {})
tmp[keys[-1]] = tmp.setdefault(keys[-1], 0) + val
Example:
>>> counter = {}
>>> incr(counter, 1, 'a')
>>> counter
{'a': 1}
>>> incr(counter, 2, 'a')
>>> counter
{'a': 3}
>>> incr(counter, 2, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 2}}}
>>> incr(counter, 3, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 5}}}

Resources