Reputation: 3
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
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
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
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
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