2012-12-03 98 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更改爲不同的值。請注意,這種方法在靠近數據空間邊界時效果不佳。

正如您所看到的,二階導數(藍線)的「接近零」要求對於黃色部分(數據爲線性部分)非常適用。