ggplot2中x轴分类仅可降序重排的问题求助
ggplot2按fill变量对x轴分类排序的问题及解决
问题说明
使用ggplot2绘制条形图时,x为字符变量、y为数值变量、fill为字符变量,期望按fill变量(z)对x轴分类分组排序,但实际操作中遇到异常:
- 仅使用
reorder(x, desc(z))时,x能按z的降序分组排序; - 使用
reorder(x,-z)会报错「! invalid argument to unary operator」; - 使用
reorder(x,z)时,图表仅按x自身字符顺序排列,还会产生36条「In mean.default(X[[i]], ...) : argument is not numeric or logical: returning NA」警告。
有效实现代码(降序分组)
以下代码可实现按z降序对x分类分组排序的需求:
df <- data.frame(x=c("loc9","loc2", "loc3","loc7","loc5","loc6","loc4","loc1","loc8"), y=c(1,2,5,3,5,6,7,9,5), z=c("A","A", "B","B","B","B","C","C","C"), se=c(0.1, 0.14, 0.2, 1,0.25,0.3,0.21,0.23,0.2), n=c(2,2,3,2,1,1,3,5,4)) plot <- df %>% ggplot(aes(x=reorder(x, desc(z)), y=y, fill=z))+ geom_bar(stat="identity")+ geom_errorbar(aes(x=reorder(x, desc(z)), ymin=y-se, ymax=y+se), width=0.3, colour="blue3", alpha =0.9, size=0.7)+ geom_text(aes(x=reorder(x, desc(z)), y=0.5, label=n))+ theme(legend.position="bottom",plot.title = element_text(size=12), text = element_text(size=14), axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+ ggtitle("图表标题") + labs(x="", y="y轴标签", fill=NULL)
生成的图表中,x轴按z的降序(C、B、A)分组排列各个loc类别。
错误代码及问题
若使用reorder(x,z)尝试按z升序排序,代码如下:
plot <- df %>% ggplot(aes(x=reorder(x, z), y=y, fill=z))+ geom_bar(stat="identity")+ geom_errorbar(aes(x=reorder(x, z), ymin=y-se, ymax=y+se), width=0.3, colour="blue3", alpha =0.9, size=0.7)+ geom_text(aes(x=reorder(x, z), y=0.5, label=n))+ theme(legend.position="bottom",plot.title = element_text(size=12), text = element_text(size=14), axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+ ggtitle("图表标题") + labs(x="", y="y轴标签", fill=NULL) plot
运行后会弹出36条上述警告,且x轴仅按loc1到loc9的字符顺序排列,未按z分组排序。
原因分析
reorder()函数的核心逻辑是对第二个参数取均值,再以此为依据排序x。z是字符型变量,直接传入时函数无法对字符取均值,因此抛出警告,排序逻辑失效;-z报错是因为字符型变量不支持负号取反操作,只有数值型变量可以用这种方式实现降序;desc(z)能生效是因为desc()是dplyr包的函数,它可以反转字符型因子的水平顺序,reorder(x, desc(z))本质上是利用反转后的因子水平作为排序依据,而非数值均值。
优化方案
更规范的做法是提前将z转换为因子并指定顺序,或使用forcats包的fct_reorder()函数,避免重复调用reorder():
方法1:提前处理数据排序
# 按z的升序水平排序x df_sorted <- df %>% mutate(z = factor(z, levels = c("A", "B", "C")), # 指定z的升序水平 x = reorder(x, z)) # 按z的水平排序x # 绘制图表,无需在各图层重复指定reorder plot <- df_sorted %>% ggplot(aes(x=x, y=y, fill=z))+ geom_bar(stat="identity")+ geom_errorbar(aes(ymin=y-se, ymax=y+se), width=0.3, colour="blue3", alpha =0.9, size=0.7)+ geom_text(aes(y=0.5, label=n))+ theme(legend.position="bottom",plot.title = element_text(size=12), text = element_text(size=14), axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+ ggtitle("按z升序分组排序的条形图") + labs(x="", y="y轴标签", fill=NULL)
方法2:使用forcats包的fct_reorder()
library(forcats) # 按z升序排序x plot <- df %>% ggplot(aes(x=fct_reorder(x, z), y=y, fill=z))+ geom_bar(stat="identity")+ geom_errorbar(aes(ymin=y-se, ymax=y+se), width=0.3, colour="blue3", alpha =0.9, size=0.7)+ geom_text(aes(y=0.5, label=n))+ theme(legend.position="bottom",plot.title = element_text(size=12), text = element_text(size=14), axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))+ ggtitle("按z升序分组排序的条形图") + labs(x="", y="y轴标签", fill=NULL)
若需降序,只需将fct_reorder(x, z)改为fct_reorder(x, desc(z))即可,无报错风险。
内容的提问来源于stack exchange,提问作者Asier
相关产品推荐
相关产品推荐

