In Python merge two dictionaries so that their keys are added/subtracted - python

I've two dictionaries, output of factorint from sympy.ntheory. I need to merge them so that the common keys gets their values summed up, i.e. MergedDict[key] = Dict1[key] + Dict2[key], while unique keys remain same.
Also I need to get a merged dictionary with the common keys being differenced, i.e. MergedDict[key] = Dict1[key] - Dict2[key]. Here Dict2 keys will be always a subset of Dict1 keys, so no problem of negative numbers.
I've tried to follow this question. But I'm unable to make it work. So far my approach has been as follows:
from sympy.ntheory import factorint
from collections import defaultdict
d=factorint(12)
dd = defaultdict(lambda: defaultdict(int))
for key, values_dict in d.items():
for date, integer in values_dict.items():
dd[key] += integer
for n in range(2,6):
u = factorint(n)
for key, values_dict in u.items():
for date, integer in values_dict.items():
dd[key] += integer
It gives the error AttributeError: 'int' object has no attribute 'items'. The code above in only for the summing up part. Yet to do anything on the differencing part, assuming that summing up can be changed to work for differencing in case of common keys.

Not sure what you goal is but factorint gives you key/value pairs of ints so you should be summing the values, you are trying to call items on each val from the dict which is an integer and obviously not going to work:
from sympy.ntheory import factorint
from collections import defaultdict
d=factorint(12)
dd = defaultdict(int)
for key, val in d.items():
dd[key] += val
for n in range(2, 6):
u = factorint(n)
for key, val in u.items():
dd[key] += val
print(dd)
Output:
defaultdict(<type 'int'>, {2: 5, 3: 2, 5: 1})
factorint being a dict cannot have duplicate keys so the first loop cann be done using update:
d = factorint(12)
dd = defaultdict(int)
dd.update(d)
for n in range(2, 6):
u = factorint(n)
for key, val in u.items():
dd[key] += val

It seems that collections.Counter can do most of what you want. It might be as simple as (untested, I do not have sympy installed):
from collections import Counter
cnt1 = Counter(Dict1)
cnt2 = Counter(Dict2)
sum_cnt = cnt1 + cnt2
diff_cnt = cnt1 - cnt2

Related

How do I remove all the nonunique key value pairs after inverting a dictionary?

So I have a dictionary that contains nonunique key value pairs, I want to return a new dictionary where the key value pairs are reversed and all the non unique pair are removed from the new dictionary without modifying the original dictionary.
pls help :(
Input:
>>> invert({'one':1, 'two':2, 'uno':1, 'dos':2, 'three':3, 'un':1})
Expected result {3: 'three'}
This is my attempt at solving the problem:
inverted_dict = {}
for key,value in d.items(): #iterate throught a dict
nonu = value
if nonu in inverted_dict:
del inverted_dict[nonu]
else:
inverted_dict[value] = key
return inverted_dict
It returns: {3: 'three', 1: 'un'}
You need a way to keep track of keys you've seen other that the inverted dict because when you delete those keys from the dict, it is not longer effective. This has the effect of only saving keys with odd number of occurrences. Here's one way using a set to keep track of what you've seen:
def invert(d):
seen = set()
ret = {}
for k, v in d.items():
if v not in seen:
seen.add(v)
ret[v] = k
elif v in ret:
del ret[v]
return ret
invert({'one':1, 'two':2, 'uno':1, 'dos':2, 'three':3, 'un':1})
# {3: 'three'}
Here's how you might do it by counting using Python's built-in Collections.Counter:
from collections import Counter
def invert(d):
counts = Counter(d.values())
return {v:k for k, v in d.items() if counts[v] == 1}
invert({'one':1, 'two':2, 'uno':1, 'dos':2, 'three':3, 'un':1})
# {3: 'three'}

Add values of same key in a dictionary

My input is a list and I want to convert it into a dictionary and add all values for the same keys. like in the give example in a random list k has two values 1 and 3 so value of k in dictionary will be {'k':4,'D':2}. And then sort it in alphabetic order
Input: ['k:1','D:2','k:3']
Output {'k':4,'D':2}
dlist = ['k:1','D:2','k:3']
dick ={}
for x in dlist:
key,value = x.split(':')
dick[key] = int(value)
print(dick)
I have the above code but I don't know how to add two values for k?
You need to actually add the value to the pre-existing value. In your code now you just overwrite the old value with the new one.
dick = {}
for x in dlist:
key, value = x.split(':')
dick[key] = dick.get(key, 0) + int(value)
dict.get(key, 0) gets the value of the key in the dictionary, with a default of zero
This is possible by making dick a defaultdict. So, now you can just += the value.
from collections import defaultdict
dlist = ['k:1','D:2','k:3']
dick = defaultdict(int)
for x in dlist:
key, value = x.split(':')
dick[key] += int(value)
print(dick)
You can use groupby from itertools to group same values and then sum.
import itertools
a=['k:1','D:2','k:3']
print({k:sum([int(j[1]) for j in v]) for k,v in itertools.groupby([i.split(":") for i in sorted(a)], lambda x: x[0])})
Output
{'D': 2, 'k': 4}

Adding nonzero items from a dictionary to another dictionary

I have a set of reactions (keys) with values (0.0 or 100) stored in mydict.
Now I want to place non zero values in a new dictionary (nonzerodict).
def nonzero(cmod):
mydict = cmod.getReactionValues()
nonzerodict = {}
for key in mydict:
if mydict.values() != float(0):
nonzerodict[nz] = mydict.values
print nz
Unfortunately this is not working.
My questions:
Am I iterating over a dictionary correctly?
Am I adding items to the new dictionary correctly?
You are testing if the list of values is not equal to float(0). Test each value instead, using the key to retrieve it:
if mydict[key] != 0:
nonzerodict[key] = mydict[key]
You are iterating over the keys correctly, but you could also iterate over the key-value pairs:
for key, value in mydict.iteritems():
if value != 0:
nonzerodict[key] = value
Note that with floating point values, chances are you'll have very small values, close to zero, that you may want to filter out too. If so, test if the value is close to zero instead:
if abs(value) > 1e-9:
You can do the whole thing in a single dictionary expression:
def nonzero(cmod):
return {k: v for k, v in cmod.getReactionValues().iteritems() if abs(v) > 1e-9}
Its simple and you can it by below way -
>>> d = {'a':4,'b':2, 'c':0}
>>> dict((k,v) for k,v in d.iteritems() if v!=0)
{'a': 4, 'b': 2}
>>>
Replace if condition in you code with:
if mydict[key]:
nonzerodict[key] = mydict[key]
Your solution can be further simplified as:
def nonzero(cmod):
mydict = cmod.getReactionValues()
nonzerodict = {key: value for key, value in mydict.iteritems() if value}

How to quickly get a list of keys from dict

I construct a dictionary from an excel sheet and end up with something like:
d = {('a','b','c'): val1, ('a','d'): val2}
The tuples I use as keys contain a handful of values, the goal is to get a list of these values which occur more than a certain number of times.
I've tried two solutions, both of which take entirely too long.
Attempt 1, simple list comprehension filter:
keyList = []
for k in d.keys():
keyList.extend(list(k))
# The script makes it to here before hanging
commonkeylist = [key for key in keyList if keyList.count(key) > 5]
This takes forever since list.count() traverses the least on each iteration of the comprehension.
Attempt 2, create a count dictionary
keyList = []
keydict = {}
for k in d.keys():
keyList.extend(list(k))
# The script makes it to here before hanging
for k in keyList:
if k in keydict.keys():
keydict[k] += 1
else:
keydict[k] = 1
commonkeylist = [k for k in keyList if keydict[k] > 50]
I thought this would be faster since we only traverse all of keyList a handful of times, but it still hangs the script.
What other steps can I take to improve the efficiency of this operation?
Use collections.Counter() and a generator expression:
from collections import Counter
counts = Counter(item for key in d for item in key)
commonkkeylist = [item for item, count in counts.most_common() if count > 50]
where iterating over the dictionary directly yields the keys without creating an intermediary list object.
Demo with a lower count filter:
>>> from collections import Counter
>>> d = {('a','b','c'): 'val1', ('a','d'): 'val2'}
>>> counts = Counter(item for key in d for item in key)
>>> counts
Counter({'a': 2, 'c': 1, 'b': 1, 'd': 1})
>>> [item for item, count in counts.most_common() if count > 1]
['a']
I thought this would be faster since we only traverse all of keyList a
handful of times, but it still hangs the script.
That's because you're still doing an O(n) search. Replace this:
for k in keyList:
if k in keydict.keys():
with this:
for k in keyList:
if k in keydict:
and see if that helps your 2nd attempt perform better.

Remove the smallest element(s) from a dictionary

I have a function such that there is a dictionary as parameters, with the value associated to be an integer. I'm trying to remove the minimum element(s) and return a set of the remaining keys.
I am programming in python. I cant seem to remove key value pairs with the same key or values. My code does not work for the 2nd and 3rd example
This is how it would work:
remaining({A: 1, B: 2, C: 2})
{B, C}
remaining({B: 2, C : 2})
{}
remaining({A: 1, B: 1, C: 1, D: 4})
{D}
This is what I have:
def remaining(d : {str:int}) -> {str}:
Remaining = set(d)
Remaining.remove(min(d, key=d.get))
return Remaining
One approach is to take the minimum value, then build a list of keys that are equal to it and utilise dict.viewkeys() which has set-like behaviour and remove the keys matching the minimum value from it.
d = {'A': 1, 'B': 1, 'C': 1, 'D': 4}
# Use .values() and .keys() and .items() for Python 3.x
min_val = min(d.itervalues())
remaining = d.viewkeys() - (k for k, v in d.iteritems() if v == min_val)
# set(['D'])
On a side note, I find it odd that {B: 2, C : 2} should be {} as there's not actually anything greater for those to be the minimum as it were.
That's because you're trying to map values to keys and map allows different keys to have the same values but not the other way! you should implement a map "reversal" as described here, remove the minimum key, and then reverse the map back to its original form.
from collections import defaultdict
# your example
l = {'A': 1, 'B': 1, 'C': 1, 'D': 4}
# reverse the dict
d1 = {}
for k, v in l.iteritems():
d1[v] = d1.get(v, []) + [k]
# remove the min element
del d1[min(d1, key=d1.get)]
#recover the rest to the original dict minus the min
res = {}
for k, v in d1.iteritems():
for e in v:
res[e] = k
print res
Comment:
#Jon Clements's solution is more elegant and should be accepted as the answer
Take the minimum value and construct a set with all the keys which are not associated to that value:
def remaining(d):
m = min(d.values())
return {k for k,v in d.items() if v != m}
If you don't like set comprehensions that's the same as:
def remaining(d):
m = min(d.values())
s = set()
for k,v in d.items():
if v != m:
s.add(k)
return s
This removes all the items with the minimum value.
import copy
def remaining(dic):
minimum = min([i for i in dic.values()])
for k, v in copy.copy(dic.items()):
if v == minimum: dic.pop(k)
return set(dic.keys())
An easier way would be to use pd.Series.idxmin() or pd.Series.min(). These functions allow you to find the index of the minimum value or the minimum value in a series, plus pandas allows you to create a named index.
import pandas as pd
import numpy as np
A = pd.Series(np.full(shape=5,fill_value=0))#create series of 0
A = A.reindex(['a','b','c','d','e'])#set index, similar to dictionary names
A['a'] = 2
print(A.max())
#output 2.0
print(A.idxmax())#you can also pop by index without changing other indices
#output a

Categories

Resources