Effectively summarize items by type

I have a list of items with the "Type" and "Time" properties that I want to quickly add up the time for each "Type" and add it to another list. The list is as follows:

 Items = [{'Name': A, 'Type': 'Run', 'Time': 5}, {'Name': B, 'Type': 'Walk', 'Time': 15}, {'Name': C, 'Type': 'Drive', 'Time': 2}, {'Name': D, 'Type': 'Walk', 'Time': 17}, {'Name': E, 'Type': 'Run', 'Time': 5}] 

I want to do something that works like this:

 Travel_Times=[("Time_Running","Time_Walking","Time_Driving")] Run=0 Walk=0 Drive=0 for I in Items: if I['Type'] == 'Run': Run=Run+I['Time'] elif I['Type'] == 'Walk': Walk=Walk+I['Time'] elif I['Type'] == 'Drive': Drive=Drive+I['Time'] Travel_Times.append((Run,Walk,Drive)) 

With Travel_Times , it finally looks like this:

 print(Travel_Times) [("Time_Running","Time_Walking","Time_Driving") (10,32,2)] 

This is similar to what should be easy to do efficiently with list comprehension or something similar to collections.Counter , but I can't figure it out. The best way I understood is to use a separate list comprehension for each "Type", but this requires repeated repetition in the list. I would appreciate any ideas on how to speed it up.

thanks

+5
source share
7 answers

Please note: this case is very important for Python:

  • For not a valid expression
  • Travel_times does not match Travel_times
  • no : after elif
  • Travel_Times.append(... has a leading space that confuses Python
  • items has one [ too many
  • A not defined

Having said that Counter great for your example:

 from collections import Counter time_counter = Counter() items = [{'Name': 'A', 'Type': 'Run', 'Time': 5}, {'Name': 'B', 'Type': 'Walk', 'Time': 15}, {'Name': 'C', 'Type': 'Drive', 'Time': 2}, {'Name': 'D', 'Type': 'Walk', 'Time': 17}, {'Name': 'E', 'Type': 'Run', 'Time': 5}] for item in items: time_counter[item['Type']] += item['Time'] print(time_counter) # Counter({'Walk': 32, 'Run': 10, 'Drive': 2}) 

To get a list of tuples:

 [tuple(time_counter.keys()), tuple(time_counter.values())] # [('Run', 'Drive', 'Walk'), (10, 2, 32)] 
+2
source

You can use dict to track total time. Using the .get() method, you can calculate the total time. If the key for the action does not yet exist, set its value to zero and count from there.

 items = [{'Name': 'A', 'Type': 'Run', 'Time': 5}, {'Name': 'B', 'Type': 'Walk', 'Time': 15}, {'Name': 'C', 'Type': 'Drive', 'Time': 2}, {'Name': 'D', 'Type': 'Walk', 'Time': 17}, {'Name': 'E', 'Type': 'Run', 'Time': 5}] totals = {} for item in items: totals[item['Type']] = totals.get(item['Type'], 0) + item['Time'] for k, v in totals.items(): print("Time {}ing:\t {} mins".format(k, v)) 
+2
source

You can use Counter from collections along with chain and repeat from itertools :

 from itertools import chain, repeat from collections import Counter from_it = chain.from_iterable res = Counter(from_it(repeat(d['Type'], d['Time']) for d in Items)) 

This small snippet leads to a Counter instance containing the amounts:

 print(res) Counter({'Drive': 2, 'Run': 10, 'Walk': 32}) 

It uses repeat to obviously repeat d['Type'] for d['Time'] times, and then pass all of these values ​​to Counter for summing using chain.from_iterable .


If your Items list contains many entries, you can use chain.from_iterable again to link them all together:

 res = Counter(from_it(repeat(d['Type'], d['Time']) for d in from_it(Items))) 

This will give you the sum of all types in all nested lists.

+2
source

You can use reduce with collections.Counter :

 # from functools import reduce # Python 3 d = reduce(lambda x, y: x + Counter({y['Type']: y['Time']}), Items, Counter()) print(d) # Counter({'Walk': 32, 'Run': 10, 'Drive': 2}) 

It simply increments Counter by updating each Type using the appropriate Time value.

+1
source

Here is a brief way to express what you would like on one line. By the way, your Items list should not be enclosed in two lines:

 >>> Items = [{'Type': 'Run', 'Name': 'A', 'Time': 5}, {'Type': 'Walk', 'Name': 'B', 'Time': 15}, {'Type': 'Drive', 'Name': 'C', 'Time': 2}, {'Type': 'Walk', 'Name': 'D', 'Time': 17}, {'Type': 'Run', 'Name': 'E', 'Time': 5}] >>> zip(("Time_Running","Time_Walking","Time_Driving"), (sum(d['Time'] for d in Items if d['Type'] == atype) for atype in 'Run Walk Drive'.split())) [('Time_Running', 10), ('Time_Walking', 32), ('Time_Driving', 2)] 

Here I fixed your output labels with a generator that calculates the sum for each of the three types of vehicles listed. For your exact output, you can simply use:

 >>> [("Time_Running","Time_Walking","Time_Driving"), tuple(sum(d['Time'] for d in Items if d['Type'] == atype) for atype in 'Run Walk Drive'.split())] [('Time_Running', 'Time_Walking', 'Time_Driving'), (10, 32, 2)] 
+1
source

If you are willing to abuse generators for your side effects:

 from collections import Counter count = Counter() # throw away the resulting elements, as .update does the work for us [_ for _ in (count.update({item['Type']:item['Time']}) for item in items) if _] >>> count Counter({'Walk': 32, 'Run': 10, 'Drive': 2}) 

This works because Counter.update() returns None . if None will always evaluate to False and throw this element. Thus, this generates empty side effect lists [] as the only memory overhead. if False will work equally well.

+1
source

Just use the dictionary! Note that in python iteratively use snake_case for variables and keys.

 travel_times = {'run': 0, 'walk': 0, 'drive': 0} for item in items: action, time = item['type'], item['time'] travel_times[action] += time 
0
source

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


All Articles