0

I am still quite new to Python and trying to figure how to perform this using all the existing built-in functions in Python 2.

I have a set of data in a nested tuple and would like to get the max value of the corresponding y within a specified x range. My x and y refers to ((x1, y1), (x2, y2), ...). For example:

data = ((1,33),(2,24),(3,42),(4,2),(8,12))

I would like to write a code such that when I specify when x is between 2 to 4, I get the maximum of y which is 42 in this case.

Cheche
  • 1,456
  • 10
  • 27
Zeywery
  • 3
  • 2
  • 1
    If you are new to Python, I recommend moving to Python 3. Python 2 will be sunset eventually, and Python 3 has **lots** of good stuff in it. – Jonah Bishop Nov 08 '18 at 02:33
  • Yes I would think that the latest version of Python offers more functionality. Is just that right now the software (ABAQUS) I am using still uses python 2... – Zeywery Nov 08 '18 at 14:32

4 Answers4

3

Just do:

def get_max_with_boundaries(data, vmax, vmin):
    try:
        return max([x[1] for x in data if x[0] >= vmin and x[0] <= vmax])
    except ValueError:
        return None

Adding filter built-in function it becomes:

def get_max_with_boundaries(data, vmax, vmin):
    try:
        bound_data = filter(lambda x: x[0] >= vmin and x[0] <= vmax, data)
        return max(bound_data, key=lambda x: x[1])
    except ValueError:
        return None
Cheche
  • 1,456
  • 10
  • 27
0

Use max() with range condition:

data = ((1,33),(2,24),(3,42),(4,2),(8,12))

print(max((y for x, y in data if x in range(2, 5)), default=None))
# 42

You could also set default to, say None, so if there is no value of x in the range specified, this returns a None.

On Python 2:

print max(y for x, y in data if x in range(2, 5))
# 42
Austin
  • 25,759
  • 4
  • 25
  • 48
  • May want to state that this is Py3 only. – paxdiablo Nov 08 '18 at 02:59
  • Yea tried running it and the "default = None" part does not work in Py2. Also tried testing using a floating number for the range but it does not run. – Zeywery Nov 08 '18 at 14:36
  • @Zeywery, remove `default=None` and then it works on Py2 like `max((y for x, y in data if x in range(2, 5)))`. But you should seriously consider using Py3 – Austin Nov 08 '18 at 14:49
0

The straightforward answer is to use filter then max. Like so

from operator import itemgetter

# straightforward linear search
data = ((1, 33), (2, 24), (3, 42), (4, 2), (8, 12))

filtered = filter(lambda t: 2 <= t[0] <= 4, data)
x, y = max(filtered, key=itemgetter(1))
print(y)

There is however a more efficient way. You could use a binary search to find the start and the end of the range, given that list is ordered by the first tuple element (assumption but it seems that it is from your example). It is probably only needed if your input is quite large, the extra complexity isn't warranted otherwise.

from operator import itemgetter
from bisect import bisect_left, bisect_right


class KeyWrapper:
    def __init__(self, iterable, key):
        self.it = iterable
        self.key = key

    def __getitem__(self, i):
        return self.key(self.it[i])

    def __len__(self):
        return len(self.it)


def slicerange(iterable, lo, hi, key=None):

    wrapped = KeyWrapper(iterable, key) if key else iterable

    start = bisect_left(wrapped, x=lo)
    end = bisect_right(wrapped, x=hi, lo=start)

    return iterable[start: end]


myrange = slicerange(data, 2, 4, key=itemgetter(0))
x, y = max(myrange, key=itemgetter(1))
print(y)

We are binary searching on an element of the provided data and not using its natural order, so I'm using the KeyWrapper class from this answer, to allow the binary search with a key function.

You find the start and end, slice it and find the max with another key function looking at the element at index 1.

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
0

There is a python built-in function called "max" for doing this.
Most built-in functions generally take an optional argument called key (>python2.5)

In [1]: data = ((1,33),(2,24),(3,42),(4,2),(8,12))
In [2]: max(data, key=lambda (x,y): -1 if x not in range(2,4) else y)
Out[2]: (3, 42)

The max operation is performed on the return value of the function for each item in data
In this case, for each (x, y) , I am returning a "-1" in case 'x' is not in range(2,4) else, I am returning 'y'.

The max function takes care of calculating the maximum value of y

From: https://docs.python.org/2/library/functions.html#max

The optional key argument specifies a one-argument ordering function like that used for list.sort(). The key argument, if supplied, must be in keyword form (for example, max(a,b,c,key=func)).

pvpks
  • 351
  • 1
  • 8