我有一组数据点,这些数据点看起来像是一条起点附近有曲线的线。请参见下图,该图显示了一条最适合的点(适合整个数据集)。
相反,它们可以用两个线性函数来描述(一条线穿过最左边的点集,另一条直线穿过其余的数据点)。这些点实际上对应的是中子衰变,它包含两种不同的同位素。
我不知道哪个点对应于哪个同位素,所以我需要以某种方式做出最佳猜测。一种同位素的曲线将是一条直线,而另一种同位素的曲线将是另一条直线。如何将两条最佳拟合(线性)的不同线拟合到一组数据点,从而优化两者的拟合?
我的一个想法是在t=100
(x轴)上选择一个“ cutouff点”,然后将左侧的点拟合到一条线上,将右侧的点拟合到另一条线上。然后,我可以为两条线计算chi ^ 2以获得拟合的“优度”。然后,我可以继续做多次相同的事情,但截取点略有不同,直到找到最适合整体的那条线。
似乎更复杂的另一种想法是将这些数据点描述为两行y= m1*t + m2*t + b1 + b2
的组合,其中m
是斜率,b
是是y截距。然后,取总曲线的导数,我将得到dy/dt = m1+m2
。然后,也许我可以在不同的“ cutuuff点”之间循环,并拟合线,直到得到导数最接近m1+m2
的组合。但是我不确定该怎么做,因为我最初不使用一条曲线,而只是处理一堆离散数据点。
在Python中优化两个拟合的最佳方法是什么?
答案 0 :(得分:1)
这可以与time-series segmentation一起解释为linear regression问题。有多种方法可以解决此问题。您已经提到其中一个:一个手动选择的数据分段位置,另一个试图将错误最小化。
首先,我尝试重新创建数据:
import numpy as np; import matplotlib.pyplot as plt
y1 = np.linspace(5.5, 3.7, num=100)
y1 = y1 + np.random.rand(y1.shape[0]) * np.linspace(0, .3, num=y1.shape[0])
y2 = np.linspace(3.7, 1.1, num=500)
y2 = y2 + np.random.rand(y2.shape[0]) * np.linspace(0.3, 1.9, num=y2.shape[0])
y = np.append(y1, y2)
x = np.array(range(len(y)))
然后我使用numpy.linalg.lstsq
做两个线性拟合,least squares本身是基于的方法:
x1 = x[:100]
y1 = y[:100]
A1 = np.vstack([x1, np.ones(len(x1))]).T
m1, c1 = np.linalg.lstsq(A1, y1, rcond=None)[0]
x2 = x[100:]
y2 = y[100:]
A2 = np.vstack([x2, np.ones(len(x2))]).T
m2, c2 = np.linalg.lstsq(A2, y2, rcond=None)[0]
绘制此图将得到以下图像:
plt.scatter(x, y)
plt.plot(x1, m1 * x1 + c1, 'r')
plt.plot(x2, m2 * x2 + c2, 'r')
plt.show()
您现在可以使用dataloader之类的自动分段算法来替换[100:]
和[:100]
切片,但是如果可以手动决定,我建议您不要这样做哪个指向拆分数据。如果需要实现,请查看提供的链接。
答案 1 :(得分:0)
形式上,您提到的第二种方法实质上是尝试将数据拟合到圆锥形部分。本质上是a pair of straight lines (POSL) is a type of conic section,因此可以由一般圆锥方程ax^2 + by^2 + 2hxy + 2gx + 2fy + c = 0
表示(而不是您在问题中提到的那个,最终将变成斜率m1 + m2的一条直线) 。为了明确起见,肯定有一个上述形式的等式,当绘制时会给出一个适合您数据的POSL。您的算法是否可以收敛到它是另一回事。
您可以尝试使用方法来找到系数a,b,h,g,f和c。理想情况下,您在圆锥曲线部分获得的系数将形成一个POSL,该POSL将非常适合您的数据集。
如果您决定实施此方法,则必须记住,该一般方程可以表示很多形状,例如抛物线,双曲线等。经过训练,您可能发现回归陷入困境,或不收敛并采用不同的形状,例如抛物线的形状。您可以尝试通过奖励遵守these conditions来使圆锥形截面成为POSL(在回归方法中)是必要的,从而使POSL形状微调。但这可能会使事情复杂化。
这种方法在数学上非常整洁,但我敢打赌,试图让您训练有素的模型收敛到POSL上,就等于在刀沿上保持平衡(本质上说,POSL的条件非常狭窄)。最有可能的是,它最终将为您提供抛物线,椭圆或双曲线的方程式(它们可能最终仅能完全拟合您的数据集,甚至使非最佳圆锥回归解决方案也值得)。
不过,如果您没有找到令人满意的结果,那么更好的方法可能只是停止担心形状,而将神经网络用于这种形式的非线性回归。那样的话,或者您可以盯上肘点并按照您在第一种方法中所建议的那样划分数据集。
答案 2 :(得分:0)
这里是将两条直线拟合到一个数据集的示例,其中两条直线之间的交叉点也被拟合为参数。本示例使用scipy的差异进化(DE)遗传算法来确定初始参数估计。 DE的scipy实现使用Latin Hypercube算法来确保对参数空间的彻底搜索,并且该算法需要在搜索范围内进行搜索-在本示例中,这些范围是从数据最大值和最小值中获取的。
import numpy, scipy, matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import differential_evolution
import warnings
xData = numpy.array([19.1647, 18.0189, 16.9550, 15.7683, 14.7044, 13.6269, 12.6040, 11.4309, 10.2987, 9.23465, 8.18440, 7.89789, 7.62498, 7.36571, 7.01106, 6.71094, 6.46548, 6.27436, 6.16543, 6.05569, 5.91904, 5.78247, 5.53661, 4.85425, 4.29468, 3.74888, 3.16206, 2.58882, 1.93371, 1.52426, 1.14211, 0.719035, 0.377708, 0.0226971, -0.223181, -0.537231, -0.878491, -1.27484, -1.45266, -1.57583, -1.61717])
yData = numpy.array([0.644557, 0.641059, 0.637555, 0.634059, 0.634135, 0.631825, 0.631899, 0.627209, 0.622516, 0.617818, 0.616103, 0.613736, 0.610175, 0.606613, 0.605445, 0.603676, 0.604887, 0.600127, 0.604909, 0.588207, 0.581056, 0.576292, 0.566761, 0.555472, 0.545367, 0.538842, 0.529336, 0.518635, 0.506747, 0.499018, 0.491885, 0.484754, 0.475230, 0.464514, 0.454387, 0.444861, 0.437128, 0.415076, 0.401363, 0.390034, 0.378698])
def func(xArray, breakpoint, slopeA, offsetA, slopeB, offsetB):
returnArray = []
for x in xArray:
if x < breakpoint:
returnArray.append(slopeA * x + offsetA)
else:
returnArray.append(slopeB * x + offsetB)
return returnArray
# function for genetic algorithm to minimize (sum of squared error)
def sumOfSquaredError(parameterTuple):
warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
val = func(xData, *parameterTuple)
return numpy.sum((yData - val) ** 2.0)
def generate_Initial_Parameters():
# min and max used for bounds
maxX = max(xData)
minX = min(xData)
maxY = max(yData)
minY = min(yData)
slope = 10.0 * (maxY - minY) / (maxX - minX) # times 10 for safety margin
parameterBounds = []
parameterBounds.append([minX, maxX]) # search bounds for breakpoint
parameterBounds.append([-slope, slope]) # search bounds for slopeA
parameterBounds.append([minY, maxY]) # search bounds for offsetA
parameterBounds.append([-slope, slope]) # search bounds for slopeB
parameterBounds.append([minY, maxY]) # search bounds for offsetB
result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3)
return result.x
# by default, differential_evolution completes by calling curve_fit() using parameter bounds
geneticParameters = generate_Initial_Parameters()
# call curve_fit without passing bounds from genetic algorithm
fittedParameters, pcov = curve_fit(func, xData, yData, geneticParameters)
print('Parameters:', fittedParameters)
print()
modelPredictions = func(xData, *fittedParameters)
absError = modelPredictions - yData
SE = numpy.square(absError) # squared errors
MSE = numpy.mean(SE) # mean squared errors
RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
Rsquared = 1.0 - (numpy.var(absError) / numpy.var(yData))
print()
print('RMSE:', RMSE)
print('R-squared:', Rsquared)
print()
##########################################################
# graphics output section
def ModelAndScatterPlot(graphWidth, graphHeight):
f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
axes = f.add_subplot(111)
# first the raw data as a scatter plot
axes.plot(xData, yData, 'D')
# create data for the fitted equation plot
xModel = numpy.linspace(min(xData), max(xData))
yModel = func(xModel, *fittedParameters)
# now the model as a line plot
axes.plot(xModel, yModel)
axes.set_xlabel('X Data') # X axis data label
axes.set_ylabel('Y Data') # Y axis data label
plt.show()
plt.close('all') # clean up after using pyplot
graphWidth = 800
graphHeight = 600
ModelAndScatterPlot(graphWidth, graphHeight)