Zeywery
Zeywery

Reputation: 3

Get Max value of y within a specified range of x

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.

Upvotes: 0

Views: 3537

Answers (4)

Paul Rooney
Paul Rooney

Reputation: 21619

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.

Upvotes: 0

Austin
Austin

Reputation: 26039

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

Upvotes: 0

pvpks
pvpks

Reputation: 371

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)).

Upvotes: 0

Cheche
Cheche

Reputation: 1516

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

Upvotes: 3

Related Questions