开发者

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 + "&#x00B0; 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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜