How to take a linear combination of several dictionaries in Python? - python

Here's some code to take a linear combination of two dictionaries:
def linearcombination(a1,d1,a2,d2):
return {k:a1*d1.get(k,0)+a2*d2.get(k,0) for k in {**d1,**d2}.keys()}
choosy1={"a":1,"b":2,"c":3}
choosy2={"a":1,"d":1}
choosy=linearcombination(1,choosy1,10,choosy2)
choosy is:
{'a': 11, 'c': 3, 'd': 10, 'b': 2}
How can I generalise it to allow linear combinations of arbitrary numbers of dictionaries?

Solution using sum in a dict comprehension over a set of keys:
from itertools import chain
def linear_combination_of_dicts(dicts, weights):
return {
k: sum( w * d.get(k, 0) for d, w in zip(dicts, weights) )
for k in set(chain.from_iterable(dicts))
}
Example:
>>> dicts = [{'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'd': 1}]
>>> weights = [1, 10]
>>> linear_combination_of_dicts(dicts, weights)
{'c': 3, 'd': 10, 'a': 11, 'b': 2}

Here's an approach with pandas to handle dict key alignment:
def lc(coeffs, dicts):
return (pd.concat(pd.Series(d).fillna(0)*a for a,d in zip(coeffs,dicts))
.sum(level=0)
.to_dict()
)
lc([1,10], [choosy1, choosy2])
# {'a': 11, 'b': 2, 'c': 3, 'd': 10}

Related

Python Find permutable list in a dict list

Given
listOfDict = [{'ref': 1, 'a': 1, 'b': 2, 'c': 3},
{'ref': 2, 'a': 4, 'b': 5, 'c': 6},
{'ref': 3, 'a': 7, 'b': 8, 'c': 9}]
Lets' consider a list of permutable integer
[7,8,9]=[7,9,8]=[8,7,9]=[8,9,7]=[9,7,8]=[9,8,7] # (3!)
Each of this list has a unique mapping ref, so how given for (8,7,9) can I get ref=3 ?
Also in real case I might until 10 (a,b,c,d,e,f,g,h,i,j)...
You can generate a dictionary that maps the values as frozenset to the value of ref:
listOfDict = [{'ref': 1, 'a': 1, 'b': 2, 'c': 3},
{'ref': 2, 'a': 4, 'b': 5, 'c': 6},
{'ref': 3, 'a': 7, 'b': 8, 'c': 9}]
keys = ['a', 'b', 'c']
out = {frozenset(d[k] for k in keys): d['ref'] for d in listOfDict}
# {frozenset({1, 2, 3}): 1,
# frozenset({4, 5, 6}): 2,
# frozenset({7, 8, 9}): 3}
example:
check = frozenset((8,7,9))
out[check]
# 3
but I don't know in advance the name of the other keys!
Then use this approach:
out = {}
for d in listOfDict:
d2 = d.copy() # this is to avoid modifying the original object
out[frozenset(d2.values())] = d2.pop('ref')
out
or as a comprehension:
out = dict(((d2:=d.copy()).pop('ref'), frozenset(d2.values()))[::-1]
for d in listOfDict)
Here is a commented solution to your problem. The idea is to compare the sorted list of the values in a, b, c etc with the sorted values in list_of_ints. The sorted values will be the same for all permutations of a given set of numbers.
def get_ref(list_of_ints):
# Loop through dictionaries in listOfDict.
for dictionary in listOfDict:
# Get list of values in each dictionary.
vals = [dictionary[key] for key in dictionary if key != "ref"]
if sorted(vals) == sorted(list_of_ints):
# If sorted values are equal to sorted list of ints, return ref.
return dictionary["ref"])
By the way, I believe it would be cleaner to structure this data as a dict of dicts in the following way:
dicts = {
1: {'a': 1, 'b': 2, 'c': 3},
2: {'a': 4, 'b': 5, 'c': 6},
3: {'a': 7, 'b': 8, 'c': 9}
}
The code would then be:
def get_ref(list_of_ints):
for ref, dictionary in dicts.items():
if sorted(dictionary.values()) == sorted(list_of_ints):
return ref
Assuming that all integers in the permutations are unique, the code can be simplified further using sets instead of sorted lists.
Since its a list of dict I can call each dict as it self by using for loop
and record the first number on ref
for i in listOfDict:
ref_num=i["ref"]
and to turn dictunary to list we simply use:
z=list(i.values())
then the last step is to find if its the same input list if so we print/return the ref number
if z[1:]==InputList:
return ref_num
and the code should be like this:
listOfDict = [
{"ref": 1,
"a": 1,
"b": 2,
"c": 3},
{"ref": 2,
"a": 4,
"b": 5,
"c": 6},
{"ref": 3,
"a": 7,
"b": 8,
"c": 9},]
def find_ref_Num(InputList):
for i in listOfDict:
ref_num=i["ref"]
z=list(i.values())
if z[1:]==InputList:
return ref_num
print ("your ref number is: "+str(find_ref_Num([7,8,9])))

Python: how to create a parameter grid with dynamic number of parameters

Suppose that the range of parameters of interest are given a dictionary that contains the range for each parameter of interest:
G = {'a': [1,2], 'b': [3], 'c': [1, 2.5] }
The goal is to extract every parameter configuration on this grid. In the example above, there are 4 such, corresponding to 2 values of a, and two values of b:
G1 = {'a': 1, 'b': 3, 'c': 1 }
G2 = {'a': 2, 'b': 3, 'c': 1 }
G3 = {'a': 1, 'b': 3, 'c': 2.5 }
G4 = {'a': 2, 'b': 3, 'c': 2.5 }
It's straightforward to write two nested for loops to produce all such configurations, it becomes less trivial how to do it for a general case, when there are a variable number of lists in G.
The only solution that comes to my mind is to create a multi-index vector vec=[0,0] which is as long as the number of parameters, and increment to iterate over all possible configurations: [0,0] -> [1,0] -> [0,1] -> [1,1]:
G = {'a': [1,2], 'b': [3], 'c': [1, 2.5] }
def get_configs(G):
keys = list(G.keys())
lists = list(G.values())
sizes = [len(l) for l in lists]
num_confs = np.prod(sizes)
index = [0]*(len(G)+1)
configs = []
while len(configs)<num_confs:
configs.append( {keys[i]: lists[i][index[i]] for i in range(len(G))})
index[0] += 1
cur = 0
while len(configs)<num_confs and index[cur]>=sizes[cur]:
index[cur]=0
cur += 1
index[cur] += 1
return configs
configs = get_configs(G)
print(configs)
However, the solution seems a bit over-complicated and ugly. Is there a clean solution using python?
Here is a generalizable implementation using itertools.product:
from itertools import product
def dict_configs(d):
for vcomb in product(*d.values()):
yield dict(zip(d.keys(), vcomb))
Usage:
>>> G = {'a': [1,2], 'b': [3], 'c': [1, 2.5] }
>>> for config in dict_configs(G):
... print(config)
...
{'a': 1, 'b': 3, 'c': 1}
{'a': 1, 'b': 3, 'c': 2.5}
{'a': 2, 'b': 3, 'c': 1}
{'a': 2, 'b': 3, 'c': 2.5}

Nesting dictionary algorithm

Suppose I have the following dictionary:
{'a': 0, 'b': 1, 'c': 2, 'c.1': 3, 'd': 4, 'd.1': 5, 'd.1.2': 6}
I wish to write an algorithm which outputs the following:
{
"a": 0,
"b": 1,
"c": {
"c": 2,
"c.1": 3
},
"d":{
"d": 4,
"d.1": {
"d.1": 5,
"d.1.2": 6
}
}
}
Note how the names are repeated inside the dictionary. And some have variable level of nesting (eg. "d").
I was wondering how you would go about doing this, or if there is a python library for this? I know you'd have to use recursion for something like this, but my recursion skills are quite poor. Any thoughts would be highly appreciated.
You can use a recursive function for this or just a loop. The tricky part is wrapping existing values into dictionaries if further child nodes have to be added below them.
def nested(d):
res = {}
for key, val in d.items():
t = res
# descend deeper into the nested dict
for x in [key[:i] for i, c in enumerate(key) if c == "."]:
if x in t and not isinstance(t[x], dict):
# wrap leaf value into another dict
t[x] = {x: t[x]}
t = t.setdefault(x, {})
# add actual key to nested dict
if key in t:
# already exists, go one level deeper
t[key][key] = val
else:
t[key] = val
return res
Your example:
d = {'a': 0, 'b': 1, 'c': 2, 'c.1': 3, 'd': 4, 'd.1': 5, 'd.1.2': 6}
print(nested(d))
# {'a': 0,
# 'b': 1,
# 'c': {'c': 2, 'c.1': 3},
# 'd': {'d': 4, 'd.1': {'d.1': 5, 'd.1.2': 6}}}
Nesting dictionary algorithm ...
how you would go about doing this,
sort the dictionary items
group the result by index 0 of the keys (first item in the tuples)
iterate over the groups
if there are is than one item in a group make a key for the group and add the group items as the values.
Slightly shorter recursion approach with collections.defaultdict:
from collections import defaultdict
data = {'a': 0, 'b': 1, 'c': 2, 'c.1': 3, 'd': 4, 'd.1': 5, 'd.1.2': 6}
def group(d, p = []):
_d, r = defaultdict(list), {}
for n, [a, *b], c in d:
_d[a].append((n, b, c))
for a, b in _d.items():
if (k:=[i for i in b if i[1]]):
r['.'.join(p+[a])] = {**{i[0]:i[-1] for i in b if not i[1]}, **group(k, p+[a])}
else:
r[b[0][0]] = b[0][-1]
return r
print(group([(a, a.split('.'), b) for a, b in data.items()]))
Output:
{'a': 0, 'b': 1, 'c': {'c': 2, 'c.1': 3}, 'd': {'d': 4, 'd.1': {'d.1': 5, 'd.1.2': 6}}}

Add two dictionaries in python and subtract result from another

I have three dictionaries:
X = {'a':2, 'b':3,'e':4}
Y = {'c':3, 'b':4,'a':5, 'd':7}
Z = {'c':8, 'b':7,'a':9, 'e':10,'f':10}
I want to add elements of X and Y if they are present in both dicts and then subtract them from z i.e. Z-X+Y
How can I do that ?
expected result:
res = {'a':2,'b':0,'c':5,'d':7,'e':6,'f':10}
What I tried:
from collections import Counter
xy = Counter(X) + Counter(Y)
res = Counter(Z) - xy
which return:
Counter({'c': 5, 'a': 2, 'e': 6, 'f': 10})
as you can see b and d are missing from my attempt
Your expected result is actually an operation of symmetric difference in terms of sets, but since collections.Counter doesn't support such an operation, you can emulate it with:
xy = Counter(X) + Counter(Y)
z = Counter(Z)
res = z - xy | xy - z
res becomes:
Counter({'f': 10, 'd': 7, 'e': 6, 'c': 5, 'a': 2})
But if you do want keys with value of 0, which Counter would hide from its output, you would have to iterate through a union of the keys of the 3 dicts:
{k: res.get(k, 0) for k in {*X, *Y, *Z}}
This returns:
{'a': 2, 'd': 7, 'e': 6, 'b': 0, 'f': 10, 'c': 5}

Python set an item value based other items in a dictionary

A = {0:{a:1, b:7}, 1:{a:5,b:5}, 2:{a:4,b:6}}
I want to attach an item guess to each sub dictionary based on the value b accounting of all b's in each sub dictionary.
Saying, in Dictionary A:
0-b-7 percentage of b: 7/(7+5+6)
1-b-5 percentage of b: 5/(7+5+6)
2-b-6 percentage of b: 1 - 7/(7+5+6) - 5/(7+5+6)
The desired Dictionary should be like
A = {0:{a:1, b:7, 'guess': 7/(7+5+6)},
1:{a:5,b:5, 'guess': 5/(7+5+6)},
2:{a:4,b:6, 'guess': 1 - 7/(7+5+6) - 5/(7+5+6)}}
I don't know how to incorporate the other two b's for a specific subdictionary.
One approach is to precompute the sum of all bs and then use it to add a new key-value pair to your dictionary.
b_total = float(sum(A[k]['b'] for k in A))
for k in A:
A[k]['guess'] = A[k]['b'] / b_total
#{0: {'a': 1, 'b': 7, 'guess': 0.3888888888888889},
# 1: {'a': 5, 'b': 5, 'guess': 0.2777777777777778},
# 2: {'a': 4, 'b': 6, 'guess': 0.3333333333333333}}
A = {0:{"a":1, "b":7}, 1:{"a":5,"b":5}, 2:{"a":4,"b":6}}
char = "b"
denominator = 0
# =========================
# First Calculate the sum
# =========================
for key in A:
inner_map = A[key]
denominator += inner_map[char]
# ========================================
# Now insert the new key to the inner_map
# ========================================
for key in A:
inner_map = A[key]
inner_map["guess"] = inner_map[char]/denominator
print(A)
Output:
{0: {'a': 1, 'b': 7, 'guess': 0.3888888888888889}, 1: {'a': 5, 'b': 5, 'guess': 0.2777777777777778}, 2: {'a': 4, 'b': 6, 'guess': 0.3333333333333333}}
Try this:
def add_calc(my_dict):
total_guesses = sum(map(lambda x: my_dict.get(x).get('b'), my_dict))
for item in my_dict.itervalues():
item.update({'guess': 1.0 * item.get('b') / total_guesses})
return my_dict
d = add_calc(A)
{0: {'a': 1, 'b': 7, 'guess': 0.3888888888888889},
1: {'a': 5, 'b': 5, 'guess': 0.2777777777777778},
2: {'a': 4, 'b': 6, 'guess': 0.3333333333333333}}
I'm on Python 2 btw, you didn't specify version
You can use dictionary unpacking:
A = {0:{'a':1, 'b':7}, 1:{'a':5, 'b':5}, 2:{'a':4, 'b':6}}
results = {a:{**b, **{'guess':b['b']/float(sum(c['b'] for _, c in A.items()))}} for a, b in A.items()}
Output:
{0: {'guess': 0.3888888888888889, 'b': 7, 'a': 1}, 1: {'guess': 0.2777777777777778, 'b': 5, 'a': 5}, 2: {'guess': 0.3333333333333333, 'b': 6, 'a': 4}}

Categories