Extract values from class instances inside a dictionary - python

I have this code:
class B():
def __init__(self, valueA, valueB ):
self.valueA = valueA
self.valueB = valueB
def __repr__(self):
return 'B({0},{1})'.format(self.valueA, self.valueB)
My data is :
thedata = {'a': [B(1, 0.1),B(2, 0.2)],
'b': [B(3, 0.3),B(4, 0.4)]}
What I want to do is extract the a and b attributes from above dictionary to 2 new dictionaries according the key.
So, I want :
thedata_a_valueA = {'a':[1, 2]}
thedata_a_valueB = {'a':[0.1, 0.2]}
thedata_b_valueA = {'b':[3, 4]}
thedata_b_valueB = {'b':[0.3, 0.4]}
and finally I want 2 dictionaries:
newdict_valueA = {'a':[1, 2], 'b':[3,4]}
newdict_valueB = {'a':[0.1, 0.2], 'b':[0.3,0.4]}
One solution is to use lists but I must create quite a few lists, loop over them, append etc
Is there any cleaner/faster solution working on thedata ?

Something like this will work:
# Starting from two empty dictionaries
newdict_valueA, newdict_valueB = {}, {}
# Iterate over the key/value pairs of the data
for key, value in thedata.items():
# assign to the same key of each result dictionary a list of
# the valueA for each B item in the value of the original dictionary
newdict_valueA[key] = [item.valueA for item in value]
newdict_valueB[key] = [item.valueB for item in value]
newdict_valueA:
{'a': [1, 2], 'b': [3, 4]}
newdict_valueB:
{'a': [0.1, 0.2], 'b': [0.3, 0.4]}

If I got your question right, you can use comprehensions:
newdict_valueA = { k : [ b.valueA for b in l ] for k, l in thedata.items() }
newdict_valueB = { k : [ b.valueB for b in l ] for k, l in thedata.items() }

You can have all of the expected items in a list for each key in your dictionary. You just need to loop over thedata's items and for each key create a dictionary contain separate items for valueA and valueB. The dict.setdefault() attribute would be nice choice for this task.
In [18]: d = {}
In [19]: for i, j in thedata.items():
for instance in j:
d.setdefault(i, {}).setdefault('valueA', []).append(instance.valueA)
d.setdefault(i, {}).setdefault('valueB', []).append(instance.valueB)
In [20]: d
Out[21]:
{'a': {'valueB': [0.1, 0.2], 'valueA': [1, 2]},
'b': {'valueB': [0.3, 0.4], 'valueA': [3, 4]}}
Note that as a more cleaner way you can use collections.defaultdict() instead of the dict.setdefault() method:
In [33]: d = defaultdict(lambda: defaultdict(list))
In [34]: for i, j in thedata.items():
for instance in j:
d[i]['valueA'].append(instance.valueA)
d[i]['valueB'].append(instance.valueB)
....:
In [35]: d
Out[35]: defaultdict(<function <lambda> at 0x7fb5888786a8>,
{'a': defaultdict(<class 'list'>,
{'valueB': [0.1, 0.2], 'valueA': [1, 2]}),
'b': defaultdict(<class 'list'>,
{'valueB': [0.3, 0.4], 'valueA': [3, 4]})})

Related

Adding to a dictionary based on key and value from lists?

I have a dictionary defined as:
letters = {'a': 2, 'b': 1, 'c': 5}
I want to add values to this dictionary based on two lists: one which contains the keys and another which contains the values.
key_list = [a, c]
value_list = [2, 5]
This should give the output:
{a: 4, b: 1, c: 10}
Any ideas on how I can accomplish this? I am new to working with the dictionary structure so I apologise if this is extremely simple.
Thanks.
You can zip the two lists and then add to the dictionary as so;
letters = {'a': 2, 'b': 1, 'c': 5}
key_list = ['a', 'c']
value_list = [2, 5]
for k,v in zip(key_list, value_list):
letters[k] = letters.get(k, 0) + v
Using the dictionary's get() method as above allows you to add letters that aren't already in the dictionary.
for i in range(len(key_list)):
letters[key_list[i]] += value_list[i]
You can simply add or modify values from a dictionary using the key
For example:
letters = {'a': 2, 'b':1 , 'c': 5}
letters['a'] += 2
letters['c'] += 5
print(letters)
output = {'a': 4, 'b': 1, 'c': 10}

Python Dictionary - How to find all keys with the lowest amount of unique value?

Let's say I have a dictionary:
dict = {'a' : [1, 2], 'b' : [1], 'c' : [3, 3, 3]}
How do I find which keys have the lowest amount of unique values? In this case, it would be the dictionary with the keys 'b' and 'c' as they each only have 1 unique value.
You can create an inverted mapping using collections.defaultdict and access the keys corresponding to the least number of unique values:
from collections import defaultdict
d = {'a' : [1, 2], 'b' : [1], 'c' : [3, 3, 3]}
# dictionary mapping len of unique values to corresponding key
d2 = defaultdict(list)
for k in d:
d2[len(set(d[k]))].append(k)
print (d2)
# defaultdict(list, {2: ['a'], 1: ['b', 'c']})
print (d2[min(d2)])
# ['b', 'c']
You can do the following:
d = {'a' : [1, 2], 'b' : [1], 'c' : [3, 3, 3]} #avoid using 'dict' as name of dictionary
temp={k:len(set(v)) for k, v in d.items()} #get the count of unique values for each key
res=[i for i in temp if temp[i]==min(temp.values())] #get the keys with min value
>>> print(res)
['b', 'c']
Try like this:
d = {'a' : [1, 2], 'b' : [1], 'c' : [3, 3, 3]}
ml={i:len(set(m)) for i,m in d.items()}
res={i:d[i] for i,z in ml.items() if z==min(ml.values())}
print(res)
You can take the following three steps
# Create a dictionary d with format {key: number of unique values}
d = {key: len(set(value)) for key, value in dict.items()}
# Find the minimum value in this dictionary
min_value = min(d.values())
# Find all the keys with value equal to the minimum
min_list = [key for key, value in d.items() if value == min_value]
The result will be a list containing b and c in your example
You could use an auxiliary dict or comprehension as in other answers if you need to do further processing, otherwise IMO no need for anything fancy, a simple for loop should do the trick, using set() conversion as a quick way to count unique values
As a minor aside, it's recommended not to use dict as your dict variable name since it's a reserved keyword in python and it will shadow use of that keyword
def keys_with_fewest_uniq_values(d):
min_num_uniq, min_keys = None, []
for key, val in dict.items():
num_uniq = len(set(val))
if min_num_uniq is None or num_uniq < min_num_uniq:
min_num_uniq = num_uniq
min_keys = [key]
elif num_uniq == min_num_uniq:
min_keys.append(key)
return min_keys
>> my_dict = {'a' : [1, 2], 'b' : [1], 'c' : [3, 3, 3]}
>> print(keys_with_fewest_uniq_values(my_dict))
['b','c']

Divide list of values in dictionary by list of values in another dictionary

I'm trying to take a dictionary with list values and divide each element in that list by the corresponding element in another dictionary list values.
For example, if you have these two dictionaries where the keys of the dictionaries do not match,
dict1={"A": [1,2,3], "B": [4,5,6], "C":[7,8,9]}
dict2={"D": [10,20,30], "B":[40,50,60], "C":[70,80,90]}
I'd like to iterate through the list elements and divide the elements of the list, such that the output is something like this,
new_dict={"A": [1/10, 2/20, 3/30], "B":[4/40, 5/50, 6/60], "C": [7/70, 8/80, 9/90]}
I've tried something like this, but get am getting held up with figuring out how to get into the lists.
new_dict={}
for key, value in dict1:
new_dict={key: [i/j for i, j in zip(value, dict2.values()]}
new_dict
Thank you so much for any and all help!
You can use a dict comprehension to build the resulting dictionary, and a list comprehension to build the values of that dictionary:
>>> dict1
{'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}
>>> dict2
{'A': [10, 20, 30], 'B': [40, 50, 60], 'C': [70, 80, 90]}
>>> res = {key: [x1/x2 for (x1,x2) in zip(dict1[key],dict2[key])] for key in dict2}
>>> res
{'A': [0.1, 0.1, 0.1], 'B': [0.1, 0.1, 0.1], 'C': [0.1, 0.1, 0.1]}
If they keys don't match and you are going only off the ordering of the keys (which I don't recommend), then you can zip the keys of the first dictionary and the values of the second, then operate on that:
>>> dict1={"A": [1,2,3], "B": [4,5,6], "C":[7,8,9]}
>>> dict2={"D": [10,20,30], "B":[40,50,60], "C":[70,80,90]}
>>> res = {k: [x1/x2 for (x1,x2) in zip(dict1[k],v2)] for (k,v2) in zip(
... dict1.keys(), dict2.values())}
>>> res
{'A': [0.1, 0.1, 0.1], 'B': [0.1, 0.1, 0.1], 'C': [0.1, 0.1, 0.1]}
Dictionary comprehension is what you are looking for:
dict1={"A": [1,2,3], "B": [4,5,6], "C":[7,8,9]}
dict2={"A": [10,20,30], "B":[40,50,60], "C":[70,80,90]}
new_dict={key: [i/j for i, j in zip(dict1[key], dict2[key])] for key in dict1}
Output:
{'A': [0.1, 0.1, 0.1], 'B': [0.1, 0.1, 0.1], 'C': [0.1, 0.1, 0.1]}

Calculate average of list values grouped by second list

I didn't know how to better express myself in the title. Basically what I have is two lists:
a = ['A','B','A','C','D','C','A',...]
b = [2,4,8,3,5,2,1,...]
a and b have the same length, b represents a value related to the letter in a .
Now I would like to calculate the Average value in b for each letter in a. So at the end I would have:
a = ['A','B','C','D',...]
b = [3.67, 4, 2.5, 5,...]
Is there a standard implementation for this in python?
You can first perform a group by. We can do this for instance with a defaultdict:
from collections import defaultdict
col = defaultdict(list)
for ai,bi in zip(a,b):
col[ai].append(bi)
Now the dictionary col will look like:
>>> col
defaultdict(<class 'list'>, {'C': [3, 2], 'B': [4], 'D': [5], 'A': [2, 8, 1]})
and now we can calculate the average of all elements in the dictionary for instance like:
>>> {key:sum(vals)/len(vals) for key,vals in col.items()}
{'C': 2.5, 'B': 4.0, 'D': 5.0, 'A': 3.6666666666666665}
You can also convert it to two tuples by using zip:
a,b = zip(*[(key,sum(vals)/len(vals)) for key,vals in col.items()])
resulting in:
>>> a,b = zip(*[(key,sum(vals)/len(vals)) for key,vals in col.items()])
>>> a
('C', 'B', 'D', 'A')
>>> b
(2.5, 4.0, 5.0, 3.6666666666666665)
If you want to generate lists instead, you can convert them to lists:
a,b = map(list,zip(*[(key,sum(vals)/len(vals)) for key,vals in col.items()]))
This results in:
>>> a,b = map(list,zip(*[(key,sum(vals)/len(vals)) for key,vals in col.items()]))
>>> a
['C', 'B', 'D', 'A']
>>> b
[2.5, 4.0, 5.0, 3.6666666666666665]
I believe a cleaner way to do this would be to simply use a pandas groupby:
import pandas as pd
data = pd.DataFrame(b,index=a)
a,b = (list(data.groupby(data.index)[0].mean().index),list(data.groupby(data.index)[0].mean()))
You can use numpy as follows:
>>> import numpy as np
>>> array_a = np.array(a)
>>> array_b = np.array(b)
>>> avrg_of_a = np.average(array_b[array_a == 'A'])
>>> avrg_of_a
3.6666666666666665
>>> avrg_of_b = np.average(array_b[array_a == 'B'])
4.0
You can generate a list use list comprehensions [np.average(array_b[array_a == item]) for item in np.unique(array_a)]

Is there any pythonic way to combine two dicts (making a list for common values)?

For example, I have two dicts.
A = {'a':1, 'b':10, 'c':2}
B = {'b':3, 'c':4, 'd':10}
I want a result like this:
{'a':1, 'b': [10, 3], 'c':[2, 4], 'd':10}
If a key appears in both the dicts, I want to list of both the values.
I'd make all values lists:
{k: filter(None, [A.get(k), B.get(k)]) for k in A.viewkeys() | B}
using dictionary view objects.
Demo:
>>> A = {'a':1, 'b':10, 'c':2}
>>> B = {'b':3, 'c':4, 'd':10}
>>> {k: filter(None, [A.get(k), B.get(k)]) for k in A.viewkeys() | B}
{'a': [1], 'c': [2, 4], 'b': [10, 3], 'd': [10]}
This at least keeps your value types consistent.
To produce your output, you need to use the set intersection and symmetric differences between the two dictionaries:
dict({k: [A[k], B[k]] for k in A.viewkeys() & B},
**{k: A.get(k, B.get(k)) for k in A.viewkeys() ^ B})
Demo:
>>> dict({k: [A[k], B[k]] for k in A.viewkeys() & B},
... **{k: A.get(k, B.get(k)) for k in A.viewkeys() ^ B})
{'a': 1, 'c': [2, 4], 'b': [10, 3], 'd': 10}
In Python 3, dict.keys() is a dictionary view, so you can just replace all .viewkeys() calls with .keys() to get the same functionality there.
I would second the notion of Martijn Pieters that you problably want to have the same type for all the values in your result dict.
To give a second option:
you could also use the defaultdict to achieve your result quite intuitively.
a defaultdict is like a dict, but it has a default constructor that is called if the key doesn't exist yet.
so you would go:
from collections import defaultdict
A = {'a':1, 'b':10, 'c':2}
B = {'b':3, 'c':4, 'd':10}
result = defaultdict(list)
for d in [A, B]:
for k, v in d.items():
result[k].append(v)
then in a later stage you still easily add more values to your result.
you can also switch to
defaultdict(set)
if you don't want duplicate values

Categories