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

如何将geom_label_repel标签居中至每个条形图的中间位置?

分面水平条形图百分比标签居中解决方案

问题描述

在分面水平条形图中使用geom_label_repel放置百分比标签时,无法将标签定位到每个条形的中间,当前标签右对齐,条形间距较近时易混淆。尝试hjust/vjust仅能让文本在标签框内居中,使用nudge_x会导致条形消失。

原绘图函数

plot_single_result_with_facets <- function(x) {
  x %>% 
    pivot_longer(!response, names_to = "bin_name", values_to = "b") %>% 
    count(response, bin_name, b) %>%
    filter(!is.na(response)) %>%
    group_by(bin_name,b) %>%
    mutate(perc=paste0(round(n*100/sum(n),0),"%")) %>% 
    ggplot(aes(x = n, y = "", fill = response, label = perc)) +
    geom_col(position=position_fill(), aes(fill=forcats::fct_rev(response))) +
    coord_cartesian(clip = "off") +
    geom_vline(xintercept = x_limits, linetype = 3) +
    geom_label_repel(
      aes(group = forcats::fct_rev(response), label = perc),
      hjust = 0.5,
      vjust = 0.5,
      direction = "y",
      force = 1.5,
      fill = "white", 
      size = 1.5,
      max.overlaps = Inf,
      position = position_fill(),
      segment.size = 0.2,
      point.size = NA,
      box.padding = 0.001
    ) +
    scale_fill_brewer(palette="YlOrBr", direction = -1) +
    scale_x_continuous(labels = scales::percent_format(), expand = c(0.05, 0.05)) +
    facet_grid(vars(b), vars(bin_name), labeller=as_labeller(facet_names)) + 
    labs(title = title, caption = caption, x = "", y = "") + 
    guides(fill = guide_legend(title = NULL)) +
    theme_classic()
}

修改后的解决方案

核心是手动计算每个条形的中点位置,替代position_fill()的自动定位:

plot_single_result_with_facets <- function(x) {
  x %>% 
    pivot_longer(!response, names_to = "bin_name", values_to = "b") %>% 
    count(response, bin_name, b) %>%
    filter(!is.na(response)) %>%
    group_by(bin_name,b) %>%
    # 计算百分比文本、比例、累积比例和中点
    mutate(
      perc = paste0(round(n*100/sum(n),0),"%"),
      prop = n / sum(n),
      # 反转响应变量顺序,匹配fill的fct_rev
      response_rev = forcats::fct_rev(response)
    ) %>%
    # 按反转后的响应变量排序,计算累积比例
    arrange(bin_name, b, response_rev) %>%
    mutate(
      cum_prop = cumsum(prop),
      mid_point = cum_prop - prop/2
    ) %>%
    ungroup() %>%
    ggplot(aes(y = "")) +
    # 条形用prop作为x,position_fill的reverse参数匹配反转顺序
    geom_col(aes(x = prop, fill = response_rev), position = position_fill(reverse = TRUE)) +
    coord_cartesian(clip = "off") +
    geom_vline(xintercept = x_limits, linetype = 3) +
    geom_label_repel(
      aes(x = mid_point, label = perc),
      hjust = 0.5,
      vjust = 0.5,
      direction = "y",
      force = 1.5,
      fill = "white", 
      size = 1.5,
      max.overlaps = Inf,
      position = "identity", # 使用identity定位到手动计算的中点
      segment.size = 0.2,
      point.size = NA,
      box.padding = 0.001
    ) +
    scale_fill_brewer(palette="YlOrBr", direction = -1) +
    scale_x_continuous(labels = scales::percent_format(), expand = c(0.05, 0.05)) +
    facet_grid(vars(b), vars(bin_name), labeller=as_labeller(facet_names)) + 
    labs(title = title, caption = caption, x = "", y = "") + 
    guides(fill = guide_legend(title = NULL)) +
    theme_classic()
}

修改说明

  1. 数据处理层:新增prop(单个条形占比)、cum_prop(累积占比)、mid_point(条形中点位置)的计算,同时显式反转响应变量顺序确保和绘图一致
  2. 条形绘制:将x映射改为prop,并设置position_fill(reverse = TRUE)匹配反转后的响应变量顺序
  3. 标签定位:geom_label_repel的x映射改为手动计算的mid_point,使用position="identity"直接定位到中点,不再依赖position_fill()

可复现数据

df <- structure(list(Q51_bin = structure(c(2L, 2L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q52_bin = structure(c(3L, 2L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q57_bin = structure(c(2L, 2L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q53_bin = structure(c(2L, 3L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q4 = structure(c(2, 3, 3, 5), label = "How much have you thought about climate change before today?", format.spss = "F40.0", display_width = 5L, labels = c(`Not at all` = 1, `A little` = 2, Some = 3, `A lot` = 4, `A great deal` = 5, `Don't know` = 99), class = c("haven_labelled", "vctrs_vctr", "double"))), class = c("rowwise_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, -4L), groups = structure(list(.rows = structure(list(1L, 2L, 3L, 4L), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame")))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 05:24:20