Home > Net >  Select sublist from list by checking max and mins
Select sublist from list by checking max and mins

Time:06-22

Background:

I have a list of lists, where each inner list is of the same length:

list_of_lists = [[1.0, 3.0, 5.0], [3.0, 5.0, 0.0], [2.0, 1.0, 4.0]]

I would like to find the inner list that meets the following criteria:

criteria = [True, True, False]

Where a True criterion would mean that the element should be 'maximized' and a False criterion would mean that the element should be 'minimized'.

Expected Output:

Considering the above case, if the values to be maximized are summed and set as the first value in a tuple, and the values to be minimized are summed and set as the second value in a tuple, the following would occur:

  • inner list 1: (4.0, 5.0)
  • inner list 2: (8.0, 0.0)
  • inner list 3: (3.0, 4.0)

It is clear from the above example that inner list 2 should be the selection, as it has both the highest 'to be maximized' value (8.0) and the lowest 'to be minimized' value (0.0).

However, the real world data that I'm using is not always so clear-cut; the maximums and minimums may belong to different inner lists. In addition, these lists (both the inner and outer lists) are far longer than just three elements.

Current Method:

I'm currently selecting a list by determining the difference between the first and second values of the above tuples, then selecting the inner list that exhibits the maximum difference, like so:

def element_wise_iterable_selection(iterable: list[list[float]], criteria: list[bool | None]) -> int:
        
    differences = []
    
    for sublist in iterable:

        temporary_max_sum, temporary_min_sum = 0, 0

        for i, element in enumerate(sublist):

            if criteria[i]:

                temporary_max_sum  = element

            if not criteria[i]:

                temporary_min_sum  = element

        differences.append(abs(temporary_max_sum - temporary_min_sum))

    return differences.index(max(differences))

This method seems very 'thrown-together'. Is there a better / more pythonic way of doing this? I was thinking that NumPy may have a method to handle cases like this.

CodePudding user response:

By using the compress function from itertools and also avoiding creating a useless list of differences you can do that :

from itertools import compress

def element_wise_iterable_selection(iterable: list[list[float]], criteria: list[bool]) -> int:
    maxDiff = (0,0)
    notCriteria = [not elem for elem in criteria]
    for index, sublist in enumerate(iterable):
        tempMaxSum = sum(compress(sublist, criteria))
        tempMinSum = sum(compress(sublist, notCriteria))
        if abs(tempMaxSum - tempMinSum) > maxDiff[0]:
            maxDiff = (abs(tempMaxSum - tempMinSum), index)
    return maxDiff[1]

list_of_lists = [[1.0, 3.0, 5.0], [3.0, 5.0, 0.0], [2.0, 1.0, 4.0]]
criteria = [True, True, False]  
print(element_wise_iterable_selection(list_of_lists, criteria))

CodePudding user response:

I'm not sure about NumPy, but you can certainly do this in a 1-liner

differences = [abs(sum([e for i, e in enumerate(sublist) if criteria[i]])-sum([e for i, e in enumerate(sublist) if not criteria[i]])) for sublist in list_of_lists]

CodePudding user response:

Numpy does make it much easier. This might not be the most elegant solution, but it works. Since we are indexing 2d arrays, I changed your sample list_of_lists so that both axes are different, making it easier to follow.

import numpy as np
list_of_lists = [[1.0, 3.0, 5.0], [3.0, 5.0, 0.0], [2.0, 1.0, 4.0], [3.0, 0.0, 1.0]]
lol = np.array(list_of_lists)
dims = np.shape(lol)
criteria = [True, True, False]
mask = np.repeat(np.reshape(criteria, (1, dims[1])), dims[0], axis=0)

maxima = np.sum(lol* mask, axis=1)
minima = np.sum(lol* ~mask, axis=1)
differences = maxima - minima # add an np.abs if you need
print(np.argmax(differences))

CodePudding user response:

def element_wise_iterable_selection(list_of_lists, criteria):
    def sort_helper(x):
        total = 0
        for index, value in enumerate(x):
            if criteria[index]:
                total  = value
            else:
                total -= value
        return abs(total)

    sublist = sorted(list_of_lists, key=lambda x: sort_helper(x))[-1]
    return list_of_lists.index(sublist)

Output:

>>> print(element_wise_iterable_selection(list_of_lists, criteria))
>>> 1
  • Related