Summing values in dictionary who stores list of tuples - python

I have a list of tuples named lista and values in this list looks like:
[('A', 1234),('A', 9876),('B',6574),('B',9562), etc]
Next I create defaultdict(list) where I store my tuple and I get:
([('A', [1234, 9876]),('B',[6547.9562]), etc])
To create this I wrote:
//list
lista = []
for w in data:
if self.getAmountOfProceededInSpecificYear(w.status,w.year,w.district):
lista.append(tuple((w.district,w.amount)))
//dict
passed_dict = defaultdict(list)
for k,v in lista:
passed_dict[k].append(v)
Now I want to sum up values for each key and get:
'A', 11110
Does anobody know how to sum up this values?

You can use defaultdict(int) instead of defaultdict(list):
lista = [('A', 1234),('A', 9876),('B',6574),('B',9562)]
passed_dict = defaultdict(int)
for k,v in lista:
passed_dict[k] += v
passed_dict
defaultdict(int, {'A': 11110, 'B': 16136})

You can use itemgetter and groupby as suggested here
If you want a list as an output
from itertools import groupby
from operator import itemgetter
lista = [('A', 1234),('A', 9876),('B',6574),('B',9562)]
passed_dict = [(k, sum(list(zip(*v))[1])) for k, v in groupby(lista, itemgetter(0))]
# [('A', 11110), ('B', 16136)]
If you want a dictionary as an output
passed_dict = {k: sum(list(zip(*v))[1]) for k, v in groupby(lista, itemgetter(0))}
# {'A': 11110, 'B': 16136}

Related

How to replace items in list with a keys from dictionary in Python

I have a dictionary
my_dict = {"a":1, "b":2, "c":3}
And list
my_list = [2,3,1]
I want to replace items in my_list with keys from my_dict, something like...
my_list = [b, c, a]
How can i achieve this?
I'm sure it's possible to manufacture a list comprehension but this could be one approach (which only ever iterates over the list once and allows you to cover potential edge cases inside the loop):
for key, value in my_dict.items():
if value not in my_list:
# Does this case need special handling?
continue
index = my_list.index(value)
my_list[index] = key
There are a few edge cases to consider here, e.g. what happens if not all items match, what if the dictionary and list are of unequal lengths etc. Depending on your use case you want to make sure to cover all possible edge cases accordingly.
Applied to your example code, it yields the following output:
>>> my_dict = {"a":1, "b":2, "c":3}
>>> my_list = [2,3,1]
>>> my_dict
{'a': 1, 'b': 2, 'c': 3}
>>> my_list
[2, 3, 1]
>>> for key, value in my_dict.items():
... if value not in my_list:
... # Does this case need special handling?
... continue
... index = my_list.index(value)
... my_list[index] = key
>>> my_list
['b', 'c', 'a']
Dictionaries are mappings. You want to use reverse mappings (use values to find keys), so let's reverse the dict.
my_dict = {"a":1, "b":2, "c":3}
reversed_dict = {my_dict[k]:k for k in my_dict}
Then we just apply the dict to each element:
my_list = [2,3,1]
result = [reversed_dict[elem] for elem in my_list]
You can reverse the key value pair of your dict and then iterate your list to get the corresponding keys.
>>> rev_dict = dict((v,k) for k,v in my_dict.items())
>>> rev_dict
{1: 'a', 2: 'b', 3: 'c'}
>>> [rev_dict[x] for x in my_list]
['b', 'c', 'a']
rev = { v:k for k,v in my_dict.items()}
new_list = [rev[item] for item in my_list]
Output new_list:
['b', 'c', 'a']
This will probably work
for key, val in my_dict.items():
for i, v in enumerate(my_list):
if v == val:
my_list[i] = key
You can do this with list comprehension, what you need to do is: sort the dict.items according to your list, then return the key:
>>> my_dict = {"a":1, "b":2, "c":3}
>>> my_list = [2,3,1]
>>> [key for key, value in sorted(my_dict.items(), key = lambda x:my_list.index(x[1]))]
['b', 'c', 'a']
You can use the function itemgetter. First, you need to swap keys and values in your dictionary:
from operator import itemgetter
dct = {"a": 1, "b": 2, "c": 3}
lst = [2, 3, 1]
dct = {v: k for k, v in dct.items()}
# {1: 'a', 2: 'b', 3: 'c'}
print(list(itemgetter(*lst)(dct)))
# ['b', 'c', 'a']

Python reduce sum tuple

I have an input that will vary in size.
data = [(("101","A"),5), (("105","C"),12), (("101", "B"),4)]
Looking for an output that groups by key[0], keeps all items of key[1]. And, sums up the values.
output = [(("101", "A", "B"),9), (("105", "C"),12)]
I've tried.
my_dict = dict(data)
final_values = {}
for k,v in my_dict.items():
key1 = k[0]
key2 = k[1]
if key1 not in final_values:
final_values[key1] = []
final_values[key1].append(key2)
final_values[key1].append(v)
Which returns.
{'101': ['A', 5, 'B', 4], '105': ['C', 12]}
I'd like to get the sum of the numbers in the list.
for k in final_values:
print '%s: sum is %d' % (k, sum([x for x in final_values[k] if type(x) is int]))
You can try using a collections.defaultdict() to group the items, then flattening the results at the end:
from collections import defaultdict
from operator import itemgetter
data = [(("101","A"),5), (("105","C"),12), (("101", "B"),4)]
d = defaultdict(list)
for (x, y), z in data:
d[x].append((y, z))
result = [
((k, *tuple(map(itemgetter(0), v))), sum(map(itemgetter(1), v)))
for k, v in d.items()
]
print(result)
# [(('101', 'A', 'B'), 9), (('105', 'C'), 12)]

Python: Possible combinations for items in a list of lists

Suppose I have a nested list that looks like this:
nested_list = [[a,b,1],[c,d,3],[a,f,8],[a,c,5]]
Now I want to get all possible combinations between the second items of the lists, but only if the first items in both lists are equal. So what should be printed is the following:
Combinations for 'a' as first item in the lists: [b,f], [b,c], [f,c]
I came up with this:
comprehension = [x[0] for x in nested_list if x[1] in nested_list]
But this does not work (ofcourse). I'm not sure how to loop over all lists in order to find the combinations.
You can use collectoins.defaultdict and combinations:
In [2]: nested_list = [['a','b',1],['c','d',3],['a','f',8],['a','c',5]]
In [3]: from collections import defaultdict
In [4]: from itertools import combinations
In [5]: d = defaultdict(list)
In [6]: for i, j, _ in nested_list:
...: d[i].append(j)
...:
In [7]: {k: list(combinations(v, 2)) for k, v in d.items()}
Out[7]: {'a': [('b', 'f'), ('b', 'c'), ('f', 'c')], 'c': []}

weighted counting in python

I want to count the instances of X in a list, similar to
How can I count the occurrences of a list item in Python?
but taking into account a weight for each instance.
For example,
L = [(a,4), (a,1), (b,1), (b,1)]
the function weighted_count() should return something like
[(a,5), (b,2)]
Edited to add: my a, b will be integers.
you can still use counter:
from collections import Counter
c = Counter()
for k,v in L:
c.update({k:v})
print c
The following will give you a dictionary of all the letters in the array and their corresponding counts
counts = {}
for value in L:
if value[0] in counts:
counts[value[0]] += value[1]
else:
counts[value[0]] = value[1]
Alternatively, if you're looking for a very specific value. You can filter the list for that value, then map the list to the weights and find the sum of them.
def countOf(x,L):
filteredL = list(filter(lambda value: value[0] == x,L))
return sum(list(map(lambda value: value[1], filteredL)))
>>> import itertools
>>> L = [ ('a',4), ('a',1), ('b',1), ('b',1) ]
>>> [(k, sum(amt for _,amt in v)) for k,v in itertools.groupby(sorted(L), key=lambda tup: tup[0])]
[('a', 5), ('b', 2)]
defaultdict will do:
from collections import defaultdict
L = [('a',4), ('a',1), ('b',1), ('b',1)]
res = defaultdict(int)
for k, v in L:
res[k] += v
print(list(res.items()))
prints:
[('b', 2), ('a', 5)]
Group items with the occurrence of first element of each tuple using groupby from itertools:
>>> from itertools import groupby
>>> from operator import itemgetter
>>> L = [('a',4), ('a',1), ('b',1), ('b',1)]
>>> L_new = []
>>> for k,v in groupby(L,key=itemgetter(0)):
L_new.append((k,sum(map(itemgetter(1), v))))
>>> L_new
[('a', 5), ('b', 2)]
>>> L_new = [(k,sum(map(itemgetter(1), v))) for k,v in groupby(L, key=itemgetter(0))] #for those fun of list comprehension and one liner expression
>>> L_new
[('a', 5), ('b', 2)]
Tested in both Python2 & Python3
Use the dictionaries get method.
>>> d = {}
>>> for item in L:
... d[item[0]] = d.get(item[0], 0) + item[1]
...
>>> d
{'a': 5, 'b': 2}

Concatenate strings by groups python

I would like to concatenate a list of strings into new strings grouped over values in a list. Here is an example of what I mean:
Input
key = ['1','2','2','3']
data = ['a','b','c','d']
Result
newkey = ['1','2','3']
newdata = ['a','b c','d']
I understand how to join text. But I don't know how to iterate correctly over the values of the list to aggregate the strings that are common to the same key value.
Any help or suggestions appreciated. Thanks.
from collections import defaultdict
d = defaultdict(list)
for k, v in zip(key, data):
d[k].append(v)
print [(k, ' '.join(v)) for k, v in d.items()]
Output:
[('1', 'a'), ('3', 'd'), ('2', 'b c')]
And how to get new lists:
newkey, newvalue = d.keys(), [' '.join(v) for v in d.values()]
And with saved order:
newkey, newvalue = zip(*[(k, ' '.join(d.pop(k))) for k in key if k in d])
Use the itertools.groupby() function to combine elements; zip will let you group two input lists into two output lists:
import itertools
import operator
newkey, newdata = [], []
for key, items in itertools.groupby(zip(key, data), key=operator.itemgetter(0)):
# key is the grouped key, items an iterable of key, data pairs
newkey.append(key)
newdata.append(' '.join(d for k, d in items))
You can turn this into a list comprehension with a bit more zip() magic:
from itertools import groupby
from operator import itemgetter
newkey, newdata = zip(*[(k, ' '.join(d for _, d in it)) for k, it in groupby(zip(key, data), key=itemgetter(0))])
Note that this does require the input to be sorted; groupby only groups elements based on the consecutive keys being the same. On the other hand, it does preserve that initial sorted order.
you can use itertools.groupby() on zip(key,data):
In [128]: from itertools import *
In [129]: from operator import *
In [133]: lis=[(k," ".join(x[1] for x in g)) for k,g in groupby(zip(key,data),key=itemgetter(0))]
In [134]: newkey,newdata=zip(*lis)
In [135]: newkey
Out[135]: ('1', '2', '3')
In [136]: newdata
Out[136]: ('a', 'b c', 'd')
If you dont feel like importing collections you can always use a regular dictionary.
key = ['1','2','2','3']
data = ['a','b','c','d']
newkeydata = {}
for k,d in zip(key,data):
newkeydata[k] = newkeydata.get(k, []).append(d)
Just for the sake of variety, here is a solution that works without any external libraries and without dictionaries:
def group_vals(keys, vals):
new_keys= sorted(set(keys))
zipped_keys = zip(keys, keys[1:]+[''])
zipped_vals = zip(vals, vals[1:]+[''])
new_vals = []
for i, (key1, key2) in enumerate(zipped_keys):
if key1 == key2:
new_vals.append(' '.join(zipped_vals[i]))
else:
new_vals.append(zipped_vals[i][0])
return new_keys, new_vals
group_vals([1,2,2,3], ['a','b','c','d'])
# --> ([1, 2, 3], ['a', 'b c', 'd'])
But I know that it's quite ugly and probably not as performant as the other solutions. Just for demonstration purposes. :)

Categories

Resources