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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:15:03