R语言nls拟合Curing~a*atan(b*Time)报错及分组平滑拟合问题
问题根因
- 报错
Error in assign(xname, newdata[, xname]) : first argument not valid来自两个逻辑错误:一是预测数据集new.data的自变量列名写为小写time,但模型中自变量命名为大写Time,predFit无法匹配到对应字段直接触发报错;二是后续mutate(Time = RawData$Time)的赋值逻辑完全错误,RawData$Time仅12个观测值,和预测结果的数百行长度不匹配,即便不报错也会出现数值循环错位。 - 未按产品分组拟合的原因是构建的
nls模型未引入Grade分组因子,默认两类产品共用一套拟合参数,本质是用混合数据算出来的全局曲线,自然无法匹配两个产品差异极大的固化趋势。另外要立刻停止使用attach()加载数据框,该函数会造成全局变量环境污染,是R语言里非常容易触发隐性bug的写法。
修正代码
直接运行以下代码即可完成分组拟合并输出正确图形:
library(tidyverse) library(investr) library(ggplot2) # 构建原始数据集 RawData <- data.frame( Time = c(0, 4, 8, 24, 28, 32, 0, 4, 8, 24, 28, 32), Curing = c(0, 28.57, 56.19, 86.67, 89.52, 91.42, 0, 85.71, 93.33, 94.28, 97.62, 98.09), Grade = rep(c("Product A", "Product B"), each = 6) ) # 按产品分组嵌套,分别拟合模型、生成预测值与置信区间 fit_result <- RawData %>% group_by(Grade) %>% nest() %>% mutate( # 分组拟合nls模型,补充合理初始值保证迭代收敛 nls_model = map(data, ~nls( formula = Curing ~ a * atan(b * Time), data = .x, start = list(a = 62, b = 0.08), # 初始值参考反正切函数极限:x→+∞时atan(x)≈π/2≈1.57,a≈最大固化度/1.57 control = nls.control(minFactor = 2^-24, warnOnly = TRUE) )), # 生成预测用时间序列,列名严格和模型自变量保持一致(大写Time) pred_grid = map(nls_model, ~data.frame(Time = seq(0, 32, by = 0.1))), # 分组计算90%置信区间 pred_ci = map2(nls_model, pred_grid, ~as_tibble(predFit( object = .x, newdata = .y, interval = "confidence", level = 0.9 ))) ) # 解压嵌套结果,得到标准化绘图数据集 plot_data <- fit_result %>% select(Grade, pred_grid, pred_ci) %>% unnest(cols = c(pred_grid, pred_ci)) # 绘图 curing_plot <- ggplot(RawData, aes(x = Time, y = Curing, color = Grade, fill = Grade)) + geom_point(shape = 1, size = 2.5) + geom_line(data = plot_data, aes(y = fit), linewidth = 1) + geom_ribbon(data = plot_data, aes(ymin = lwr, ymax = upr), alpha = 0.2, color = NA) + theme_classic() print(curing_plot)
关键说明
- 代码中给
nls补充了start初始值参数:nls采用高斯-牛顿迭代寻优,没有合理初始值极易出现不收敛报错,这里的初始值根据反正切函数的渐近特性设置,适配两组数据的取值范围,可保证迭代正常收敛。 - 所有列名严格保持大小写一致,从根源解决字段匹配报错;分组拟合时两类产品各自计算独立的
a/b参数,能正确匹配Product B前期固化速度远高于Product A的趋势,置信区间也按分组单独计算,填充色和对应曲线自动匹配,不会出现系列混淆。
内容的提问来源于stack exchange,提问作者GiacomoDB
相关产品推荐
相关产品推荐

