R/ggplot2:如何在散点图中折叠或删除y轴的部分段

7
我正在尝试使用ggplot2在R中制作散点图,其中y轴中间被折叠或移除,因为那里没有数据。我在下面的Photoshop中完成了它,但是否有一种方法可以使用ggplot创建类似的图形? 这是具有连续比例尺的数据:enter image description here 但我想做出像这样的东西: enter image description here 这是代码:
ggplot(data=distance_data) +
    geom_point(
        aes(
            x = mdistance,
            y = maxZ,
            shape = factor(subj),
            color = factor(side),
            size = (cSA)
        )
    ) +
    scale_size_continuous(range = c(4, 10)) +
    theme(
        axis.text.x = element_text(colour = "black", size = 15),
        axis.text.y = element_text(colour = "black", size = 15),
        axis.title.x = element_text(colour = "black", size= 20, vjust = 0),
        axis.title.y = element_text(colour = "black", size= 20),
        legend.position = "none"
    ) +
    ylab("Z-score") +
    xlab("Distance")

也许你可以在y轴上插入一个断点(http://docs.ggplot2.org/current/scale_continuous.html) - MLavoie
任意操纵比例尺可能导致错误的结论。 - mtoto
@MLavoie 我尝试使用如下的breaks,但它只改变了刻度线而没有改变图形的尺寸。scale_y_continuous(limits=c(-6,6), breaks=c(-6,-4,-2,2,4,6)) - Jon
@mtoto 我同意那是对的,但我显然并不想隐瞒任何东西。我只是想去掉不必要的空格。 - Jon
1
你可以使用 facets。 - Matthew Plourde
Facets不能解决所有问题,而破碎的轴可以。您无法在facet_wrap()中使用space =“free”,也无法在facet_grid()中指定每个facet的比例尺。关于破碎轴的整个争论令我感到愤怒。图表并不具有误导性,_人们_才具有误导性。我的意思是,如果我想要误导,我会改变数据。此外,您可以对非线性刻度做出同样的论点。它们是误导性的!破碎的轴是非线性刻度,就像对数刻度一样。只有当您不知道如何阅读图表时,它才会产生误导性。 - ccoffman
1个回答

15

你可以通过定义坐标转换来实现这一点。一个标准的例子是对数坐标,可以通过在ggplot中使用scale_y_log10()来实现。

但你也可以通过向scale_y_continuous()(以及类似的scale_x_continuous())提供trans参数来定义自定义变换函数。为此,您需要使用scales包中的trans_new()函数。它接受转换函数及其逆函数作为参数。

我首先讨论了OP示例的特殊解决方案,然后还展示了如何将其推广。

OP的示例

OP想要缩小-2和2之间的区间。以下定义了一个函数(和它的逆函数),该函数将该区间缩小了4倍:

library(scales)
trans <- function(x) {
  ifelse(x > 2, x - 1.5, ifelse(x < -2, x + 1.5, x/4))
}
inv <- function(x) {
  ifelse(x > 0.5, x + 1.5, ifelse(x < -0.5, x - 1.5, x*4))
}
my_trans <- trans_new("my_trans", trans, inv)

这段代码定义了一个转换。为了看到其效果,我将定义一些样本数据:

x_val <- 0:250
y_val <- c(-6:-2, 2:6)
set.seed(1234)
data <- data.frame(x = sample(x_val, 30, replace = TRUE),
                   y = sample(y_val, 30, replace = TRUE))

我首先未进行转换就绘制它:

p <- ggplot(data, aes(x, y)) + geom_point()
p + scale_y_continuous(breaks = seq(-6, 6, by = 2))

enter image description here

现在我使用 scale_y_continuous() 函数进行转换:

p + scale_y_continuous(trans = my_trans,
                       breaks = seq(-6, 6, by = 2))

输入图像描述

如果你想进行另一种变换,你需要改变trans()inv()的定义,然后再次运行trans_new()。你必须确保inv()确实是trans()的逆函数。我检查了这点,方法如下:

x <- runif(100, -100, 100)
identical(x, trans(inv(x)))
## [1] TRUE

通用解决方案

下面的函数定义了一种转换方式,您可以选择要压缩的区域的上限和下限,以及要使用的因子。它直接返回 trans 对象,该对象可在 scale_y_continuous 内使用:

library(scales)
squish_trans <- function(from, to, factor) {
  
  trans <- function(x) {
    
    if (any(is.na(x))) return(x)

    # get indices for the relevant regions
    isq <- x > from & x < to
    ito <- x >= to
    
    # apply transformation
    x[isq] <- from + (x[isq] - from)/factor
    x[ito] <- from + (to - from)/factor + (x[ito] - to)
    
    return(x)
  }

  inv <- function(x) {
    
    if (any(is.na(x))) return(x)

    # get indices for the relevant regions
    isq <- x > from & x < from + (to - from)/factor
    ito <- x >= from + (to - from)/factor
    
    # apply transformation
    x[isq] <- from + (x[isq] - from) * factor
    x[ito] <- to + (x[ito] - (from + (to - from)/factor))
    
    return(x)
  }
  
  # return the transformation
  return(trans_new("squished", trans, inv))
}

trans()inv() 中的第一行处理了在调用转换时 x = c(NA, NA) 的情况。(似乎在我最初撰写这个问题时,ggplot2 的版本并没有出现这种情况。不幸的是,我不知道从哪个版本开始出现了这种情况。)

现在可以使用这个函数方便地重新绘制第一节中的图表:

p + scale_y_continuous(trans = squish_trans(-2, 2, 4),
                       breaks = seq(-6, 6, by = 2))
以下示例显示您可以在任意位置压缩比例尺,并且这也适用于除点以外的其他几何对象:

下面的示例显示,您可以在任意位置压缩比例尺,而且这也适用于除了点以外的其他几何对象:

df <- data.frame(class = LETTERS[1:4],
                 val = c(1, 2, 101, 102))
ggplot(df, aes(x = class, y = val)) + geom_bar(stat = "identity") +
  scale_y_continuous(trans = squish_trans(3, 100, 50),
                     breaks = c(0, 1, 2, 3, 50, 100, 101, 102))

图片描述请看这里

最后强调一下评论中已经提到的:这种图表可能会误导人,需要小心使用!


1
@Stibu,你写的这个转换能否推广到不同的范围?本质上,你正在将从-2:2的范围“压缩”到-0.5:0.5。在我看来,你可以对任意范围做类似的事情。我有一些数据想要这样做,其中有一个从2000到150,000的间隙,我想要压缩。我已经尝试了一个小时左右修改你发布的函数来实现这个目标,但是我无法使它正常工作。我找不到任何关于trans_new()的好文档/示例,这里所做的分段转换都没有涉及。有什么想法吗? - ccoffman
1
@Stibu,你是一个黄金般的神!我的论文刚刚变得更好了5%! - ccoffman
5%!我甚至不确定我自己的论文值得那么多的赞扬!;-) 无论如何,这是一件有趣的事情,我很高兴它能够帮助到别人。 - Stibu
我认为你刚刚展示了实现 ggplot2 中备受期待、备受争议的“断轴”最简单的方法。 - ccoffman
我试图将其用于y轴压缩,但一直出现错误:“Error in x [isq] <- from +(x [isq] - from)* factor:不允许在下标分配中使用NA” 这可能是由于我的y轴在0到1之间(它的相关数据)吗?有什么快速修改可以避免错误吗?我已经验证了我的数据没有NA值,并且两列都是数字类型。我想发布一个示例,但评论中没有足够的字符限制。谢谢! - C. John
1
@C.John 现在的 ggplot2 版本调用变换时,会使用 x = c(NA, NA),而我写问题时使用的版本没有出现这种情况。我已经修改了解决方案,使其在这种情况下也能正常运行。感谢您指出这一点!(还有很抱歉这么晚才回复...) - Stibu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接