ggplot2条形图中的顺序条

Posted

技术标签:

【中文标题】ggplot2条形图中的顺序条【英文标题】:Order Bars in ggplot2 bar graph 【发布时间】:2020-08-24 15:21:35 【问题描述】:

我正在尝试制作一个条形图,其中最大的条形图最接近 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 轴,守门员最接近,最后是前锋。 谢谢

【问题讨论】:

不能 ggplot 为您重新排序它们而不必弄乱表格(或数据框)吗? @MattO'Brien 我发现这不是在一个简单的命令中完成的令人难以置信 @Zimano 太糟糕了,这就是你从我的评论中得到的。我的观察是针对 ggplot2 的创建者,而不是 OP @Euler_Salter 谢谢你的澄清,我真诚地为你这样跳楼道歉。我已经删除了我原来的评论。 ggplot2 当前忽略 binwidth = 1 并发出警告。要控制条的宽度(并且条之间没有间隙),您可能需要改用width = 1 【参考方案1】:

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

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

【讨论】:

您能否编辑您的答案以包含解释?【参考方案2】:

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

ggcharts::bar_chart(theTable, Position)

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

【讨论】:

【参考方案3】:

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

## 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)

在最一般的意义上,我们只需将因子水平设置为所需的顺序。如果未指定,因子的水平将按字母顺序排序。您还可以在上述调用 factor 时指定级别顺序,其他方式也是可能的。

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

【讨论】:

@Gavin: 2 个简化:因为你已经在使用within,所以没有必要使用theTable$Position,你可以使用sort(-table(...)) 进行降序。 @Prasad 前者是测试的剩余部分,所以感谢您指出这一点。至于后者,我更喜欢明确要求反向排序而不是您使用的-,因为从decreasing = TRUE 获得意图比在所有其余代码中注意到- 要容易得多。 @GavinSimpson;我认为关于levels(theTable$Position) &lt;- c(...) 的部分会导致数据框的实际条目被重新排序的不良行为,而不仅仅是因子的水平。见this question。也许您应该修改或删除这些行? 非常同意安东的观点。我刚看到this question 并四处寻找他们在哪里得到了使用levels&lt;- 的坏建议。我打算把那部分删掉,至少是暂时的。 @Anton 感谢您的建议(并感谢 Gregor 的编辑);我今天永远不会通过levels&lt;-() 这样做。这是 8 年前的事情,我不记得当时的情况是否有所不同,或者我是否完全错了,但无论如何,它是错误的,应该被删除!谢谢!【参考方案4】:

由于我们只查看单个变量(“位置”)的分布,而不是查看两个变量之间的关系,因此可能是@987654321 @ 将是更合适的图表。 ggplot 有geom_histogram(),这很容易:

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

使用 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()

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

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

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

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

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

【讨论】:

我不知道为什么提到这个解决方案,因为你的第一个例子完全等同于ggplot(theTable, aes(x = Position)) + geom_bar()(即,对于 ggplot2 的当前版本 3.3.2,char 的顺序是按字母顺序排列的变量,或者如果它是有序因子,则尊重因子顺序)。或者也许曾经有所不同?【参考方案5】:

使用 reorder 对因子的水平进行排序的另一种方法。以升序 (n) 或降序 (-n) 为基础的计数。与使用 forcats 包中的 fct_reorder 的非常相似:

降序

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

升序

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

数据框:

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))

【讨论】:

我认为事先添加计数是最简单的方法【参考方案6】:

如果图表列来自下面数据框中的数字变量,您可以使用更简单的解决方案:

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

当我找到这个帖子时,这就是我正在寻找的答案。希望对其他人有用。

【讨论】:

【参考方案7】:

除了forcats::fct_infreq,由 @HolgerBrandl,有forcats::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)             

【讨论】:

"fct_infreq(Position)" 这个小东西能做这么多,谢谢!!【参考方案8】:

我认为已经提供的解决方案过于冗长。使用 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()

【讨论】:

我不明白 reorder 函数的第二个参数以及它的作用。你能解释一下发生了什么吗? @user3282777 你试过文档stat.ethz.ch/R-manual/R-devel/library/stats/html/… 吗? 很好的解决方案!很高兴看到其他人采用 tidyverse 解决方案!【参考方案9】:

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

遗憾的是,在 OP 的问题中,所需的顺序也是按字母顺序排列的,因为这是您创建因子时的默认排序顺序,因此将隐藏此函数实际执行的操作。为了更清楚起见,我将“守门员”替换为“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')

【讨论】:

恕我直言,作为 forcats 的最佳解决方案是 dplyr 以及一个 tidyverse 包。 为 Zoalkeeper 点赞【参考方案10】:

我同意 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

【讨论】:

【参考方案11】:

一个简单的基于 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

【讨论】:

【参考方案12】:

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

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

【讨论】:

您的解决方案最适合我的情况,因为我想通过编程来绘制 x 是由 data.frame 中的变量表示的任意列。其他建议将更难通过涉及变量的表达式来表达 x 的顺序排列。谢谢!如果有兴趣,我可以使用您的建议分享我的解决方案。还有一个问题,添加 scale_x_discrete(limits = ...),我发现在图表的右侧有与条形图一样宽的空白区域。我怎样才能摆脱空白?因为它没有任何用途。 这对于排序直方图条似乎是必要的 QIBIN: 哇...这里的其他答案有效,但到目前为止,您的答案似乎不仅最简洁优雅,而且在 ggplot 的框架内思考时也是最明显的。谢谢。 当我尝试这个解决方案时,在我的数据上,它没有绘制 NAs。有没有办法使用这个解决方案并让它绘制 NA? 这是一个优雅而简单的解决方案 - 谢谢!!【参考方案13】:

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

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

【讨论】:

确实是 +1,尤其是在这种情况下,我们可以利用数字来利用逻辑顺序。如果我们考虑类别的任意排序并且我们不希望按字母顺序排列,那么直接指定级别就很容易(更容易?),如图所示。 这是最整洁的。取消修改原始数据框的需要 可爱,只是注意到你可以更简洁一点,如果你只想按长度函数排序并且升序是可以的,这是我经常想做的事情:@987654323 @【参考方案14】:

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

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

(注意table(Position) 产生Position 列的频率计数。)

然后您的ggplot 函数将按计数的递减顺序显示条形图。 我不知道geom_bar 中是否有一个选项可以做到这一点,而不必显式创建有序因子。

【讨论】:

我没有完全解析你的代码,但我很确定来自 stats 库的 reorder() 完成了同样的任务。 @Chase 在这种情况下您如何建议使用reorder()?需要重新排序的因素需要通过其自身的某些功能重新排序,我正在努力寻找一种好的方法来做到这一点。 好的,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)))),但这些都一样令人费解...... 我稍微简化了答案以使用sort 而不是order @Gavin - 也许我误解了 Prasad 的原始代码(我在这台机器上没有 R 来测试......)但看起来他是根据频率重新排序类别,@987654333 @擅长做事。对于这个问题,我同意需要更多涉及的内容。很抱歉造成混乱。

以上是关于ggplot2条形图中的顺序条的主要内容,如果未能解决你的问题,请参考以下文章

仅使用一个变量(无值或排名)对 ggplot2 条形图中的条形重新排序?

在ggplot2条形图中对国家名称进行排序[重复]

在堆积条形图中反转 geom_text() (ggplot2)

更改 ggplot2 中条形图和图例的默认颜色

R语言可视化及作图6--ggplot2之点图、条形图、盒形图、直方图、线图

Python Matplotlib 在条形图中绘制样本均值,具有置信区间,但看起来像箱形图