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

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)

关键改进点解释

  1. 天然对齐:单个ggplot绘制,左右条形共享同一个坐标轴,完全不会出现对齐偏差
  2. 中心刻度加粗:通过axis.text.y = element_text(face = "bold")直接设置中间的工作时长刻度为加粗样式
  3. 更简洁的代码:不需要拼接多个图,数据预处理一步到位,可读性更强
  4. 灵活的刻度控制:scale_y_continuous可以统一设置正负刻度的显示格式,避免两个图刻度不一致的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:16:52