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

如何让ggplot2图例显示原始数据缺失的数值区间?

解决ggplot2图例中缺失非数据区间颜色与大小的问题

你希望在ggplot2的图例中展示原始数据不存在的数值区间,但目前这些区间仅显示文字,没有对应的颜色和大小。你的原始数据如下:

merged_data<-structure(list(Order = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
13, 14, 15, 16, 17, 18, 19, 20), latitude = c(43.2143, 43.3697, 
43.3909, 43.3926, 43.3961, 43.3978, 43.066, 43.2215, 43.368, 
43.435, 43.4434, 43.4623, 43.4895, 43.4856, 43.3738, 43.4761, 
43.5102, 43.5118, 43.5062, 43.4933), longitude = c(-4.479, -4.4804, 
-4.4606, -4.4484, -4.4241, -4.412, -4.1046, -4.1049, -4.1031, 
-4.0818, -4.021, -3.9502, -3.8184, -3.7798, -3.7279, -3.7147, 
-3.5971, -3.5849, -3.5585, -3.5177), median_mafruit = c(2.73, 
1.095, 1.115, 2.73, 0.527, 0.527, 0.962, 1.039, 1.039, 2.73, 
2.73, 2.73, 2.73, 2.73, 0.544, 2.73, 2.73, 2.73, 0.478, 2.73)), row.names = c(NA, 
-20L), spec = structure(list(cols = list(Order = structure(list(), class = c("collector_double", 
"collector")), latitude = structure(list(), class = c("collector_double", 
"collector")), longitude = structure(list(), class = c("collector_double", 
"collector")), median_mafruit = structure(list(), class = c("collector_double", 
"collector"))), default = structure(list(), class = c("collector_guess", 
"collector")), delim = ","), class = "col_spec"), class = c("spec_tbl_df", 
"tbl_df", "tbl", "data.frame"))

可以看到数据中不存在[1.5, 2)、[2, 2.5)、[3, 3.5)、[3.5, 4]这些区间。你使用的绘图代码如下:

library(ggplot2)
library(dplyr)
library(sf)
library(rnaturalearth)
library(rnaturalearthdata)

merged_data_sf <- st_as_sf(merged_data, coords = c("longitude", "latitude"), crs = 4326)
world <- ne_countries(scale = "medium", returnclass = "sf")

# Define color and size breaks and values
color_breaks <- c(0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0)
colors <- c("#E31A1C", "#FF7F00", "#FDBF6F", "#E9E4A6", "#A4D4A9", "#B2DF8A", "#33A02C", "#1F78B4")

size_breaks <- c(0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0)
sizes <- c(0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4)

merged_data_sf$median_mafruit_cut <- cut(
 merged_data_sf$median_mafruit,
 breaks = color_breaks,
 include.lowest = TRUE,
 right = FALSE
)

levels_cut <- levels(merged_data_sf$median_mafruit_cut)

p1 <- ggplot() +
 geom_sf(data = world, fill = "grey87", color = "white") + 
 geom_sf(
   data = merged_data_sf,
   aes(
     size = median_mafruit_cut,
     color = median_mafruit_cut
   ),
   alpha = 0.7
 ) +
 labs(
   x = "Longitude (°)",
   y = "Latitude (°)"
 ) +
 scale_color_manual(
   values = setNames(colors, levels_cut),
   breaks = levels_cut,
   drop = FALSE
 ) +
 scale_size_manual(
   values = setNames(sizes, levels_cut),
   breaks = levels_cut,
   drop = FALSE  # Ensure full range in legend
 ) +
 coord_sf(
   xlim = c(-5, -3),
   ylim = c(43, 44),
   expand = FALSE
 ) +
 guides(
   size = guide_legend(
     title = bquote("Yield (t·ha"^.(1)*")"),
     order = 1
   ),
   color = guide_legend(
     title = bquote("Yield (t·ha"^.(1)*")"),
     order = 1
   )
 )+
 theme_minimal() 

p1

问题在于:虽然设置了drop=FALSE,但cut函数生成的因子仅包含数据中实际存在的区间水平,导致后续的scale无法匹配到缺失的区间,最终图例中这些区间没有颜色和大小。


解决方案

核心是手动指定完整的因子水平,确保所有需要展示的区间都被包含在因子中,具体步骤如下:

  1. 先手动生成所有需要的区间标签,与断点一一对应
  2. 在cut函数中通过levels参数指定完整的区间标签
  3. 确保颜色和大小的映射直接对应完整的区间标签

修改后的完整代码:

library(ggplot2)
library(dplyr)
library(sf)
library(rnaturalearth)
library(rnaturalearthdata)

merged_data_sf <- st_as_sf(merged_data, coords = c("longitude", "latitude"), crs = 4326)
world <- ne_countries(scale = "medium", returnclass = "sf")

# Define color and size breaks and values
color_breaks <- c(0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0)
colors <- c("#E31A1C", "#FF7F00", "#FDBF6F", "#E9E4A6", "#A4D4A9", "#B2DF8A", "#33A02C", "#1F78B4")

size_breaks <- c(0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0)
sizes <- c(0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4)

# 手动生成完整的区间标签,与breaks严格对应
full_levels <- c(
  "[0,0.5)", "[0.5,1)", "[1,1.5)", "[1.5,2)", 
  "[2,2.5)", "[2.5,3)", "[3,3.5)", "[3.5,4]"
)

merged_data_sf$median_mafruit_cut <- cut(
  merged_data_sf$median_mafruit,
  breaks = color_breaks,
  include.lowest = TRUE,
  right = FALSE,
  labels = full_levels  # 指定完整的区间标签
)

# 强制因子保留所有水平,避免自动过滤无数据的区间
merged_data_sf$median_mafruit_cut <- factor(merged_data_sf$median_mafruit_cut, levels = full_levels)

p1 <- ggplot() +
  geom_sf(data = world, fill = "grey87", color = "white") + 
  geom_sf(
    data = merged_data_sf,
    aes(
      size = median_mafruit_cut,
      color = median_mafruit_cut
    ),
    alpha = 0.7
  ) +
  labs(
    x = "Longitude (°)",
    y = "Latitude (°)"
  ) +
  scale_color_manual(
    values = setNames(colors, full_levels),  # 颜色与完整水平绑定
    breaks = full_levels,
    drop = FALSE
  ) +
  scale_size_manual(
    values = setNames(sizes, full_levels),  # 大小与完整水平绑定
    breaks = full_levels,
    drop = FALSE
  ) +
  coord_sf(
    xlim = c(-5, -3),
    ylim = c(43, 44),
    expand = FALSE
  ) +
  guides(
    size = guide_legend(
      title = bquote("Yield (t·ha"^.(-1)*")"),  # 修正单位指数为-1,符合吨/公顷的规范
      order = 1,
      override.aes = list(alpha = 0.7)  # 确保图例标记透明度与图形一致
    ),
    color = guide_legend(
      title = bquote("Yield (t·ha"^.(-1)*")"),
      order = 1,
      override.aes = list(alpha = 0.7)
    )
  )+
  theme_minimal() 

p1

关键修改点说明:

  • 手动生成full_levels:明确列出所有需要展示的区间,确保与断点完全对应
  • cut中指定labels参数:强制生成的因子包含所有区间标签,即使数据中不存在
  • 重新转换为因子并指定levels:锁定因子水平,防止ggplot自动过滤无数据的区间
  • scale映射绑定full_levels:颜色和大小的取值直接与完整水平关联,保证每个区间都有对应样式
  • 修正单位指数:将原代码中的^.(1)改为^.(-1),符合吨每公顷(t·ha⁻¹)的单位规范

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:25:54