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

使用ggplot绘制双轴图时遭遇secondary axes非单调转换错误求助

解决ggplot二级轴非单调转换错误

错误信息

Error in `f()`:
! transformation for secondary axes must be monotonic
Run `rlang::last_error()` to see where the error occurred.
Warning messages:
1: In (vsmow + 1000)/(d18osw) :
  longer object length is not a multiple of shorter object length
2: In log(al) : NaNs produced

问题代码

temp_function=function(x){
  vsmow=((x*1.03086)+30.86)
  al=((vsmow+1000)/(d18osw)+1000)
  temp= ((2.559*(10^6))/(1000*log(al))-0.715)
  temp2=((sqrt(temp))-273.15)
  return(temp2)
}

d18o= c(1.57250607,  1.80213529,  2.04688684,  2.05768980,  1.98708459,  1.34778009,  1.00992742,  0.49403459,  0.56191185,  0.19579498,
         0.08956884, -0.45381030, -0.60158300, -0.92339630, -1.24378840, -1.26242530, -1.08654800, -1.26757590, -1.55244220, -1.53874470,
         -1.11724190, -1.48195450, -1.42071900, -1.34024320, -1.02823520, -1.18611540, -0.70669460, -0.07921330,  0.62126673,  0.72444074,
          0.87499759,  0.90721387,  1.24767007,  1.21508923,  1.03826488,  1.04047116,  1.18316613,  1.08727293,  1.20401308,  1.09286830,
          1.06370239,  1.02918556,  1.02596777,  0.77820699,  0.80191575,  0.68288250,  0.41364236, 0.52444324,  0.42412122,  0.47631340,
          0.18180808,  0.24770451, -0.13031250, -0.29202420, -0.63853890, -0.75578190, -0.97535620, -0.94015680, -1.32330740, -1.25992660,
        -1.42660920, -1.63518900, -1.49774430, -1.74429840, -2.01900870, -1.56466740,  0.16242522,  1.01083316,  1.40150034, 1.32526660,
          1.30147320,  1.46371530,  1.28995767,  0.99914156,  1.01710591,  1.03839202,  0.98460735,  0.85265740,  0.66789528,  0.39425361,
         -0.32123960, -0.65520020, -1.19385120, -1.13205130, -1.12317560, -1.36345920, -1.56758210, -1.82638240, -1.89979770, -1.65500800,
         -1.62422980, -0.18052760,  1.01357724)
d18osw=c(0.29183729,  0.27975000,  0.10502778, -0.06572483,  0.17118316, -0.19654514, -0.06997483,  0.32036111, -0.13143750,  0.42998436,
          0.52024392,  0.56215365,  0.64008507,  0.41656163,  0.34580208,  0.52225087,  0.46624826,  0.31072483,  0.33935330,  0.26682292,
          0.70016768,  0.40168663, -0.20191667,  0.41197222,  0.24525257, -0.02140972,  0.47027628,  0.89164670,  0.77396007,  0.56274392,
           0.49325347,  0.33585665,  0.53992969,  0.16491146,  0.23636150,  0.34210183,  0.44513814,  0.46146701,  0.46509146,  0.48237162,
           0.43156944,  0.09048896,  0.09671962,  0.04099018,  0.50198958,  0.31100521,  0.05997069, -0.15857716,  0.76692962,  0.73520833,
           0.64020312,  0.61566233,  0.65118229,  0.77298611,  0.75675347,  0.55096351,  0.55380645,  0.56227947,  0.51668750,  0.53445486,
           0.37260069, 0.34081424,  0.40993576,  0.15124653,  0.30723284, -0.03553212,  0.36715538,  0.50027778,  0.50542795,  0.49255990,
           0.27760284,  0.08988271,  0.06961960,  0.48275673,  0.03034288,  0.53711111,  0.61154514,  0.65742448,  0.46065538, -0.51660851,
           0.16469010, -0.30371007,  0.60969817,  0.08988271, -0.41425000,  0.10897251,  0.08988271,  0.08988271,  0.16909337,  0.19873437,
           0.30460382, -0.04704174, -0.20544909)
samnum=seq(1,93,1)

df=as.data.frame(cbind(samnum,d18o))
colnames(df)=c("samnum","d18o")

dfplot=ggplot(df, aes(x=samnum)) +
  theme_bw()+
  theme(panel.border = element_blank(), panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(), axis.line = element_line(colour = "black"))+
  geom_point( aes(y=d18o,x=samnum),size =2.5)+
  scale_y_reverse(name = expression(paste(delta^'18','O (‰)')),breaks=c(-3,-1.5,0,1.5,3),
                  sec.axis = sec_axis(~temp_function(.),
                                                       name = "Temperature (°C)"))

问题原因

  1. 非单调转换:ggplot二级轴要求转换函数是纯函数(输入值唯一对应输出值,且输入单调变化时输出也单调递增/递减)。但你的temp_function依赖全局向量d18osw,相同的d18o输入可能对应不同的温度输出,导致转换关系非单调。
  2. 向量长度不匹配:d18o和d18osw长度一致,但函数内运算时未按逐元素绑定处理,触发R的循环补齐警告。
  3. NaN生成:al可能出现<=0的情况,导致log(al)生成NaN,后续sqrt(temp)也会因temp为负报错。

正式解决方案

方案一:预计算温度,建立全局单调映射

先计算每个样本的温度,再拟合d18o与温度的单调模型,用模型作为二级轴的转换函数:

# 修正后的温度计算函数(处理NaN问题)
temp_function_vec <- function(x, d18osw) {
  vsmow <- ((x * 1.03086) + 30.86)
  al <- ((vsmow + 1000)/(d18osw) + 1000)
  # 确保al>0,避免log报错
  al <- pmax(al, 1e-6)
  temp <- ((2.559*(10^6))/(1000*log(al)) - 0.715)
  # 确保temp>0,避免sqrt报错
  temp <- pmax(temp, 1e-6)
  temp2 <- ((sqrt(temp)) - 273.15)
  return(temp2)
}

# 构建包含温度列的数据框
df <- data.frame(samnum = samnum, d18o = d18o, temp = temp_function_vec(d18o, d18osw))

# 拟合d18o与温度的线性模型(保证单调)
fit <- lm(temp ~ d18o, data = df)
# 生成二级轴转换函数:d18o -> 温度
trans_func <- function(x) predict(fit, newdata = data.frame(d18o = x))

# 绘制图表
dfplot <- ggplot(df, aes(x = samnum)) +
  theme_bw() +
  theme(panel.border = element_blank(), panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(), axis.line = element_line(colour = "black")) +
  geom_point(aes(y = d18o), size = 2.5) +
  scale_y_reverse(name = expression(paste(delta^'18','O (‰)')),
                  breaks = c(-3, -1.5, 0, 1.5, 3),
                  sec.axis = sec_axis(trans_func,
                                      name = "Temperature (°C)",
                                      breaks = seq(10, 30, 5)))
dfplot

方案二:单调插值映射(更贴合逐样本计算)

如果线性拟合不够准确,可基于排序后的d18o和温度生成单调插值函数:

# 预计算温度列(同方案一)
df <- data.frame(samnum = samnum, d18o = d18o, temp = temp_function_vec(d18o, d18osw))

# 排序数据,生成单调插值函数
sorted_df <- df[order(df$d18o), ]
mono_trans <- approxfun(sorted_df$d18o, sorted_df$temp, rule = 2)

# 绘制图表
dfplot <- ggplot(df, aes(x = samnum)) +
  theme_bw() +
  theme(panel.border = element_blank(), panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(), axis.line = element_line(colour = "black")) +
  geom_point(aes(y = d18o), size = 2.5) +
  scale_y_reverse(name = expression(paste(delta^'18','O (‰)')),
                  breaks = c(-3, -1.5, 0, 1.5, 3),
                  sec.axis = sec_axis(mono_trans,
                                      name = "Temperature (°C)",
                                      breaks = seq(10, 30, 5)))
dfplot

关键说明

  • ggplot二级轴是视觉映射工具,不是数据关联工具,必须使用单调、输入输出一一对应的转换函数。
  • 预计算温度后再建立全局映射,既满足逐样本计算的业务需求,又符合ggplot的设计逻辑。
  • 修正后的函数通过pmax处理了NaN生成问题,避免运算报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 09:50:17