如何用cowplot与ggplot排列多图并添加自定义图表元素
解决方案:用cowplot合并ggplot图表并实现定制化格式需求
以下是满足你需求的完整代码,实现了蓝色背景主标题栏、子图标题上方添加说明文本,同时适配RMarkdown HTML和PNG输出:
library(tidyverse) library(ggrepel) library(ggtext) library(lubridate) library(scales) library(cowplot) # ---------------------- 数据准备(原代码保留) ---------------------- #plot 1 data date <- as.Date(c("2019-09-01","2019-10-01","2019-11-01","2019-12-01", "2020-01-01","2020-02-01","2020-03-01","2020-04-01")) year <- c(2019,2019,2019,2019,2020,2020,2020,2020) month <- c("Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr") sales <- c(100,200,600,200,100,100,800,100) df <- data.frame(date,year,month,sales) df <- add_row(df, date = as.Date("2019-08-15"), year = 2019, month = "Aug", sales = NA) df <- add_row(df, date = as.Date("2020-04-15"), year = 2020, month = "Apr", sales = NA) df <- df %>% arrange(date) #plot 1 without facet years data_start <- df %>% filter(row_number()==2) data_mid <- df %>% filter(row_number()==5) data_end <- df %>% filter(row_number()==9) p1 <- ggplot(df, aes(x = date, y = sales)) + geom_line() + scale_x_date( date_labels = "%b", date_breaks = "month", expand = c(0, 0)) + theme_bw() + theme( strip.placement = "outside", strip.background = element_rect(fill = NA, colour = NA), panel.spacing = unit(0, "lines"), axis.line = element_line(colour = "gray"), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank(), axis.title.x = element_text(colour = "gray"), axis.title.y = element_text(hjust=1, colour = "gray"), axis.text.x = element_text(colour = "gray"), axis.text.y = element_text(colour = "gray"), strip.text.x = element_text(colour = "gray", size = 9), plot.title = element_markdown(size = 14), plot.subtitle = element_text(size=10, colour = "#818589"), plot.margin = margin(10,0,0,0, "pt")) + # 微调顶部margin避免和说明文本重叠 geom_point(data_start, mapping=aes(x=date,y=sales), colour="blue", size=2) + geom_point(data_mid, mapping=aes(x=date,y=sales), colour="blue", size=2) + geom_point(data_end, mapping=aes(x=date,y=sales), colour="blue", size=2) + geom_text(data=subset(df, year == 2019 & month == "Sep"), aes(date,sales,label=sales), position = position_dodge(width = 1), vjust = -1, hjust=1,size = 5) + geom_text(data=subset(df, year == 2019 & month == "Dec"), aes(date,sales,label=sales), position = position_dodge(width = 1), vjust = 2, size = 5) + geom_text(data=subset(df, year == 2020 & month == "Apr"), aes(date,sales,label=sales), position = position_dodge(width = 1), vjust = 0, hjust=0,size = 5) + geom_text_repel(data=df[5, ], label="Total number of dollars circulation", nudge_y=500, nudge_x=10, color = "blue") + labs(title = "Chocolate sales", y = "Number of Units", x = "2019 2020") #plot 2 choco_date <- as.Date(c("2021-01-01","2021-01-01","2021-01-01", "2022-01-01","2022-01-01","2022-01-01")) choco_type <- c("Dark", "Milk", "White","Dark", "Milk", "White") choco_sales <- c(1000,600,100,800,400,200) df2 <- data.frame(choco_date,choco_type,choco_sales) dark_start <- df2 %>% filter(choco_type == "Dark" & choco_date == "2021-01-01") milk_start <- df2 %>% filter(choco_type == "Milk" & choco_date == "2021-01-01") white_start <- df2 %>% filter(choco_type == "White" & choco_date == "2021-01-01") dark_end <- df2 %>% filter(choco_type == "Dark" & choco_date == "2022-01-01") milk_end <- df2 %>% filter(choco_type == "Milk" & choco_date == "2022-01-01") white_end <- df2 %>% filter(choco_type == "White" & choco_date == "2022-01-01") p2 <- df2 %>% ggplot(aes(x=choco_date, y=choco_sales, color = choco_type)) + geom_line() + geom_point(dark_start, mapping=aes(x=choco_date,y=choco_sales), colour="red", size=2) + geom_point(milk_start, mapping=aes(x=choco_date,y=choco_sales), colour="green", size=2) + geom_point(white_start, mapping=aes(x=choco_date,y=choco_sales), colour="blue", size=2) + geom_point(dark_end, mapping=aes(x=choco_date,y=choco_sales), colour="red", size=2) + geom_point(milk_end, mapping=aes(x=choco_date,y=choco_sales), colour="green", size=2) + geom_point(white_end, mapping=aes(x=choco_date,y=choco_sales), colour="blue", size=2) + geom_text(data=df2[1, ], label = "1000", nudge_x = -30, size = 3) + geom_text(data=df2[2, ], label = "600", nudge_x = -25, size = 3) + geom_text(data=df2[3, ], label = "100", nudge_x = -25, size = 3)+ geom_text(data=df2[4, ], label = "800 Dark", nudge_x = 105, size = 3) + geom_text(data=df2[5, ], label = "400 Milk", nudge_x = 100, size = 3) + geom_text(data=df2[6, ], label = "200 White", nudge_x = 110, size = 3) + geom_text(data=df2[1, ], label = "- 20%", nudge_x = 200, nudge_y = -40, size = 3) + geom_text(data=df2[2, ], label = "- 33%", nudge_x = 200, nudge_y = -40, size = 3) + geom_text(data=df2[3, ], label = "100%", nudge_x = 200, nudge_y = 100, size = 3)+ scale_x_date(breaks = as.Date(c("2021-01-01", "2022-01-01")), labels = c("Last Month \n Mar-20", "This Month \n Apr-20")) + labs(title = "Number of chocolate bars sold", subtitle = "# OF UNITS % CHANGE") + theme_bw() + theme(strip.placement = "outside", strip.background = element_rect(fill=NA,colour="grey50"), panel.spacing=unit(0,"cm"), axis.line.y = element_blank(), axis.line.x = element_line(), axis.ticks.y = element_blank(), axis.text.y = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.border = element_blank(), panel.background = element_blank(), aspect.ratio = 1, plot.title = element_text(color = "black", size = 14), plot.subtitle = element_text(color = "dark gray", size = 8), legend.position = "none", plot.margin = margin(10,0,0,0, "pt")) + # 微调顶部margin coord_fixed(ratio = 1, clip = 'off') + labs(x="",y="") # ---------------------- 定制化需求实现 ---------------------- # 1. 为每个子图添加标题上方的说明文本 # 子图1的说明文本 p1_note <- ggdraw() + draw_label( "**月度销售趋势**:展示2019-2020年巧克力销售的月度波动及关键节点", size = 10, x = 0, hjust = 0 ) + theme(plot.margin = margin(0,0,5,7, "pt")) # 组合说明文本和子图1 p1_with_note <- plot_grid( p1_note, p1, ncol = 1, rel_heights = c(0.1, 1) ) # 子图2的说明文本 p2_note <- ggdraw() + draw_label( "**品类销售对比**:对比黑巧、牛奶巧、白巧的销量变化及同比变动", size = 10, x = 0, hjust = 0 ) + theme(plot.margin = margin(0,0,5,7, "pt")) # 组合说明文本和子图2 p2_with_note <- plot_grid( p2_note, p2, ncol = 1, rel_heights = c(0.1, 1) ) # 2. 合并两个带说明的子图 combined_plots <- plot_grid( p1_with_note, p2_with_note, rel_widths = c(2,1) ) # 3. 创建带蓝色背景的主标题栏 main_title <- ggdraw() + # 先绘制蓝色背景矩形 draw_rect( x = 0, y = 0, width = 1, height = 1, fill = "#1E88E5", color = NA ) + # 再绘制白色标题文字 draw_label( "巧克力销售数据分析", fontface = 'bold', size = 16, color = "white", x = 0, hjust = 0 ) + theme( plot.margin = margin(10,10,10,10, "pt") ) # 最终合并标题和子图 final_plot <- plot_grid( main_title, combined_plots, ncol = 1, rel_heights = c(0.1, 1) ) # 输出图表 final_plot
关键修改说明
- 子图说明文本:用
ggdraw()创建独立的文本块,通过plot_grid()和子图垂直拼接,rel_heights控制文本和子图的高度比例,plot.margin调整间距避免重叠。 - 蓝色背景主标题:先调用
draw_rect()绘制覆盖整个标题区域的蓝色背景,再叠加白色标题文字,确保视觉层级正确;调整plot.margin让标题有足够留白。 - 适配RMarkdown HTML:所有
margin单位统一用pt,避免不同输出格式的间距差异;保留ggtext支持的 markdown 格式文本,让说明文本更清晰。
内容的提问来源于stack exchange,提问作者OTA
相关产品推荐
相关产品推荐

