在R中使用for循环绘制多物种分国家直方图时重复绘图的问题
问题与解决方案
问题描述
现有名为pairs的数据集,结构示例如下:
id_density sp_code country dens_ab ... <dbl> <fct> <chr> <dbl> 1 15 LALO US 24.0 2 16 LALO US 32.0 3 17 LALO US 20.0 4 18 LALO US 30.0 5 19 LALO US 17.5 6 20 LALO US 32.5 ...
需求:为每个物种(sp_code共21种)生成一组小倍数直方图,每组包含3个国家的dens_ab分布直方图。
尝试的代码如下:
# libraries library(ggplot2) library(dplyr) # group by country pairs <- pairs %>% group_by(country) dplyr::is_grouped_df(pairs) # Grouped (TRUE) # Loop plot.country <- function(x = pairs) { sp_code <- unique(x$sp_code) for (i in seq_along(sp_code)) { plot <- x %>% ggplot(aes(x = dens_ab, color = country, fill = country)) + geom_histogram(data = filter(x, sp_code == sp_code[i]), alpha = 0.6, binwidth = 0.5) + scale_fill_viridis(discrete=TRUE) + scale_color_viridis(discrete=TRUE) + theme_ipsum()+ xlab("Density (pairs/km2)") + ylab("Frequency") + facet_wrap(~country) if (dir.exists("output")) { } else {dir.create("output")} ggsave(filename = paste0("output/", sp_code[i], "plot_site_dens.png"), plot = plot, width = 11, height = 8.5, units = "in") print(plot) } } # Plot function execution plot.country()
遇到的问题:
- 生成的直方图存在重复内容,不符合实际数据分布
- 循环生成的图背景为黑色(非循环绘制时背景为白色)
问题分析与修正
1. 移除不必要的分组操作
一开始对pairs执行group_by(country)完全多余,分组后的数据集会让后续的filter操作按组执行,导致过滤逻辑混乱,先取消分组:
pairs <- pairs %>% ungroup() # 取消分组,或者直接删除原代码中的group_by步骤
2. 修正ggplot数据过滤逻辑
原代码中,ggplot初始化时使用整个分组数据集,然后在geom_histogram里单独过滤,这会导致facet的分组基于整个数据集的country,但实际绘图数据是过滤后的,容易出现重复或错误分布。正确的做法是先过滤出当前物种的数据,再传入ggplot。
3. 解决黑色背景问题
theme_ipsum()是hrbrthemes包的主题,默认使用深色背景。如果需要白色背景,可以:
- 改用默认白色背景主题(如
theme_bw()) - 或修改
theme_ipsum()的参数(如theme_ipsum(plot_background = "white", panel_background = "white"))
修正后的完整代码
# 加载所需包 library(ggplot2) library(dplyr) # 如果使用theme_ipsum需要加载hrbrthemes # library(hrbrthemes) # 确保数据集未分组 pairs <- pairs %>% ungroup() # 定义绘图函数 plot_country <- function(data = pairs) { # 获取所有唯一物种代码 sp_list <- unique(data$sp_code) # 创建输出目录(放在循环外,避免重复检查) if (!dir.exists("output")) { dir.create("output") } # 循环每个物种 for (sp in sp_list) { # 过滤当前物种的数据 sp_data <- data %>% filter(sp_code == sp) # 绘制小倍数直方图 p <- ggplot(sp_data, aes(x = dens_ab, color = country, fill = country)) + geom_histogram(alpha = 0.6, binwidth = 0.5) + scale_fill_viridis(discrete = TRUE) + scale_color_viridis(discrete = TRUE) + theme_bw() # 使用白色背景主题,替换theme_ipsum() # 如果坚持用theme_ipsum,改为: # theme_ipsum(plot_background = "white", panel_background = "white") + xlab("Density (pairs/km2)") + ylab("Frequency") + facet_wrap(~country) # 保存图片 ggsave( filename = paste0("output/", sp, "_plot_site_dens.png"), plot = p, width = 11, height = 8.5, units = "in" ) # 打印图片 print(p) } } # 执行绘图 plot_country()
额外优化建议
- 避免使用
plot作为变量名(它是R的内置函数),改用p或其他名称 - 把创建目录的逻辑放在循环外,避免每次循环都重复检查
- 使用
sp in sp_list的循环方式更易读,比seq_along更直观
内容的提问来源于stack exchange,提问作者Mag
相关产品推荐
相关产品推荐

