You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ggplot中填充置信区间曲线区域(解决曲线交叉问题)

问题

我想用ggplot填充两条函数曲线之间的区域来制作自定义置信区间。我用Monod方程构建了nls拟合函数,基于拟合参数绘制了拟合曲线;同时通过bootstrap方法得到置信区间的数值,绘制了两条曲线作为区间边界,但没能成功填充区域,还发现边界曲线存在交叉问题。请问有没有可行的填充方法,或者更合适的置信区间绘制方式?

代码

pr20Y = subset(donnees_tot_g_J4, color == "20Y")

start.values <- list(gm=0.7, s=0.05, k=0.2)
nls1 = nls(growth_rate ~ gm*((quantity-s)/(quantity-s+k)), data = pr20Y, start=start.values)  
summary(nls1)
# gm = 0.65 / s = 0.06 / k = 0.11
nls1.boot <- nlsBoot(nls1, niter = 1000)
summary(nls1.boot)
    # Median       2.5%      97.5%
# gm 0.65636222 0.61994560 0.69597337
# s  0.05735049 0.03765189 0.07138767
# k  0.11705112 0.07833971 0.16174788
growthJ4=ggplot(donnees_tot_g_J4, aes(x=quantity, y=growth_rate, col=color))+
  geom_point(size=3,aes(col=color))+
  stat_function(fun = function (quantity) 0.65*((quantity-0.06)/(quantity-0.06+0.11)), color="darkgoldenrod1", size=1)+
  stat_function(fun = function (quantity) 0.61994560*((quantity-0.03765189)/(quantity-0.03765189+0.07833971)), color="darkgoldenrod1", size=1, alpha=0.5)+
  stat_function(fun =function (quantity) 0.69597337*((quantity-0.07138767)/(quantity-0.07138767+0.16605772)), color="darkgoldenrod1", size=1, alpha=0.5)+
  scale_x_continuous(breaks=c(0.1,0.3,0.6,0.9,1.5), limits=c(0.1,1.5))+
  scale_y_continuous(limits=c(0,0.8))+
  scale_colour_manual(values=c("20S" = "aquamarine1","25S" = "aquamarine3","28S" = "aquamarine4","20Y" = "darkgoldenrod1","25Y" = "darkgoldenrod3", "28Y" = "darkgoldenrod4"))+
  scale_fill_manual(values=c("20S" = "aquamarine1","25S" = "aquamarine3","28S" = "aquamarine4","20Y" = "darkgoldenrod1", "25Y" = "darkgoldenrod3","28Y" = "darkgoldenrod4"))+
  theme_minimal()+
  ggtitle("Taux de croissance à J4 en fonction du traitement de nourriture et de la température")+
  xlab("Quantité") + ylab("Taux de croissance µg/j")+
  theme_grey(base_size = 22)
growthJ4

数据

structure(list(name = c("J4_S01AC", "J4_S01CC", "J4_S01EC", "J4_S01FC", 
"J4_S03BC", "J4_S03CC", "J4_S03DC", "J4_S03EC", "J4_S03FC", "J4_S06AC", 
"J4_S06DC", "J4_S06EC", "J4_S06FC", "J4_S06KC", "J4_S06MC", "J4_S06NC", 
"J4_S09AC", "J4_S09BC", "J4_S09CC", "J4_S09DC", "J4_S03AM", "J4_S03BM", 
"J4_S03CM", "J4_S15AM", "J4_S15BM", "J4_S15CM", "J4_S15DM", "J4_S01AAC"
), day = c("J4", "J4", "J4", "J4", "J4", "J4", "J4", "J4", "J4", 
"J4", "J4", "J4", "J4", "J4", "J4", "J4", "J4", "J4", "J4", "J4", 
"J4", "J4", "J4", "J4", "J4", "J4", "J4", "J4"), quality = c("S", 
"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", 
"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", 
"S"), quantity = c(0.1, 0.1, 0.1, 0.1, 0.3, 0.3, 0.3, 0.3, 0.3, 
0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.9, 0.9, 0.9, 0.9, 0.3, 0.3, 
0.3, 1.5, 1.5, 1.5, 1.5, 0.1), qual_quant = c("S01", "S01", "S01", 
"S01", "S03", "S03", "S03", "S03", "S03", "S06", "S06", "S06", 
"S06", "S06", "S06", "S06", "S09", "S09", "S09", "S09", "S03", 
"S03", "S03", "S15", "S15", "S15", "S15", "S01"), temperature = c(28, 
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28), time = c(101, 101, 
101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 
101, 102, 102, 102, 102, 107.5, 107.5, 107.5, 107.5, 107.5, 107.5, 
107.5, 109), size = c(1344.366, 1291.962, 1304.811, 1128.264, 
1286.151, 1358.421, 1396.671, 1353.076, 1505.565, 1297.17, 0, 
1243.029, 1323.85, 1368.364, 1506.396, 0, 1663.735, 1632.28, 
2115.303, 1921.46, 1506.581, 1501.196, 1370.99, 1870.489, 1941.425, 
1942.186, 1827.395, 1336.588), weight = c(11, 16, 10, 10, 12, 
12, 13, 16, 14, 12, 11, 12, 10, 10, 15, 25, 46, 35, 66, 46, 20, 
16, 15, 49, 73, 63, 60, 11), growth_rate = c(0.0378568479840844, 0.126892202895244, 
0.0152090656484838, 0.0152090656484838, 0.0585326533474545, 0.0585326533474545, 
0.0775525505364441, 0.126892202895244, 0.0951622248242906, 0.0585326533474545, 
0.0378568479840844, 0.0585326533474545, 0.0152090656484838, 0.0152090656484838, 
0.111556439370444, 0.232939774941222, 0.374129156018743, 0.309825356331568, 
0.459072793066665, 0.374129156018743, 0.169037347727828, 0.119219651092275, 
0.104811166292231, 0.369092608582107, 0.458090402952369, 0.425199566970549, 
0.414306966296783, 0.0350783637283718), color = c("28S", "28S", 
"28S", "28S", "28S", "28S", "28S", "28S", "28S", "28S", "28S", 
"28S", "28S", "28S", "28S", "28S", "28S", "28S", "28S", "28S", 
"28S", "28S", "28S", "28S", "28S", "28S", "28S", "28S")), row.names = c(NA, 
-28L), class = c("tbl_df", "tbl", "data.frame"))

解决方案

1. 解决曲线交叉问题

你当前直接用参数的2.5%和97.5%分位点组合成边界曲线,忽略了参数间的相关性,这是导致曲线交叉的核心原因。正确做法是利用bootstrap得到的所有参数样本,对每个x值计算对应的y值,再取每个x的2.5%和97.5%分位点作为置信区间的上下限,保留参数间的关联,避免曲线交叉。

2. 填充置信区间区域

ggplot无法直接用stat_function填充两条曲线间的区域,需要先生成包含x、拟合值、置信上下限的数据集,再用geom_ribbon完成填充。

具体实现代码

library(ggplot2)
library(nlstools) # 用于nlsBoot

# 提取bootstrap参数样本
boot_params <- as.data.frame(nls1.boot$coefboot)

# 生成覆盖绘图范围的x序列
x_seq <- seq(0.1, 1.5, length.out = 100)

# 定义Monod方程函数
monod_fun <- function(x, gm, s, k) {
  gm * ((x - s) / (x - s + k))
}

# 计算每个bootstrap样本在x_seq上的预测值
boot_preds <- apply(boot_params, 1, function(params) {
  monod_fun(x_seq, gm = params["gm"], s = params["s"], k = params["k"])
})

# 计算每个x对应的拟合中位数、2.5%和97.5%分位点
pred_df <- data.frame(
  quantity = x_seq,
  fit = apply(boot_preds, 1, median),
  lower = apply(boot_preds, 1, quantile, probs = 0.025),
  upper = apply(boot_preds, 1, quantile, probs = 0.975)
)

# 绘制最终图形
growthJ4 <- ggplot() +
  # 原始散点
  geom_point(data = donnees_tot_g_J4, aes(x = quantity, y = growth_rate, col = color), size = 3) +
  # 置信区间填充区域
  geom_ribbon(data = pred_df, aes(x = quantity, ymin = lower, ymax = upper), fill = "darkgoldenrod1", alpha = 0.3) +
  # 拟合曲线
  geom_line(data = pred_df, aes(x = quantity, y = fit), color = "darkgoldenrod1", size = 1) +
  # 坐标轴与主题设置
  scale_x_continuous(breaks = c(0.1, 0.3, 0.6, 0.9, 1.5), limits = c(0.1, 1.5)) +
  scale_y_continuous(limits = c(0, 0.8)) +
  scale_colour_manual(values = c("20S" = "aquamarine1","25S" = "aquamarine3","28S" = "aquamarine4","20Y" = "darkgoldenrod1","25Y" = "darkgoldenrod3", "28Y" = "darkgoldenrod4")) +
  ggtitle("Taux de croissance à J4 en fonction du traitement de nourriture et de la température") +
  xlab("Quantité") + ylab("Taux de croissance µg/j") +
  theme_grey(base_size = 22)

growthJ4

关键说明

  • 通过遍历所有bootstrap参数样本计算预测值,再按x维度取分位点,确保置信区间基于参数的联合分布,不会出现交叉。
  • geom_ribbon是ggplot中专门用于填充区间的图层,设置alpha参数可以让填充区域不遮挡散点和拟合曲线。
  • 注意:当前提供的样本数据中仅包含28S组,若实际数据中无20Y组,需要补充对应数据才能得到有效拟合结果。

内容的提问来源于stack exchange,提问作者Nate Trf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 02:03:12