使用 ggplot2,我可以在轴上插入中断吗?

Using ggplot2, can I insert a break in the axis?

提问人:djq 提问时间:8/26/2011 最后编辑:Gregor Thomasdjq 更新时间:8/23/2022 访问量:127552

问:

我想制作一个条形图,其中一个值比所有其他值大得多。有没有办法有一个不连续的 y 轴?我的数据如下:

df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))

p <- ggplot(data = df, aes(x = b, y = a)) + geom_bar() 
p <- p + opts(axis.text.x=theme_text(angle= 90, hjust=1))  + coord_flip()
p

enter image description here

有没有办法让我的轴从 1 到 10,然后是 490 - 500?我想不出任何其他绘制数据的方法(除了转换它,我不想这样做)

[编辑 2019-05-06]:

8 年后,需要修改上述代码以使用 3.1.1 版本才能创建相同的图表:ggplot2

library(ggplot2)
ggplot(df) + 
  aes(x = b, y = a) +
  geom_col() +
  coord_flip()
GGPLOT2 R-常见问题

评论

5赞 Ramnath 8/26/2011
我认为你不能在 ggplot2 中引入中断。另一种方法是使用比例,这将使图表更易于阅读。log
1赞 djq 8/26/2011
我意识到这将使在对数刻度上更容易阅读,但我不想以这种方式显示信息,因为小值之间存在显着差异,这些小值在转换时会被隐藏。
6赞 Chase 8/26/2011
facet_wrap()scales = "free_x"
0赞 Gregor Thomas 8/29/2019
也可以通过自定义转换来解决这个问题......当我有时间时,我会写一个答案
0赞 tassones 11/3/2021
请考虑以下 stackoverflow 线程

答:

28赞 Brian Diggs 8/26/2011 #1

不,不使用 ggplot。请参阅 http://groups.google.com/group/ggplot2/browse_thread/thread/8d2acbfc59d2f247 线程中的讨论,其中 Hadley 解释了为什么这是不可能的,但给出了一个建议的替代方案(分面图,一个包含所有数据,一个在特定区域放大)。

55赞 joran 8/26/2011 #2

正如其他地方所指出的,这不是一件可以很好地处理的事情,因为断裂的轴通常被认为是有问题的。ggplot2

其他策略通常被认为是解决这个问题的更好方法。布莱恩提到了一些(刻面,两个专注于不同值集的图)。人们经常忽略的另一个选择,特别是对于条形图,是制作表格

enter image description here

从实际值来看,500 并没有掩盖其他值的差异!出于某种原因,表格没有得到足够的尊重,因为数据是一种可视化技术。您可能会反对您的数据有很多很多类别,这在表中变得笨拙。如果是这样,您的条形图很可能也会有太多的条形。

而且我不是一直在争论桌子。但是,如果您要制作条形相对较少的条形图,它们绝对是需要考虑的事情。如果你正在制作包含大量条形的条形图,你可能需要重新考虑这一点。

最后,包中还有一个实现断轴的函数。但是,根据我收集的信息,您必须自己手动指定轴标签和位置。axis.breakplotrix

评论

1赞 djq 8/26/2011
乔兰,我确实对使用这种类型的情节有复杂的感觉。你是对的 - 表格可能是显示这一点的最佳方式。
1赞 joran 8/26/2011
@celenius - 我不是故意说教,也不是在骂你。我只是觉得桌子没有得到太多的爱,有时我会为此感到厌烦。;)
1赞 Brian Diggs 8/26/2011
@joran,我同意对于这个特定的数据,表格是最佳的表示(如果只是给出如何最好地表示它的问题,我会推荐什么)。在我的回答中,我过于狭隘地关注确切的问题,而无法回答应该问的更广泛的问题。
0赞 BestGirl 2/16/2023
“折断的斧头通常被认为是有问题的”,---是谁?我同意表格通常很有用,但有时你有太多的数据无法在表格中显示,而绘图在任何情况下都是理解数据的有用可视化工具。假设您要显示一个包含 100 个条柱的直方图,而该最大条柱的观测值是下一个最大条柱的 20 或 50 倍。折断的轴 - 理想情况下与折断的杆相结合 - 是完全合理的,而桌子将是次优的。
0赞 joran 2/25/2023
@BestGirl我说的是“有问题”,而不是“总是,在所有情况下都是明确的坏”。断轴有点像双 y 轴或将 y 轴截断到一个狭窄的范围:如果您非常小心,有时还可以,但通常这是不良或误导性图形的常见来源。
1赞 Ed Staub 10/1/2011 #3

我怀疑 R 中有什么现成的东西,但您可以将数据显示为一系列 3D 部分立方体。500 只有 5*10*10,所以它可以很好地扩展。确切的值可以是标签。

这可能只有在出于某种原因必须具有图形表示时才应使用。

26赞 Wouter 6/6/2012 #4

不是使用 ggplot,而是使用 plotrix,您可以轻松做到这一点:

library(plotrix)
gap.barplot(df$a, gap=c(5,495),horiz=T)

评论

0赞 Shajar 3/17/2023
这样会更好:gap.barplot(y=df$a, gap=c(5,495),horiz=T, xaxlab=df$b, ylim=c(0,12), ytics=df$a, yaxlab=df$a)
16赞 milo 2/24/2016 #5

不,很遗憾不是

人们担心的是,允许不连续的轴会导致观众的欺骗。但是,在某些情况下,没有不连续的轴会导致失真。

例如,如果轴被截断,但通常位于某个区间内(例如 [0,1]),则受众可能不会注意到截断并对数据做出扭曲的结论。在这种情况下,显式不连续轴将更合适和透明。

比较:

Example of good use of continuous vs discontinuous axis

0赞 webscale 10/20/2018 #6

一种策略是更改轴以绘制对数刻度。这样一来,您就可以将指数级更高的值减少 10 倍

评论

3赞 Gregor Thomas 8/29/2019
对于某些图形来说,这是一个不错的选择,但对于从 0 开始的条形图,它就不能很好地工作了。
54赞 Uwe 5/6/2019 #7

八年后,该软件包提供了一个扩展,该扩展是 Hadley Wickham 建议的实现,即显示两个情节(如 Brian Diggs 的回答中所述)。ggforcefacet_zoom()

缩放分面

library(ggforce)
ggplot(df) + 
  aes(x = b, y = a) +
  geom_col() +
  facet_zoom(ylim = c(0, 10))

enter image description here

不幸的是,当前版本的 0.2.2 会抛出错误,因此只能显示垂直条。ggforcecoord_flip()

缩放的 facet 显示小值的变化,但仍包含大的(现在已裁剪的)条形图。该参数控制在缩放的分面中显示哪些值:a4zoom.data

library(ggforce)
ggplot(df) + 
  aes(x = b, y = a) +
  geom_col() +
  facet_zoom(ylim = c(0, 10), zoom.data = ifelse(a <= 10, NA, FALSE))

enter image description here

两块地

哈德利·威克姆(Hadley Wickham)建议道

我认为展示两个情节更合适——其中一个 数据,并且只是小值之一。

此代码创建两个绘图

library(ggplot2)
g1 <- ggplot(df) + 
  aes(x = b, y = a) +
  geom_col() +
  coord_flip()
g2 <- ggplot(df) + 
  aes(x = b, y = a) +
  geom_col() +
  coord_flip() +
  ylim(NA, 10)

可以通过以下方式组合成一个图

cowplot::plot_grid(g1, g2) # or ggpubr::ggarrange(g1, g2)

enter image description here

gridExtra::grid.arrange(g1, g2) # or egg::ggarrange(g1, g2)

enter image description here

两个方面

这是在蔡斯的评论中提出的,布莱恩·迪格斯(Brian Diggs)在他的回答中也提出了这一点,他解释了哈德利的建议

多面图,一个包含所有数据,一个在特定区域放大

但到目前为止,还没有为这种方法提供代码。

由于没有简单的方法可以单独缩放分面(例如,参见相关问题),因此需要对数据进行操作:

library(dplyr)
library(ggplot2)
ggplot() + 
  aes(x = b, y = a) +
  geom_col(data = df %>% mutate(subset = "all")) +
  geom_col(data = df %>% filter(a <= 10) %>% mutate(subset = "small")) +
  coord_flip() + 
  facet_wrap(~ subset, scales = "free_x")

enter image description here

评论

1赞 Uwe 5/6/2019
、 和 的错误已报告给 github.com/thomasp85/ggforce/issues/143facet_zoom()geom_col()coord_flip()
0赞 charlesdarwin 11/11/2019
谢谢,我真的很喜欢ggforce小平面缩放功能!你知道是否可以放大两个范围,一个在图的两侧吗?或者只有一条只有 y 轴的垂直线,两侧各有一个图,图在两个不同的 y 值范围内?
0赞 djq 10/14/2023
谢谢@Uwe - 11 年后重新阅读了这个问题的答案,您概述了有用和有趣的建议。我个人更喜欢多个图表;虽然很漂亮,但我担心可能很难向不熟悉它的人解释这张图表。facet_zoom()
2赞 user3799203 12/11/2020 #8

Jörg Steinkamp 使用 facet_grid 提供了一个巧妙的 ggplot 解决方案。简单来说,它是这样的:

library("tidyverse")
df <- data.frame(myLetter=LETTERS[1:4], myValue=runif(12) + rep(c(4,0,0),2))  # cluster a few values well above 1
df$myFacet <- df$myValue > 3
(ggplot(df, aes(y=myLetter, x=myValue)) 
  + geom_point() 
  + facet_grid(. ~ myFacet, scales="free", space="free")
  + scale_x_continuous(breaks = seq(0, 5, .25)) # this gives both facets equal interval spacing.
  + theme(strip.text.x = element_blank()) # get rid of the facet labels
)

enter image description here

评论

0赞 Miao Cai 12/15/2020
有没有办法控制左右面板的比例?
0赞 user3799203 2/1/2021
@MiaoCai - 控制比例的一种方法是使用“网格”包,根据 stackoverflow.com/a/49225527/3799203 中非常详细的示例
0赞 SplitInf 8/30/2021
这是一个很好的解决方案,但它假定跳过的部分中没有数据点
3赞 MonkeyCousin 8/19/2022 #9

截至 2022-06-01,我们有了外观优雅的包装,它似乎回答了 OP 的问题。虽然我没有在自己的数据上尝试过它,但它看起来与许多或所有其他功能兼容。还提供差异缩放,可能对 OP 和类似用途有用。ggbreakggplot2

library(ggplot2)
library(ggbreak) 

set.seed(2019-01-19)
d <- data.frame(x = 1:20,
   y = c(rnorm(5) + 4, rnorm(5) + 20, rnorm(5) + 5, rnorm(5) + 22))
 
p1 <- ggplot(d, aes(y, x)) + geom_col(orientation="y") + 
theme_minimal()
p1 + scale_x_break(c(7, 17), scales = 1.5) + scale_x_break(c(18, 21), scales=2)

enter image description here

16赞 Quinten 8/23/2022 #10

一个选项是使用 scale_y_cut() 或 scale_x_cut() 函数使用 ggbreak 包。此功能可以将对象切割成多个部分,并可以指定放大或缩小哪个部分。这是一个可重现的示例,左图为正常图,右图使用函数:ggplot

df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))

library(ggplot2)
library(ggbreak)
library(patchwork)
p1 <- ggplot(df) + 
  aes(x = b, y = a) +
  geom_col() 
p2 <- ggplot(df) + 
  aes(x = b, y = a) +
  geom_col() +
  scale_y_cut(breaks=c(4, 30), which=c(1, 3), scales=c(0.5, 3)) 
  
p1 + p2

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

从示例中可以看出,某些部分被放大和缩小。这可以通过使用不同的参数来更改。

使用的参数:

  • 休息:

数值或数值向量,要划分的点

  • 哪:

整数,子图到刻度的位置,从左开始 从右或从上到下。

  • 尺度:

子图的数字、相对宽度或高度。

要更改子图之间的间距,可以使用参数 。space

有关一些额外的信息和示例,请查看本教程

评论

2赞 Denis Cousineau 8/23/2022
哇,这个包非常令人印象深刻,谢谢!
0赞 jimmy 8/31/2023
这是一个耸人听闻的消息,可以跟随 ggplot2 图生成截断轴。答案超越了 ChatGPT3 和 4。