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

ggplot2中sec_axis结合pretty_breaks无法显示次坐标轴负值百分比问题

解决ggplot2次坐标轴无法显示负值百分比的问题

尝试绘制带双坐标轴的图表时,使用scale_y_continuous的sec_axis参数搭配pretty_breaks()生成次坐标轴刻度,发现次坐标轴无法显示负值百分比,核心代码片段如下:

scale_y_continuous(sec.axis=sec_axis(breaks = pretty_breaks(), labels = label_percent()), breaks = pretty_breaks(), labels = label_dollar())

需要实现包含负值百分比的次坐标轴效果,以下是最小可复现代码:

library(dplyr)
library(tidyr)
library(ggplot2)
library(scales)

test = structure(list(Año = c(2017L, 2017L, 2017L, 2017L, 2017L, 2017L, 
                              2018L, 2018L, 2018L, 2018L, 2018L, 2018L, 2019L, 2019L, 2019L, 
                              2019L, 2019L, 2019L, 2020L, 2020L, 2020L, 2020L, 2020L, 2020L
), Vars = c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements", "Oil.GAV.%", 
            "Non-Oil.GAV.%", "Other.GDP.elements.%", "Oil.GAV", "Non-Oil.GAV", 
            "Other.GDP.elements", "Oil.GAV.%", "Non-Oil.GAV.%", "Other.GDP.elements.%", 
            "Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements", "Oil.GAV.%", 
            "Non-Oil.GAV.%", "Other.GDP.elements.%", "Oil.GAV", "Non-Oil.GAV", 
            "Other.GDP.elements", "Oil.GAV.%", "Non-Oil.GAV.%", "Other.GDP.elements.%"
), value = c(5639977, 91442756, 7213129, 0.0987732330025509, 
             0.869107003693333, 0.0321197633041161, 6918891, 93037062, 7606055, 
             0.0913116709595953, 0.875434164471086, 0.0332541645693185, 6519993, 
             94351584, 7236432, 0.0923079337383433, 0.876825035531481, 0.0308670307301762, 
             4951333, 88365192, 5491485, -0.06, -0.35, -0.078)), row.names = c(NA, 
                                                                               -24L), class = c("tbl_df", "tbl", "data.frame"))


scaleFactor_t <- max(test %>% 
                     filter(Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")) %>% 
                     select(value)) / max(test %>% 
                                            filter(!Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")) %>% 
                                            select(value))

ggplot() +
  geom_col(data = test %>% 
             filter(Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")),
           aes(x = Año, y = value , fill = Vars))+
  geom_line(data = test %>% 
              filter(!Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")),
            aes(x = Año, y = value*scaleFactor_t, colour = Vars), size = 1)+
  geom_point(data = test %>% 
               filter(!Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")),
             aes(x = Año, y = value*scaleFactor_t, colour = Vars), size = 2)+
  scale_y_continuous(name="Miles de dólares", 
                     sec.axis=sec_axis(~./scaleFactor_t, name="Kcal per day", breaks =scales::pretty_breaks(), labels = scales::label_percent()), 
                     breaks =scales::pretty_breaks(), labels = scales::label_dollar())

问题原因

pretty_breaks()默认基于主坐标轴的数值范围生成刻度,而主坐标轴是正值的美元金额,转换到次坐标轴后,负值对应的主坐标轴数值不在默认生成的刻度范围内,因此次坐标轴不会显示负值刻度。

解决方案

自定义次坐标轴的刻度生成逻辑,确保覆盖负值范围,以下是两种可行方法:

方法1:手动指定次坐标轴刻度

直接设定包含负值的刻度数组,覆盖数据中的负数值区间:

scale_y_continuous(
  name="Miles de dólares", 
  sec.axis=sec_axis(
    ~./scaleFactor_t, 
    name="Porcentaje", 
    breaks = c(-0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1), # 手动指定包含负值的刻度
    labels = scales::label_percent()
  ), 
  breaks = scales::pretty_breaks(), 
  labels = scales::label_dollar()
)

方法2:自定义breaks函数,自动适配次坐标轴范围

基于pretty_breaks()扩展,让它根据次坐标轴的实际数值范围生成刻度:

# 自定义breaks函数,计算次坐标轴的实际数值范围
custom_breaks <- function() {
  function(x) {
    sec_range <- x / scaleFactor_t
    scales::pretty_breaks()(sec_range)
  }
}

# 应用到绘图代码
ggplot() +
  geom_col(data = test %>% 
             filter(Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")),
           aes(x = Año, y = value , fill = Vars))+
  geom_line(data = test %>% 
              filter(!Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")),
            aes(x = Año, y = value*scaleFactor_t, colour = Vars), size = 1)+
  geom_point(data = test %>% 
               filter(!Vars %in% c("Oil.GAV", "Non-Oil.GAV", "Other.GDP.elements")),
             aes(x = Año, y = value*scaleFactor_t, colour = Vars), size = 2)+
  scale_y_continuous(
    name="Miles de dólares", 
    sec.axis=sec_axis(
      ~./scaleFactor_t, 
      name="Porcentaje", 
      breaks = custom_breaks(), 
      labels = scales::label_percent()
    ), 
    breaks = scales::pretty_breaks(), 
    labels = scales::label_dollar()
  )

效果说明

修改后,次坐标轴会显示包含负值的百分比刻度,匹配2020年的负增长数据。建议将次坐标轴名称从"Kcal per day"修正为对应含义的"Porcentaje"(西班牙语“百分比”),契合数据语境。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:50:27