Python sicpy interp1d更精确

Python sicpy interp1d更精确,python,scipy,Python,Scipy,我试图实现一个非参数的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-i

我试图实现一个非参数的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 

如何获得更精确的插值?

随着
e
变得越来越小,您正在有效地计算
p
Q
的导数的比率。正如您所发现的,以这种方式进行浮点运算时,精度很快就会耗尽


另一种方法是使用可以直接返回导数的插值函数。例如,你可以试试。你说的是
kind='linear'
interp1d
,因此等价物是
k=1
。构造样条曲线后,样条曲线的方法将为您提供不同点的所有导数。对于
e
的较小值,您可以切换到使用导数。

e
变小时,您将有效地尝试以数字方式计算
p
Q
的导数比率。正如您所发现的,以这种方式进行浮点运算时,精度很快就会耗尽

另一种方法是使用可以直接返回导数的插值函数。例如,你可以试试。你说的是
kind='linear'
interp1d
,因此等价物是
k=1
。构造样条曲线后,样条曲线的方法将为您提供不同点的所有导数。对于
e
的小值,可以切换到使用导数