2012-12-03 100 views
12

我有几个情节看起来像以下:的Python:精确定位斜率的直线部分

enter image description here

我想知道什么样的方法有可能是发现约5.5和8之间的斜率为x轴。如果有几个这样的情节,我更想知道是否有办法自动找到斜率值。

有什么建议吗?

我在想ployfit()或线性回归。问题是我不确定如何自动查找值。

+2

5.5和8是固定的,还是你需要自动找到它们呢? – NPE

+1

c.f. http://stackoverflow.com/questions/9300430/algorithm-is-there-a-linear-trend-in-data?rq=1 – Dave

+0

5.5和8只是基于看图的估计。他们确实显示我将在哪里寻找计算斜率。 – user1620716

回答

0

如果数据的“模型”由大部分符合直线的数据组成,并且末尾有几个异常值或摆动位,则可以尝试使用RANSAC算法。

的(很罗嗦,不好意思)这里伪是:

choose a small threshold distance D 

for N iterations: 
    pick two random points from your data, a and b 
    fit a straight line, L, to a and b 
    count the inliers: data points within a distance D of the line L 
    save the parameters of the line with the most inliers so far 

estimate the final line using ALL the inliers of the best line 
1

这只是一个可能的解决方案时,会发现其中有最小的志^ 2值是大于预设的最低点的直线段;

from matplotlib.pyplot import figure, show 
from numpy import pi, sin, linspace, exp, polyfit 
from matplotlib.mlab import stineman_interp 

x = linspace(0,2*pi,20); 
y = x + sin(x) + exp(-0.5*(x-2)**2); 

num_points = len(x) 

min_fit_length = 5 

chi = 0 

chi_min = 10000 

i_best = 0 
j_best = 0 

for i in range(len(x) - min_fit_length): 
    for j in range(i+min_fit_length, len(x)): 

     coefs = polyfit(x[i:j],y[i:j],1) 
     y_linear = x * coefs[0] + coefs[1] 
     chi = 0 
     for k in range(i,j): 
      chi += (y_linear[k] - y[k])**2 

     if chi < chi_min: 
      i_best = i 
      j_best = j 
      chi_min = chi 
      print chi_min 

coefs = polyfit(x[i_best:j_best],y[i_best:j_best],1) 
y_linear = x[i_best:j_best] * coefs[0] + coefs[1] 


fig = figure() 
ax = fig.add_subplot(111) 
ax.plot(x,y,'ro') 
ax.plot(x[i_best:j_best],y_linear,'b-') 


show() 

enter image description here

我能看到它获得为虽然更大的数据集问题...

22

一个通用的方法来查找数据集的线性部分是计算函数的二阶导数,并看看它在哪里(接近)零。在解决方案的过程中需要考虑几件事情:

  • 如何计算噪声数据的二阶导数?一种可以很容易地适应不同噪声级别,数据集大小和线性补丁预期长度的快速简单方法是将数据与卷积核卷积,该卷积核等于高斯的二阶导数。可调部分是内核的宽度。

  • 在您的情况下,“接近于零”意味着什么?要回答这个问题,你必须试验你的数据。

  • 该方法的结果可用作上述chi^2方法的输入,以识别数据集中的候选区域。

这里一些源代码,将让你开始:

from matplotlib import pyplot as plt 

import numpy as np 

# create theoretical data 
x_a = np.linspace(-8,0, 60) 
y_a = np.sin(x_a) 
x_b = np.linspace(0,4,30)[1:] 
y_b = x_b[:] 
x_c = np.linspace(4,6,15)[1:] 
y_c = np.sin((x_c - 4)/4*np.pi)/np.pi*4. + 4 
x_d = np.linspace(6,14,120)[1:] 
y_d = np.zeros(len(x_d)) + 4 + (4/np.pi) 

x = np.concatenate((x_a, x_b, x_c, x_d)) 
y = np.concatenate((y_a, y_b, y_c, y_d)) 


# make noisy data from theoretical data 
y_n = y + np.random.normal(0, 0.27, len(x)) 

# create convolution kernel for calculating 
# the smoothed second order derivative 
smooth_width = 59 
x1 = np.linspace(-3,3,smooth_width) 
norm = np.sum(np.exp(-x1**2)) * (x1[1]-x1[0]) # ad hoc normalization 
y1 = (4*x1**2 - 2) * np.exp(-x1**2)/smooth_width *8#norm*(x1[1]-x1[0]) 



# calculate second order deriv. 
y_conv = np.convolve(y_n, y1, mode="same") 

# plot data 
plt.plot(x,y_conv, label = "second deriv") 
plt.plot(x, y_n,"o", label = "noisy data") 
plt.plot(x, y, label="theory") 
plt.plot(x, x, "0.3", label = "linear data") 
plt.hlines([0],-10, 20) 
plt.axvspan(0,4, color="y", alpha=0.2) 
plt.axvspan(6,14, color="y", alpha=0.2) 
plt.axhspan(-1,1, color="b", alpha=0.2) 
plt.vlines([0, 4, 6],-10, 10) 
plt.xlim(-2.5,12) 
plt.ylim(-2.5,6) 
plt.legend(loc=0) 
plt.show() 

这是结果: enter image description here

smooth_width是卷积核的宽度。为了调整噪声量,请将random.normal中的值0.27更改为不同的值。请注意,这种方法在靠近数据空间边界时效果不佳。

正如您所看到的,二阶导数(蓝线)的“接近零”要求对于黄色部分(数据为线性部分)非常适用。