Python; 异常感知的 map()

9 投票
4 回答
4015 浏览
提问于 2025-04-16 14:45

我正在用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 个回答

1

你在拒绝使用循环结构的时候,实际上是在给自己设限。

在你的情况下,当达到某个特定值时,你想停止遍历数据,这正是forloops(循环)和breaks(中断)的用途。

yvals_ = []
for y in yvals:
    y_ = f(y)
    if y_ == -140:
        break
    else:
        yvals_.append(y_)

p1.plot(xvals[:len(yvals_)],yvals_)
2

如果你在使用 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()

这样做会得到 enter image description here

注意,图表的结束点是 xval 等于89,因为最后10个 yval 的值被屏蔽了。

10

也许在绘图库里有一些技巧,但更好的办法是干脆不要生成这样的数据。并不是说用 map 可以省下三十行代码……

可以使用 itertools.takewhile(lambda y: y != NO_VALUE, (f(y) for y in yvals))(如果绘图库需要列表而不是可迭代对象,可以把它包裹在 list 里)。

补充:我还有一个更好的主意:在包装函数里加上

except ValueError:
    raise StopIteration

这就是一个异常,表示“迭代结束”,而 map 会尊重这个信号。

撰写回答