How to create a master dictionary from a from a dictionary - python

I have a dictionary:
d = {1: 2,
2:3,
3: 4,
5:6,
6:7}
I want this output:
d= { 1: 4 , 2 :4 , 3:4 , 5:7 , 6:7}
basically 2 is parent of 1, 3 is parent of 2, 4 is parent of 3. I want to say that 1,2 and 3 is related to 4

For each key-value pair in the dict, you can use a while loop to keep checking if the current value is a valid key in the dict, and make the value of that key the new value, until the value is not a key, at which point you have found the leaf and you can assign that value to the current key:
for k, v in d.items():
while v in d:
v = d[v]
d[k] = v
so that given:
d = {1: 2,
2: 3,
3: 4,
5: 6,
6: 7}
d becomes:
{1: 4, 2: 4, 3: 4, 5: 7, 6: 7}

Related

How do I get a multiple of leaf nodes for each element in dict as tree structure?

Let's say here is input:
input = {1:[2,3], 2:[], 3:[4,5,6], 4:[], 5:[], 6:[]}
This can be represented like below:
tree
If all leaf nodes are 1, we can change like this:
for idx, val in input[1]:
if len(input[val]) == 0:
input[1][idx] = [] # input[1] = [[], 3]
else:
input[1][idx] = input[val] # input[1] = [[], [4,5,6]]
And then somehow, input[1] can be input[1] = [[],[[],[],[]]].
So finally, i wanna get multiple numbers of each dict key elements compared to leaf nodes.
I am not sure the description that i wrote is clear.
Anyways, What i want to get is like:
# 2, 4, 5, 6 are all leaf nodes.
# 3 is including [4, 5, 6] which are all leaf nodes, then 3's value must be 3 (len[4,5,6])
# 1 is including [2, 3]. Among them, only 2 is leaf. And 3's value is 3. So output[1] = 1 + 3 = 4
output = {1:4, 2:1, 3:3, 4:1, 5:1, 6:1}
Simple recursion with functools.lru_cache:
from functools import lru_cache
def leaves_count(tree):
#lru_cache
def cntr(key):
value = tree[key]
return sum(map(cntr, value)) if value else 1
return {k: cntr(k) for k in tree}
Test:
>>> tree = {1: [2, 3], 2: [], 3: [4, 5, 6], 4: [], 5: [], 6: []}
>>> leaves_count(tree)
{1: 4, 2: 1, 3: 3, 4: 1, 5: 1, 6: 1}
Manually implement the cached version:
def leaves_count(tree):
def cntr(key):
cnt = cache.get(key)
if cnt:
return cnt
value = tree[key]
cache[key] = cnt = sum(map(cntr, value)) if value else 1
return cnt
cache = {}
return {k: cntr(k) for k in tree}
Using recursion method
Code:
dic= {1:[2,3], 2:[], 3:[4,5,6], 4:[], 5:[], 6:[]}
def recur(val, leaf):
for v in val:
if v in dic.keys():
if len(dic[v])==0:
leaf.append(v)
else:
recur(dic[v],leaf)
else:
leaf.append(v)
return len(leaf)
{key : 1 if recur(val,[])==0 else recur(val,[]) for key,val in dic.items()}
Output:
{1: 4, 2: 1, 3: 3, 4: 1, 5: 1, 6: 1}
Input: {1:[2,3], 2:[], 3:[4,5,6], 4:[], 5:[], 6:[7,8], 7:[], 8:[9,10], 9:[]}
Output: {1: 6, 2: 1, 3: 5, 4: 1, 5: 1, 6: 3, 7: 1, 8: 2, 9: 1}

Merging two lists with different lengths in python and counting elements

I have a list as follows:
[[a,a,b,a,b,b,b],[5,5,4,5],[5,1,5],[2,5],[4,5],[5],[3],[5]]
The number of lists containing numerical values is the same as the elements in the first list (that contains letters "a" or "b"). The length of the lists containing numbers is unknown a priori.
Each letter corresponds to a list in this way:
a --> 5,5,4,5
a --> 5,1,5
b --> 2,5
a --> 4,5
b --> 5
b --> 3
b --> 5
And then, count each value by letters "a" or "b", while keeping the values, for example "a" has in total 6 "5", 2 "4", and 1 "1". "b" has in total 3 "5", 1 "2", and 1 "3".
Expected result:
"a" has in total 6 "5", 2 "4", and 1 "1".
"b" has in total 3 "5", 1 "2", and 1 "3".
One approach:
from collections import Counter, defaultdict
lst = [["a", "a", "b", "a", "b", "b", "b"], [5, 5, 4, 5], [5, 1, 5], [2, 5], [4, 5], [5], [3], [5]]
result = defaultdict(Counter)
head, *tail = lst
for key, values in zip(head, tail):
result[key] += Counter(values)
for key, counts in result.items():
print(key, counts)
Output
a Counter({5: 6, 4: 2, 1: 1})
b Counter({5: 3, 2: 1, 3: 1})
An alternative:
head, *tail = lst
counts = Counter((key, value) for key, values in zip(head, tail) for value in values)
result = defaultdict(dict)
for (key, value), count in counts.items():
result[key][value] = count
for key, value in result.items():
print(key, value)
Output
a {5: 6, 4: 2, 1: 1}
b {2: 1, 5: 3, 3: 1}
This would be my approach:
inputs = [['a','a','b','a','b','b','b'],[5,5,4,5],[5,1,5],[2,5],[4,5],[5],[3],[5]]
counts = {}
for k, v in zip(inputs[0], inputs[1:]):
if k in counts.keys():
counts[k] += v
else:
counts[k] = v
My output for this:
{'a': [5, 5, 4, 5, 5, 1, 5, 4, 5], 'b': [2, 5, 5, 3, 5]}
Then you can use Counters, len(), etc. to get the exact output formatting you want.
See below ( Counter and defaultdict are the main "players")
from collections import Counter,defaultdict
results = defaultdict(Counter)
data = [['a','a','b','a','b','b','b'],[5,5,4,5],[5,1,5],[2,5],[4,5],[5],[3],[5]]
for idx,x in enumerate(data[0],1):
results[x].update(data[idx])
for letter,counter in results.items():
print(f'{letter} -> {counter}')
output
a -> Counter({5: 6, 4: 2, 1: 1})
b -> Counter({5: 3, 2: 1, 3: 1})
It's a fun problem to solve.
from collections import Counter
data = [['a','a','b','a','b','b','b'],[5,5,4,5],[5,1,5],[2,5],[4,5],[5],[3],[5]]
counts = { k:Counter() for k in list(set(data[0])) }
for i, k in enumerate(data[0], 1):
counts[k].update(data[i])
print(counts)
# {'a': Counter({5: 6, 4: 2, 1: 1}), 'b': Counter({5: 3, 2: 1, 3: 1})}
l = [["a","a","b","a","b","b","b"],[5,5,4,5],[5,1,5],[2,5],[4,5],[5],[3],[5]]
d = {}
for i in l[0]:
if i not in d.keys():
d[i] = []
for i, item in zip(l[0],l[1:]):
d[i].append(item)
count_dict = {}
for i in d.keys():
count_dict[i] = {}
for j in d[i]:
elements = set(j)
for k in elements:
if str(k) not in count_dict[i].keys():
count_dict[i][str(k)] = j.count(k)
else:
count_dict[i][str(k)] += j.count(k)
for i,j in count_dict.items():
print('{} has {}'.format(i,j))
First off, you should really be doing your own homework
... I just love a good algo problem (adventOfCode, anyone?)
Steps:
don't forget " around strings in the list
create a dictionary to store counts within "a" and "b"
iterate over lists in indices 1-n
get the a/b bucket from: l[0][sublist_index - 1]
start a count for value, if not already counted: if v not in d[bucket].keys(): d[bucket][v] = 0
increment the counter for v, in the appropriate bucket: d[bucket][v] += 1
l = [["a","a","b","a","b","b","b"],[5,5,4,5],[5,1,5],[2,5],[4,5],[5],[3],[5]]
d = {"a": {}, "b": {}}
for sublist_index in range(1, len(l)):
bucket = l[0][sublist_index - 1]
for v in l[sublist_index]:
if v not in d[bucket].keys():
d[bucket][v] = 0
d[bucket][v] += 1
print(d)
and the output:
{'a': {5: 6, 4: 2, 1: 1}, 'b': {2: 1, 5: 3, 3: 1}}

Why will the output be 11032? Python3.6

d = {10:"x", 1:"wx", 2:"yz"}
a = d.setdefault(1)
b = d.setdefault(3)
s = "{}" * len(d)
print(s.format(*d))
Why will the output be 11032?
After 2 setdefault calls,
d = {10: "x", 1: "wx", 2: "yz"}
d.setdefault(1) # does not change the dictionary because there's already 1
d.setdefault(3) # add 3 with value None (default if not speicfied)
d becomes:
>>> d
{10: 'x', 1: 'wx', 2: 'yz', 3: None}
Iterating dictionary yields dictionary keys: 10, 1, 2, 3. (iteration performed by *d to unpack the argument d):
>>> for key in d:
... print(key)
...
10
1
2
3
So, s.format(*d) is equivalent to '{}{}{}{}'.format(10, 1, 2, 3).

Using secondary key to sum dictionary values

I have a dict structured like [a][b]=(c) such as:
{'cat': {1:1, 2:3, 3:1, 4:1}, 'dog': {1:8, 2:2, 3:4}, 'egg': {5:1, 6:2}, 'frog': {2:1, 4:1, 5:1}, 'nuts': {3:1}, 'idea': {4:1}}
What I'd like to be able to do is search by the [b] key and sum the corresponding c belonging to that. So I'd get the following outputs:
1: 9, 2: 6, 3: 6
...and so on.
Does this require restructuring of the dict?
You can iterate on the dictionary values which are dicts and sum up the values for each key using a collections.defaultdict. Then you'll simply access the result dictionary to find out the summed value for each key with no need to search:
from collections import defaultdict
d = {'cat': {1:1, 2:3, 3:1, 4:1}, 'dog': {1:8, 2:2, 3:4}, 'egg': {5:1, 6:2}, 'frog': {2:1, 4:1, 5:1}, 'nuts': {3:1}, 'idea': {4:1}}
result = defaultdict(int)
for i in d.values():
for j in i:
result[j] += i[j]
print(result)
# defaultdict(<class 'int'>, {1: 9, 2: 6, 3: 6, 4: 3, 5: 2, 6: 2})
>>> print(result[1])
9
I'll assume you have such dictionary:
d = {'cat': {1:1, 2:3, 3:1, 4:1}, 'dog': {1:8, 2:2, 3:4}, 'egg': {5:1, 6:2}, 'frog': {2:1, 4:1, 5:1}, 'nuts': {3:1}, 'idea': {4:1}}}
Now we have to write a function which will take a parameter (an integer) and sum all values for this integer across all inner dictionaries.
def calc(b):
result = 0
for val in d.values():
if b in val:
result += val[b]
If all you need is the total then you can sum up all the values using:
>>> b = 2
>>> sum(a.get(b, 0) for a in d.values())
6
If you want all the bs then you use collections.Counter() which behaves like a dict to do all the heavy lifting:
>>> from collections import Counter
>>> sum((Counter(a) for a in d.values()), Counter())
Counter({1: 9, 2: 6, 3: 6, 4: 3, 5: 2, 6: 2})
But if you are really fussy and want a dict:
>>> dict(sum((Counter(a) for a in d.values()), Counter()))
{1: 9, 2: 6, 3: 6, 4: 3, 5: 2, 6: 2}

Adding missing keys in dictionary in Python

I have a list of dictionaries:
L = [{0:1,1:7,2:3,4:8},{0:3,2:6},{1:2,4:6}....{0:2,3:2}].
As you can see, the dictionaries have different length. What I need is to add missing keys:values to every dictionary to make them being with the same length:
L1 = [{0:1,1:7,2:3,4:8},{0:3,1:0,2:6,3:0,4:0},{0:0, 1:2,3:0,4:6}....{0:2,1:0,2:0,3:2,4:0}],
Means to add zeros for missing values. The maximum length isn't given in advance, so one may get it only iterating through the list.
I tried to make something with defaultdicts, like L1 = defaultdict(L) but it seems I don't understand properly how does it work.
You'll have to make two passes: 1 to get the union of all keys, and another to add the missing keys:
max_key = max(max(d) for d in L)
empty = dict.fromkeys(range(max_key + 1), 0)
L1 = [dict(empty, **d) for d in L]
This uses an 'empty' dictionary as a base to quickly produce all keys; a new copy of this dictionary plus an original dictionary produces the output you want.
Note that this assumes your keys are always sequential. If they are not, you can produce the union of all existing keys instead:
empty = dict.fromkeys(set().union(*L), 0)
L1 = [dict(empty, **d) for d in L]
Demo:
>>> L = [{0: 1, 1: 7, 2: 3, 4: 8}, {0: 3, 2: 6}, {1: 2, 4: 6}, {0: 2, 3: 2}]
>>> max_key = max(max(d) for d in L)
>>> empty = dict.fromkeys(range(max_key + 1), 0)
>>> [dict(empty, **d) for d in L]
[{0: 1, 1: 7, 2: 3, 3: 0, 4: 8}, {0: 3, 1: 0, 2: 6, 3: 0, 4: 0}, {0: 0, 1: 2, 2: 0, 3: 0, 4: 6}, {0: 2, 1: 0, 2: 0, 3: 2, 4: 0}]
or the set approach:
>>> empty = dict.fromkeys(set().union(*L), 0)
>>> [dict(empty, **d) for d in L]
[{0: 1, 1: 7, 2: 3, 3: 0, 4: 8}, {0: 3, 1: 0, 2: 6, 3: 0, 4: 0}, {0: 0, 1: 2, 2: 0, 3: 0, 4: 6}, {0: 2, 1: 0, 2: 0, 3: 2, 4: 0}]
The above approach to merge two dictionaries into a new one with dict(d1, **d2) always works in Python 2. In Python 3 additional constraints have been set on what kind of keys you can use this trick with; only string keys are allowed for the second dictionary. For this example, where you have numeric keys, but you can use dictionary unpacking instead:
{**empty, **d} # Python 3 dictionary unpacking
That'll work in Python 3.5 and newer.
a bit of caution: changes L
>>> allkeys = frozenset().union(*L)
>>> for i in L:
... for j in allkeys:
... if j not in i:
... i[j]=0
>>> L
[{0: 1, 1: 7, 2: 3, 3: 0, 4: 8}, {0: 3, 1: 0, 2: 6, 3: 0, 4: 0}, {0: 0, 1: 2, 2:
0, 3: 0, 4: 6}, {0: 2, 1: 0, 2: 0, 3: 2, 4: 0}]
Maybe not the most elegant solution, but should be working:
L = [{0:1,1:7,2:3,4:8},{0:3,2:6},{1:2,4:6},{0:2,3:2}]
alldicts = {}
for d in L:
alldicts.update(d)
allkeys = alldicts.keys()
for d in L:
for key in allkeys:
if key not in d:
d[key] = 0
print(L)
This is only a solution, but I think it's simple and straightforward. Note that it modifies the dictionaries in place, so if you want them to be copied, let me know and I'll revise accordingly.
keys_seen = []
for D in L: #loop through the list
for key in D.keys(): #loop through each dictionary's keys
if key not in keys_seen: #if we haven't seen this key before, then...
keys_seen.append(key) #add it to the list of keys seen
for D1 in L: #loop through the list again
for key in keys_seen: #loop through the list of keys that we've seen
if key not in D1: #if the dictionary is missing that key, then...
D1[key] = 0 #add it and set it to 0
This is quick and slim:
missing_keys = set(dict1.keys()) - set(dict2.keys())
for k in missing_keys:
dict1[k] = dict2[k]
Unless None is a valid value for a dictionary key you have herein is a great solution for you
L = [{0: 1, 1: 7, 2: 3, 4: 8}, {0: 3, 2: 6}, {1: 2, 4: 6}, {0: 2, 3: 2}]
for i0, d0 in enumerate(L[:-1]):
for d1 in L[i0:]:
_ = [d0.__setitem__(k,d1[k]) for k in d1 if d0.get(k,None) is None]
_ = [d1.__setitem__(k,d0[k]) for k in d0 if d1.get(k,None) is None]
print(L)
>>> [{0: 1, 1: 7, 2: 3, 3: 2, 4: 8}, {0: 3, 1: 2, 2: 6, 3: 2, 4: 6}, {0: 2, 1: 2, 2: 3, 3: 2, 4: 6}, {0: 2, 1: 7, 2: 3, 3: 2, 4: 8}]

Categories