跳转至

警惕 SMOOTH 函数的行为

原文链接:https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/beware-the-behavior-of-smooth

16554 为本文评分:

1.5

警惕 SMOOTH 函数的行为

匿名 2013年5月30日,星期四

假设我有一个包含异常值的数组。一个非常大的异常值:



IDL> a = [1.0, 1.0, 2.0, 3.0, 4.0, 1.0d18, 4.0, 3.0, 2.0, 1.0, 1.0]

我想使用滑动平均(或称框状滤波、顶帽滤波,具体叫法取决于您学习的背景)滤波器对这个双精度数组进行平滑处理,例如使用 IDL 的 SMOOTH 函数。为了效率,SMOOTH 函数将核宽度除以相邻值之间差值的滑动总和。以下是将 SMOOTH 应用于数组 a、滤波器宽度为 3 的结果:



IDL> print, smooth(a, 3)

       1.0000000       1.3333333       2.0000000       3.0000000  3.3333333e+017

  3.3333333e+017  3.3333333e+017      0.00000000      -1.0000000      -1.6666667

       1.0000000

哇。输入数组是对称的,为什么输出不对称?此外——这令人担忧——输入数组由正数组成,那么这些数字的任何子集的平均值怎么会是负数呢?答案在于计算机上浮点数的表示方式。为了理解原因,我们可以使用 MACHAR 函数返回的信息:



IDL> m = machar(/double)

IDL> help, m

** 结构体 DMACHAR,13 个标签,长度 72,数据长度 68:

IBETA           LONG                 2
IT              LONG                53
IRND            LONG                 5
NGRD            LONG                 0
MACHEP          LONG               -52
NEGEP           LONG               -53
IEXP            LONG                11
MINEXP          LONG             -1022
MAXEXP          LONG              1024
EPS             DOUBLE     2.2204460e-016
EPSNEG          DOUBLE     1.1102230e-016
XMIN            DOUBLE     2.2250739e-308
XMAX            DOUBLE     1.7976931e+308

使用 IBETA 字段(给出构造数字所用的基数,2,意料之中)和 IT 字段(给出数字尾数中使用的基数为 2 的位数),两个双精度数字之间可解析的最大距离必然由下式给出:



IDL> mrd = double(m.ibeta)^m.it

IDL> print, mrd

  9.0071993e+015

因此,当 SMOOTH 尝试对距离大于 MRD 的两个数字求差时,由于精度损失,可能会发生糟糕的事情。让我们将这些信息应用到上面的例子中。定义略高于和略低于 MRD 设定的阈值的值:



IDL> below = double(m.ibeta)^(m.it-1)

IDL> above = double(m.ibeta)^(m.it+1)

IDL> print, below, above

  4.5035996e+015  1.8014399e+016

并将它们代入上面使用的数组中:



IDL> b = [1.0, 1.0, 2.0, 3.0, 4.0, below, 4.0, 3.0, 2.0, 1.0, 1.0]

IDL> c = [1.0, 1.0, 2.0, 3.0, 4.0, above, 4.0, 3.0, 2.0, 1.0, 1.0]

现在对这两个数组应用 SMOOTH 并评估结果:



IDL> print, smooth(b, 3)

       1.0000000       1.3333333       2.0000000       3.0000000  1.5011999e+015

  1.5011999e+015  1.5011999e+015       3.0000000       2.0000000       1.3333333

       1.0000000

IDL> print, smooth(c, 3)

       1.0000000       1.3333333       2.0000000       3.0000000  6.0047995e+015

  6.0047995e+015  6.0047995e+015       3.3333333       2.3333333       1.6666667

       1.0000000

注意,SMOOTH 应用于数组 b 时效果良好,但应用于 c 时则不然——结果不对称。

IDL 帮助页面中 SMOOTH 函数的 关于平滑处理大范围数据的注意事项 部分对此行为进行了更好的讨论,并提供了针对这种情况的解决方案。

更新(2013-07-01):我忘了提,这篇博文的灵感以及帮助中的说明,都源于几年前与 DRDC Atlantic 的 Carmen Lucas 的讨论。感谢 Carmen 向我指出这个意外行为。

共同理念与新方法 开源软件是自由软件吗?