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