Read Multiple Dictionaries into a nested list - python

I am reading below list into a Counter and I want to group all keys into a nested list as shown below
import collections
A=["cool","lock","cook"]
B=[]
d={}
for i in A:
B.append(collections.Counter(i))
print(B)
## B value is [Counter({'o': 2, 'c': 1, 'l': 1}), Counter({'l': 1, 'o': 1, 'c': 1, 'k': 1}), Counter({'o': 2, 'c': 1, 'k': 1})]
for i in B:
for j in i.keys():
d.setdefault( d[j],[]).append(i.values())
print(d)
I am getting a Key Error, I have used Setdefault() but able to get it work.
Needed output:
{'o':[2,1,2],'c':[1,1,1],'l':[1,1],'k':[1,1] }

Here is how:
import collections
A = ["cool", "lock", "cook"]
B = []
d = {}
for i in A:
B.append(collections.Counter(i))
for i in B:
for j in i:
if j in d:
d[j].append(i[j])
else:
d[j] = [i[j]]
print(d)
Output:
{'c': [1, 1, 1], 'o': [2, 1, 2], 'l': [1, 1], 'k': [1, 1]}
You may even use map when defining B to improve the efficiency:
import collections
A = ["cool", "lock", "cook"]
B = map(collections.Counter, A)
d = {}
for i in B:
for j in i:
if j in d:
d[j].append(i[j])
else:
d[j] = [i[j]]
print(d)

Related

Merge a list of dicts into a single dict adding values for common keys

How can I turn a list of dicts like this
dico = [{'a':1}, {'b':2}, {'c':1}, {'d':2}, {'e':2}, {'d':3}, {'g':1}, {'h':4}, {'h':2}, {'f':6}, {'a':2}, {'b':2}]
Into a single dict like this
{'a':3, 'b':4, 'c':1, 'd':5,'e':2,'f':6 , 'g':1 ,'h':6}
At the moment when doing this
result = {}
for d in dico:
result.update(d)
print(result)
Result :
{'a': 2, 'b': 2, 'c': 1, 'd': 3, 'e': 2, 'g': 1, 'h': 2, 'f': 6}
Just replace your dictionary with collections.Counter and it will work:
from collections import Counter
dico = [{'a':1}, {'b':2}, {'c':1}, {'d':2}, {'e':2}, {'d':3}, {'g':1}, {'h':4}, {'h':2}, {'f':6}, {'a':2}, {'b':2}]
result = Counter()
for d in dico:
result.update(d)
print(result)
Output:
Counter({'h': 6, 'f': 6, 'd': 5, 'b': 4, 'a': 3, 'e': 2, 'c': 1, 'g': 1})
Why the above works with update for Counter from the docs:
Elements are counted from an iterable or added-in from another mapping (or counter). Like dict.update() but adds counts instead of replacing them. Also, the iterable is expected to be a sequence of elements, not a sequence of (key, value) pairs.
Here's a fancy way to do it using collections.Counter, which is a kind of dictionary:
from collections import Counter
def add_dicts(dicts):
return sum(map(Counter, dicts), Counter())
The above is not efficient for a large number of dictionaries since it creates many intermediate Counter objects for the result, rather than updating one result in-place, so it runs in quadratic time. Here's a similar solution which runs in linear time:
from collections import Counter
def add_dicts(dicts):
out = Counter()
for d in dicts:
out += d
return out
Using a defaultdict:
from collections import defaultdict
dct = defaultdict(int)
for element in dico:
for key, value in element.items():
dct[key] += value
print(dct)
Which yields
defaultdict(<class 'int'>,
{'a': 3, 'b': 4, 'c': 1, 'd': 5, 'e': 2, 'g': 1, 'h': 6, 'f': 6})
As for time measurements, this is a comparison between the four answers:
from collections import defaultdict, Counter
from timeit import timeit
def solution_dani():
result = sum((Counter(e) for e in dico), Counter())
def solution_kaya():
return sum(map(Counter, dico), Counter())
def solution_roadrunner():
result = Counter()
for d in dico:
result.update(d)
return result
def solution_jan():
dct = defaultdict(int)
for element in dico:
for key, value in element.items():
dct[key] += value
return dct
print(timeit(solution_dani, number=10000))
print(timeit(solution_kaya, number=10000))
print(timeit(solution_roadrunner, number=10000))
print(timeit(solution_jan, number=10000))
On my MacBookAir this yields
0.839742998
0.8093687279999999
0.18643740100000006
0.04764247300000002
So the solution with a default dict is by far the fastest (factor 15-20), followed by #RoadRunner.
Use collections.Counter and sum:
from collections import Counter
dico = [{'a':1}, {'b':2}, {'c':1}, {'d':2}, {'e':2}, {'d':3}, {'g':1}, {'h':4}, {'h':2}, {'f':6}, {'a':2}, {'b':2}]
result = sum((Counter(e) for e in dico), Counter())
print(result)
Output
Counter({'h': 6, 'f': 6, 'd': 5, 'b': 4, 'a': 3, 'e': 2, 'c': 1, 'g': 1})
If you need an strict dictionary do:
result = dict(sum((Counter(e) for e in dico), Counter()))
print(result)
You could modify your approach, like this:
result = {}
for d in dico:
for key, value in d.items():
result[key] = result.get(key, 0) + value
print(result)
The update method will replace the values of existing keys, from the documentation:
Update the dictionary with the key/value pairs from other, overwriting
existing keys.
import collections
counter = collections.Counter()
for d in dico:
counter.update(d)
result = dict(counter)
print(result)
Output
{'a': 3, 'b': 4, 'c': 1, 'd': 5, 'e': 2, 'g': 1, 'h': 6, 'f': 6}

Subtraction if an items matches in both dictionaries working wrong

I have two dictionaries and I want to subtract value of an item if found in both the dictionaries for which I am doing this:
My Code:
X = {'a':7, 'b':8,'c':9,'d':10}
Y = {'a':3, 'b':4,'c':9}
res = {}
for k,v in X.items():
for m,n in Y.items():
if k == m:
res[k] = v-n
else:
res[k] = v
It is giving me output:
res = {'a': 7, 'b': 8, 'c': 0, 'd': 10}
whereas what I need is this:
res = {'a': 4, 'b': 4, 'c': 0, 'd': 10}
How can I get that ? And also why the above code isn't working ?
insert break statement in 1st condition as below:
X = {'a':7, 'b':8,'c':9,'d':10}
Y = {'a':3, 'b':4,'c':9}
res = {}
for k,v in X.items():
for m,n in Y.items():
if k == m:
res[k] = v-n
break
else:
res[k] = v
print(res)
You can try this.
{k:X.get(k,0)-Y.get(k,0) for k in X.keys()|Y.keys()}
# {'a': 4, 'b': 4, 'c': 0, 'd': 10} -->Order will not be maintained

Relationship graph in Python

I want to calculate number of friends for each person given a relationship graph without using any libraries. The graph is represented as lists of lists:
graph = [[A,B],[A,C],[C,B],[B,D],[E]]
Expected dictionary output: {'A':2, 'B':3, 'C':2, 'D':1, 'E':0}
Note: Since E has no friends, E should be 0
Straightforward solution without changing input format
>>> graph = [['A', 'B'], ['A', 'C'],['C', 'B'], ['B', 'D'], ['E']]
>>> from collections import defaultdict
>>> friends_counter = defaultdict(int)
>>> for friends in graph:
... for person in friends:
... friends_counter[person] += len(friends) - 1
>>> dict(friends_counter)
{'A': 2, 'B': 3, 'C': 2, 'D': 1, 'E': 0}
You could use a python library specific to graphs called NetworkX. I changed the data to be easier to load.
import networkx as nx
graph = [['A','B'],['A','C'],['C','B'],['B','D']]
G = nx.Graph()
G.add_edges_from(graph)
G.add_node('E')
dict(G.degree)
# {'A': 2, 'B': 3, 'C': 2, 'D': 1, 'E': 0}
Edit: this answer was given before the "without using any libraries" caveat was added.
Got the solution. Is there a better way to do this?
graph = [['A','B'],['A','C'],['C','B'],['B','D'],['E']]
dct ={}
for v in graph:
for x in v:
if x in v:
if x in dct.keys():
dct[x] += 1
else:
dct[x]= len(v)-1
print(dct)
{'A': 2, 'B': 3, 'C': 2, 'D': 1, 'E': 0}
so you can do like this
graph = [["A","B"],["A","C"],["C","B"],["B","D"],["E"]]
ans = {}
for n in graph:
if len(n) == 1:
ans[n[0]] = ans.get(n[0], 0)
else:
l, r = n
ans[l] = ans.get(l, 0) + 1
ans[r] = ans.get(r, 0) + 1
print(ans)
# {'A': 2, 'B': 3, 'C': 2, 'D': 1, 'E': 0}

How to keep my original dict when doing addition of Counter

To my understanding, I know when I invoke Counter to covert dict. This dict includes value of keys is zero will disappear.
from collections import Counter
a = {"a": 1, "b": 5, "d": 0}
b = {"b": 1, "c": 2}
print Counter(a) + Counter(b)
If I want to keep my keys, how to do?
This is my expected result:
Counter({'b': 6, 'c': 2, 'a': 1, 'd': 0})
You can also use the update() method of Counter instead of + operator, example -
>>> a = {"a": 1, "b": 5, "d": 0}
>>> b = {"b": 1, "c": 2}
>>> x = Counter(a)
>>> x.update(Counter(b))
>>> x
Counter({'b': 6, 'c': 2, 'a': 1, 'd': 0})
update() function adds counts instead of replacing them , and it does not remove the zero value one either. We can also do Counter(b) first, then update with Counter(a), Example -
>>> y = Counter(b)
>>> y.update(Counter(a))
>>> y
Counter({'b': 6, 'c': 2, 'a': 1, 'd': 0})
Unfortunately, when summing two counter, only elements with a positive count are used.
If you want to keep the elements with a count of zero, you could define a function like this:
def addall(a, b):
c = Counter(a) # copy the counter a, preserving the zero elements
for x in b: # for each key in the other counter
c[x] += b[x] # add the value in the other counter to the first
return c
You can just subclass Counter and adjust its __add__ method:
from collections import Counter
class MyCounter(Counter):
def __add__(self, other):
"""Add counts from two counters.
Preserves counts with zero values.
>>> MyCounter('abbb') + MyCounter('bcc')
MyCounter({'b': 4, 'c': 2, 'a': 1})
>>> MyCounter({'a': 1, 'b': 0}) + MyCounter({'a': 2, 'c': 3})
MyCounter({'a': 3, 'c': 3, 'b': 0})
"""
if not isinstance(other, Counter):
return NotImplemented
result = MyCounter()
for elem, count in self.items():
newcount = count + other[elem]
result[elem] = newcount
for elem, count in other.items():
if elem not in self:
result[elem] = count
return result
counter1 = MyCounter({'a': 1, 'b': 0})
counter2 = MyCounter({'a': 2, 'c': 3})
print(counter1 + counter2) # MyCounter({'a': 3, 'c': 3, 'b': 0})
I help Anand S Kumar to do more a additional explanation.
Even though your dict includes negative value, it still keep your keys.
from collections import Counter
a = {"a": 1, "b": 5, "d": -1}
b = {"b": 1, "c": 2}
print Counter(a) + Counter(b)
#Counter({'b': 6, 'c': 2, 'a': 1})
x = Counter(a)
x.update(Counter(b))
print x
#Counter({'b': 6, 'c': 2, 'a': 1, 'd': -1})

Performing circular shif on values in a dictionary

I have to construct a large dictionary which looks something like this
{'A' : {'A' : 1, 'B' : 0, 'C' : 0},
'B' : {'A' : 0, 'B' : 1, 'C' : 0}}
............................
Each inner dictionary is shifted by one position. Is there a way I can automate this process.
I can loop and use np.roll() function if it were ndarray so I am wondering if there's something similiar I could do with dictionaries.
You can use collections.deque for fast dictionary values (list) shifting (deque rotate function) but there is no specific function for dictionary, you should make it yourself, for example:
# shift dct (dict) values by n to right if n is positive
# and to left if n is negative; returns new dictionary
def dict_roll(dct, n):
shift_values = deque(dct.values())
shift_values.rotate(n)
return dict(zip(dct.keys(), shift_values))
Demo:
>>> d = {'A': {'A': 1, 'C': 0, 'B': 0}, 'B': {'A': 0, 'C': 0, 'B': 1}}
>>> for k in d:
... d[k] = dict_roll(d[k], 1)
...
>>> d
{'A': {'A': 0, 'C': 1, 'B': 0}, 'B': {'A': 1, 'C': 0, 'B': 0}}
>>> for k in d:
... d[k] = dict_roll(d[k], 1)
...
>>> d
{'A': {'A': 0, 'C': 0, 'B': 1}, 'B': {'A': 0, 'C': 1, 'B': 0}}
And another solution.
Solution 1:
out = dict()
syms = ['A', 'B', 'C']
for sym_outer in syms:
inner_dict = dict()
for sym_inner in syms:
inner_dict[sym_inner] = 0 if sym_inner != sym_outer else 1
out[sym_outer] = inner_dict
Solution 2:
This is a partial solution, but you get out['A']['A'] == 1 and out['A']['B'] == 0.
from collections import defaultdict
out = defaultdict(lambda: defaultdict(int))
for sym in ['A', 'B', 'C']:
out[sym][sym] = 1
This will create your string in r:
import string
r = {}
a = [1] + [0] * 25
for i in string.ascii_uppercase:
r[i] = dict(zip(string.ascii_uppercase,a))
a = a[-1:] + a[:-1]
print r
Bonus one-liner
If you are using Python 2.7+ or Python 3, then you can get a one-liner using dictionary comprehensions:
import string
{i:{j:1 if i==j else 0 for j in string.ascii_uppercase} for i in string.ascii_uppercase}

Categories

Resources