Reduce using key in python

I am trying to think of the most efficient way to do this in python.

Suppose I have a list of tuples:

[('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)] 

And let me have a function that takes two of these tuples and combines them:

 def my_reduce(obj1, obj2): return (obj1[0],max(obj1[1],obj2[1]),min(obj1[2],obj2[2])) 

How to perform effective reduction to “key”, where the key here may be the first value, so the end result will be something like this:

 [('dog',12,1), ('cat',16,1)] 
+6
source share
4 answers

If you want to use your my_reduce and reduce , you can do it this way. This is actually quite short:

Cooking:

 from itertools import groupby from operator import itemgetter pets = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)] def my_reduce(obj1, obj2): return (obj1[0],max(obj1[1],obj2[1]),min(obj1[2],obj2[2])) 

Decision:

 print [reduce(my_reduce, group) for _, group in groupby(sorted(pets), key=itemgetter(0))] 

Output:

 [('cat', 16, 1), ('dog', 12, 1)] 
+5
source

Alternatively, if you have pandas installed:

 import pandas as pd l = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)] pd.DataFrame(data=l, columns=['animal', 'm', 'n']).groupby('animal').agg({'m':'max', 'n':'min'}) Out[6]: mn animal cat 16 1 dog 12 1 

To get the original format:

 zip(df.index, *df.values.T) # df is the result above Out[14]: [('cat', 16, 1), ('dog', 12, 1)] 
+9
source

I don't think reduce is a good tool for this to work, because you have to use itertools first or similarly group the list by key. Otherwise, you will compare cats and dogs , and all hell will break!

Instead, just a simple loop in order:

 >>> my_list = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2)] >>> output = {} >>> for animal, high, low in my_list: ... try: ... prev_high, prev_low = output[animal] ... except KeyError: ... output[animal] = high, low ... else: ... output[animal] = max(prev_high, high), min(prev_low, low) 

Then, if you want to return the original format:

 >>> output = [(k,) + v for k, v in output.items()] >>> output [('dog', 12, 1), ('cat', 15, 1)] 

Please note that this will destroy the order from the original list. If you want to keep the order in which the keys first appear, initialize the output with OrderedDict .

+7
source

If you really want to use shortening, I think this works (it gives you an answer instead of a list, but meh)

 def my_reduce(obj1, obj2): if not isinstance(obj1,dict): return reduce(my_reduce,[{},obj1,obj2]) try: obj1[obj2[0]] = max(obj1[obj2[0]][0],obj2[1]),min(obj1[obj2[0]][1],obj2[2]) except KeyError: obj1[obj2[0]] = obj2[1:] return obj1 my_list = [('dog',12,2), ('cat',15,1), ('dog',11,1), ('cat',15,2), ('dog',10,3), ('cat',16,3)] print reduce(my_reduce,my_list) 

I think both other solutions are better, however

0
source

Source: https://habr.com/ru/post/986196/


All Articles