使用sicpy interp1d更准确

时间:2017-07-11 19:28:39

标签: python scipy

我正在尝试实现此paper

中显示的KL分歧的非参数估计

这是我的代码:

import numpy as np
import math
import itertools
import random
from scipy.interpolate import interp1d

def log(x):
    if x > 0: return math.log(x)
    else: return 0

g = lambda x, inp,N : sum(0.5 + 0.5 * np.sign(x-inp))/N

def ecdf(x,N):
    out = [g(i,x,N) for i in x]
    fun = interp1d(x, out, kind='linear', bounds_error = False, fill_value = (0,1))
    return fun

def KL_est(x,y):
    ex = min(np.diff(sorted(np.unique(x))))
    ey = min(np.diff(sorted(np.unique(y))))
    e = min(ex,ey) * 0.9
    N = len(x)
    x.sort()
    y.sort()
    P = ecdf(x,N)
    Q = ecdf(y,N)
    KL = sum(log(v) for v in ((P(x)-P(x-e))/(Q(x)-Q(x-e))) ) / N
    return KL

我的麻烦在于scipy interp1d。我正在使用interp1d返回的函数来查找新输入的值。问题是,一些输入值非常接近(相隔10 ^ -5),并且函数返回两者的相同值。在上面的代码中,Q(x) - Q(x-e)导致除以零的错误。

以下是一些可以重现问题的测试代码:

x = np.random.normal(0, 1, 10)
y = np.random.normal(0, 1, 10)
ex = min(np.diff(sorted(np.unique(x))))
ey = min(np.diff(sorted(np.unique(y))))
e = min(ex,ey) * 0.9
N = len(x)
x.sort()
y.sort()
P = ecdf(x,N)
Q = ecdf(y,N)
KL = sum(log(v) for v in ((P(x)-P(x-e))/(Q(x)-Q(x-e))) ) / N 

我如何获得更精确的插值?

1 个答案:

答案 0 :(得分:2)

e变小时,您实际上是在尝试以数字方式计算PQ的导数的比率。正如您所发现的那样,在浮点运行时,您会以这种方式快速耗尽精度。

另一种方法是使用可以直接返回导数的插值函数。例如,您可以尝试scipy.interpolate.InterpolatedUnivariateSpline。您说的是kind='linear'interp1d,因此等效于k=1。构造它之后,样条曲线有方法derivatives(),它为您提供不同点的所有导数。对于较小的e值,您可以切换到使用派生词。