Вот то, что я придумал после нескольких часов работы над этим. Я начал с наблюдения, что существует примерно две области данных, нижняя половина и верхняя половина диапазонов данных, с разными характеристиками в каждой половине. Верхняя половина более плоская с меньшим количеством точек данных, а нижняя половина имеет большую кривизну с небольшими группами почти перекрывающихся точек данных. Ниже приведена моя попытка смоделировать эти два региона по отдельности в качестве первого подхода к проблеме. Я включил «увеличенный» график, показывающий разделенную область перекрытия, которая делает этот код неудовлетворительным в его нынешнем виде. Я уверен, что смогу победить в этом еще один или два дня и привести его в лучшую форму, но это решение может оказаться не тем, что вам нужно.


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)