如何在ggplot的geom_col中按分栏值排序x轴因子?
ggplot中基于原始宽数据列排序长数据的x轴因子
问题背景
现有如下R代码生成带分栏(position_dodge)的柱状图:
library(tidyverse) adf <- data.frame(a=c(5,6,8), b=c(3,2,1), z=factor(c('a','b','c'),levels=c('b','c','a'))) ldf <- adf |> pivot_longer(cols = c(a,b)) p <- ggplot() p <- p+geom_col(data=ldf, aes(x=z, y=value, fill=name), position = position_dodge()) print(p)
需要生成两个图表:
- 其一将x轴因子
z按原数据a列值降序排列 - 其二按原数据
b列值降序排列
已知可通过修改原始数据框重设因子水平实现,但希望了解能否在ggplot的aes参数中,使用forcats包的fct_reorder(或其他函数)直接处理?由于传入ggplot的是长格式数据,无法直接引用原数据的a、b列,是否必须在原始数据框中显式重排因子z?
解决方案
不需要提前修改原始数据框的因子水平,有两种可行方法实现需求:
方法一:在ggplot的aes中直接引用原始宽数据列
利用fct_reorder可以接受外部匹配长度向量的特性,直接在aes中引用原始数据adf的a/b列作为排序依据,负号用于实现降序排列:
# 按原数据a列降序排列x轴z因子 p_a <- ggplot() + geom_col(data = ldf, aes(x = fct_reorder(z, -adf$a), y = value, fill = name), position = position_dodge()) + labs(x = "z (按a列降序)") print(p_a) # 按原数据b列降序排列x轴z因子 p_b <- ggplot() + geom_col(data = ldf, aes(x = fct_reorder(z, -adf$b), y = value, fill = name), position = position_dodge()) + labs(x = "z (按b列降序)") print(p_b)
原理:长数据ldf中的z与原始数据adf的z是一一对应的(每个z水平对应原始数据的一行,长数据仅将每行拆分为两行),因此adf$a/adf$b的长度与ldf$z的分组长度匹配,fct_reorder可以正确为每个z水平赋予排序权重。
方法二:转换长数据时保留原始排序依据
如果希望完全基于长数据内部的列操作,可以在pivot_longer时将原始a/b列的信息保留到长数据中,再用fct_reorder排序:
# 转换长数据时保留原始a、b列的数值 ldf_with_base <- adf |> pivot_longer(cols = c(a, b), names_to = "name", values_to = "value") |> mutate( a_val = rep(adf$a, each = 2), # 每个z对应两行,重复a列数值两次 b_val = rep(adf$b, each = 2) # 同理处理b列 ) # 按a列降序的图表 p_a2 <- ggplot(ldf_with_base) + geom_col(aes(x = fct_reorder(z, -a_val), y = value, fill = name), position = position_dodge()) + labs(x = "z (按a列降序)") # 按b列降序的图表 p_b2 <- ggplot(ldf_with_base) + geom_col(aes(x = fct_reorder(z, -b_val), y = value, fill = name), position = position_dodge()) + labs(x = "z (按b列降序)")
这种方式避免了在ggplot中引用外部数据,所有操作都基于长数据自身的列完成。
内容的提问来源于stack exchange,提问作者PM.
相关产品推荐
相关产品推荐

