在 ggplot2 条形图中对柱线进行排序

Order Bars in ggplot2 bar graph

提问人:Julio Diaz 提问时间:3/6/2011 最后编辑:Roland EwaldJulio Diaz 更新时间:3/15/2023 访问量:558603

问:

我正在尝试制作一个条形图,其中最大的条形最靠近 y 轴,最短的条形最远。所以这有点像我的桌子

    Name   Position
1   James  Goalkeeper
2   Frank  Goalkeeper
3   Jean   Defense
4   Steve  Defense
5   John   Defense
6   Tim    Striker

因此,我正在尝试构建一个条形图,该条形图将根据位置显示球员数量

p <- ggplot(theTable, aes(x = Position)) + geom_bar(binwidth = 1)

但该图首先显示了守门员,然后是防守,最后是前锋。我希望对图形进行排序,以便防守条最接近 y 轴,守门员最接近,最后是前锋。 谢谢

GGPLOT2 R-常见问题

评论

17赞 tumultous_rooster 3/23/2014
ggplot 不能在不弄乱表(或数据帧)的情况下为您重新排序它们吗?
3赞 Euler_Salter 12/28/2019
@MattO'Brien,我觉得这不可思议,这不是在一个简单的命令中完成的
0赞 Euler_Salter 1/24/2020
@Zimano 太糟糕了,这就是你从我的评论中得到的。我的观察是针对 的创造者,而不是 OPggplot2
3赞 Zimano 1/24/2020
@Euler_Salter 谢谢你的澄清,我真诚地道歉,因为你这样跳到你身上。我已经删除了我的原始评论。
0赞 stragu 10/27/2020
ggplot2 当前忽略并发出警告。若要控制条形的宽度(并且条形之间没有间隙),可能需要改用。binwidth = 1width = 1

答:

20赞 Prasad Chalasani 3/6/2011 #1

您只需要将列指定为有序因子,其中级别按其计数排序:Position

theTable <- transform( theTable,
       Position = ordered(Position, levels = names( sort(-table(Position)))))

(请注意,生成列的频率计数。table(Position)Position

然后,您的函数将按计数递减顺序显示条形图。 我不知道是否有选项可以在不必显式创建有序因子的情况下执行此操作。ggplotgeom_bar

评论

0赞 Chase 3/6/2011
我没有完全解析您的代码,但我很确定统计库中的代码可以完成相同的任务。reorder()
0赞 Gavin Simpson 3/6/2011
@Chase在这种情况下,您建议如何使用?需要重新排序的因素需要通过自身的某些函数重新排序,我正在努力寻找一种好的方法。reorder()
0赞 Gavin Simpson 3/6/2011
好吧,是一种方式,另一种方式,但这些同样令人费解......with(theTable, reorder(Position, as.character(Position), function(x) sum(duplicated(x))))with(theTable, reorder(Position, as.character(Position), function(x) as.numeric(table(x))))
0赞 Prasad Chalasani 3/6/2011
我稍微简化了答案以使用而不是sortorder
0赞 Chase 3/6/2011
@Gavin - 也许我误解了 Prasad 的原始代码(我在这台机器上没有 R 来测试......),但看起来他正在根据频率重新排序类别,这很擅长。对于这个问题,我同意需要更多涉及的东西。很抱歉造成混乱。reorder
255赞 Gavin Simpson 3/6/2011 #2

排序的关键是按照您想要的顺序设置因子的水平。不需要有序因子;有序因子中的额外信息不是必需的,如果这些数据被用于任何统计模型,可能会导致错误的参数化——多项式对比不适合这样的名义数据。

## set the levels in order we want
theTable <- within(theTable, 
                   Position <- factor(Position, 
                                      levels=names(sort(table(Position), 
                                                        decreasing=TRUE))))
## plot
ggplot(theTable,aes(x=Position))+geom_bar(binwidth=1)

barplot figure

从最一般的意义上讲,我们只需要将因子水平设置为所需的顺序。如果未指定,因子的水平将按字母顺序排序。您还可以如上所述在对因子的调用中指定级别顺序,也可以使用其他方法。

theTable$Position <- factor(theTable$Position, levels = c(...))

评论

1赞 Prasad Chalasani 3/6/2011
@Gavin:2 个简化:既然你已经在用了,就没有必要用了,你可以做降序。withintheTable$Positionsort(-table(...))
2赞 Gavin Simpson 3/6/2011
@Prasad前者是测试的遗留物,所以感谢您指出这一点。就后者而言,我更喜欢明确要求反向排序而不是您使用的排序,因为从中获得意图比在代码的所有其他代码中注意到意图要容易得多。-decreasing = TRUE-
2赞 Anton 2/18/2019
@GavinSimpson;我认为这部分会导致不良行为,其中数据框的实际条目被重新排序,而不仅仅是因子的水平。请参阅此问题。也许您应该修改或删除这些行?levels(theTable$Position) <- c(...)
2赞 Gregor Thomas 2/19/2019
强烈同意安东的观点。我刚刚看到了这个问题,然后四处闲逛,看看他们从哪里得到了不好的建议。我将至少暂时编辑该部分。levels<-
2赞 Gavin Simpson 2/19/2019
@Anton 感谢您的建议(以及 Gregor 的编辑);我永远不会通过今天这样做。这是 8 年前的事情,我不记得当时的情况是否不同,或者我是否完全错了,但无论如何,这是错误的,应该被抹去!谢谢!levels<-()
270赞 Alex Brown 2/11/2012 #3

@GavinSimpson:是一个强大而有效的解决方案:reorder

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()

评论

7赞 Gavin Simpson 6/14/2012
确实是+1,尤其是在这种情况下,我们可以在数字上利用逻辑顺序。如果我们考虑类别的任意排序,并且我们不希望按字母顺序排列,那么直接指定级别也同样容易(更容易?),如图所示。
3赞 Sweepy Dodo 8/9/2019
这是最整洁的。消除修改原始数据帧的需要
3赞 postylem 4/18/2020
可爱,刚刚注意到你可以更简洁地做到这一点,如果你只想按长度函数排序,升序就可以了,这是我经常想做的事情:ggplot(theTable,aes(x=reorder(Position,Position,length))+geom_bar()
204赞 QIBIN LI 12/1/2014 #4

用于指定条形的顺序。scale_x_discrete (limits = ...)

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)

评论

14赞 Yu Shen 4/28/2015
您的解决方案最适合我的情况,因为我想编程以 x 作为由 data.frame 中的变量表示的任意列进行绘图。其他建议将更难通过涉及变量的表达式来表达 x 顺序的排列。谢谢!如果有兴趣,我可以使用您的建议分享我的解决方案。再问一个问题,添加 scale_x_discrete(limits = ...),我发现在图表右侧有与条形图一样宽的空白区域。我怎样才能摆脱空白?因为它没有任何目的。
1赞 geotheory 8/4/2015
这对于订购直方图条似乎是必要的
11赞 dancow 9/10/2015
七斌: 哇...这里的其他答案是有效的,但到目前为止,你的答案似乎不仅是最简洁和优雅的,而且在 ggplot 的框架内思考时是最明显的。谢谢。
0赞 user2460499 5/26/2017
当我尝试这个解决方案时,根据我的数据,它没有绘制 NA。有没有办法使用此解决方案并让它绘制 NA 图?
0赞 Lauren Fitch 11/10/2018
这个解决方案对我有用,而上面的其他人则没有。
106赞 Holger Brandl 12/13/2014 #5

我认为已经提供的解决方案过于冗长。使用 ggplot 进行频率排序条形图的更简洁方法是

ggplot(theTable, aes(x=reorder(Position, -table(Position)[Position]))) + geom_bar()

它与 Alex Brown 的建议类似,但时间更短,并且无需任何函数定义即可工作。

更新

我认为我的旧解决方案在当时很好,但现在我宁愿使用按频率对因子水平进行排序:forcats::fct_infreq

require(forcats)

ggplot(theTable, aes(fct_infreq(Position))) + geom_bar()

评论

0赞 Ashok K Harnal 9/20/2015
我不明白重新排序函数的第二个参数以及它的作用。你能解释一下发生了什么吗?
1赞 Holger Brandl 9/21/2015
@user3282777你试过 stat.ethz.ch/R-manual/R-devel/library/stats/html/ 的文档吗?
1赞 Mike 3/11/2019
很棒的解决方案!很高兴看到其他人采用整洁的解决方案!
29赞 zach 7/30/2016 #6

一个简单的基于 dplyr 的因子重排序可以解决这个问题:

library(dplyr)

#reorder the table and reset the factor to that ordering
theTable %>%
  group_by(Position) %>%                              # calculate the counts
  summarize(counts = n()) %>%
  arrange(-counts) %>%                                # sort by counts
  mutate(Position = factor(Position, Position)) %>%   # reset factor
  ggplot(aes(x=Position, y=counts)) +                 # plot 
    geom_bar(stat="identity")                         # plot histogram
13赞 Alexandru Papiu 8/1/2016 #7

我同意 zach 的观点,即在 dplyr 内计数是最好的解决方案。我发现这是最短的版本:

dplyr::count(theTable, Position) %>%
          arrange(-n) %>%
          mutate(Position = factor(Position, Position)) %>%
          ggplot(aes(x=Position, y=n)) + geom_bar(stat="identity")

这也比事先重新排序因子水平要快得多,因为计数是在 dplyr 中完成的,而不是在 ggplot 中或使用 .table

42赞 user2739472 12/8/2016 #8

就像 Alex Brown 的回答一样,我们也可以使用 .它基本上会根据应用指定函数后第二个参数中的值对第一个参数中指定的因子进行排序(默认 = 中位数,这就是我们在这里使用的,因为每个因子级别只有一个值)。reorder()forcats::fct_reorder()

遗憾的是,在 OP 的问题中,所需的顺序也是按字母顺序排列的,因为这是创建因子时的默认排序顺序,因此会隐藏此函数的实际操作。为了更清楚起见,我将“Goalkeeper”替换为“Zoalkeeper”。

library(tidyverse)
library(forcats)

theTable <- data.frame(
                Name = c('James', 'Frank', 'Jean', 'Steve', 'John', 'Tim'),
                Position = c('Zoalkeeper', 'Zoalkeeper', 'Defense',
                             'Defense', 'Defense', 'Striker'))

theTable %>%
    count(Position) %>%
    mutate(Position = fct_reorder(Position, n, .desc = TRUE)) %>%
    ggplot(aes(x = Position, y = n)) + geom_bar(stat = 'identity')

enter image description here

评论

1赞 c0bra 8/27/2018
恕我直言,forcats 的最佳解决方案是 dplyr 一个整洁的包。
2赞 otwtm 5/20/2020
为Zoalkeeper竖起大拇指
20赞 Robert McDonald 2/24/2018 #9

除了 ,还提到 @HolgerBrandl,有 ,它颠倒了因子顺序。forcats::fct_infreqforcats::fct_rev

theTable <- data.frame(
    Position= 
        c("Zoalkeeper", "Zoalkeeper", "Defense",
          "Defense", "Defense", "Striker"),
    Name=c("James", "Frank","Jean",
           "Steve","John", "Tim"))

p1 <- ggplot(theTable, aes(x = Position)) + geom_bar()
p2 <- ggplot(theTable, aes(x = fct_infreq(Position))) + geom_bar()
p3 <- ggplot(theTable, aes(x = fct_rev(fct_infreq(Position)))) + geom_bar()

gridExtra::grid.arrange(p1, p2, p3, nrow=3)             

enter image description here

评论

0赞 Paul 2/26/2019
“fct_infreq(Position)”是一件小事,谢谢!!
14赞 JColares 8/3/2018 #10

如果图表列来自数值变量,如下面的数据帧所示,则可以使用更简单的解决方案:

ggplot(df, aes(x = reorder(Colors, -Qty, sum), y = Qty)) 
+ geom_bar(stat = "identity")  

排序变量 (-Qty) 前面的减号控制排序方向(升序/降序)

以下是一些用于测试的数据:

df <- data.frame(Colors = c("Green","Yellow","Blue","Red","Yellow","Blue"),  
                 Qty = c(7,4,5,1,3,6)
                )

**Sample data:**
  Colors Qty
1  Green   7
2 Yellow   4
3   Blue   5
4    Red   1
5 Yellow   3
6   Blue   6

当我找到这个线程时,这就是我一直在寻找的答案。希望它对其他人有用。

31赞 mpalanco 2/3/2019 #11

另一种选择是使用重新排序来对因子的水平进行排序。根据计数按升序 (n) 或降序 (-n) 排列。与从包装中使用的非常相似:fct_reorderforcats

降序

df %>%
  count(Position) %>%
  ggplot(aes(x = reorder(Position, -n), y = n)) +
  geom_bar(stat = 'identity') +
  xlab("Position")

enter image description here

升序

df %>%
  count(Position) %>%
  ggplot(aes(x = reorder(Position, n), y = n)) +
  geom_bar(stat = 'identity') +
  xlab("Position")

enter image description here

数据框:

df <- structure(list(Position = structure(c(3L, 3L, 1L, 1L, 1L, 2L), .Label = c("Defense", 
"Striker", "Zoalkeeper"), class = "factor"), Name = structure(c(2L, 
1L, 3L, 5L, 4L, 6L), .Label = c("Frank", "James", "Jean", "John", 
"Steve", "Tim"), class = "factor")), class = "data.frame", row.names = c(NA, 
-6L))

评论

1赞 Kenan 10/27/2021
我认为事先添加计数是最简单的方法
3赞 indubitably 2/14/2019 #12

由于我们只查看单个变量(“位置”)的分布,而不是查看两个变量之间的关系,因此直方图可能是更合适的图形。ggplot 有 geom_histogram() 可以很容易地实现:

ggplot(theTable, aes(x = Position)) + geom_histogram(stat="count")

enter image description here

使用 geom_histogram():

我认为 geom_histogram() 有点古怪,因为它以不同的方式处理连续和离散数据。

对于连续数据,您可以只使用不带参数的 geom_histogram()。 例如,如果我们添加一个数字向量“Score”......

    Name   Position   Score  
1   James  Goalkeeper 10
2   Frank  Goalkeeper 20
3   Jean   Defense    10
4   Steve  Defense    10
5   John   Defense    20
6   Tim    Striker    50

并在 “Score” 变量上使用 geom_histogram()...

ggplot(theTable, aes(x = Score)) + geom_histogram()

enter image description here

对于像“位置”这样的离散数据,我们必须指定一个由美学计算的计算统计数据,以给出柱线高度的 y 值,使用:stat = "count"

 ggplot(theTable, aes(x = Position)) + geom_histogram(stat = "count")

注意:奇怪和令人困惑的是,您也可以将其用于连续数据,我认为它提供了一个更美观的图表。stat = "count"

ggplot(theTable, aes(x = Score)) + geom_histogram(stat = "count")

enter image description here

编辑:针对 DebanjanB 的有用建议的扩展答案。

评论

1赞 stragu 10/27/2020
我不确定为什么提到这个解决方案,因为您的第一个示例完全等价于(即,在当前版本的 ggplot2 中,char 变量的顺序是按字母顺序排列的,或者如果它是有序因子,则遵循因子顺序)。或者也许曾经有过区别?ggplot(theTable, aes(x = Position)) + geom_bar()
12赞 Thomas Neitmann 4/12/2020 #13

我发现没有为此提供“自动”解决方案非常烦人。这就是我在 ggcharts 中创建函数的原因。ggplot2bar_chart()

ggcharts::bar_chart(theTable, Position)

enter image description here

默认情况下,对条形进行排序并显示水平图。要更改该集 .此外,还消除了杆和轴之间难看的“间隙”。bar_chart()horizontal = FALSEbar_chart()

-2赞 user14056554 8/6/2020 #14

您可以简单地使用以下代码:

ggplot(yourdatasetname, aes(Position, fill = Name)) + 
     geom_bar(col = "black", size = 2)

enter image description here

评论

2赞 Greg 8/6/2020
你能编辑你的答案以包含解释吗?
0赞 Quinten 8/15/2022 #15

如果你不想使用 ,还有 ggpubr 为该函数提供了一个非常有用的参数。您可以按“desc”和“asc”中的条形图进行排序,如下所示:ggplot2ggbarplotsort.val

library(dplyr)
library(ggpubr)
# desc
df %>%
  count(Position) %>%
  ggbarplot(x = "Position", 
            y = "n",
            sort.val = "desc")

# asc
df %>%
  count(Position) %>%
  ggbarplot(x = "Position", 
            y = "n",
            sort.val = "asc")

创建于 2022-08-14 由 reprex 软件包 (v2.0.1)

如您所见,对条形进行排序非常简单。如果条形图被分组,也可以这样做。查看上面的链接以获取一些有用的示例。

2赞 Chirag 8/30/2022 #16
library(ggplot2)
library(magrittr)

dd <- tibble::tribble(
    ~Name,    ~Position,
  "James", "Goalkeeper",
  "Frank", "Goalkeeper",
   "Jean",    "Defense",
   "John",    "Defense",
  "Steve",    "Defense",
    "Tim",    "Striker"
  )


dd %>% ggplot(aes(x = forcats::fct_infreq(Position))) + geom_bar()

创建于 2022-08-30 with reprex v2.0.2