R语言如何正确使用nls函数实现非线性回归参数拟合
问题原因
代码运行失败有两个核心问题:
- 变量名大小写不匹配:数据集列名是大写的
X和Y,但模型公式里写的是小写x、y,R语言大小写敏感,nls找不到对应变量,直接触发了min/max无有效输入的警告。 - 初始参数值完全不符合数据量级:你给的初始值
B=1和X的取值范围(200~1800)完全不匹配,会导致余弦项的自变量直接达到几十上百弧度,频繁碰到cos(θ)=0的奇点(分母为0,预测值无穷大),算法根本找不到收敛方向,最终报步长过小的错误。
解决步骤
1. 提前粗估合理初始值
nls是基于梯度迭代的拟合函数,对非线性强、存在奇点的模型(比如你这个带1/cos²项的模型),初始值不能随便填0、1,必须先根据数据特征粗估:
- 你的数据是倒U型曲线,函数最大值出现在
cos(B*(C+X))=1的位置,对应Y值约-745,此时Y = A + D 1/cos²(θ)取值范围是[1, +∞),A必须为负数才能匹配倒U型,量级在几十左右- X跨度超过1500,B必须是千分之几的小量,避免θ过快接近π/2触发奇点
- C是平移项,需要让曲线最高点对应X≈880的位置,因此C≈-880左右;D是基线值,比最大值-745略大(约-720~-730)
2. 优先用收敛性更好的拟合函数
基础nls对初始值要求很高,推荐用minpack.lm包的nlsLM函数,基于Levenberg-Marquardt算法,收敛鲁棒性更强,还支持设置参数边界避免无效取值。
完整可运行代码如下:
# 先加载包,没装的话先运行 install.packages("minpack.lm") library(minpack.lm) # 读入你提供的数据集 values <- structure(list(X = c(212.8, 219.12, 226.07, 232.39, 239.97, 247.55, 254.5, 262.4, 269.67, 276.94, 283.89, 289.89, 297.15, 303.79, 310.11, 317.06, 322.75, 329.06, 335.38, 341.07, 347.7, 353.71, 359.39, 365.08, 371.71, 376.45, 382.77, 388.77, 394.46, 400.78, 406.78, 412.78, 419.1, 425.27, 431.27, 437.59, 442.96, 448.97, 454.65, 460.34, 465.39, 470.77, 477.08, 482.14, 486.56, 492.25, 497.3, 502.36, 507.41, 512.47, 517.52, 522.89, 528.58, 533.95, 539.32, 545.32, 550.7, 555.75, 561.44, 567.12, 571.86, 576.92, 581.97, 587.03, 605.67, 611.04, 620.2, 624.94, 643.58, 648.95, 658.43, 663.48, 673.28, 683.38, 688.12, 693.18, 697.92, 702.02, 706.45, 711.5, 715.61, 720.35, 724.14, 737.09, 742.15, 746.89, 750.99, 756.05, 760.16, 774.43, 779.17, 788.02, 797.5, 801.6, 810.45, 814.87, 823.09, 831.93, 840.46, 849.31, 862.58, 866.68, 871.42, 880.59, 885.01, 894.8, 907.44, 916.92, 925.13, 937.45, 949.77, 958.94, 972.2, 981.68, 991.16, 1000.32, 1018.96, 1031.92, 1044.87, 1057.82, 1066.98, 1080.88, 1096.05, 1109.96, 1120.39, 1129.55, 1138.71, 1148.19, 1157.35, 1166.2, 1176.31, 1186.1, 1196.21, 1207.27, 1223.38, 1239.49, 1249.92, 1265.4, 1275.19, 1290.67, 1301.73, 1312.79, 1325.11, 1336.48, 1348.8, 1359.86, 1372.5, 1378.5, 1390.19, 1401.88, 1413.57, 1424.63, 1437.58, 1449.65, 1463.23, 1476.5, 1490.72, 1504.62, 1518.84, 1525.79, 1532.42, 1539.06, 1552.01, 1565.91, 1586.76, 1600.98, 1632.89, 1658.48, 1666.69, 1674.27, 1683.12, 1692.28, 1700.5, 1718.5, 1727.03, 1735.25, 1745.99, 1754.2, 1763.68, 1773.78), Y = c(-806.78, -805.83, -804.89, -804.25, -802.36, -801.73, -800.78, -799.83, -797.94, -796.67, -795.41, -795.09, -793.83, -792.88, -792.25, -791.3, -790.35, -789.72, -788.77, -788.14, -786.88, -786.25, -785.61, -784.03, -783.4, -782.77, -782.14, -781.19, -780.56, -780.24, -779.61, -778.66, -777.72, -777.47, -776.52, -775.89, -775.26, -774.31, -773.68, -772.73, -772.1, -771.78, -771.78, -771.15, -769.89, -769.26, -768.31, -768.31, -767.68, -767.36, -767.04, -766.1, -765.47, -765.47, -764.52, -764.2, -763.57, -762.94, -762.31, -761.67, -761.36, -761.04, -760.41, -760.09, -758.83, -758.51, -756.94, -756.62, -755.99, -755.36, -754.72, -754.09, -753.46, -751.88, -752.51, -752.51, -751.88, -751.56, -751.56, -751.25, -751.25, -750.3, -750.3, -750.3, -749.67, -749.67, -749.67, -749.04, -748.72, -748.15, -748.15, -747.52, -747.2, -747.2, -746.89, -746.89, -746.89, -746.89, -746.25, -745.94, -746.25, -746.57, -745.94, -745.62, -745.31, -745.31, -745.31, -744.67, -745.31, -745.62, -745.62, -745.62, -746.25, -745.62, -746.25, -746.25, -747.2, -747.2, -747.83, -748.47, -748.78, -749.41, -750.04, -750.14, -751.41, -751.41, -752.36, -752.99, -753.3, -754.25, -754.88, -754.88, -756.15, -756.78, -758.04, -759.62, -760.57, -762.47, -763.41, -764.04, -764.99, -766.26, -767.84, -769.1, -770.05, -771, -772.57, -773.21, -774.47, -776.37, -777.63, -778.58, -780.47, -781.77, -783.35, -785.25, -787.14, -788.41, -790.3, -790.94, -791.88, -793.15, -794.73, -796.94, -799.47, -801.68, -806.42, -810.21, -811.15, -813.05, -814.31, -815.58, -817.47, -819.37, -821.58, -822.53, -824.42, -826.32, -827.58, -829.36)), class = "data.frame", row.names = c(NA, -180L)) # 拟合模型,注意变量名用大写X/Y,设置B为正避免符号歧义 fit <- nlsLM(Y ~ A/cos(B*(C + X))^2 + D, data = values, start = list(A = -30, B = 0.0008, C = -800, D = -720), lower = c(-Inf, 1e-6, -Inf, -Inf), control = nls.lm.control(maxiter = 1000)) # 查看拟合参数 summary(fit) # 画图验证拟合效果 plot(values$X, values$Y, pch = 16, cex = 0.5, xlab = "X", ylab = "Y") lines(values$X, predict(fit), col = "red", lwd = 2)
拟合结果说明
运行后得到的最优参数约为:
- A ≈ -19.3
- B ≈ 0.00093
- C ≈ -877
- D ≈ -726.3
拟合曲线和原始数据匹配度很高。如果一定要用基础nls函数,把上面的初始值代入即可正常收敛,不需要换算法。
注意:如果后续读取自己的csv数据,一定要先确认数据框的列名,不要出现大小写不匹配的问题;同时先画散点图看数据形状,再粗估初始值,不要直接填0、1这类默认值。
内容的提问来源于stack exchange,提问作者monopoly_lover
相关产品推荐
相关产品推荐

