ggplot2背靠背图对齐问题:面板边距设置及中心刻度加粗
解决ggplot2人口金字塔对齐与中心刻度加粗问题
嘿,我来帮你搞定这个人口金字塔的对齐和刻度样式问题!你之前用grid.arrange拼接两个独立ggplot的思路,很容易因为两个图的坐标轴范围、布局边距不一致导致对齐偏差,而且没法统一控制中间的刻度。下面给你一套更简洁可靠的方案:
核心思路
放弃拼接两个独立图,改用单个ggplot绘制背靠背条形:把男性的百分比设为负数,女性保持正数,这样在同一个坐标轴上就能形成天然对齐的金字塔效果,同时能直接控制中间的刻度样式。
完整修正代码(基于你的原始数据)
我用更现代的dplyr替代plyr处理数据,代码更清晰易维护:
library(ggplot2) library(dplyr) # 你的原始数据 WorkHours <- structure(list(serial = c(11080911, 19210711, 45051018, 15110802, 13190105, 12140718, 14300612, 18131002, 20011206, 17031104), DMSex = c(2, 2, 1, 2, 2, 2, 2, 2, 2, 1), dtotac = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("[3,20]", "(20,30]", "(30,40]", "(40,50]", "(50,60]", "(60,70]", "(70,80]", "(80,90]", "(90,100]", "(100,110]", "(110,120]"), class = c("ordered", "factor"))), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame")) # 预处理:计算每个性别-时长区间的百分比 work_data <- WorkHours %>% mutate(gender = case_when(DMSex == 1 ~ "Male", DMSex == 2 ~ "Female")) %>% group_by(gender, dtotac) %>% summarise(count = n(), .groups = "drop") %>% group_by(gender) %>% mutate(percent = count / sum(count)) %>% # 男性百分比设为负数,实现向左条形 mutate(percent = ifelse(gender == "Male", -percent, percent)) # 绘制金字塔图 ggplot(work_data, aes(x = dtotac, y = percent, fill = dtotac)) + geom_bar(stat = "identity") + # 设置y轴刻度:正负都显示百分比,去掉负号 scale_y_continuous(labels = function(x) paste0(abs(x)*100, "%"), breaks = seq(-0.5, 0.5, 0.1)) + # 翻转坐标轴,形成金字塔样式 coord_flip() + # 标题和标签设置 ggtitle("How many hours do men and women usually work in the week?") + labs(x = "Work Hours", y = "") + theme_bw() + theme( plot.title = element_text(hjust = 0.5, size = 18, face = "bold"), # 加粗中心刻度(也就是翻转后的y轴文本) axis.text.y = element_text(face = "bold", size = 10), legend.position = "none", # 调整布局边距 plot.margin = unit(c(0.5, 0.5, 0.5, 0.5), "cm") ) + # 添加性别标签(可选,让图表更清晰) annotate("text", x = length(unique(work_data$dtotac)) + 0.5, y = 0, label = "Female", hjust = 0.5, size = 5) + annotate("text", x = -0.5, y = 0, label = "Male", hjust = 0.5, size = 5)
可复现示例修正版(修复你代码中的变量名错误)
你原来的可复现代码里有SerialGenderWorkN的拼写错误,我已经修正并改用统一思路改写:
library(ggplot2) library(dplyr) # 生成模拟数据 SerialGenderWork <- data.frame( Type = sample(c('Male', 'Female'), 11421, replace=TRUE), dtotac = sample(0:60, 11421, replace=TRUE) ) # 切割时长区间 SerialGenderWork$dtotac <- ordered(cut(SerialGenderWork$dtotac, breaks = c(0, seq(20, 60, 15)), include.lowest = TRUE)) # 预处理数据 sim_data <- SerialGenderWork %>% group_by(Type, dtotac) %>% summarise(count = n(), .groups = "drop") %>% group_by(Type) %>% mutate(percent = count / sum(count)) %>% mutate(percent = ifelse(Type == "Male", -percent, percent)) # 绘制金字塔图 ggplot(sim_data, aes(x = dtotac, y = percent, fill = dtotac)) + geom_bar(stat = "identity") + scale_y_continuous(labels = function(x) paste0(abs(x)*100, "%"), breaks = seq(-0.4, 0.4, 0.1)) + coord_flip() + ggtitle("Work Hours by Gender") + labs(x = "Work Hours", y = "") + theme_bw() + theme( plot.title = element_text(hjust = 0.5, size = 16), axis.text.y = element_text(face = "bold", size = 10), legend.position = "none" ) + annotate("text", x = length(unique(sim_data$dtotac)) + 0.5, y = 0, label = "Female", size = 4) + annotate("text", x = -0.5, y = 0, label = "Male", size = 4)
关键改进点解释
- 天然对齐:单个ggplot绘制,左右条形共享同一个坐标轴,完全不会出现对齐偏差
- 中心刻度加粗:通过
axis.text.y = element_text(face = "bold")直接设置中间的工作时长刻度为加粗样式 - 更简洁的代码:不需要拼接多个图,数据预处理一步到位,可读性更强
- 灵活的刻度控制:
scale_y_continuous可以统一设置正负刻度的显示格式,避免两个图刻度不一致的问题
内容的提问来源于stack exchange,提问作者Rfanatic
相关产品推荐
相关产品推荐

