ggplot分面图表各分面x轴因子自定义排序实现问题
问题描述
需要绘制分面散点图,要求每个分面(按Pal分组)内的x轴(Effect)按数据中Value的递增顺序排列,同时保留分面独立背景色、点样式等设置。尝试用facetscales包的facet_grid_sc设置独立x轴刻度后,图表显示错乱。
初始数据集
dat<-data.frame(Pal=rep(c("A","B","C","D"),each=5), Rol=c("aa","aa","aa","aa","bb","aa","aa","aa","aa","aa","cc","cc","cc","cc","cc","aa","aa","aa","aa","aa"), Cel=rep(c("home","tree","hat","ball","pen","rope"),times=c(5,3,2,5,2,3)), Value=c(7701.1,59897.3,59897.3,59897.3,744438.1,1226.4,1454.6,1454.6,1454.6,1454.6,56600,92400,5010000,7010000,15740000,28.5,34.2,39.9,48.5,57), Col=c("black","red","black","black","red","red","red","black","black","black","red","red","red","red","red","red","black","black","black","black"), Effect=c("length","length","length","length","height","weight","length","length","age of youngest individual found miles from the closest coastline","age of youngest individual found miles from the closest coastline","pressure","speed","rate","rate","length","length","rate","rate","O2","fecundity") )
初始绘图代码(未实现排序)
highlights<-data.frame(Pal=c("A","B","C","D")) highlights$Pal<-factor(highlights$Pal,levels=c("A","B","C","D")) ggplot() + geom_rect(data=highlights,aes(xmin=-Inf, xmax=Inf, ymin=1, ymax=1000000000), fill=c("yellow","blue","red","green"), alpha=0.05) + geom_point(data = dat, aes(x=Effect, y=Value, shape=Rol, col=Col)) + scale_color_manual(breaks=unique(dat$Col), values=as.character(unique(dat$Col))) + labs(x="",y="Activity") + facet_grid(.~Pal, scales = "free_x")+ scale_y_log10(limits=c(1,1000000000),breaks = c(1,10,100,1000,10000,100000,1000000,10000000,100000000,100000000,1000000000)) + scale_x_discrete(labels = function(x) str_wrap(x, width = 40)) + theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1), panel.background = element_rect(fill = "white", colour = "white"), strip.background = element_rect(fill = "white", colour = "black"), legend.key = element_rect(fill = "white"))
尝试的排序方法(导致图表错乱)
用facetscales包设置分面独立x轴刻度,按每个Pal分组的Effect出现顺序排序,但图表显示异常:
my_scales <- list( "A" = scale_x_discrete(limits = as.character(unique(dat[which(dat$Pal=="A"),"Effect"]))), "B" = scale_x_discrete(limits = as.character(unique(dat[which(dat$Pal=="B"),"Effect"]))), "C" = scale_x_discrete(limits = as.character(unique(dat[which(dat$Pal=="C"),"Effect"]))), "D" = scale_x_discrete(limits = as.character(unique(dat[which(dat$Pal=="D"),"Effect"]))) ) ggplot() + geom_rect(data=highlights,aes(xmin=-Inf, xmax=Inf, ymin=1, ymax=1000000000), fill=c("yellow","blue","red","green"), alpha=0.05) + geom_point(data = dat, aes(x=Effect, y=Value, shape=Rol, col=Col)) + scale_color_manual(breaks=unique(dat$Col), values=as.character(unique(dat$Col))) + labs(x="",y="Activity") + facet_grid_sc(cols = vars(Pal), scales=list(x = my_scales)) + scale_y_log10(limits=c(1,1000000000),breaks = c(1,10,100,1000,10000,100000,1000000,10000000,100000000,100000000,1000000000)) + scale_x_discrete(labels = function(x) str_wrap(x, width = 40)) + theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1), panel.background = element_rect(fill = "white", colour = "white"), strip.background = element_rect(fill = "white", colour = "black"), legend.key = element_rect(fill = "white"))
补充测试数据集(更大规模,原方案失效)
dat<-data.frame(Pal=rep(c("A","B","C","D"),times=c(20,40,60,30)), Rol=c(rep("aa",15),rep("bb",5),rep("aa",40),rep("cc",60),rep("aa",30)), Cel=rep(c("home","tree","hat","ball","arm","leg","beer","stick","pen","rope"),times=c(15,13,12,12,13,12,15,18,17,23)), Value=c(runif(n=20,min = 7000, max = 100000),runif(n=40,min = 100, max = 100000),runif(n=60,min = 1000, max = 1000000),runif(n=30,min = 100000, max = 10000000)), Col=rep(c("red","black"),each=5,times=15), Effect=c(rep(c("length","height"),times=c(15,5)),rep(c("weight","length","age of youngest individual found miles from the closest coastline"),times=c(10,7,23)),rep(c("pressure","speed","rate","length"),times=c(10,3,7,40)),rep(c("length","rate","O2","fecundity"),times=c(3,4,7,16))) )
解决方案
问题根源在于facetscales的刻度设置与全局scale_x_discrete冲突,且直接用unique(Effect)无法保证按Value递增排序。正确做法是为每个Pal分组内的Effect创建基于Value排序的自定义因子水平,再用常规facet_grid(scales="free_x")即可实现需求,无需依赖facetscales。
步骤1:为每个Pal分组的Effect设置排序后的因子水平
按Value均值排序(适合同一Effect对应多个Value的场景)
library(dplyr) library(ggplot2) library(stringr) dat_sorted <- dat %>% group_by(Pal, Effect) %>% summarise(mean_value = mean(Value), .groups = "drop") %>% arrange(Pal, mean_value) %>% group_by(Pal) %>% mutate(Effect_ordered = factor(Effect, levels = Effect)) %>% select(Pal, Effect, Effect_ordered) %>% left_join(dat, by = c("Pal", "Effect"))
按原始Value顺序排序(严格遵循数据中Value的递增顺序)
dat_sorted <- dat %>% arrange(Pal, Value) %>% group_by(Pal) %>% mutate(Effect_ordered = factor(Effect, levels = unique(Effect))) %>% ungroup()
步骤2:绘制图表(用排序后的Effect_ordered作为x轴)
highlights<-data.frame(Pal=c("A","B","C","D")) highlights$Pal<-factor(highlights$Pal,levels=c("A","B","C","D")) ggplot() + # 分面背景色 geom_rect(data=highlights,aes(xmin=-Inf, xmax=Inf, ymin=1, ymax=1e9), fill=c("yellow","blue","red","green"), alpha=0.05) + # 散点图使用排序后的x轴 geom_point(data = dat_sorted, aes(x=Effect_ordered, y=Value, shape=Rol, col=Col)) + # 颜色映射保持原设置 scale_color_manual(breaks=unique(dat$Col), values=as.character(unique(dat$Col))) + labs(x="",y="Activity") + # 分面设置自由x轴 facet_grid(.~Pal, scales = "free_x")+ # y轴对数刻度保持原样式 scale_y_log10(limits=c(1,1e9), breaks = c(1,10,100,1000,10000,1e5,1e6,1e7,1e8,1e9)) + # x轴标签换行处理 scale_x_discrete(labels = function(x) str_wrap(x, width = 40)) + # 主题设置保持原样式 theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1), panel.background = element_rect(fill = "white", colour = "white"), strip.background = element_rect(fill = "white", colour = "black"), legend.key = element_rect(fill = "white"))
说明
- 此方法避免了
facetscales的兼容性问题,同时保证每个分面的x轴按需求排序,图表显示正常。 - 两种排序方式可根据实际需求选择:若同一
Effect对应多个Value,用均值排序更合理;若需严格遵循数据中Value的出现顺序,选择原始顺序排序。
内容的提问来源于stack exchange,提问作者Bithorax
相关产品推荐
相关产品推荐

