You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ggplot2分面条形图排序 固定All在顶部Z在底部

ggplot2 分面条形图固定指定条目上下位置实现方法

现有基础

我有2个数值变量(sale、cost)和2个分类变量(model、level),样例数据如下:

model level sale cost    Colour
1      A  HIGH  100 -110     black
2      B  HIGH  120  -89    khaki1
3      C  HIGH  500  -20       red
4      D  HIGH  455 -300     green
5      E  HIGH  456 -110 chocolate
6      F  HIGH   12 -120    orange
7      G  HIGH   56  -56      blue
8      H  HIGH   98 -300      gold
9    All  HIGH  889 -790    sienna
10     Z  HIGH  456  -30    yellow
11     A   LOW  432  -70     black
12     B   LOW  567  -40    khaki1
13     C   LOW  345  -30       red
14     D   LOW  345 -120     green
15     E   LOW  100  -10 chocolate
16     F   LOW   67  -21    orange
17     G   LOW   34  -24      blue
18     H   LOW  110 -540      gold
19   All   LOW  670 -123    sienna
20     Z   LOW  160  -45    yellow

基于上述数据绘制了包含正负值的条形图,图表按level列拆分为2个分面,默认按sale值从上到下排序,原有绘图代码如下:

ggplot() +
  geom_col(aes(value, tidytext::reorder_within(model, value, level),
               fill = model, color = model), 
           data = df[df$sign == "sale", ],
           position = "stack") + 
  scale_fill_manual(values = Colour, breaks = df$model) +
  scale_color_manual(values = Colour, breaks = df$model)+
  geom_col(aes(value, tidytext::reorder_within(model, value, level),
               color = model), 
           data = df[df$sign == "cost", ], 
           fill = "white",
           position = "stack") +
  tidytext::scale_y_reordered() +
  labs(fill = "model") +
  facet_grid(level ~ ., switch = "y",scales = "free_y") +
  theme(axis.text.x = element_text(angle = 90),
        strip.background = element_rect(fill = "white"),
        strip.placement = "outside",
        strip.text.y.left = element_text(angle = 0),
        panel.spacing = unit(0, "lines")) + 
  theme(legend.position="none") + 
  labs(y = " ") + geom_vline(xintercept = 0,linetype="dotted", color="white")

调整需求

修改条形排序逻辑,实现:

  • model取值为All的条目始终显示在每个分面的最顶部
  • model取值为Z的条目始终显示在每个分面的最底部
  • 其余条目保持原有按sale值从上到下排序的规则

实现方案

核心思路是自定义reorder_within用到的排序权重,不需要改动原有绘图的图层逻辑,只需要提前给数据集增加排序键、替换排序变量即可:

  1. 数据预处理阶段,按level分组生成自定义排序键:All赋最高排序权重,Z赋最低排序权重,其余条目用sale值作为排序依据,同时完成宽表转长表的操作适配原有绘图逻辑
library(dplyr)
library(tidyr)

df <- df %>%
  group_by(level) %>%
  mutate(
    sort_key = case_when(
      model == "All" ~ Inf,   # 无穷大权重保证排在最顶
      model == "Z" ~ -Inf,    # 负无穷权重保证排在最底
      TRUE ~ sale             # 其余条目保持按sale排序
    )
  ) %>%
  ungroup() %>%
  pivot_longer(cols = c(sale, cost), names_to = "sign", values_to = "value")
  1. 把原有两处geom_col调用里reorder_within的排序变量从value替换为刚生成的sort_key,其余代码完全保留即可:
ggplot() +
  geom_col(aes(value, tidytext::reorder_within(model, sort_key, level),
               fill = model, color = model), 
           data = df[df$sign == "sale", ],
           position = "stack") + 
  scale_fill_manual(values = Colour, breaks = df$model) +
  scale_color_manual(values = Colour, breaks = df$model)+
  geom_col(aes(value, tidytext::reorder_within(model, sort_key, level),
               color = model), 
           data = df[df$sign == "cost", ], 
           fill = "white",
           position = "stack") +
  tidytext::scale_y_reordered() +
  labs(fill = "model") +
  facet_grid(level ~ ., switch = "y",scales = "free_y") +
  theme(axis.text.x = element_text(angle = 90),
        strip.background = element_rect(fill = "white"),
        strip.placement = "outside",
        strip.text.y.left = element_text(angle = 0),
        panel.spacing = unit(0, "lines")) + 
  theme(legend.position="none") + 
  labs(y = " ") + geom_vline(xintercept = 0,linetype="dotted", color="white")

说明:转长表后同一个model+level组合下的sale和cost行会继承同一个sort_key值,不会出现同一模型两个条形错位的问题。


内容的提问来源于stack exchange,提问作者user18937485

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:18:17