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

如何优化R中drc与ggplot2绘制的剂量-反应曲线?

剂量-反应曲线绘图问题解决

问题描述

作为剂量-反应建模新手,使用drc和ggplot2绘制曲线时遇到两个问题:

  • 数据点与坐标轴之间存在间隙
  • x轴数值聚集在末端,无法均匀间隔分布

原始数据

structure(list(nickel = c(0.5, 0.5, 0.5, 0.5, 50, 50, 75, 75, 
                          113, 113, 169, 169, 253, 253, 380, 380, 570, 570, 854, 854, 1281, 
                          1281, 1922, 1922, 2883, 2883, 4325, 4325), growth = c(100L, 104L, 
                          101L, 93L, 101L, 100L, 94L, 75L, 74L, 71L, 60L, 85L, 72L, 52L, 
                          53L, 71L, 65L, 43L, 55L, 49L, 38L, 40L, 50L, 35L, 20L, 0L, 0L, 
                          17L)), row.names = c(NA, -28L), class = "data.frame")

原始代码

Ni.m1 <- drm(dose_response.data$growth~dose_response.data$nickel, data = dose_response.data, fct = LL.3())

newdata <- expand.grid(conc=exp(seq(log(0.1), log(6000), length=200)))
pm <- predict(Ni.m1, newdata=newdata, interval="prediction")

newdata$p <- pm[,1]
newdata$pmin <- pm[,2]
newdata$pmax <- pm[,3]

dose_response.data$nickel <- dose_response.data$nickel
dose_response.data$nickel[dose_response.data$nickel == 0] <- 0.5

ggplot(dose_response.data, aes(x = nickel, y = growth))+
  geom_point(pch=1,cex=2) +
  geom_ribbon(data=newdata, aes(x=conc, y=p, ymin=pmin, ymax=pmax), alpha=0.2) +
  geom_line(data=newdata, aes(x=conc, y=p)) +
  coord_trans(x="log") +
  xlab("Nickel"~(µg.L^{-1})) + 
  ylab("Growth (% control)") +
  ylim (-20,130) +
#manually add lines representing the EC10 concentration
  geom_segment(aes(x = 42.5, y = 0, xend = 42.5, yend = 90), linetype = 2, lwd=1) +
  geom_segment(aes(x = 0.1, y = 90, xend = 42.5, yend = 90), linetype = 2, lwd=1) 

原始绘图结果

生成的剂量-反应曲线

解决方案

1. 消除数据与坐标轴的间隙

coord_trans(x="log")是先绘制图形再对坐标轴做对数转换,容易导致坐标轴范围和实际数据范围错位,产生间隙。改用scale_x_log10(),它会先对x轴数据做对数转换再绘制,同时设置expand = c(0, 0)可以彻底消除间隙。

2. 让x轴刻度均匀分布

在scale_x_log10()中使用breaks参数指定对数刻度,比如用scales::log_breaks()自动生成均匀分布的对数刻度,或者手动指定刻度值,确保刻度在对数轴上均匀分布。

修改后的完整代码

library(drc)
library(ggplot2)
library(scales)

# 数据处理
dose_response.data <- structure(list(nickel = c(0.5, 0.5, 0.5, 0.5, 50, 50, 75, 75, 
                          113, 113, 169, 169, 253, 253, 380, 380, 570, 570, 854, 854, 1281, 
                          1281, 1922, 1922, 2883, 2883, 4325, 4325), growth = c(100L, 104L, 
                          101L, 93L, 101L, 100L, 94L, 75L, 74L, 71L, 60L, 85L, 72L, 52L, 
                          53L, 71L, 65L, 43L, 55L, 49L, 38L, 40L, 50L, 35L, 20L, 0L, 0L, 
                          17L)), row.names = c(NA, -28L), class = "data.frame")

Ni.m1 <- drm(growth ~ nickel, data = dose_response.data, fct = LL.3())

newdata <- expand.grid(conc=exp(seq(log(0.1), log(6000), length=200)))
pm <- predict(Ni.m1, newdata=newdata, interval="prediction")

newdata$p <- pm[,1]
newdata$pmin <- pm[,2]
newdata$pmax <- pm[,3]

dose_response.data$nickel[dose_response.data$nickel == 0] <- 0.5

# ggplot绘图
ggplot(dose_response.data, aes(x = nickel, y = growth))+
  geom_point(pch=1, cex=2) +
  geom_ribbon(data=newdata, aes(x=conc, y=p, ymin=pmin, ymax=pmax), alpha=0.2) +
  geom_line(data=newdata, aes(x=conc, y=p)) +
  # 替换coord_trans为scale_x_log10,设置刻度和消除间隙
  scale_x_log10(
    breaks = log_breaks(n = 8),  # 自动生成8个均匀分布的对数刻度
    expand = c(0, 0),            # 消除坐标轴间隙
    labels = label_number()      # 保持刻度为常规数字格式
  ) +
  xlab(expression(Nickel ~ (µg.L^{-1}))) + 
  ylab("Growth (% control)") +
  ylim (-20,130) +
  # EC10标注线
  geom_segment(aes(x = 42.5, y = 0, xend = 42.5, yend = 90), linetype = 2, lwd=1) +
  geom_segment(aes(x = 0.1, y = 90, xend = 42.5, yend = 90), linetype = 2, lwd=1) +
  theme_bw()

关键修改说明

  • 替换coord_trans(x="log")为scale_x_log10(),从根本上解决对数转换导致的坐标轴错位问题
  • expand = c(0, 0):移除x轴两侧的空白间隙,让数据贴合坐标轴
  • breaks = log_breaks(n = 8):自动生成均匀分布的对数刻度,避免刻度聚集在末端
  • 优化了drm函数的公式写法,更符合R语法规范

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 12:37:33