基于ggpattern包的地图图例创建及绘制逻辑优化咨询
问题回顾
我用ggpattern包绘制了一张为不同县区分配颜色和图案的地图,但无法生成包含对应颜色+图案组合的图例;同时当前逐个添加县区的方式较为繁琐,希望得到更优实现方案。
原代码:
library(ggplot2) library(sf) library(tidyverse) library(ggpattern) library(cowplot) nc <- st_read(system.file("shape/nc.shp", package="sf")) nc<-nc%>%mutate(row_id=row_number()) ids=unique(nc$row_id) ##pattern df1 <- data.frame( x = rep(1:6, 9), y = rep(1:9, each=6), name = gridpattern::names_magick, stringsAsFactors = FALSE ) df1<-df1%>%mutate(row_id=row_number()) df_color<-data.frame() pattern_rand_df_final<-data.frame() p1 <- ggplot() ids=1:10 for(id in ids){ df <- nc%>%filter(id==row_id) x=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") y=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") z=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") zz=rgb(x,y,z, maxColorValue = 255) df_color<<-rbind(df_color,zz) df_color=df_color%>%mutate(row_id=row_number()) df_color_f<- df_color%>%filter(row_id==id) colnames(df_color_f)[1]='fill_color' num_rand=sample(1:54,1) pattern_rand_df_final<<-rbind(pattern_rand_df_final,num_rand) pattern_rand_df_final=pattern_rand_df_final%>%mutate(row_id=row_number()) pattern_rand_df_final_filter<- pattern_rand_df_final%>%filter(row_id==id) colnames(pattern_rand_df_final_filter)[1]='random_pattern' df1_f<-df1%>%filter(row_id==pattern_rand_df_final_filter$random_pattern) </think_never_used_51bce0c785ca2f68081bfa7d91973934>用户现在需要把之前的问题和代码整理成符合要求的Markdown格式,还要翻译成中文,去掉AI味,最后标注来源。首先,先理清楚结构: 首先是问题描述,然后是代码,然后是解决方案,分两部分:一是优化绘图方式(不用循环逐个加),二是创建带颜色和图案的图例。 首先,问题部分: 我用ggpattern包做了一张给不同县区添加颜色和图案的地图,但没法生成对应颜色和图案的图例。我逐个添加县区是为了控制特定县区的图案,也想知道有没有更好的实现方式。 然后是原代码,用代码块包裹。 接下来是解决方案,分两部分: ## 一、优化绘图逻辑(避免循环逐个添加) 原来的循环方式不仅繁琐,还不利于自动生成图例。正确的做法是把颜色、图案信息提前合并到nc的sf数据框里,然后一次调用geom_sf_pattern,这样ggplot能自动识别映射关系,方便后续做图例。 代码示例: 先处理颜色和图案的随机分配,合并到nc数据框: ```r library(ggplot2) library(sf) library(tidyverse) library(ggpattern) library(cowplot) # 读取数据 nc <- st_read(system.file("shape/nc.shp", package="sf")) %>% mutate(row_id = row_number()) # 准备图案列表 df1 <- data.frame( x = rep(1:6, 9), y = rep(1:9, each=6), name = gridpattern::names_magick, stringsAsFactors = FALSE ) %>% mutate(row_id = row_number()) # 为前10个县区随机分配颜色和图案 set.seed(123) # 设置种子保证结果可复现 nc_plot <- nc %>% filter(row_id %in% 1:10) %>% mutate( # 随机生成填充色 fill_color = rgb( red = sample(0:255, n(), replace = TRUE), green = sample(0:255, n(), replace = TRUE), blue = sample(0:255, n(), replace = TRUE), maxColorValue = 255 ), # 随机分配图案ID和名称 random_pattern_id = sample(1:54, n(), replace = TRUE) ) %>% left_join(df1, by = c("random_pattern_id" = "row_id")) # 绘制地图 p1 <- ggplot() + geom_sf_pattern( data = nc_plot, aes(fill = fill_color, pattern_type = name), color = "black", pattern = "magick" ) p1
二、创建自定义颜色+图案图例
因为ggplot默认的图例分开显示颜色和图案,我们需要用cowplot手动创建组合图例,步骤如下:
- 先制作每个图例项的小图(颜色+图案)
- 组合这些小图和标签,生成自定义图例
- 把地图和图例拼接在一起
代码示例:
# 准备图例数据(包含县区名、颜色、图案) legend_data <- nc_plot %>% st_drop_geometry() %>% select(county = NAME, fill_color, name) # 生成每个图例项的小绘图 legend_plots <- map2(legend_data$fill_color, legend_data$name, function(col, pat) { ggplot() + geom_rect_pattern( aes(xmin = 0, xmax = 1, ymin = 0, ymax = 1), fill = col, pattern = "magick", pattern_type = pat, color = "black" ) + theme_void() }) # 组合图例项和标签 legend <- plot_grid( plotlist = legend_plots, ncol = 1, labels = legend_data$county, label_size = 9, label_x = 0.1, hjust = 0 ) # 拼接地图和图例 final_plot <- plot_grid(p1, legend, rel_widths = c(2, 1)) final_plot
然后说明这样做的好处:
- 避免循环,代码更简洁易维护
- 数据和绘图逻辑分离,便于调整颜色、图案规则
- 自定义图例完美匹配每个县区的颜色+图案组合
最后标注:内容的提问来源于stack exchange,提问作者jsimpsno
现在要把这些整理成Markdown,注意格式:标题用#,列表用-,代码用```r,强调用*。还要去掉AI味,语言要像stackoverflow的专家回答,直接给解决方案,不要多余的话。
现在把结构整理好:
问题回顾
我用ggpattern包绘制了一张为不同县区分配颜色和图案的地图,但无法生成包含对应颜色+图案组合的图例;同时当前逐个添加县区的方式较为繁琐,希望得到更优实现方案。
原代码:
library(ggplot2) library(sf) library(tidyverse) library(ggpattern) library(cowplot) nc <- st_read(system.file("shape/nc.shp", package="sf")) nc<-nc%>%mutate(row_id=row_number()) ids=unique(nc$row_id) ##pattern df1 <- data.frame( x = rep(1:6, 9), y = rep(1:9, each=6), name = gridpattern::names_magick, stringsAsFactors = FALSE ) df1<-df1%>%mutate(row_id=row_number()) df_color<-data.frame() pattern_rand_df_final<-data.frame() p1 <- ggplot() ids=1:10 for(id in ids){ df <- nc%>%filter(id==row_id) x=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") y=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") z=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") zz=rgb(x,y,z, maxColorValue = 255) df_color<<-rbind(df_color,zz) df_color=df_color%>%mutate(row_id=row_number()) df_color_f<- df_color%>%filter(row_id==id) colnames(df_color_f)[1]='fill_color' num_rand=sample(1:54,1) pattern_rand_df_final<<-rbind(pattern_rand_df_final,num_rand) pattern_rand_df_final=pattern_rand_df_final%>%mutate(row_id=row_number()) pattern_rand_df_final_filter<- pattern_rand_df_final%>%filter(row_id==id) colnames(pattern_rand_df_final_filter)[1]='random_pattern' df1_f<-df1%>%filter(row_id==pattern_rand_df_final_filter$random_pattern) p1<-p1 + geom_sf_pattern(data = df, fill=df_color_f$fill_color, color = "black",pattern='magick', pattern_type = I(df1_f$name)) } ##this creates the map p1 legend_df<-full_join(df_color,pattern_rand_df_final) colnames(legend_df)[1]='color_fill' colnames(legend_df)[3]='hex_fill_number_id' legend_df<-left_join(legend_df,df1%>%select(name,row_id),by=c("hex_fill_number_id"="row_id")) ###this is the legend information i would like to add using cowplot legend_df<-left_join(legend_df,st_drop_geometry(nc)%>%select(row_id,NAME)%>%rename(county=NAME))
一、优化绘图逻辑(替代循环逐个添加)
循环添加图层的方式不仅冗余,还会导致ggplot无法自动识别映射关系,不利于图例生成。正确做法是将颜色、图案信息提前合并到sf数据框中,一次性调用geom_sf_pattern:
library(ggplot2) library(sf) library(tidyverse) library(ggpattern) library(cowplot) # 读取并预处理数据 nc <- st_read(system.file("shape/nc.shp", package="sf")) %>% mutate(row_id = row_number()) # 准备magick图案列表 df1 <- data.frame( x = rep(1:6, 9), y = rep(1:9, each=6), name = gridpattern::names_magick, stringsAsFactors = FALSE ) %>% mutate(row_id = row_number()) # 为目标县区分配随机颜色和图案(这里取前10个) set.seed(123) # 设置种子保证结果可复现 nc_plot <- nc %>% filter(row_id %in% 1:10) %>% mutate( # 生成随机RGB填充色 fill_color = rgb( red = sample(0:255, n(), replace = TRUE), green = sample(0:255, n(), replace = TRUE), blue = sample(0:255, n(), replace = TRUE), maxColorValue = 255 ), # 随机分配图案ID random_pattern_id = sample(1:54, n(), replace = TRUE) ) %>% # 关联图案名称 left_join(df1, by = c("random_pattern_id" = "row_id")) # 绘制地图 p1 <- ggplot() + geom_sf_pattern( data = nc_plot, aes(fill = fill_color, pattern_type = name), color = "black", pattern = "magick" ) + theme_minimal() p1
二、创建自定义颜色+图案组合图例
ggplot默认会将颜色和图案拆分为两个独立图例,我们可以用cowplot手动生成组合式图例,步骤如下:
# 提取图例所需数据(去除几何信息) legend_data <- nc_plot %>% st_drop_geometry() %>% select(county = NAME, fill_color, name) # 为每个县区生成带颜色和图案的小矩形图 legend_items <- map2(legend_data$fill_color, legend_data$name, function(col, pat) { ggplot() + geom_rect_pattern( aes(xmin = 0, xmax = 1, ymin = 0, ymax = 1), fill = col, pattern = "magick", pattern_type = pat, color = "black" ) + theme_void() # 去除多余元素 }) # 组合图例项与县区名称标签 custom_legend <- plot_grid( plotlist = legend_items, ncol = 1, labels = legend_data$county, label_size = 9, label_x = 0.1, hjust = 0, align = "v" ) # 拼接地图与自定义图例 final_plot <- plot_grid(p1, custom_legend, rel_widths = c(2, 1), align = "h") final_plot
优化点说明
- 去除循环逻辑,代码更简洁易维护,便于后续调整颜色/图案规则
- 数据与绘图逻辑分离,符合ggplot的映射思想
- 自定义图例完美匹配每个县区的颜色+图案组合,解决原问题中的图例缺失问题
内容的提问来源于stack exchange,提问作者jsimpsno
这样应该就符合要求了,格式正确,语言专业,没有AI味,解决了用户的两个问题:图例创建和代码优化。</think_never_used_51bce0c785ca2f68081bfa7d91973934># 解决方案:ggpattern地图的图例创建与代码优化
问题回顾
我用ggpattern包绘制了一张为不同县区分配颜色和图案的地图,但无法生成包含对应颜色+图案组合的图例;同时当前逐个添加县区的方式较为繁琐,希望得到更优实现方案。
原代码:
library(ggplot2) library(sf) library(tidyverse) library(ggpattern) library(cowplot) nc <- st_read(system.file("shape/nc.shp", package="sf")) nc<-nc%>%mutate(row_id=row_number()) ids=unique(nc$row_id) ##pattern df1 <- data.frame( x = rep(1:6, 9), y = rep(1:9, each=6), name = gridpattern::names_magick, stringsAsFactors = FALSE ) df1<-df1%>%mutate(row_id=row_number()) df_color<-data.frame() pattern_rand_df_final<-data.frame() p1 <- ggplot() ids=1:10 for(id in ids){ df <- nc%>%filter(id==row_id) x=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") y=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") z=paste(sample(0:255,size=1,replace=TRUE),collapse=" ") zz=rgb(x,y,z, maxColorValue = 255) df_color<<-rbind(df_color,zz) df_color=df_color%>%mutate(row_id=row_number()) df_color_f<- df_color%>%filter(row_id==id) colnames(df_color_f)[1]='fill_color' num_rand=sample(1:54,1) pattern_rand_df_final<<-rbind(pattern_rand_df_final,num_rand) pattern_rand_df_final=pattern_rand_df_final%>%mutate(row_id=row_number()) pattern_rand_df_final_filter<- pattern_rand_df_final%>%filter(row_id==id) colnames(pattern_rand_df_final_filter)[1]='random_pattern' df1_f<-df1%>%filter(row_id==pattern_rand_df_final_filter$random_pattern) p1<-p1 + geom_sf_pattern(data = df, fill=df_color_f$fill_color, color = "black",pattern='magick', pattern_type = I(df1_f$name)) } ##this creates the map p1 legend_df<-full_join(df_color,pattern_rand_df_final) colnames(legend_df)[1]='color_fill' colnames(legend_df)[3]='hex_fill_number_id' legend_df<-left_join(legend_df,df1%>%select(name,row_id),by=c("hex_fill_number_id"="row_id")) ###this is the legend information i would like to add using cowplot legend_df<-left_join(legend_df,st_drop_geometry(nc)%>%select(row_id,NAME)%>%rename(county=NAME))
一、优化绘图逻辑(替代循环逐个添加)
循环添加图层的方式不仅冗余,还会导致ggplot无法自动识别映射关系,不利于图例生成

