如何在ggplot中交换双Y轴的左右位置?
ggplot双Y轴:变量分配的决策原则与实践
核心决策逻辑
选择变量对应左右Y轴,核心围绕阅读习惯和图表核心目标,以下是具体原则:
- 「绝对数值类指标」(如种群规模
TotalAlive)优先放左侧主Y轴:读者对绝对数量的感知更直观,左侧作为默认视觉焦点,承载核心信息。 - 「比例/相对值类指标」(如年龄组占比
Proportion*)放右侧次Y轴:相对值是对主指标的补充分析,放在次轴既避免干扰主信息,又能关联对比。 - 若两类指标都是绝对值,优先把波动幅度更大、更核心的指标放在左侧:避免线条因轴范围被压缩,保证视觉清晰度。
结合你的代码调整
你的代码逻辑方向正确,只需修正几处变量名错误,并明确变量分配的合理性:
- 把
TotalAlive(种群规模,绝对数)放在左侧主轴,作为核心展示指标; - 把各年龄组占比通过
* max(TotalAlive)缩放后映射到主轴范围,再通过sec_axis(~ . / max(Ind_Count_Year$TotalAlive))转换为比例,放在右侧次轴。
修正后的完整代码:
##### 定义并标记年龄组!!!#### data$AgeGroup <- case_when( data$Age_y >= 0 & data$Age_y < 2 ~ "Juvenile", data$Age_y >= 2 & data$Age_y < 7 ~ "Sub-adult", data$Age_y >= 7 ~ "Adult", TRUE ~ NA_character_ # 处理缺失或无效年龄情况 ) # 创建数据框以追踪每年存活动物数量 years <- seq(from = as.numeric(format(min(data$BirthDate), "%Y")), to = as.numeric(format(DateDownload, "%Y")), by = 1) Ind_Count_Year <- data.frame(Year = integer(), TotalAlive = integer(), Juveniles = integer(), SubAdults = integer(), Adults = integer(), ProportionJuveniles = numeric(), ProportionSubAdults = numeric(), ProportionAdults = numeric()) for (year in years) { # 筛选当年存活的动物(出生年份≤当年且消失年份为NA或≥当年则存活) alive_in_year <- data %>% filter(format(BirthDate, "%Y") <= year & (is.na(GoneDate) | format(GoneDate, "%Y") >= year)) # 统计各年龄组个体数量 juveniles <- sum(alive_in_year$AgeGroup == "Juvenile") subadults <- sum(alive_in_year$AgeGroup == "Sub-adult") adults <- sum(alive_in_year$AgeGroup == "Adult") # 存活动物总数 total_alive <- juveniles + subadults + adults # 各年龄组占比 proportion_juveniles <- juveniles / total_alive proportion_subadults <- subadults / total_alive proportion_adults <- adults / total_alive # 将数据添加至数据框(修正变量名错误:alive_data → Ind_Count_Year) Ind_Count_Year <- rbind(Ind_Count_Year, data.frame(Year = year, TotalAlive = total_alive, Juveniles = juveniles, SubAdults = subadults, Adults = adults, ProportionJuveniles = proportion_juveniles, ProportionSubAdults = proportion_subadults, ProportionAdults = proportion_adults)) } # 查看生成的数据框(可选) print(Ind_Count_Year) # 绘制每年存活动物数量及各年龄组占比的双Y轴图 ggplot(Ind_Count_Year, aes(x = Year)) + geom_line(aes(y = ProportionJuveniles * max(Ind_Count_Year$TotalAlive), color = "Juveniles (<2yrs)"), size = 1.75) + geom_line(aes(y = ProportionSubAdults * max(Ind_Count_Year$TotalAlive), color = "Sub-adults (>2yrs to 7yrs)"), size = 1.75) + geom_line(aes(y = ProportionAdults * max(Ind_Count_Year$TotalAlive), color = "Adults (>7yrs)"), size = 1.75) + geom_line(aes(y = TotalAlive, color = "Pop. Size"), size = 1.25, linetype = "twodash") + labs(title = "Population History") + # 添加标题与标签 scale_y_continuous( name = "Population Size", expand = c(0, 0), breaks = waiver(), n.breaks = 12, # 修正变量名错误:alive_data$TotalAlive → Ind_Count_Year$TotalAlive sec.axis = sec_axis(~ . / max(Ind_Count_Year$TotalAlive), name = "% of Total Population") ) + scale_color_manual(values = c("Pop. Size" = "black","Juveniles (<2yrs)" = "#FFD966", "Sub-adults (>2yrs to 7yrs)" = "#70AD47","Adults (>7yrs)" = "#ED7D31")) + theme_minimal() + theme( plot.title = element_text(hjust = 0.5), legend.position = "bottom", legend.title = element_blank(), axis.line = element_line(linewidth = 1), axis.ticks = element_line(linewidth = 1), panel.grid.minor = element_blank() # 移除次要网格线 ) + scale_x_continuous(name = "Year", breaks = seq(1950, max(Ind_Count_Year$Year)+1, by = 5), limits = c(1970, NA))
数据示例
| Year | TotalAlive | Juveniles | SubAdults | Adults | ProportionJuveniles | ProportionSubAdults | ProportionAdults |
|---|---|---|---|---|---|---|---|
| 1949 | 4 | 0 | 3 | 1 | 0.00000000 | 0.75000000 | 0.2500000 |
| 1950 | 4 | 0 | 3 | 1 | 0.00000000 | 0.75000000 | 0.2500000 |
| 1951 | 4 | 0 | 3 | 1 | 0.00000000 | 0.75000000 | 0.2500000 |
| 1952 | 5 | 1 | 3 | 1 | 0.20000000 | 0.60000000 | 0.2000000 |
| 1953 | 5 | 0 | 3 | 2 | 0.00000000 | 0.60000000 | 0.4000000 |
内容的提问来源于stack exchange,提问作者JPM
相关产品推荐
相关产品推荐

