我有来自宇宙射线探测器的能谱。光谱遵循指数曲线,但它会有宽的(也可能是非常轻微的)肿块。显然,数据包含噪声元素。
我正在尝试平滑数据,然后绘制其渐变。 到目前为止,我一直在使用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
但是,它只会再次返回相同的图形!
答案 0 :(得分:9)
在此发布了一个有趣的方法:Numerical Differentiation of Noisy Data。它应该为您的问题提供一个很好的解决方案。更多细节在另一个accompanying paper中给出。作者还给出了Matlab code that implements it;另外一个implementation in Python也可用。
如果您想使用样条方法进行插值,我建议您调整s
的平滑因子scipy.interpolate.UnivariateSpline()
。
另一个解决方案是通过卷积(比如高斯)来平滑你的功能。
我链接的论文声称可以防止出现卷积方法产生的一些伪影(样条方法可能会遇到类似的困难)。
答案 1 :(得分: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()
答案 2 :(得分:1)
您也可以使用scipy.signal.savgol_filter
。
import matplotlib.pyplot as plt
import numpy as np
import scipy
from random import random
# generate data
x = np.array(range(100))/10
y = np.sin(x) + np.array([random()*0.25 for _ in x])
dydx = scipy.signal.savgol_filter(y, window_length=11, polyorder=2, deriv=1)
# Plot result
plt.plot(x, y, label='Original signal')
plt.plot(x, dydx*10, label='1st Derivative')
plt.plot(x, np.cos(x), label='Expected 1st Derivative')
plt.legend()
plt.show()