如何在ggplot中对坐标轴做对数缩放且不干扰线性回归模型?
问题:对数缩放X轴时保持原线性回归模型不变
我有一组qPCR标准曲线数据,需要拟合Ct ~ log(浓度)的线性模型,同时希望对X轴做对数缩放以更好展示低浓度数据,但添加scale_x_continuous(trans="log2")后模型发生了变化,如何同时实现X轴对数缩放和保留原回归模型?
数据示例
data <- structure(list(standard_conc_ngul = c(50, 50, 50, 5, 5, 0.5, 0.5, 0.05, 0.05, 0.005, 0.005, 0.005), ct = c(18.3305377960205, 18.133768081665, 17.8813705444336, 21.5002365112305, 21.4915542602539, 22.7616996765137, 23.6836719512939, 25.3699340820312, 25.3488445281982, 28.984302520752, 26.7397594451904, 27.8844776153564)), row.names = c(NA, -12L), class = c("tbl_df", "tbl", "data.frame"))
原拟合代码(r²=0.973):
ggplot(data, aes(x = standard_conc_ngul, y = ct)) + geom_point() + stat_smooth(method = lm, formula = y ~ log(x)) + stat_poly_eq(formula = y ~ log(x), aes(label = paste(after_stat(eq.label), after_stat(rr.label), sep = "~~~")), parse = TRUE, coef.digits = 3, f.digits = 3, p.digits = 3, rr.digits = 3)
解决方案
核心问题是:X轴做对数缩放后,ggplot会自动将x值转换为对数形式,此时仍用y ~ log(x)会导致二次对数转换,模型偏离。只需调整回归公式匹配轴的转换逻辑即可。
方法1:直接在绘图时调整公式
library(ggplot2) library(ggpmisc) ggplot(data, aes(x = standard_conc_ngul, y = ct)) + geom_point() + # 对数缩放X轴,手动设置刻度匹配数据点 scale_x_continuous(trans = "log2", breaks = c(0.005, 0.05, 0.5, 5, 50), labels = as.character(c(0.005, 0.05, 0.5, 5, 50))) + # X轴已做对数转换,模型改为y ~ x,等价于原模型y ~ log(x) stat_smooth(method = lm, formula = y ~ x, se = TRUE) + # 保留原公式用于显示标签,保证r²和原模型一致 stat_poly_eq(formula = y ~ log(x), aes(label = paste(after_stat(eq.label), after_stat(rr.label), sep = "~~~")), parse = TRUE, coef.digits = 3, rr.digits = 3) + labs(x = "标准浓度 (ng/μL)", y = "Ct值") + theme_bw()
方法2:提前转换数据(更直观)
先对浓度做对数转换,再拟合线性模型,X轴显示原始浓度标签:
library(ggplot2) library(ggpmisc) # 提前计算log2转换后的浓度 data$log2_conc <- log2(data$standard_conc_ngul) ggplot(data, aes(x = log2_conc, y = ct)) + geom_point() + stat_smooth(method = lm, formula = y ~ x, se = TRUE) + # 自定义标签显示原始变量的模型形式 stat_poly_eq(formula = y ~ x, aes(label = paste("Ct = ", after_stat(coef.labels)[1], " + ", after_stat(coef.labels)[2], "*log2(conc)", "~~~", after_stat(rr.label), sep = "")), parse = TRUE, coef.digits = 3, rr.digits = 3) + # X轴刻度映射回原始浓度值 scale_x_continuous(breaks = log2(c(0.005, 0.05, 0.5, 5, 50)), labels = as.character(c(0.005, 0.05, 0.5, 5, 50))) + labs(x = "标准浓度 (ng/μL)", y = "Ct值") + theme_bw()
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

