Python:如何在列表中找到小于目标的值
比如说,我有一组没有顺序的数字:[10, 20, 50, 200, 100, 300, 250, 150]
我有一段代码可以找出下一个更大的数字:
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])
例如,如果我输入的数字是55,它会返回'100'。
但是我该怎么找到比这个数字小的值呢?也就是说,怎么让它返回'50'呢?
谢谢。
编辑 - 现在可以用了
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"
6 个回答
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]
这是怎么回事呢:看一下 nextHighest
,传给 min
的参数是一个列表推导式,它计算列表中每个值与输入的 x 之间的差,但只针对那些大于等于 x 的值。因为我们想要的是实际的值,所以列表中的元素需要同时包含与该值的差和实际值。元组是逐个比较的,从左到右,所以对于序列中的每个值 i
,生成的元组是 (i-x,i)
- 最小的元组会在 [1]
这个位置上包含实际值。
如果输入的 x 值超出了 seq 中的值范围(或者 seq 为空),那么列表推导式会返回一个空列表,这样在 min
中就会引发一个 ValueError 错误。为了处理这种情况,我们在 min
的参数中添加了 or [(0,None)]
。如果列表推导式是空的,它会被视为 False,这样 min
就会查看包含单个元组 (0,None)
的序列。在这种情况下,[1]
这个位置的值是 None,表示 seq 中没有任何元素大于 x。
以下是一些测试案例:
>>> 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
一个更好且更快的方法(在代码和CPU使用上)是使用bisect模块,它可以进行二分查找。不过在使用之前,你需要先把列表排序。下面是一个示例:
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]
输出结果:
Greater than target 100
Smaller than or equal to target 50
另外,你还需要检查返回的索引。如果它是 0
,那就意味着你查找的目标值比列表中最小的值还要小。
编辑: 啊,我用的是 templist
而不是 list
-- 所以才会让人困惑。我并不是想让它变成一行的函数;你还是得进行转换。(当然,正如 Mike DeSimone 指出的那样,把 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
感谢 Mike 提出的建议,在空列表的情况下返回 None
-- 我喜欢这个想法。
你甚至可以这样进一步简化:
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