有没有办法通过分散的X,Y坐标绘制“最紧密”的最佳拟合线?

时间:2019-05-23 14:09:25

标签: python numpy matplotlib curve-fitting

我无法在数据中拟合一条平均曲线以找到长度。在一个大熊猫数据框中,我有很多X,Y点,看起来像:

x = np.asarray([731501.13, 731430.24, 731360.29, 731289.36, 731909.72, 731827.89,
   731742.  , 731657.74, 731577.95, 731502.64, 731430.39, 731359.12,
   731287.3 , 731214.21, 732015.59, 731966.88, 731902.67, 731826.31,
   731743.79, 731660.94, 731581.29, 731505.4 , 731431.95, 732048.71,
   732026.66, 731995.46, 731952.18, 731894.29, 731823.58, 731745.16,
   732149.61, 732091.53, 732052.98, 732026.82, 732005.17, 731977.63,
   732691.84, 732596.62, 732499.45, 732401.62, 732306.18, 732218.35,
   732141.82, 732080.91, 732038.21, 732009.08, 733023.08, 732951.99,
   732873.32, 732787.51])

y = np.asarray([7873771.69, 7873705.34, 7873638.03, 7873571.73, 7874082.33,
   7874027.2 , 7873976.22, 7873923.58, 7873866.35, 7873804.53,
   7873739.58, 7873673.62, 7873608.23, 7873544.15, 7874286.21,
   7874197.15, 7874123.96, 7874063.21, 7874008.78, 7873954.69,
   7873897.31, 7873836.09, 7873772.38, 7874564.62, 7874448.23,
   7874341.23, 7874246.59, 7874166.93, 7874100.4 , 7874041.77,
   7874912.56, 7874833.09, 7874733.62, 7874621.43, 7874504.65,
   7874393.89, 7875225.26, 7875183.85, 7875144.42, 7875105.69,
   7875064.49, 7875015.5 , 7874954.94, 7874878.36, 7874783.13,
   7874674.  , 7875476.18, 7875410.05, 7875351.67, 7875300.61])

x和y是地图视图的坐标,我想计算长度。我可以编码欧几里德距离,但是由于这些点是分散的并且不是一个接一个的点,因此在尝试通过该点拟合移动线时遇到了麻烦。我已经尝试过polyfit,但是即使度数较高,也可以产生一条直线,例如:

from numpy.polynomial.polynomial import polyfit
import numpy as np
import matplotlib.pyplot as plt
z = np.polyfit(x,y,10) 
p = np.poly1d(z)
plt.scatter(x,y, marker='x')
plt.scatter(x, p(x), marker='.')

plt.show()

这是为了证明我的意思1

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:2)

这将是适合您数据的经验函数:

import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit


x = np.asarray([731501.13, 731430.24, 731360.29, 731289.36, 731909.72, 731827.89,
   731742.  , 731657.74, 731577.95, 731502.64, 731430.39, 731359.12,
   731287.3 , 731214.21, 732015.59, 731966.88, 731902.67, 731826.31,
   731743.79, 731660.94, 731581.29, 731505.4 , 731431.95, 732048.71,
   732026.66, 731995.46, 731952.18, 731894.29, 731823.58, 731745.16,
   732149.61, 732091.53, 732052.98, 732026.82, 732005.17, 731977.63,
   732691.84, 732596.62, 732499.45, 732401.62, 732306.18, 732218.35,
   732141.82, 732080.91, 732038.21, 732009.08, 733023.08, 732951.99,
   732873.32, 732787.51])/732 -1000

y = np.asarray([7873771.69, 7873705.34, 7873638.03, 7873571.73, 7874082.33,
   7874027.2 , 7873976.22, 7873923.58, 7873866.35, 7873804.53,
   7873739.58, 7873673.62, 7873608.23, 7873544.15, 7874286.21,
   7874197.15, 7874123.96, 7874063.21, 7874008.78, 7873954.69,
   7873897.31, 7873836.09, 7873772.38, 7874564.62, 7874448.23,
   7874341.23, 7874246.59, 7874166.93, 7874100.4 , 7874041.77,
   7874912.56, 7874833.09, 7874733.62, 7874621.43, 7874504.65,
   7874393.89, 7875225.26, 7875183.85, 7875144.42, 7875105.69,
   7875064.49, 7875015.5 , 7874954.94, 7874878.36, 7874783.13,
   7874674.  , 7875476.18, 7875410.05, 7875351.67, 7875300.61])/7873 - 1000

def my_func( x, x0, y0, a, b, c, t, s):
    xs = x-x0
    p = a * xs**3 + b * xs**2 + c * xs + y0
    t = t * np.tanh( s * xs )
    return p + t

xth = np.linspace( -1.15, 1.5, 50 )
yth = my_func( xth, 0.03, 0.18, .01, 0, 0.05, .05 , 10)

sol, err = curve_fit( my_func, x, y, p0=[0.03, 0.18, .01, 0, 0.05, .05 , 10] ) 
print sol 
fig = plt.figure()
ax = fig.add_subplot( 1, 1, 1 )
ax.scatter( x, y )
ax.plot( xth, yth )
ax.plot( xth, my_func( xth, *sol) )
plt.show()

给予

>>[ 2.86281016e-02  1.95292660e-01  9.62290944e-03 -1.26304655e-02 5.11281073e-02  4.63955967e-02  1.02260568e+01]

out of the blue....comes orange

答案 1 :(得分:1)

这是我锤几个小时后想到的。首先,我观察到大约两个数据区域,即数据范围的下半部分和上半部分,每半部分都有不同的特性。上半部分比较平坦,数据点较少,下半部分则有较大的曲率,只有几组几乎重叠的数据点。下面是我尝试将这两个区域分别建模为该问题的第一个切入点。我包括一个“缩放”图,该图显示了不相交的重叠区域,这使得此代码在目前的形式上无法令人满意。我有信心可以再坚持一两天,使它变得更好,但是这种解决方案可能不是您所需要的。

plot 1

plot 2

import numpy
import matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

cutoffVal = 732200.0 # x below or above this value

xData = numpy.asarray([731501.13, 731430.24, 731360.29, 731289.36, 731909.72, 731827.89,
   731742, 731657.74, 731577.95, 731502.64, 731430.39, 731359.12,
   731287.3, 731214.21, 732015.59, 731966.88, 731902.67, 731826.31,
   731743.79, 731660.94, 731581.29, 731505.4, 731431.95, 732048.71,
   732026.66, 731995.46, 731952.18, 731894.29, 731823.58, 731745.16,
   732149.61, 732091.53, 732052.98, 732026.82, 732005.17, 731977.63,
   732691.84, 732596.62, 732499.45, 732401.62, 732306.18, 732218.35,
   732141.82, 732080.91, 732038.21, 732009.08, 733023.08, 732951.99,
   732873.32, 732787.51])


yData = numpy.asarray([7873771.69, 7873705.34, 7873638.03, 7873571.73, 7874082.33,
   7874027.2, 7873976.22, 7873923.58, 7873866.35, 7873804.53,
   7873739.58, 7873673.62, 7873608.23, 7873544.15, 7874286.21,
   7874197.15, 7874123.96, 7874063.21, 7874008.78, 7873954.69,
   7873897.31, 7873836.09, 7873772.38, 7874564.62, 7874448.23,
   7874341.23, 7874246.59, 7874166.93, 7874100.4, 7874041.77,
   7874912.56, 7874833.09, 7874733.62, 7874621.43, 7874504.65,
   7874393.89, 7875225.26, 7875183.85, 7875144.42, 7875105.69,
   7875064.49, 7875015.5, 7874954.94, 7874878.36, 7874783.13,
   7874674. , 7875476.18, 7875410.05, 7875351.67, 7875300.61])


# split off data into above and below cutoff
xAboveList = []
yAboveList = []
xBelowList = []
yBelowList = []
for i in range(len(xData)):
    if xData[i] > cutoffVal:
        xAboveList.append(xData[i])
        yAboveList.append(yData[i])
    else:
        xBelowList.append(xData[i])
        yBelowList.append(yData[i])

xAbove = numpy.array(xAboveList)        
xBelow = numpy.array(xBelowList)        
yAbove = numpy.array(yAboveList)        
yBelow = numpy.array(yBelowList)        

# to fit for data above the cutoff value use a quadratic logarithmic equation
def aboveFunc(x, a, b, c):
    return a + b*numpy.log(x) + c*numpy.power(numpy.log(x), 2.0)

# to fit for data below the cutoff value use a hyperbolic type with offset
def belowFunc(x, a, b, c):
    val = x - cutoffVal
    return val / (a + (b * val) - ((a + b) * val * val)) + c

# some initial parameter values
initialParameters_above = numpy.array([1.0, 1.0, 1.0])
initialParameters_below = numpy.array([-4.29E-04, 4.31E-04,  7.87E+06])

# curve fit the equations individually to their respective data
aboveParameters, pcov = curve_fit(aboveFunc, xAbove, yAbove, initialParameters_above)
belowParameters, pcov = curve_fit(belowFunc, xBelow, yBelow, initialParameters_below)

# for plotting the fitting results
xModelAbove = numpy.linspace(max(xBelow), max(xAbove))
xModelBelow = numpy.linspace(min(xBelow), max(xBelow))
y_fitAbove = aboveFunc(xModelAbove, *aboveParameters)
y_fitBelow = belowFunc(xModelBelow, *belowParameters)

plt.plot(xData, yData, 'D') # plot the raw data as a scatterplot
plt.plot(xModelAbove, y_fitAbove) # plot the above equation using the fitted parameters
plt.plot(xModelBelow, y_fitBelow) # plot the below equation using the fitted parameters
plt.show()

print('Above parameters:', aboveParameters)
print('Below parameters:', belowParameters)