如何将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() }
修改说明
- 数据处理层:新增
prop(单个条形占比)、cum_prop(累积占比)、mid_point(条形中点位置)的计算,同时显式反转响应变量顺序确保和绘图一致 - 条形绘制:将
x映射改为prop,并设置position_fill(reverse = TRUE)匹配反转后的响应变量顺序 - 标签定位:
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
相关产品推荐
相关产品推荐

