Python: how to find value in list smaller than target
For example I have a non-ordered list of values [10, 20, 50, 200, 100, 300, 250, 150]
I have this code which returns the next greater value:
def GetNextHighTemp(self, temp, templist):
target = int(temp)
list = []
for t in templist:
if t != "":
list.append(int(t))
return str(min((abs(target - i), i) for i in list)[1])
e.g. If temp = 55, it will return '100'.
But how can I get the lesser of the value? That is how to get it to return '50'?
Thank you.
EDIT - now working
def OnTWMatCurrentIndexChanged(self):
self.ClearTWSelectInputs()
material = self.cb_TW_mat.currentText()
temp = self.txt_design_temp.text()
if material != "":
Eref = self.GetMaterialData(material, "25", "elast")
if Eref and Eref != "":
Eref = str(float(Eref) / 1000000000)
self.txt_TW_Eref.setText(Eref)
else:
self.txt_TW_Eref.setText("194.8")
self.ShowMsg("No temperature match found for E<sub>ref</sub> in material data file. Value of 194.8 GPa will be used.", "blue")
if material != "" and temp != "":
if self.CheckTWTemp(material, temp):
dens = self.GetMaterialData(material, temp, "dens")
self.txt_TW_dens.setText(dens)
elast = self.GetMaterialData(material, temp, "elast")
elast = str(float(elast) / 1000000000)
self.txt_TW_Et.setText(elast)
stress = self.GetMaterialData(material, temp, "stress")
stress = str(float(stress) / 1000000)
self.txt_TW_stress_limit.setText(stress)
else:
self.ShowMsg("No temperature match found for " + temp + "° C in material data file. Extrapolated data will be used where possible or add new material data.", "blue")
dens = self.GetExtrapolatedMaterialData(material, temp, "dens")
self.txt_TW_dens.setText(dens)
elast = self.GetExtrapolatedMaterialData(material, temp, "elast")
elast = str(float(elast) / 1000000000)
self.txt_TW_Et.setText(elast)
stress = self.GetExtrapolatedMaterialData(material, temp, "stress")
stress = str(float(stress) / 1000000)
self.txt_TW_stress_limit.setText(stress)
else:
self.ClearTWSelectInputs()
def CheckTWTemp(self, matvar, tempvar):
for material in self.materials:
if material.attrib["name"] == matvar:
temps = material.getiterator("temp")
for temp in temps:
if int(temp.text) == int(tempvar):
return True
return False
def GetMaterialData(self, matvar, tempvar, tag):
for material in self.materials:
if material.attrib["name"] == matvar:
temps = material.getiterator("temp")
for temp in temps:
if temp.text == tempvar:
value = temp.find(tag)
return value.text
def GetExtrapolatedMaterialData(self, matvar, tempvar, tag):
try:
templist = QStringList()
for material in self.materials:
if material.attrib["name"] == matvar:
temps = material.getiterator("temp")
for temp in temps:
templist.append(temp.text)
templist.sort()
target = int(tempvar)
x1 = max(int(t) for t in templist if t != '' and int(t) < target)
x2 = min(int(t) for t in templist if t != '' and int(t) > target)
y1 = float(self.GetMaterialData(matvar, str(x1), tag))
y2 = float(self.GetMaterialData(matvar, str(x2), tag))
x = target
y = y1 - ((y1 - y2) * (x - x1) / (x2 - x1))
return开发者_运维百科 str(y)
except Exception, inst:
return "0"
A better and much faster (code and cpu wise) way is to use bisect module which does binary search but for that you will need to sort the list first, here is the sample usage:
import bisect
mylist = [10, 20, 50, 200, 100, 300, 250, 150]
mylist.sort()
index = bisect.bisect(mylist, 55)
print "Greater than target", mylist[index]
print "Smaller than or equal to target", mylist[index-1]
output:
Greater than target 100
Smaller than or equal to target 50
Also you will need to check the returned index, if it is 0
it means you have passed target lower than the lowest
Edit: Ah, I used templist
instead of list
-- hence the confusion. I didn't mean it to be a one-line function; you still have to do the conversions. (Of course, as Mike DeSimone rightly points out, using list as a variable name is a terrible idea!! So I had a good reason for being confusing. :)
To be more explicit about it, here's a slightly streamlined version of the function (fixed to test properly for an empty list):
def GetNextHighTemp(self, temp, templist):
templist = (int(t) for t in templist if t != '')
templist = [t for t in templist if t < int(temp)]
if templist: return max(templist)
else: return None # or raise an error
Thanks to Mike for the suggestion to return None
in case of an empty list -- I like that.
You could shorten this even more like so:
def GetNextHighTemp(self, temp, templist):
try: return str(max(int(t) for t in templist if t != '' and int(t) < int(temp)))
except ValueError: return None # or raise a different error
nextHighest = lambda seq,x: min([(i-x,i) for i in seq if x<=i] or [(0,None)])[1]
nextLowest = lambda seq,x: min([(x-i,i) for i in seq if x>=i] or [(0,None)])[1]
Here's how this works: Looking at nextHighest
, the argument to min is a list comprehension, that calculates the differences between each value in the list and the input x, but only for those values >= x. Since you want the actual value, then we need the list elements to include both the difference to the value, and the actual value. Tuples are compared value by value, left-to-right, so the tuple for each value i
in the sequence becomes (i-x,i)
- the min tuple will have the actual value in the [1]
'th element.
If the input x value is outside the range of values in seq (or if seq is just empty), then the list comprehension will give us an empty list, which will raise a ValueError in min
. In case this happens, we add the or [(0,None)]
term inside the argument to min
. If the list comprehension is empty, it will evaluate to False, in which case min will instead look at the sequence containing the single tuple (0,None)
. In the case, the [1]
'th element is None, indicating that there were no elements in seq higher than x.
Here are some test cases:
>>> t = [10, 20, 50, 200, 100, 300, 250, 150]
>>> print nextHighest(t,55)
100
>>> print nextLowest(t,55)
50
>>> print nextHighest([],55)
None
>>> print nextLowest([],55)
None
>>> print nextHighest(t,550)
None
Let the unordered list be myList
:
answer = max(x for x in myList if x < temp)
If I understand you correctly, you want the greatest value that is less than your target; e.g. in your example, if your target is 55, you want 50, but if your target is 35, you want 20. The following function should do that:
def get_closest_less(lst, target):
lst.sort()
ret_val = None
previous = lst[0]
if (previous <= target):
for ndx in xrange(1, len(lst) - 1):
if lst[ndx] > target:
ret_val = previous
break
else:
previous = lst[ndx]
return str(ret_val)
If you need to step through these values, you could use a generator to get the values in succession:
def next_lesser(l, target):
for n in l:
if n < target:
yield str(n)
Both these worked properly from within a simple program.
a=[4,3,8,2,5]
temp=4
def getSmaller(temp,alist):
alist.sort()
for i in range(len(alist)):
if(i>0 and alist[i]==temp):
print alist[i-1]
elif(i==0 and alist[i]==temp):
print alist[i]
getSmaller(temp,a)
精彩评论