y轴对数变换下如何从stat_smooth提取二次拟合并求y=0.1时的x值
问题:log变换y轴后lm()与stat_smooth拟合不一致,求y=0.1时的x坐标
我想要提取二次拟合线以计算y=0.1时的x坐标,但由于y轴采用了log变换,lm()函数与stat_smooth得到的拟合结果不一致。最小数据集及代码如下:
xvals <- c(0,1,2,2.5,3,5,7.5,10) yvals <- c(1,0.65,0.425,0.45,0.26,0.085,0.0121667,0.000675) df <- cbind.dataframe(xvals,yvals) ggplot(df, aes(x = xvals, y = yvals))+ stat_smooth(method= 'lm', formula = y~poly(x,2), se = F, color = 'blue')+ stat_function(fun=function(x) 0.93389 + -0.25608 *x + 0.01663*x^2, color = 'green')+ geom_point(color = 'blue', shape = 22, size = 3)+ geom_hline(yintercept = 0.1)+ geom_vline(xintercept = 4.675868)+ scale_y_continuous(trans = 'log10')
stat_function的参数来自lm(使用poly(x,2,raw=T)结果一致):
lm(df, formula = yvals ~ I(xvals^2) + I(xvals))
通过解方程得到y=0.1时x≈4.7,但该结果与stat_smooth的拟合线不匹配;移除y轴log变换后,两者拟合线重合,交点为4.7。推测这是因为ggplot在拟合前先转换了数据,请问如何转换lm的输入数据,或获取stat_smooth使用的方程,以得到其与y=0.1交点的x坐标?
解决方案
核心原因
当你给ggplot添加scale_y_continuous(trans='log10')时,stat_smooth会先对y值做log10转换,再在转换后的数据上做二次线性拟合;而你直接调用lm(yvals ~ poly(xvals,2))是在原始y值上拟合,两者的拟合对象完全不同,所以结果自然不一致。
方法一:手动转换数据拟合,解方程求x
直接对y值做log10转换后拟合二次模型,再反推y=0.1对应的x值:
- 拟合对数转换后的模型
# 对y做log10转换,拟合x的二次多项式 lm_log <- lm(log10(yvals) ~ poly(xvals, 2, raw = TRUE), data = df) # 查看系数 coefs <- coef(lm_log) coefs
- 解方程求x
当y=0.1时,log10(0.1) = -1,代入拟合方程log10(y) = a + b*x + c*x²,得到:c*x² + b*x + (a + 1) = 0
用R代码求解这个二次方程:
# 提取系数 a <- coefs[1] b <- coefs[2] c <- coefs[3] # 解二次方程 roots <- polyroot(c(a + 1, b, c)) # 筛选出实数根,且在x的取值范围内的解 real_root <- Re(roots)[abs(Im(roots)) < 1e-10] valid_x <- real_root[real_root >= min(df$xvals) & real_root <= max(df$xvals)] # 输出结果 valid_x
- 验证拟合曲线
将拟合方程转换回原始y轴尺度,用stat_function画出来,会和stat_smooth的蓝色曲线完全重合:
ggplot(df, aes(x = xvals, y = yvals))+ stat_smooth(method= 'lm', formula = y~poly(x,2), se = F, color = 'blue')+ stat_function(fun=function(x) 10^(a + b*x + c*x^2), color = 'red', linetype = "dashed")+ geom_point(color = 'blue', shape = 22, size = 3)+ geom_hline(yintercept = 0.1)+ scale_y_continuous(trans = 'log10')
方法二:提取stat_smooth的拟合模型参数
可以通过ggplot_build直接提取stat_smooth使用的拟合数据和模型:
# 构建ggplot对象 p <- ggplot(df, aes(x = xvals, y = yvals))+ stat_smooth(method= 'lm', formula = y~poly(x,2), se = F, color = 'blue')+ geom_point(color = 'blue', shape = 22, size = 3)+ scale_y_continuous(trans = 'log10') # 提取拟合的模型数据 smooth_data <- ggplot_build(p)$data[[1]] # smooth_data中的y值是log10转换后的预测值,x是对应的x值 # 如果你想直接找y=0.1(即log10(y)=-1)对应的x,可以用插值法 approx(x = smooth_data$x, y = smooth_data$y, xout = NULL, y = -1)$x
内容的提问来源于stack exchange,提问作者Charlie
相关产品推荐
相关产品推荐

