Pavan
Pavan

Reputation: 2851

Maximum value by groups in Python

I have a list of lists in python. Each value on the list of lists is represented by [category,type,item,score]. For each category and type, i would like to return a list of highest score items.

[["Edibles", "Fruit", "Apple", 3],
"Edibles", "Fruit", "Grapes", 8],
"Edible", "Candy", "Hershey", 4],
"Edible", "Candy", "Snickers", 6],
"NonEdible", "Bikes", "Yamaha", 5],
"NonEdible", "Bikes", "Suzuki", 7],
"NonEdible", "Cars", "Kia", 8],
"NonEdible", "Cars", "Toyota", 9]]

Desired output

[["Edibles", "Fruit", "Grapes", 8],
"Edible", "Candy", "Snickers", 6],
"NonEdible", "Bikes", "Suzuki", 7],
"NonEdible", "Cars", "Toyota", 9]]

I'm able to do this with multiple loops creating temporary lists, but as the input size increase, the computation becomes very slow. I'm looking for an efficient solution.

Upvotes: 1

Views: 91

Answers (5)

Trenton McKinney
Trenton McKinney

Reputation: 62393

Use pandas

  • Using a dataframe offers the ability to easily manipulate, analyze and visualize the data.
import pandas as pd

# setup dataframe
data = [["Edibles", "Fruit", "Apple", 3],
        ["Edibles", "Fruit", "Grapes", 8],
        ["Edible", "Candy", "Hershey", 4],
        ["Edible", "Candy", "Snickers", 6],
        ["NonEdible", "Bikes", "Yamaha", 5],
        ["NonEdible", "Bikes", "Suzuki", 7],
        ["NonEdible", "Cars", "Kia", 8],
        ["NonEdible", "Cars", "Toyota", 9]]

df = pd.DataFrame(data)

# groupby max
output = df.groupby([0, 1]).agg(max).reset_index()

           0      1         2  3
0     Edible  Candy  Snickers  6
1    Edibles  Fruit    Grapes  8
2  NonEdible  Bikes    Yamaha  7
3  NonEdible   Cars    Toyota  9

# output to a list if you want
output.to_numpy()

array([['Edible', 'Candy', 'Snickers', 6],
       ['Edibles', 'Fruit', 'Grapes', 8],
       ['NonEdible', 'Bikes', 'Yamaha', 7],
       ['NonEdible', 'Cars', 'Toyota', 9]], dtype=object)

Upvotes: 1

Maurice Meyer
Maurice Meyer

Reputation: 18106

You can use a regular dict, storing all values per unique key in a list and just grabbing the max value:

data = [
    ["Edibles", "Fruit", "Apple", 3],
    ["Edibles", "Fruit", "Grapes", 8],
    ["Edible", "Candy", "Hershey", 4],
    ["Edible", "Candy", "Snickers", 6],
    ["NonEdible", "Bikes", "Yamaha", 5],
    ["NonEdible", "Bikes", "Suzuki", 7],
    ["NonEdible", "Cars", "Kia", 8],
    ["NonEdible", "Cars", "Toyota", 9]]

dct = {}
for item in data:
    dct.setdefault((item[0], item[1]), []).append((item[-2], item[-1]))

for k, v in dct.items():
    print(list(k) + list(max(v, key=lambda x: x[1])))

Output:

['Edibles', 'Fruit', 'Grapes', 8]
['Edible', 'Candy', 'Snickers', 6]
['NonEdible', 'Bikes', 'Suzuki', 7]
['NonEdible', 'Cars', 'Toyota', 9]

Upvotes: 1

Sam
Sam

Reputation: 1415

A simple dictionary is fast and efficient!

(your list-of-lists is malformed - you don't have opening brackets for each sub-list)
You could do this in 1 pass with a dictionary:

input = [["Edibles", "Fruit", "Apple", 3],
    ["Edibles", "Fruit", "Grapes", 8],
    ["Edible", "Candy", "Hershey", 4],
    ["Edible", "Candy", "Snickers", 6],
    ["NonEdible", "Bikes", "Yamaha", 5],
    ["NonEdible", "Bikes", "Suzuki", 7],
    ["NonEdible", "Cars", "Kia", 8],
    ["NonEdible", "Cars", "Toyota", 9]
]

highest_val_dict = {}
for curr_list in input:
    curr_key = (curr_list[0], curr_list[1])  # (category,type) is the key
    curr_item = curr_list[2]
    curr_val = curr_list[3]
    highest_pair = highest_val_dict.get(curr_key, (None, -1))
    if curr_val > highest_pair[1]:
        highest_val_dict[curr_key] = (curr_item, curr_val)

>>> for key, val in highest_val_dict.items():
>>>     print(f'{key[0]}, {key[1]}, {val[0]}, {val[1]}')
Edibles, Fruit, Grapes, 8
Edible, Candy, Snickers, 6
NonEdible, Bikes, Suzuki, 7
NonEdible, Cars, Toyota, 9

Upvotes: 2

Mayank Porwal
Mayank Porwal

Reputation: 34046

You can use pandas library for this:

Install pandas like:

pip install pandas

Your code would be:

In [2271]: import pandas as pd

In [2272]: l = [["Edibles", "Fruit", "Apple", 3], 
      ...: ["Edibles", "Fruit", "Grapes", 8], 
      ...: ["Edible", "Candy", "Hershey", 4], 
      ...: ["Edible", "Candy", "Snickers", 6], 
      ...: ["NonEdible", "Bikes", "Yamaha", 5], 
      ...: ["NonEdible", "Bikes", "Suzuki", 7], 
      ...: ["NonEdible", "Cars", "Kia", 8], 
      ...: ["NonEdible", "Cars", "Toyota", 9]] 

In [2275]: df = pd.DataFrame(l, columns=['category','type','item','score'])

In [2284]: df.groupby(['category','type'], as_index=False).agg(max).values.tolist()
Out[2284]: 
[['Edible', 'Candy', 'Snickers', 6],
 ['Edibles', 'Fruit', 'Grapes', 8],
 ['NonEdible', 'Bikes', 'Yamaha', 7],
 ['NonEdible', 'Cars', 'Toyota', 9]]

Upvotes: 1

Andrej Kesely
Andrej Kesely

Reputation: 195418

You can use itertools.groupby, but you need to sort the list before grouping:

from itertools import groupby

lst = [["Edibles", "Fruit", "Apple", 3],
["Edibles", "Fruit", "Grapes", 8],
["Edible", "Candy", "Hershey", 4],
["Edible", "Candy", "Snickers", 6],
["NonEdible", "Bikes", "Yamaha", 5],
["NonEdible", "Bikes", "Suzuki", 7],
["NonEdible", "Cars", "Kia", 8],
["NonEdible", "Cars", "Toyota", 9]]

#if lst is already sorted, skip this step:
lst = sorted(lst, key=lambda k: (k[0], k[1]))

out = [max(g, key=lambda k: k[-1]) for _, g in groupby(lst, lambda k: (k[0], k[1]))]

from pprint import pprint
pprint(out)

Prints:

[['Edible', 'Candy', 'Snickers', 6],
 ['Edibles', 'Fruit', 'Grapes', 8],
 ['NonEdible', 'Bikes', 'Suzuki', 7],
 ['NonEdible', 'Cars', 'Toyota', 9]]

Upvotes: 4

Related Questions