Nested dictionary with defaults - python

Is there a way to make a nested dictionary such that I can say mydict[x][y][z] += 1, where mydict[x][y][z] did not previously exist, and defaults to 0 (and would be 1 after incrementing)?
I looked into answers to a similar question in which you can say mydict[x][y][z] = 1 using defaultdict from the collections class (Declaring a multi dimensional dictionary in python), but this does not allow you to assume a default value and then increment.

Yes, you can do this with the collections module:
from collections import defaultdict, Counter
d = defaultdict(lambda: defaultdict(lambda: Counter()))
d['A']['B']['C'] += 1
# defaultdict(<function __main__.<lambda>>,
# {'A': defaultdict(<function __main__.<lambda>.<locals>.<lambda>>,
# {'B': Counter({'C': 1})})})
Note this is also possible via only using nested defaultdict:
d = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
However, given Counter was created for the specific purpose of incrementing integers, this would be the method I prefer.

Related

Python [Nested Dictionary] - How to add new key/values to create this structure of dictionary: d = {key:{key:value, key:value, key:value}}

I hope you're good.
Can you help me to create this structure of dictionary and add the values in a loop?
d = {'key1':{'key(a)':'value', 'key(b)':'value', 'key(c)':'value'}, 'key2':{'key(a)':'value', 'key(b)':'value', 'key(c)':'value'}}
Below, a real example for you to understand my goal
d = {'ABCD3':{'2010':'25.0', '2011':'28.0', '2012':'24.0'}, RBRP11{'2010':'21.0', '2011':'30.0', '2012':'40.0'}}
This kind of structure will be easy to search after or do you have a better suggestion of structure?
defaultdict is what you need:
from collections import defaultdict
d = defaultdict(dict)
then you can do:
d['foo'][a] = 1
d['foo'].update({'b': 2, 'c': 3})
if you want to nest deeper:
d = defaultdict(lambda: defaultdict(dict))
d['foo']['bar'] = dict(a=1)
d['foo']['bar']['b'] = 2
d['foo']['bar'].update({'c': 3})

Can someone explain what this does "defaultdict(lambda:0)" [duplicate]

In someone else's code I read the following two lines:
x = defaultdict(lambda: 0)
y = defaultdict(lambda: defaultdict(lambda: 0))
As the argument of defaultdict is a default factory, I think the first line means that when I call x[k] for a nonexistent key k (such as a statement like v=x[k]), the key-value pair (k,0) will be automatically added to the dictionary, as if the statement x[k]=0 is first executed. Am I correct?
And what about y? It seems that the default factory will create a defaultdict with default 0. But what does that mean concretely? I tried to play around with it in Python shell, but couldn't figure out what it is exactly.
I think the first line means that when I call x[k] for a nonexistent key k (such as a statement like v=x[k]), the key-value pair (k,0) will be automatically added to the dictionary, as if the statement x[k]=0 is first executed.
That's right. This is more idiomatically written
x = defaultdict(int)
In the case of y, when you do y["ham"]["spam"], the key "ham" is inserted in y if it does not exist. The value associated with it becomes a defaultdict in which "spam" is automatically inserted with a value of 0.
I.e., y is a kind of "two-tiered" defaultdict. If "ham" not in y, then evaluating y["ham"]["spam"] is like doing
y["ham"] = {}
y["ham"]["spam"] = 0
in terms of ordinary dict.
You are correct for what the first one does. As for y, it will create a defaultdict with default 0 when a key doesn't exist in y, so you can think of this as a nested dictionary. Consider the following example:
y = defaultdict(lambda: defaultdict(lambda: 0))
print y['k1']['k2'] # 0
print dict(y['k1']) # {'k2': 0}
To create an equivalent nested dictionary structure without defaultdict you would need to create an inner dict for y['k1'] and then set y['k1']['k2'] to 0, but defaultdict does all of this behind the scenes when it encounters keys it hasn't seen:
y = {}
y['k1'] = {}
y['k1']['k2'] = 0
The following function may help for playing around with this on an interpreter to better your understanding:
def to_dict(d):
if isinstance(d, defaultdict):
return dict((k, to_dict(v)) for k, v in d.items())
return d
This will return the dict equivalent of a nested defaultdict, which is a lot easier to read, for example:
>>> y = defaultdict(lambda: defaultdict(lambda: 0))
>>> y['a']['b'] = 5
>>> y
defaultdict(<function <lambda> at 0xb7ea93e4>, {'a': defaultdict(<function <lambda> at 0xb7ea9374>, {'b': 5})})
>>> to_dict(y)
{'a': {'b': 5}}
defaultdict takes a zero-argument callable to its constructor, which is called when the key is not found, as you correctly explained.
lambda: 0 will of course always return zero, but the preferred method to do that is defaultdict(int), which will do the same thing.
As for the second part, the author would like to create a new defaultdict(int), or a nested dictionary, whenever a key is not found in the top-level dictionary.
All answers are good enough still I am giving the answer to add more info:
"defaultdict requires an argument that is callable. That return result of that callable object is the default value that the dictionary returns when you try to access the dictionary with a key that does not exist."
Here's an example
SAMPLE= {'Age':28, 'Salary':2000}
SAMPLE = defaultdict(lambda:0,SAMPLE)
>>> SAMPLE
defaultdict(<function <lambda> at 0x0000000002BF7C88>, {'Salary': 2000, 'Age': 28})
>>> SAMPLE['Age']----> This will return 28
>>> SAMPLE['Phone']----> This will return 0 # you got 0 as output for a non existing key inside SAMPLE
y = defaultdict(lambda:defaultdict(lambda:0))
will be helpful if you try this y['a']['b'] += 1

Usage of setdefault instead of defaultdict

I need to create a structure like this :
D = {i:{j:{k:0,l:1,m:2}},a:{b:{c:0,d:4}}}
So this can be done using defaultdict:
D = defaultdict(defaultdict(Counter))
How do i use setdefault here?
EDIT :
Is it possible to combine setdefault and defaultdict ?
To build a multi-level dictionary with setdefault() you'd need to repeatedly access the keys like this:
>>> from collections import Counter
>>> d = {}
>>> d.setdefault("i", {}).setdefault("j", Counter())
Counter()
>>> d
{'i': {'j': Counter()}}
To generalize the usage for new keys you could use a function:
def get_counter(d, i, j):
return d.setdefault(i, {}).setdefault(j, Counter())

recursive dictionary creation python

is there any way to dynamically create missing keys if i want to want to set a variable in a subdictionary.
essentially I want to create any missing keys and set my value.
self.portdict[switchname][str(neighbor['name'])]['local']['ports'] = []
currently i'm doing it but its messy:
if not switchname in self.portdict:
self.portdict[switchname] = {}
if not str(neighbor['name']) in self.portdict[switchname]:
self.portdict[switchname][str(neighbor['name'])] = {}
if not 'local' in self.portdict[switchname][str(neighbor['name'])]:
self.portdict[switchname][str(neighbor['name'])]['local'] = {}
if not 'ports' in self.portdict[switchname][str(neighbor['name'])]['local']:
self.portdict[switchname][str(neighbor['name'])]['local']['ports'] = []
Is there any way to do this in one or two lines instead?
This is easier to do without recursion:
def set_by_path(dct, path, value):
ipath = iter(path)
p_last = next(ipath)
try:
while True:
p_next = next(ipath)
dct = dct.setdefault(p_last, {})
p_last = p_next
except StopIteration:
dct[p_last] = value
And a test case:
d = {}
set_by_path(d, ['foo', 'bar', 'baz'], 'qux')
print d # {'foo': {'bar': {'baz': 'qux'}}}
If you want to have it so you don't need a function, you can use the following defaultdict factory which allows you to nest things arbitrarily deeply:
from collections import defaultdict
defaultdict_factory = lambda : defaultdict(defaultdict_factory)
d = defaultdict_factory()
d['foo']['bar']['baz'] = 'qux'
print d
Use collections.defaultdict
self.portdict = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: []))))
I've run into a similar problem in the past. I found that defaultdict was the right answer for me—but writing the super long definitions (like the one in #o11c's answer or #Apero's answer) was no good. Here's what I came up with instead:
from collections import defaultdict
from functools import partial
def NestedDefaultDict(levels, baseFn):
def NDD(lvl):
return partial(defaultdict, NDD(lvl-1)) if lvl > 0 else baseFn
return defaultdict(NDD(levels-1))
This creates a dictionary with levels of nested dictionaries. So if you have levels=3, then you need 3 keys to access the bottom-level value. The second argument is a function which is used to create the bottom-level values. Something like list or lambda: 0 or even dict would work well.
Here's an example of using the "automatic" keys with 4 levels, and list as the default function:
>>> x = NestedDefaultDict(4, list)
>>> x[1][2][3][4].append('hello')
>>> x
defaultdict(<functools.partial object at 0x10b5c22b8>, {1: defaultdict(<functools.partial object at 0x10b5c2260>, {2: defaultdict(<functools.partial object at 0x10b5c2208>, {3: defaultdict(<type 'list'>, {4: ['hello']})})})})
I think that's basically what you'd want for the case in your question. Your 4 "levels" are switch-name, neighbor-name, local, & ports—and it looks like you want a list at the bottom-level to store your ports.
Another example using 2 levels and lambda: 0 as the default:
>>> y = NestedDefaultDict(2, lambda: 0)
>>> y['foo']['bar'] += 7
>>> y['foo']['baz'] += 10
>>> y['foo']['bar'] += 1
>>> y
defaultdict(<functools.partial object at 0x1021f1310>, {'foo': defaultdict(<function <lambda> at 0x1021f3938>, {'baz': 10, 'bar': 8})})
Have a close look to collections.defaultdict:
from collections import defaultdict
foo = defaultdict(dict)
foo['bar'] = defaultdict(dict)
foo['bar']['baz'] = defaultdict(dict)
foo['bar']['baz']['aaa'] = 1
foo['bor'] = 0
foo['bir'] = defaultdict(list)
foo['bir']['biz'].append(1)
foo['bir']['biz'].append(2)
print foo
defaultdict(<type 'dict'>, {'bir': defaultdict(<type 'list'>, {'biz': [1, 2]}), 'bor': 0, 'bar': defaultdict(<type 'dict'>, {'baz': defaultdict(<type 'dict'>, {'aaa': 1})})})

How to construct a defaultdict from a dictionary?

If I have d=dict(zip(range(1,10),range(50,61))) how can I build a collections.defaultdict out of the dict?
The only argument defaultdict seems to take is the factory function, will I have to initialize and then go through the original d and update the defaultdict?
Read the docs:
The first argument provides the initial value for the default_factory
attribute; it defaults to None. All remaining arguments are treated
the same as if they were passed to the dict constructor, including
keyword arguments.
from collections import defaultdict
d=defaultdict(int, zip(range(1,10),range(50,61)))
Or given a dictionary d:
from collections import defaultdict
d=dict(zip(range(1,10),range(50,61)))
my_default_dict = defaultdict(int,d)
You can construct a defaultdict from dict, by passing the dict as the second argument.
from collections import defaultdict
d1 = {'foo': 17}
d2 = defaultdict(int, d1)
print(d2['foo']) ## should print 17
print(d2['bar']) ## should print 1 (default int val )
You can create a defaultdict with a dictionary by using a callable.
from collections import defaultdict
def dict_():
return {'foo': 1}
defaultdict_with_dict = defaultdict(dict_)

Categories