在R语言ggplot2的facet_wrap中设置变量自定义显示顺序
解决ggplot2分面时自定义填充类别顺序的问题
我有一个数据集,需要分组后用facet_wrap分面展示。目前分面功能正常,但填充变量(Name)按字母顺序排序,而非数据框中的原始顺序。常规绘图中可通过scale_x_discrete指定顺序,但搭配facet_wrap使用时出现如下错误:
Error in seq_len(n) : argument must be coercible to non-negative integer
In addition: Warning messages:
1: Removed 8 rows containing missing values (position_stack).
2: In max(f) : no non-missing arguments to max; returning -Inf
需求:左侧分面展示Jim、Bob,右侧展示Sue、Sally的顺序。
原代码示例
无scale_x_discrete的代码
library(ggplot2) library(reshape2) df_dtype <- data.frame(Name = c("Jim","Bob","Sue","Sally"), Pre = c(150, 200, 325, 120), Post = c(70,120,200,100) ) mdtype <- melt(df_dtype) mdtype$group = ifelse(mdtype$Name %in% c("Jim", "Bob"), "Group1", "Group2") names = c("Jim","Bob","Sue","Sally") p <- ggplot(mdtype, aes(x=variable, y=value, fill=Name)) + geom_bar(position="stack", stat="identity") + ylab("Frequency") + xlab("") + ggtitle("Report Type") + theme(axis.ticks.x = element_blank(),axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) p + facet_wrap(~group) #+ scale_x_discrete(limits = names)
含scale_x_discrete的代码
library(ggplot2) library(reshape2) df_dtype <- data.frame(Name = c("Jim","Bob","Sue","Sally"), Pre = c(150, 200, 325, 120), Post = c(70,120,200,100) ) mdtype <- melt(df_dtype) mdtype$group = ifelse(mdtype$Name %in% c("Jim", "Bob"), "Group1", "Group2") names = c("Jim","Bob","Sue","Sally") p <- ggplot(mdtype, aes(x=variable, y=value, fill=Name)) + geom_bar(position="stack", stat="identity") + ylab("Frequency") + xlab("") + ggtitle("Report Type") + theme(axis.ticks.x = element_blank(),axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) p + facet_wrap(~group) + scale_x_discrete(limits = names)
问题原因
错误根源是用错了scale参数:你要控制的是填充变量Name的顺序,但scale_x_discrete是用来设置x轴(此处为Pre/Post)的类别顺序。把Name的列表传给scale_x_discrete,会导致x轴找不到对应类别,进而触发报错。
解决方案
有两种可靠方法实现需求:
方法1:提前将Name转为有序因子
在数据预处理阶段,把Name设置为指定顺序的因子,ggplot会自动沿用这个顺序:
library(ggplot2) library(reshape2) df_dtype <- data.frame(Name = c("Jim","Bob","Sue","Sally"), Pre = c(150, 200, 325, 120), Post = c(70,120,200,100) ) # 自定义Name的顺序,转为有序因子 df_dtype$Name <- factor(df_dtype$Name, levels = c("Jim","Bob","Sue","Sally"), ordered = TRUE) mdtype <- melt(df_dtype) mdtype$group = ifelse(mdtype$Name %in% c("Jim", "Bob"), "Group1", "Group2") p <- ggplot(mdtype, aes(x=variable, y=value, fill=Name)) + geom_bar(position="stack", stat="identity") + ylab("Frequency") + xlab("") + ggtitle("Report Type") + theme(axis.ticks.x = element_blank(),axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) p + facet_wrap(~group)
方法2:用scale_fill_discrete控制填充顺序
直接在绘图代码中用scale_fill_discrete指定Name的顺序,无需修改原始数据:
library(ggplot2) library(reshape2) df_dtype <- data.frame(Name = c("Jim","Bob","Sue","Sally"), Pre = c(150, 200, 325, 120), Post = c(70,120,200,100) ) mdtype <- melt(df_dtype) mdtype$group = ifelse(mdtype$Name %in% c("Jim", "Bob"), "Group1", "Group2") name_order <- c("Jim","Bob","Sue","Sally") p <- ggplot(mdtype, aes(x=variable, y=value, fill=Name)) + geom_bar(position="stack", stat="identity") + ylab("Frequency") + xlab("") + ggtitle("Report Type") + theme(axis.ticks.x = element_blank(),axis.text.x = element_text(angle = 45, vjust = 1, hjust=1)) + scale_fill_discrete(limits = name_order) # 控制填充类别顺序 p + facet_wrap(~group)
两种方法都能让分面中的Name按需求顺序堆叠,且不会触发报错。
内容的提问来源于stack exchange,提问作者John Conor
相关产品推荐
相关产品推荐

