ggplot结合dplyr时top_n与arrange参数失效问题求助
解决ggplot分面中top_n筛选与分组内排序的问题
我完全懂你遇到的困扰:照着《TidyText Mining》里的代码复现到自己的数据集(比如mtcars)时,facet_wrap既不遵守top_n指定的显示数量,分面内的排序也完全不符合预期。咱们一步步拆解问题,找到对应的解决方案。
你的测试代码的核心问题
先看你写的mtcars测试代码:
require(tidyverse) mtcars %>% arrange (desc(mpg)) %>% mutate (gear = factor(gear, levels = rev(unique(gear)))) %>% group_by(am) %>% top_n(1) %>% ungroup %>% ggplot (aes (gear, mpg, fill = am)) + geom_col (show.legend = FALSE) + labs (x = NULL, y = "mpg") + facet_wrap(~am, ncol = 2, scales = "free") + coord_flip()
这里有两个致命的逻辑错误:
top_n未指定筛选权重列:默认情况下top_n()会选取数据框最后一列的最大值,mtcars的最后一列是carb,不是你想要的mpg。所以你得到的不是每个am组里mpg最高的行,而是carb数值最大的行,这直接导致筛选结果不符合预期。- 全局设置因子水平,未适配分组排序:你在全局范围内设置了
gear的因子水平,但每个am组内的mpg排序逻辑是独立的,全局水平无法满足分组内的排序需求。
修正后的代码
我们参考starwars示例的核心逻辑,调整代码顺序和参数,适配分组独立排序的需求:
require(tidyverse) mtcars %>% # 按am分组,明确指定按mpg筛选每个组的top1 group_by(am) %>% top_n(1, wt = mpg) %>% ungroup() %>% # 再次按am分组,对每个组内的gear按mpg降序设置因子水平 group_by(am) %>% mutate(gear = fct_reorder(gear, desc(mpg))) %>% ungroup() %>% ggplot(aes(gear, mpg, fill = factor(am))) + geom_col(show.legend = FALSE) + labs(x = NULL, y = "mpg") + facet_wrap(~am, ncol = 2, scales = "free") + coord_flip()
如果你更倾向于贴合starwars的写法(先排序再设置因子),可以这样写:
require(tidyverse) mtcars %>% group_by(am) %>% # 每个组内按mpg降序排列,确保分组内顺序正确 arrange(desc(mpg), .by_group = TRUE) %>% # 取每个组的前1行 slice_head(n = 1) %>% ungroup() %>% # 按am分组,将gear设置为组内排序后的因子水平 group_by(am) %>% mutate(gear = factor(gear, levels = unique(gear))) %>% ungroup() %>% ggplot(aes(gear, mpg, fill = factor(am))) + geom_col(show.legend = FALSE) + labs(x = NULL, y = "mpg") + facet_wrap(~am, ncol = 2, scales = "free") + coord_flip()
为什么starwars的代码能正常工作?
来看starwars示例的核心逻辑:
starwars %>% arrange(desc(tf_idf)) %>% mutate(word = factor(word, levels = rev(unique(word)))) %>% group_by(film) %>% top_n(10) %>% ungroup %>% ggplot(...)
它的适配场景是全局排序逻辑统一:
- 先全局按
tf_idf降序排列,把word设置为全局排序后的因子水平 - 按
film分组取top10,因为scales="free",每个分面只显示本组的top10,而这些word的因子水平已经是全局降序,所以分面内自然呈现降序
但这种写法只适用于不同分组的排序逻辑一致的场景,如果你需要每个分组内独立排序(比如mtcars中每个am组的gear按本组mpg排序),就必须在分组内设置因子水平,而不是全局设置。
关键总结
- 使用
top_n()时务必指定wt参数,明确你要筛选的依据列,避免默认逻辑出错 - 分面内的独立排序需要在分组内设置因子水平,可以用
fct_reorder()(tidyverse自带的forcats包函数)或者分组后手动设置因子水平 - 全局统一排序后取分组top的写法(如starwars示例)仅适用于排序逻辑全局一致的场景,分组独立排序必须调整逻辑
内容的提问来源于stack exchange,提问作者JMacKay
相关产品推荐
相关产品推荐

