Thursday, October 4, 2018

Get top 5 values where key total is less than or equal to X

Leave a Comment

Currently I have a list of items someone can buy as follows:

my_list = [     ('Candy', 1.0, 20.5),     ('Soda', 3.0, 10.25),     ('Coffee', 1.2, 20.335),     ('Soap', 1.2, 11.5),     ('Spoon', 0.2, 2.32),     ('Toast', 3.2, 12.335),     ('Toothpaste', 3, 20.5),     ('Creamer', .1, 5.5),     ('Sugar', 2.2, 5.2), ] 

Each item is set up like this:

('Item Name', ItemCost, ItemValue) 

I have the list pulling the items with the top 5 ItemValue.

print nlargest(5, my_list, key=itemgetter(2)) >>> [         ('Candy', 1.0, 20.5),         ('Toothpaste', 3, 20.5),         ('Coffee', 1.2, 20.335),         ('Toast', 3.2, 12.335),         ('Soap', 1.2, 11.5),     ] 

I am trying to retrieve a result where I get the top 5 total ItemValue where the top 5 total ItemCost is equal or less than 6.

Any suggestions?

9 Answers

Answers 1

You can filter first, and use all following nlargest on your filtered list.

f = [(a,b,c) for (a,b,c) in my_list if b <= 6] 

But for data manipulation like this, pandas can be very useful. Take, for example

df = pd.DataFrame(my_list, columns=('ItemName', 'ItemCost', 'ItemValue'))      ItemName    ItemCost    ItemValue 0   Candy       1.0         20.500 1   Soda        3.0         10.250 2   Coffee      1.2         20.335 3   Soap        1.2         11.500 4   Spoon       0.2         2.320 5   Toast       3.2         12.335 6   Toothpaste  3.0         20.500 7   Creamer     0.1         5.500 8   Sugar       2.2         5.200  >>> df[df.ItemCost <= 6]      ItemName    ItemCost    ItemValue 0   Candy       1.0         20.500 1   Soda        3.0         10.250 2   Coffee      1.2         20.335 3   Soap        1.2         11.500 4   Spoon       0.2         2.320 5   Toast       3.2         12.335 6   Toothpaste  3.0         20.500 7   Creamer     0.1         5.500 8   Sugar       2.2         5.200  >>> df[df.ItemCost <= 6].nlargest(n=5, columns=['ItemValue'])       ItemName    ItemCost    ItemValue 0   Candy       1.0         20.500 6   Toothpaste  3.0         20.500 2   Coffee      1.2         20.335 5   Toast       3.2         12.335 3   Soap        1.2         11.500 

If you want, you can first get the nsmallest of the ItemCost and just then get the nlargest

df.nsmallest(n=5, columns=['ItemCost']).nlargest(n=5, columns=['ItemValue'])          ItemName    ItemCost    ItemValue 0   Candy       1.0         20.500 2   Coffee      1.2         20.335 3   Soap        1.2         11.500 7   Creamer     0.1         5.500 4   Spoon       0.2         2.320 

Answers 2

Not sure if this is what you asking,

I would first create all possible combinations of 5 elements from my_list

itertools.combinations(my_list, 5) 

Then i would find all possible combinations in result where total item cost would be less than or equal to 6.

f = [element for element in itertools.combinations(my_list, 5) if  sum([e[1] for e in element]) <=6] 

Now, I would find that element where total itemValue is the greatest

h = [sum([g[2] for g in e]) for e in f] 

The index of element with maximum itemValue is

index = h.index(max(h)) 

Now, you can find that element in f.

f[index] 

The answer i got is

 Candy        1.0  20.5  Coffee       1.2  20.335  Spoon        0.2  2.32  Toothpaste   3    20.5  Creamer      0.1  5.5 

Answers 3

First you want to filter the list by the ItemCost:

  • Which can be done by: filtered_generator = filter(lambda x: x[1] <= 6, my_list)

  • Or in a more python-like way filtered_list = [x for x in my_list if x[1] <=6]

  • And to keep it a generator to save memory just use parentheses instead of the square brackets.

Then you want to get the n largest items:

  • You can use heapq.nlargest: nlargest(5, filtered_iter, key=lambda x:x[2])
  • or implement similar function yourself.

filtered_iter can be the list or one of the generators.

Answers 4

from operator import itemgetter from itertools import combinations from beautifultable import BeautifulTable  def pretty_print( lst):     table = BeautifulTable()     table.column_headers = ['Item Name','ItemCost','ItemValue']     if lst:         for item_specs in lst:             table.append_row(item_specs)         print(table)   def get_total_cost( lst):     return sum(item_specs[1] for item_specs in lst)  def get_total_Value( lst):     return sum(item_specs[2] for item_specs in lst)    def best_comb( item_list, number_of_items_to_pick, cost_constraint):       k = number_of_items_to_pick      item_list.sort(key=itemgetter(2), reverse=True) # sorting list by ItemValue      k_top_value_item_lst = item_list[:5] # picking top k items from list      total_cost = get_total_cost(k_top_value_item_lst)       def generateCombinations( take_default_val_for_best_result = True):         k_len_combination_list = list(combinations( item_list, k))          if take_default_val_for_best_result:             best_result = []# which meets total itemCost <= 6 condition and have highest total of ItemValue              best_result_sum = [0,0] # ItemCost, ItemValue             else:             best_result = k_top_value_item_lst             best_result_sum = [total_cost, get_total_Value(best_result)]           best_alternative_lst = [] # if there are any other combination which offer same Value for Cost          # ignore first comb as its been already suggested to user         for comb in k_len_combination_list:              temp_sum = [None,None]             temp_sum[0] = get_total_cost( comb)             reset_best = False              if  temp_sum[0] <= cost_constraint:                 temp_sum[1] = get_total_Value( comb)                  if best_result_sum[1] < temp_sum[1]:                     reset_best = True                  elif best_result_sum[1] == temp_sum[1]:                     if temp_sum[0] < best_result_sum[0]:                         reset_best = True                     elif temp_sum[0] == best_result_sum[0]:                         # since ItemValue as well as ItemCost are equivalent to best_result this comb is great alternative                         if comb != tuple(best_result):                             best_alternative_lst.append(comb)                  if reset_best:                     best_result = comb                     best_result_sum[1] = temp_sum[1]                     best_result_sum[0] = temp_sum[0]          print('Best Combination:')         if best_result:             pretty_print(best_result)         else:             print('not found')          if gen_alternative:             print('\nBest Alternative Combination:')             if best_alternative_lst:                 for idx,alter_comb in enumerate( best_alternative_lst):                     comb_id = idx+1                     print('combination_id ',comb_id)                     pretty_print(alter_comb)             else:                 print('not found')       if total_cost > cost_constraint:         generateCombinations()      else:         if gen_alternative:             generateCombinations(take_default_val_for_best_result = False)          else:             print('Best Combination:')             pretty_print(k_top_value_item_lst)    my_list = [     ('Candy', 2.0, 20.5),     ('Soda', 1.5, 25.7 ),      ('Coffee', 2.4, 25.7 ),     ('Soap', 1.2,20),     ('Spoon',1.2,20 ),      ('Toast',1.2,22 ),     ('Toothpaste',0.8, 20 ),      ('Creamer',0.8, 22),     ('Sugar',2.0, 20.5 ), ]  gen_alternative = input('do you want to generate alternative combinations: y/n ')[0].lower() == 'y'  best_comb( my_list, 5, 6) 

Answer to modified list ( to show extra feature)

do you want to generate alternative combinations: y/n Y Best Combination: +------------+----------+-----------+ | Item Name  | ItemCost | ItemValue | +------------+----------+-----------+ |    Soda    |   1.5    |   25.7    | +------------+----------+-----------+ |   Toast    |   1.2    |    22     | +------------+----------+-----------+ |  Creamer   |   0.8    |    22     | +------------+----------+-----------+ |    Soap    |   1.2    |    20     | +------------+----------+-----------+ | Toothpaste |   0.8    |    20     | +------------+----------+-----------+  Best Alternative Combination: combination_id  1 +------------+----------+-----------+ | Item Name  | ItemCost | ItemValue | +------------+----------+-----------+ |    Soda    |   1.5    |   25.7    | +------------+----------+-----------+ |   Toast    |   1.2    |    22     | +------------+----------+-----------+ |  Creamer   |   0.8    |    22     | +------------+----------+-----------+ |   Spoon    |   1.2    |    20     | <--- +------------+----------+-----------+ | Toothpaste |   0.8    |    20     | +------------+----------+-----------+       

Answer to your original list

Best Combination: +------------+----------+-----------+ | Item Name  | ItemCost | ItemValue | +------------+----------+-----------+ |   Candy    |   1.0    |   20.5    | +------------+----------+-----------+ | Toothpaste |    3     |   20.5    | +------------+----------+-----------+ |   Coffee   |   1.2    |  20.335   | +------------+----------+-----------+ |  Creamer   |   0.1    |    5.5    | +------------+----------+-----------+ |   Spoon    |   0.2    |   2.32    | +------------+----------+-----------+  Best Alternative Combination: not found 

Answers 5

Not totally sure of what you are trying to do, but...
If you are trying to retrieve the top x (5 or 6) based on itemcost sorted by lowest cost, you can try this.

x=5 sorted(my_list, key=lambda s : s[2])[:x]  This outputs the following:  [('Spoon', 0.2, 2.32), ('Sugar', 2.2, 5.2), ('Creamer', 0.1, 5.5), ('Soda', 3.0, 10.25), ('Soap', 1.2, 11.5)] 

Answers 6

from itertools import combinations  ...  total_cost = lambda item: int(sum(c for _, c, _ in item) <= 6) * sum(v for _, _ , v in item) chosen = max(combinations(my_list, 5), key=total_cost) 

Max can receive a function to specify max criteria.

The total_cost function, has a int(sum(c for _, c, _ in item) <= 6) portion that is 1 if total cost of the combination is less or equal then 6, and its 0 otherwise.

We then multiply this portion with the total sum of values.

combinations(my_list, 5) retrieves all possible combinations of my_list items having 5 items.

Printing chosen elements you have:

('Candy', 1.0, 20.5) ('Coffee', 1.2, 20.335) ('Spoon', 0.2, 2.32) ('Toothpaste', 3, 20.5) ('Creamer', 0.1, 5.5) 

Answers 7

Filter the list first:

print nlargest(5, [item for item in my_list if item[1]<=6], key=itemgetter(2))

You can do it with sorted too:

sorted([item for item in my_list if item[1]<=6], key=lambda x: x[1], reverse=True)[:5]

The above filters out items with ItemCost greater than 6, sorts your list descending based on ItemCost, and then returns the first 5 element

Answers 8

First using combinations we can get all possible combinations of 5 from my_list. From here we can use filter and only return combinations whose total ItemCost is less than or equal to 6. Finally we sort by the which group has the highest total ItemValue and we take the greatest one being l2[-1] we could use reverse = True and then it would be l2[0]

from itertools import combinations

l = list(combinations(my_list, 5)) l1 = list(filter(lambda x: sum([i[1] for i in x]) < 6, l)) l2 = sorted(l1, key=lambda x: sum([i[2] for i in x])) print(l2[-1]) 
(('Candy', 1.0, 20.5), ('Coffee', 1.2, 20.335), ('Spoon', 0.2, 2.32), ('Toothpaste', 3, 20.5), ('Creamer', 0.1, 5.5)) 

Answers 9

from itertools import combinations from functools import reduce  def get_valid_combs(lis):     "find all combinations that cost less than or equal to 6"      for i in combinations(lis, 5):         if reduce(lambda acc, x: acc + x[1], list(i), 0) <= 6:             yield list(i)  my_list = [     ('Candy', 1.0, 20.5),     ('Soda', 3.0, 10.25),     ('Coffee', 1.2, 20.335),     ('Soap', 1.2, 11.5),     ('Spoon', 0.2, 2.32),     ('Toast', 3.2, 12.335),     ('Toothpaste', 3, 20.5),     ('Creamer', .1, 5.5),     ('Sugar', 2.2, 5.2), ]  # find all valid combinations which  cost less than 6 valid_combinations = [i for i in get_valid_combs(my_list)]  #top_combinations_sorted = sorted(valid_combinations, key=lambda y: reduce(lambda acc, x: acc + x[2], [0]+y))  # of the valid combinations get the combination with highest total value best_combination = max(valid_combinations, key=lambda y: reduce(lambda acc, x: acc + x[2], y, 0))  print(best_combination) 

output:

[('Candy', 1.0, 20.5), ('Coffee', 1.2, 20.335), ('Spoon', 0.2, 2.32), ('Toothpaste', 3, 20.5), ('Creamer', 0.1, 5.5)] 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment