对于非常小的 y 值,InterpolatedUniveriateSpline 和 interp1d 三次样条未通过所有点?

InterpolatedUniveriateSpline and interp1d cubic splines not passing through all points for very small y values?

我一直在研究在 scipy 中使用三次样条平滑某些概率密度函数 (PDF),但我遇到了 scipy.interpolate.InterpolatedUnivariateSplinescipy.interpolate.interp1d 没有通过的问题当我的 y 值很小时所有的结。

import math
import pandas as pd
import numpy as np
from scipy.stats import norm
from scipy.interpolate import InterpolatedUnivariateSpline, interp1d


def main():
    breadth = 0.60
    stepsize = 0.01
    mean = 0.0
    stdev = 0.02

    steps = 2. * breadth / stepsize + 1
    retRange = np.linspace(-breadth, breadth, num=steps)
    probs = {}
    for ret in retRange:
        retup = ret + stepsize / 2.
        retdown = ret - stepsize / 2.
        probup = norm.cdf(retup, loc=mean, scale=stdev)
        probdown = norm.cdf(retdown, loc=mean, scale=stdev)
        prob = probup - probdown
        probs[ret] = prob
    probs = pd.Series(probs, name='PDF')
    probs = probs / probs.sum()

    spl = InterpolatedUnivariateSpline(probs.index, probs, k=3, ext='zeros')
    tmp = pd.Series(index=retRange, data=retRange, name='SmoothedPDF').map(spl)

    out = pd.concat([probs, tmp], axis=1)
    out['diff'] = out['PDF'] - out['SmoothedPDF']
    print out

if __name__ == '__main__':
    main()

问题是输出的 DataFrame 显示出差异。我可以用 interp1d 代替 InterpolateUnivariateSpline 并得到相同的问题(尽管结果不同)。这个问题值得注意,因为在结点处插值的 y 值的差异可能大于原始 y 值。

                 PDF   SmoothedPDF          diff
-0.60  8.670691e-195 -2.650272e-50  2.650272e-50
-0.59  2.245130e-188  2.120217e-49 -2.120217e-49
-0.58  4.528786e-182 -9.806006e-49  9.806006e-49
-0.57  7.116703e-176  3.816392e-48 -3.816392e-48
-0.56  8.712391e-170 -1.431147e-47  1.431147e-47
-0.55  8.309255e-164  5.342948e-47 -5.342948e-47
-0.54  6.173881e-158 -1.994065e-46  1.994065e-46
-0.53  3.573809e-152  7.441963e-46 -7.441963e-46
-0.52  1.611710e-146 -2.777379e-45  2.777379e-45
-0.51  5.662799e-141  1.036532e-44 -1.036532e-44
-0.50  1.550138e-135 -3.868390e-44  3.868390e-44
-0.49  3.306066e-130  1.443703e-43 -1.443703e-43
-0.48  5.493659e-125 -5.387972e-43  5.387972e-43
-0.47  7.112603e-120  2.010818e-42 -2.010818e-42
-0.46  7.174975e-115 -7.504477e-42  7.504477e-42
-0.45  5.639566e-110  2.800709e-41 -2.800709e-41
-0.44  3.453932e-105 -1.045239e-40  1.045239e-40
-0.43  1.648294e-100  3.900884e-40 -3.900884e-40
-0.42   6.129407e-96 -1.455830e-39  1.455830e-39
-0.41   1.776139e-91  5.433231e-39 -5.433231e-39
-0.40   4.010714e-87 -2.027709e-38  2.027709e-38
-0.39   7.057745e-83  7.567514e-38 -7.567514e-38
-0.38   9.678846e-79 -2.824235e-37  2.824235e-37
-0.37   1.034450e-74  1.054019e-36 -1.054019e-36
-0.36   8.616667e-71 -3.933652e-36  3.933652e-36
-0.35   5.594107e-67  1.468059e-35 -1.468059e-35
-0.34   2.830755e-63 -5.478870e-35  5.478870e-35
-0.33   1.116539e-59  2.044742e-34 -2.044742e-34
-0.32   3.432949e-56 -7.631082e-34  7.631082e-34
-0.31   8.228222e-53  2.847958e-33 -2.847958e-33
...              ...           ...           ...
 0.31   0.000000e+00 -7.461634e-41  7.461634e-41
 0.32   0.000000e+00 -3.730817e-41  3.730817e-41
 0.33   0.000000e+00 -5.022254e-42  5.022254e-42
 0.34   0.000000e+00  3.407958e-42 -3.407958e-42
 0.35   0.000000e+00  8.968310e-43 -8.968310e-43
 0.36   0.000000e+00 -2.578389e-43  2.578389e-43
 0.37   0.000000e+00 -1.177091e-43  1.177091e-43
 0.38   0.000000e+00  9.809089e-45 -9.809089e-45
 0.39   0.000000e+00 -3.152922e-45  3.152922e-45
 0.40   0.000000e+00 -4.816963e-46  4.816963e-46
 0.41   0.000000e+00 -6.568587e-47  6.568587e-47
 0.42   0.000000e+00  4.926440e-47 -4.926440e-47
 0.43   0.000000e+00  9.579189e-48 -9.579189e-48
 0.44   0.000000e+00  3.763253e-48 -3.763253e-48
 0.45   0.000000e+00  5.131708e-49 -5.131708e-49
 0.46   0.000000e+00 -1.924391e-49  1.924391e-49
 0.47   0.000000e+00 -1.603659e-49  1.603659e-49
 0.48   0.000000e+00 -2.405488e-50  2.405488e-50
 0.49   0.000000e+00 -1.202744e-50  1.202744e-50
 0.50   0.000000e+00 -1.586954e-51  1.586954e-51
 0.51   0.000000e+00 -2.923336e-52  2.923336e-52
 0.52   0.000000e+00 -3.132146e-53  3.132146e-53
 0.53   0.000000e+00  2.610122e-54 -2.610122e-54
 0.54   0.000000e+00  3.915183e-54 -3.915183e-54
 0.55   0.000000e+00 -1.631326e-54  1.631326e-54
 0.56   0.000000e+00 -1.386627e-54  1.386627e-54
 0.57   0.000000e+00 -2.650905e-55  2.650905e-55
 0.58   0.000000e+00  1.835242e-55 -1.835242e-55
 0.59   0.000000e+00  4.078315e-56 -4.078315e-56
 0.60   0.000000e+00  0.000000e+00  0.000000e+00

[121 rows x 3 columns]

是的,我正在研究一些概率非常低的事情,因此规模确实(有时)那么小。

如果这是一个精度问题,我可以进行一些重新缩放,但在这样做之前我想确认这是问题所在。

附带说明一下,使用线性插值 (k=1) 不会显示此问题。

问题似乎是您尝试使用的样条插值器全局插值,这非常不适合您的输入。由于指数截止,由于双精度,您的许多值恰好为零;在此范围内,样条插值器会给您带来噪音。

这是问题的数值表示,通过在半对数刻度上绘制 PDF 及其样条插值(请注意,在 PDF 恰好为零的地方,您会在对数图中丢失点):

import matplotlib.pyplot as plt

plt.figure()
plt.semilogy(out.index, out.PDF, 'r-', out.index, out.SmoothedPDF.abs(), 'bo--',
             markerfacecolor='none') # note the abs for log
plt.legend(['PDF', 'SmoothedPDF (univariate spline)'])
plt.show()

首先,我需要对内插值调用 .abs() 以绘制 负数 值(我们正在内插一个非零函数!)。其次,正如我已经指出的那样,x=0.2 以上的范围应该缺失,因为这里的函数值恰好为零。相反,我们有 1e-50 数量级的噪声,就像在 small-x 极限上一样。总之,我们可以看到,如果您的数据小于 1e-30,您就没有机会了。

我的第一个想法是使用 griddata,但它显示了同样糟糕的结果(尽管在那种情况下数字错误要小得多)。那么使用 local 插值器怎么样?输入piecewise cubic Hermite spline。使用稍微修改过的代码版本,以演示插值和数值稳定性:

import math
import pandas as pd
import numpy as np
from scipy.stats import norm
from scipy.interpolate import pchip_interpolate
import matplotlib.pyplot as plt


def main():
    breadth = 0.60
    stepsize = 0.01
    mean = 0.0
    stdev = 0.02

    steps = 2. * breadth / stepsize + 1
    bigRange = np.linspace(-breadth, breadth, num=5*steps)
    retRange = bigRange[::5]
    probs = {}
    for ret in retRange:
        retup = ret + stepsize / 2.
        retdown = ret - stepsize / 2.
        probup = norm.cdf(retup, loc=mean, scale=stdev)
        probdown = norm.cdf(retdown, loc=mean, scale=stdev)
        prob = probup - probdown
        probs[ret] = prob
    probs = pd.Series(probs, name='PDF')
    probs = probs / probs.sum()

    tmp_big = pd.Series(index=bigRange,
                  data=pchip_interpolate(probs.index.values, probs.values, bigRange),
                  name='SmoothedPDF_big') # denser interpolated case
    tmp_check = pd.Series(index=retRange,
                    data=pchip_interpolate(probs.index.values, probs.values, retRange),
                    name='SmoothedPDF') # base points for checking stability

    out = pd.concat([probs, tmp_check], axis=1)
    out['diff'] = out['PDF'] - out['SmoothedPDF']

    return out,tmp_big

if __name__ == "__main__":    
    out,pchipout = main()
    plt.figure()
    plt.semilogy(out.index, out.PDF, 'r-', pchipout.index, pchipout.abs().values, 'bo--',
                 markerfacecolor='none')
    plt.legend(['PDF', 'SmoothedPDF (piecewise cubic Hermit spline)'])

    plt.figure()
    plt.plot(out.index, out.PDF, 'r-', pchipout.index, pchipout.values, 'bo--',
             markerfacecolor='none')
    plt.legend(['PDF', 'SmoothedPDF (piecewise cubic Hermit spline)'])


    plt.show()

稳定性证明:

>>> out['diff'].any()
False

误差尺度的对数图:

如你所见,协议几乎是完美的。请注意,在您的原始数据恰好为 0 的地方明显缺少值。在您必须学习的 small-x 范围内,噪声仍然低于 1e-100 的值。

插值结果的实际图: