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

如何在ggplot2中用geom_ribbon正确绘制两条线间的着色区域?

问题描述

使用R语言ggplot2绘制不同教育水平下男性(Masculino)和女性(Femenino)的百分比折线图,希望根据哪组百分比更高着色两条线之间的区域,但当前着色区域无法正确显示,存在大量NA值或边界计算错误。

实际输出:折线图显示正常,但两条线之间的填充区域未正确渲染,仅出现零散色块或无填充。
期望输出:两条折线之间的区域根据男女百分比高低,分别填充对应颜色(女性占比高则用红色,男性占比高则用蓝色)。

使用的代码如下:

library(tidyverse)

desired_levels <- c("Ninguno", "Educación preescolar", 
                    "Educación básica primaria (1 a 5 grado)", 
                    "Educación básica primaria (6 a 9 grado)", 
                    "Educación Media (10 a 11 grado)", 
                    "Tecnológico sin graduar", 
                    "Técnico graduado", 
                    "Universitario sin graduar", 
                    "Universitario graduado", 
                    "Tecnológico graduado", 
                    "Técnico sin graduar", 
                    "Postgrado")

graph1 <- function(data, barrio) {
  # Filtering data
  data <- data %>% filter(`¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?` != "No sabe, no informa")
  
  table <- data %>%
    group_by(`¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?`, Género) %>%
    summarise(n = sum(EXPANSOR), .groups = 'drop') %>%
    group_by(`¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?`) %>%
    mutate(percentage = n / sum(n) * 100) %>%
    ungroup() %>%
    rename(NivelEducativo = `¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?`)
  
  table$NivelEducativo <- factor(
    table$NivelEducativo,
    levels = desired_levels
  )
  
  table <- table %>%
    complete(NivelEducativo, Género, fill = list(n = 0, percentage = 0))
  
  # Creating bounds table for shaded area
  bounds <- table %>%
    pivot_wider(names_from = Género, values_from = percentage) %>%
    mutate(
      ymax = pmax(Masculino, Femenino, na.rm = TRUE),
      ymin = pmin(Masculino, Femenino, na.rm = TRUE),
      fill = Femenino > Masculino
    ) %>%
    replace_na(list(Masculino = 0, Femenino = 0, ymax = 0, ymin = 0))
  
  plot <- ggplot() +
    geom_ribbon(data = bounds, aes(x = NivelEducativo, ymin = ymin, ymax = ymax, fill = fill), alpha = 0.4) +
    geom_line(data = table, aes(x = NivelEducativo, y = percentage, group = Género, color = Género), size = 1) +
    geom_point(data = table, aes(x = NivelEducativo, y = percentage, shape = Género, color = Género), size = 4) +
    geom_text(data = table, aes(x = NivelEducativo, y = percentage, label = paste0(round(n), " (", round(percentage), "%)")), 
              position = position_dodge(width = 0.9), size = 4, color = "black", vjust = -0.5) +
    scale_fill_manual(values = c("TRUE" = "#e31a1c", "FALSE" = "#1f78b4"), guide = "none") +
    scale_color_manual(values = c("Masculino" = "#1f78b4", "Femenino" = "#e31a1c")) +
    scale_shape_manual(values = c("Masculino" = 21, "Femenino" = 23)) +
    theme_minimal() +
    labs(title = "Nivel educativo más alto cursado por género en CB", 
         subtitle = "Distribución porcentual por género",
         x = "Nivel educativo", y = "Porcentaje (%)",
         color = "Género",
         shape = "Género") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 12),
          axis.text.y = element_text(size = 12),
          legend.text = element_text(size = 12),
          legend.title = element_text(size = 12),
          plot.title = element_text(face = "bold", size = 16),  
          plot.subtitle = element_text(size = 14),
          axis.title.x = element_text(size = 14),
          axis.title.y = element_text(size = 14),
          legend.position = "right",
          legend.background = element_rect(color = "gray", size = 0.1),
          legend.key = element_blank(),
          panel.grid.major = element_line(color = "gray", size = 0.2),
          panel.grid.minor = element_blank(),
          axis.line = element_line(color = "black", size = 0.5))
  
  return(plot)
}

graph1(data_cb, "CB")
错误原因
  1. x轴类型不兼容:geom_ribbon需要连续型x轴数据来计算填充区域,但当前NivelEducativo是因子(分类变量),ggplot无法在分类轴上正确生成连续的填充区域,导致边界计算错误或NA值。
  2. 分类轴无法识别过渡:分类变量作为x轴时,geom_ribbon无法识别相邻类别之间的过渡关系,无法填充折线之间的完整区域。
修正方案

将因子型x轴转换为数值型,同时保留原因子的文本标签用于显示,具体步骤如下:

  1. 在数据处理阶段添加数值型x列(使用因子的整数编码)。
  2. 修改所有几何图层的x映射为数值型x。
  3. 通过scale_x_continuous将数值x轴重新映射为原教育水平的文本标签。
修正后的代码
library(tidyverse)

desired_levels <- c("Ninguno", "Educación preescolar", 
                    "Educación básica primaria (1 a 5 grado)", 
                    "Educación básica primaria (6 a 9 grado)", 
                    "Educación Media (10 a 11 grado)", 
                    "Tecnológico sin graduar", 
                    "Técnico graduado", 
                    "Universitario sin graduar", 
                    "Universitario graduado", 
                    "Tecnológico graduado", 
                    "Técnico sin graduar", 
                    "Postgrado")

graph1 <- function(data, barrio) {
  # Filtering data
  data <- data %>% filter(`¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?` != "No sabe, no informa")
  
  table <- data %>%
    group_by(`¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?`, Género) %>%
    summarise(n = sum(EXPANSOR), .groups = 'drop') %>%
    group_by(`¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?`) %>%
    mutate(percentage = n / sum(n) * 100) %>%
    ungroup() %>%
    rename(NivelEducativo = `¿Cuál es el nivel educativo más alto que esta cursando o ha cursado?`)
  
  table$NivelEducativo <- factor(
    table$NivelEducativo,
    levels = desired_levels
  )
  
  table <- table %>%
    complete(NivelEducativo, Género, fill = list(n = 0, percentage = 0)) %>%
    # 添加数值型x列,对应因子的整数位置
    mutate(x = as.integer(NivelEducativo))
  
  # Creating bounds table for shaded area
  bounds <- table %>%
    pivot_wider(names_from = Género, values_from = percentage) %>%
    mutate(
      x = as.integer(NivelEducativo),
      ymax = pmax(Masculino, Femenino, na.rm = TRUE),
      ymin = pmin(Masculino, Femenino, na.rm = TRUE),
      fill = Femenino > Masculino
    ) %>%
    replace_na(list(Masculino = 0, Femenino = 0, ymax = 0, ymin = 0))
  
  plot <- ggplot() +
    # 使用数值型x绘制填充区域
    geom_ribbon(data = bounds, aes(x = x, ymin = ymin, ymax = ymax, fill = fill), alpha = 0.4) +
    # 折线、点、文本均使用数值型x
    geom_line(data = table, aes(x = x, y = percentage, group = Género, color = Género), size = 1) +
    geom_point(data = table, aes(x = x, y = percentage, shape = Género, color = Género), size = 4) +
    geom_text(data = table, aes(x = x, y = percentage, label = paste0(round(n), " (", round(percentage), "%)")), 
              position = position_dodge(width = 0.9), size = 4, color = "black", vjust = -0.5) +
    scale_fill_manual(values = c("TRUE" = "#e31a1c", "FALSE" = "#1f78b4"), guide = "none") +
    scale_color_manual(values = c("Masculino" = "#1f78b4", "Femenino" = "#e31a1c")) +
    scale_shape_manual(values = c("Masculino" = 21, "Femenino" = 23)) +
    # 将数值x映射回原教育水平标签
    scale_x_continuous(breaks = 1:length(desired_levels), labels = desired_levels) +
    theme_minimal() +
    labs(title = "Nivel educativo más alto cursado por género en CB", 
         subtitle = "Distribución porcentual por género",
         x = "Nivel educativo", y = "Porcentaje (%)",
         color = "Género",
         shape = "Género") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 12),
          axis.text.y = element_text(size = 12),
          legend.text = element_text(size = 12),
          legend.title = element_text(size = 12),
          plot.title = element_text(face = "bold", size = 16),  
          plot.subtitle = element_text(size = 14),
          axis.title.x = element_text(size = 14),
          axis.title.y = element_text(size = 14),
          legend.position = "right",
          legend.background = element_rect(color = "gray", size = 0.1),
          legend.key = element_blank(),
          panel.grid.major = element_line(color = "gray", size = 0.2),
          panel.grid.minor = element_blank(),
          axis.line = element_line(color = "black", size = 0.5))
  
  return(plot)
}

graph1(data_cb, "CB")
关键改动说明
  • 在table和bounds数据中添加x = as.integer(NivelEducativo),将分类变量转换为连续数值型x轴。
  • 所有几何图层的x映射改为数值型x,确保geom_ribbon能正确计算填充区域。
  • 添加scale_x_continuous将数值x轴重新标注为原教育水平的文本标签,保持视觉上的分类效果。

修改后,两条折线之间的区域会根据男女百分比高低正确填充对应颜色,符合期望输出效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:15:02