2013-04-07 53 views
6

我有一个宇宙射线探测器的能谱。光谱遵循指数曲线,但它会有广泛的(也许非常轻微)肿块。显然,这些数据包含了一些噪音。嘈杂的数据中的渐变,python

我想平滑数据,然后绘制其渐变。 到目前为止,我一直在使用scipy sline函数来平滑它,然后使用np.gradient()。

正如您从图片中看到的,梯度函数的方法是找出每个点之间的差异,并且它不会非常清楚地显示块。

我基本上需要一个平滑的梯度图。任何帮助将是惊人的!

我已经试过2样条方法:

def smooth_data(y,x,factor): 
    print "smoothing data by interpolation..." 
    xnew=np.linspace(min(x),max(x),factor*len(x)) 
    smoothy=spline(x,y,xnew) 
    return smoothy,xnew 

def smooth2_data(y,x,factor): 
    xnew=np.linspace(min(x),max(x),factor*len(x)) 
    f=interpolate.UnivariateSpline(x,y) 
    g=interpolate.interp1d(x,y) 
    return g(xnew),xnew 

编辑:尝试数值微分:

def smooth_data(y,x,factor): 
    print "smoothing data by interpolation..." 
    xnew=np.linspace(min(x),max(x),factor*len(x)) 
    smoothy=spline(x,y,xnew) 
    return smoothy,xnew 

def minim(u,f,k): 
    """"functional to be minimised to find optimum u. f is original, u is approx""" 
    integral1=abs(np.gradient(u)) 
    part1=simps(integral1) 
    part2=simps(u) 
    integral2=abs(part2-f)**2. 
    part3=simps(integral2) 
    F=k*part1+part3 
    return F 


def fit(data_x,data_y,denoising,smooth_fac): 
    smy,xnew=smooth_data(data_y,data_x,smooth_fac) 
    y0,xnnew=smooth_data(smy,xnew,1./smooth_fac) 
    y0=list(y0) 
    data_y=list(data_y) 
    data_fit=fmin(minim, y0, args=(data_y,denoising), maxiter=1000, maxfun=1000) 
    return data_fit 

然而,它只是再次返回相同的图形!

Data, smoothed data and gradients

+0

什么级别的平滑将使你的感觉呢?产生介于-10和+1之间的导数,其中大部分值介于-1和+1之间? – EOL 2013-04-08 04:22:18

+0

附注:我建议你阅读并应用[PEP 8](http://www.python.org/dev/peps/pep-0008/)到你的编码风格。这将使您的代码更易于阅读,因为大多数Python程序员都遵循它(或其中的很大一部分)。像参数列表中的'='周围的空格或参数列表中的逗号之后的细节确实使代码更清晰。 – EOL 2013-04-09 11:26:39

回答

8

有这个发表有趣的方法Numerical Differentiation of Noisy Data。它应该给你一个很好的解决你的问题。更多细节在另一个,accompanying paper。作者还给出了Matlab code that implements it;替代品implementation in Python也是可用的。

如果你想追求带有样条线方法的插值,我建议调整scipy.interpolate.UnivariateSpline()的平滑因子s

另一种解决方案是通过卷积(比如用高斯表示)来平滑你的函数。

我链接到的论文声称可以防止某些与卷积方法(样条方法可能会遇到类似困难)相关的工件。

+0

我尝试了数值微分方法: 查看新附件 – Lucidnonsense 2013-04-07 19:29:21

+0

行'part2 = simps(u)'不正确:'part2'应该是一个数组,其中包含u从0 *到每个横坐标*的积分。那么你应该尝试*指数变化*平滑系数,以找到最适合您的需求的系数。如果你真的通过考虑x中的步长来计算导数,那么我期望一个好的平滑因子约为1e6,对于一个主要介于-1和+ 1之间的导数 - 虽然我可能是错的,但你可能想要无论如何要尝试这个值,以防万一我的信封计算是正确的。 – EOL 2013-04-08 04:50:36

+0

谢谢,我会尽力的! – Lucidnonsense 2013-04-08 11:10:29

3

我不会担保此数学有效性;它看起来像是LANL的论文,EOL引用这将是值得研究的。无论如何,当使用splev时,我使用SciPy样条的内置差异获得了不错的结果。

%matplotlib inline 
from matplotlib import pyplot as plt 
import numpy as np 
from scipy.interpolate import splrep, splev 

x = np.arange(0,2,0.008) 
data = np.polynomial.polynomial.polyval(x,[0,2,1,-2,-3,2.6,-0.4]) 
noise = np.random.normal(0,0.1,250) 
noisy_data = data + noise 

f = splrep(x,noisy_data,k=5,s=3) 
#plt.plot(x, data, label="raw data") 
#plt.plot(x, noise, label="noise") 
plt.plot(x, noisy_data, label="noisy data") 
plt.plot(x, splev(x,f), label="fitted") 
plt.plot(x, splev(x,f,der=1)/10, label="1st derivative") 
#plt.plot(x, splev(x,f,der=2)/100, label="2nd derivative") 
plt.hlines(0,0,2) 
plt.legend(loc=0) 
plt.show() 

matplotlib output

+0

对于非均匀分布的数据可以使用这种方法吗?我可以为X和Y添加我的测量集吗? – Spu 2015-10-20 08:27:12

+0

此处使用的函数('scipy.interpolate.splrep()')的文档没有提到对非均匀分布的数据的任何限制。除了查看文档之外,您还可以自己尝试通过更改代码中'x'的值。更一般地说,值得赞赏的是,在回答你自己的问题时,在堆栈溢出时做出一些明显的努力,以便节省其他人一些时间(并使他们更有可能花时间回答你的问题)。 – EOL 2015-10-20 19:48:05

+1

@Spu,是的!我在两天前使用'splrep'来执行采样数据的三次b样条插值,这些样本数据是以非均匀间隔采集的,因此我可以执行FFT。 – billyjmc 2015-10-21 15:42:13