如何让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无法匹配到缺失的区间,最终图例中这些区间没有颜色和大小。
解决方案
核心是手动指定完整的因子水平,确保所有需要展示的区间都被包含在因子中,具体步骤如下:
- 先手动生成所有需要的区间标签,与断点一一对应
- 在
cut函数中通过levels参数指定完整的区间标签 - 确保颜色和大小的映射直接对应完整的区间标签
修改后的完整代码:
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
相关产品推荐
相关产品推荐

