如何在R的geom_text中单独调整特定点的文本垂直位置?
问题描述
我需要在ggplot2的geom_text中调整图表上第二个点的y轴文本位置,避免百分比文本重叠,提升可读性。但vjust会改变曲线上所有纵坐标文本的位置,因此我只需要调整这个特定点的文本位置。我知道自己的x轴存在问题,但这不是当前关注重点。以下是我的代码:
library(tidyverse) library(scales) library(ggplot2) ageC <- c("18-34","35", "35", "35","35","18-34","18-34","35","18-34","35") sexe <- c("w","m", "m", "w", "w", "w", "m", "m", "m", "w") sexuality <- c("no","yes", "no", "yes", "no", "no", "yes", "yes", "no", "yes") df <- data.frame(ageC,sexe,sexuality) # base with women b_w <- df %>% select(ageC,sexe,sexuality) %>% filter(sexe != "m") %>% mutate(sexuality = factor(sexuality)) bb_w <- b_w %>% mutate(age_c = factor(ageC, labels = c("26", "35")) %>% as.character %>% as.numeric, sexuality_c = as.numeric(sexuality=="yes")) %>% group_by(age_c) %>% summarise(sexuality_pct = mean(sexuality_c)) # base with men b_m <- df %>% select(ageC,sexe,sexuality) %>% filter(sexe != "w") %>% mutate(sexuality = factor(sexuality)) bb_m <- b_m %>% mutate(age_c = factor(ageC, labels = c("26", "35")) %>% as.character %>% as.numeric, sexuality_c = as.numeric(sexuality=="yes")) %>% group_by(age_c) %>% summarise(sexuality_pct = mean(sexuality_c)) ggplot() + geom_point(data = bb_w, aes(x = age_c, y = sexuality_pct, fill = "w"), color = "#6a51a3", size = 1.5) + geom_text(data = bb_w, aes(x = age_c, y = sexuality_pct, label=percent(sexuality_pct, accuracy = .1)), family = "Times New Roman",vjust = -0.4, size = 3.5) + geom_point(data = bb_m, aes(x = age_c, y = sexuality_pct, fill = "m"), color = "#41ae76", size = 1.5) + geom_text(data = bb_m, aes(x = age_c, y = sexuality_pct, label=percent(sexuality_pct, accuracy = .1)), family = "Times New Roman",vjust = +0.2, size = 3.5) + geom_line(data = bb_w, aes(x = age_c, y = sexuality_pct), color = "#6a51a3", lwd = 1, alpha= 0.5) + geom_line(data = bb_m, aes(x = age_c, y = sexuality_pct), color = "#41ae76", lwd = 1, alpha= 0.5 ) + labs(x = "Age class", y = "Percentage") + scale_y_continuous(labels = scales::percent) + expand_limits(x = 15, y = 0) + scale_x_continuous(limits = c(26,50), breaks = bb_w$age_c, labels = levels(b_w$age10)) + theme(plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5)) + scale_fill_manual(name = NULL, values=c("w"="#6a51a3", "m"="#41ae76" )) + guides(fill = guide_legend(override.aes = list(shape=21, size = 5))) + theme(text = element_text(family = "Times New Roman", size = 18))
图表效果:两个系列(女性和男性)的点及对应的百分比文本在x=35的位置出现重叠,影响可读性。
解决方案
要单独调整某个点的文本位置,核心思路是给目标数据添加自定义的位置控制逻辑,避免全局设置vjust带来的影响,以下是两种实用方法:
方法1:给数据框添加自定义vjust列
针对需要调整的点(比如bb_w中age_c=35的第二个点),在数据框中新增一列存储该点的vjust值,其他点保持原有设置:
# 修改bb_w数据框,添加自定义vjust列 bb_w <- bb_w %>% mutate(text_vjust = ifelse(age_c == 35, -1.2, -0.4)) # 第二个点调得更靠上,数值可按需调整
然后在geom_text中引用这个自定义列作为vjust参数:
geom_text(data = bb_w, aes(x = age_c, y = sexuality_pct, label=percent(sexuality_pct, accuracy = .1), vjust = text_vjust), # 使用自定义vjust值 family = "Times New Roman", size = 3.5)
方法2:直接偏移目标点的y坐标
如果不想用vjust,可以直接给目标点的文本设置偏移后的y值,通过ifelse在aes中动态调整:
geom_text(data = bb_w, aes(x = age_c, y = ifelse(age_c == 35, sexuality_pct + 0.05, sexuality_pct), # 第二个点y值加0.05(单位与y轴一致) label=percent(sexuality_pct, accuracy = .1)), family = "Times New Roman", vjust = -0.4, size = 3.5)
修改后的完整代码
library(tidyverse) library(scales) library(ggplot2) ageC <- c("18-34","35", "35", "35","35","18-34","18-34","35","18-34","35") sexe <- c("w","m", "m", "w", "w", "w", "m", "m", "m", "w") sexuality <- c("no","yes", "no", "yes", "no", "no", "yes", "yes", "no", "yes") df <- data.frame(ageC,sexe,sexuality) # base with women b_w <- df %>% select(ageC,sexe,sexuality) %>% filter(sexe != "m") %>% mutate(sexuality = factor(sexuality)) bb_w <- b_w %>% mutate(age_c = factor(ageC, labels = c("26", "35")) %>% as.character %>% as.numeric, sexuality_c = as.numeric(sexuality=="yes")) %>% group_by(age_c) %>% summarise(sexuality_pct = mean(sexuality_c)) %>% mutate(text_vjust = ifelse(age_c == 35, -1.2, -0.4)) # 添加自定义vjust列 # base with men b_m <- df %>% select(ageC,sexe,sexuality) %>% filter(sexe != "w") %>% mutate(sexuality = factor(sexuality)) bb_m <- b_m %>% mutate(age_c = factor(ageC, labels = c("26", "35")) %>% as.character %>% as.numeric, sexuality_c = as.numeric(sexuality=="yes")) %>% group_by(age_c) %>% summarise(sexuality_pct = mean(sexuality_c)) ggplot() + geom_point(data = bb_w, aes(x = age_c, y = sexuality_pct, fill = "w"), color = "#6a51a3", size = 1.5) + geom_text(data = bb_w, aes(x = age_c, y = sexuality_pct, label=percent(sexuality_pct, accuracy = .1), vjust = text_vjust), # 使用自定义vjust family = "Times New Roman", size = 3.5) + geom_point(data = bb_m, aes(x = age_c, y = sexuality_pct, fill = "m"), color = "#41ae76", size = 1.5) + geom_text(data = bb_m, aes(x = age_c, y = sexuality_pct, label=percent(sexuality_pct, accuracy = .1)), family = "Times New Roman",vjust = +0.2, size = 3.5) + geom_line(data = bb_w, aes(x = age_c, y = sexuality_pct), color = "#6a51a3", lwd = 1, alpha= 0.5) + geom_line(data = bb_m, aes(x = age_c, y = sexuality_pct), color = "#41ae76", lwd = 1, alpha= 0.5 ) + labs(x = "Age class", y = "Percentage") + scale_y_continuous(labels = scales::percent) + expand_limits(x = 15, y = 0) + scale_x_continuous(limits = c(26,50), breaks = bb_w$age_c, labels = levels(b_w$age10)) + theme(plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5)) + scale_fill_manual(name = NULL, values=c("w"="#6a51a3", "m"="#41ae76" )) + guides(fill = guide_legend(override.aes = list(shape=21, size = 5))) + theme(text = element_text(family = "Times New Roman", size = 18))
说明
- 方法1适合需要保持文本与点相对对齐逻辑的场景,可通过调整
text_vjust的数值控制偏移程度; - 方法2适合需要固定偏移量的场景,
+0.05是基于y轴百分比小数形式的偏移量,可根据重叠情况微调; - 如果需要调整
bb_m的点,用同样逻辑给bb_m添加对应列或偏移即可。
内容的提问来源于stack exchange,提问作者lpambout
相关产品推荐
相关产品推荐

