Python; 异常感知的 map()
我正在用pyplot处理一些通用数据,并把它们从功率值转换为分贝值。由于这些值来自的系统,0被用作“有用数据到此为止”的指示符(这是数学性质,而不是一个固定值)。
我通常处理这些情况的方法是把转换放在一个try/except块里,然后返回一个默认的“低”值,比如:
def f(value):
try:
return convert(value)
except ValueError:
return -140 #implementation specific, don't worry
这样处理在我框架的90%使用场景中都没问题,除了在绘图的时候。
我比较懒,所以现在我做的是:
pl.plot(xvals,map(f,yvals))
这样可以正确绘制数据,当数据结束时就会突然掉下去,这也是预期的行为。但我希望的是,当遇到ValueError异常时,图表就直接结束,完全不需要f()这个函数。
除了把图分成一个个循环,有没有人有什么好主意?
更新:
我在使用Pylab / Matplotlib。“结束点”是依赖于执行的;有时候上面的处理根本不重要,因为没有“坏”值。这一切都是为了让我懒一点,使用matplotlib的图表缩放,而不是根据y数据的最小值重置动态的ylim(目前我不这样做,只是ylim(-140))。
关于答案的稍微重要的更新: unutbu的答案是我实际会用到的实现方式,因为(在问题中没有提到的依赖关系),在这个常用函数中抛出StopIteration会对与问题无关的控制逻辑造成混乱,而不把所有其他情况放在try-except里;有时候-inf比你想象的更有意义。
感谢大家的快速回复,也对unutbu表示歉意,抱歉问题没问好。
相关问题:
4 个回答
你在拒绝使用循环结构的时候,实际上是在给自己设限。
在你的情况下,当达到某个特定值时,你想停止遍历数据,这正是forloops(循环)和breaks(中断)的用途。
yvals_ = []
for y in yvals:
y_ = f(y)
if y_ == -140:
break
else:
yvals_.append(y_)
p1.plot(xvals[:len(yvals_)],yvals_)
如果你在使用 matplotlib,那么说明你已经安装了 numpy。
因为你要转换成 dB,这听起来像是你可能在进行对数运算。在这种情况下,np.log(0) = -inf,也就是说对0取对数会得到负无穷。
你可以用 np.ma.masked_invalid 这个numpy函数来处理无效值(nans)和无穷大(infs),而 matplotlib 可以绘制被屏蔽的数组。例如,
import matplotlib.pyplot as plt
import numpy as np
xvals=np.arange(100)
yvals=np.cumsum(np.random.random(100))
yvals[-10:]=0
yvals=np.log(yvals)
print(yvals[-10:])
# [-Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf -Inf]
yvals=np.ma.masked_invalid(yvals)
plt.plot(xvals,yvals)
plt.show()
这样做会得到

注意,图表的结束点是 xval 等于89,因为最后10个 yval 的值被屏蔽了。
也许在绘图库里有一些技巧,但更好的办法是干脆不要生成这样的数据。并不是说用 map 可以省下三十行代码……
可以使用 itertools.takewhile(lambda y: y != NO_VALUE, (f(y) for y in yvals))(如果绘图库需要列表而不是可迭代对象,可以把它包裹在 list 里)。
补充:我还有一个更好的主意:在包装函数里加上
except ValueError:
raise StopIteration
这就是一个异常,表示“迭代结束”,而 map 会尊重这个信号。