如何在具有不同画布大小的ggplot2图的网格中保持一致的轴缩放

时间:2017-02-09 21:57:21

标签: r ggplot2

编辑:清除描述和代码示例,添加了图表。

我有一个包含多个动物位置的数据集。

我为每只动物创建了一个位置散点图网格。因为绘图的x y是距离,所以我希望每个绘图本身保持x y相同的比例(因此距离没有变形)和绘图(因此我可以比较具有相同比例的不同绘图)。

Facet是一个很自然的选择,它适用于coord_fixed()。然而,当数据中存在异常值(可能是错误)时,它变得更加复杂。我修改了@Mark彼得森很好的答案,增加了一些异常点。

set.seed(8675309)
df <-
  data.frame(
    x = runif(40, 1, 20)
    , y = runif(40, 100, 140)
    , ind = sample(LETTERS[1:4], 40, TRUE)
  )
# add some outliers to stretch the plot
outliers <- data.frame(x = c(-100, 30, 60,-50),
                       y = c(20, 200, -100, 500),
                       ind = LETTERS[1:4])
df <- rbind(df, outliers)

ggplot(df , aes(x = x, y = y)) +
  geom_point() +
  facet_wrap(~ind) +
  coord_fixed()

这就是我们得到的。 facet with outlier

1.带有coord_fixed()的曲面图:一致的刻度,对齐的轴

该图满足比例要求和比例一致要求,它也使所有轴对齐,即所有xlim ylim都相同。这很有用,因为它可以显示彼此的相对位置。

我还想检查每个情节的模式并进行比较。保持相对位置的构面图,我想添加另一个具有一致比例但轴未对齐的图。如果您单独绘制每个绘图,它将选择xlim ylim来覆盖数据而没有对齐要求。所以我只需要绘制每个情节,用gridExtracowplot来安排它们。

然后为了处理异常值,我们的计划是添加一个缩放按钮来放大所有绘图(这些绘图将在一个闪亮的应用程序中)。

我们决定将每个情节都集中在它的质心上。虽然这样会有更多的空间被浪费,所有的情节都正确居中,但是它们全部缩放将显示所有情节的大部分,并且它们在尺度上仍然具有可比性。

我有一个函数可以将每个绘图调整到它的中间位置,有点类似于@Mark Peterson代码。

我知道中位数中心在2D点上没有明确定义,但它足以满足我的需求。因为我需要单独调整每个绘图,所以我不能再使用facet了。

expand_1D_center <- function(vec){
  center <- median(vec)
  new_diff <- max(center - min(vec), 
                  max(vec) - center)
  return(c(new_min = center - new_diff, 
           new_max = center + new_diff))
}
# given x y vectors, get new x y lim to make centroid center
expand_2D_center <- function(x_vec, y_vec){
  return(list(xlim = expand_1D_center(x_vec),
              ylim = expand_1D_center(y_vec)))
}
# plot each with center adjusted
id_vector <- sort(unique(df$ind))
g_list <- vector("list", length = length(id_vector))
for (i in seq_along(id_vector)) {
  data_i <- df[df$ind == id_vector[i], ]
  new_lim <- expand_2D_center(data_i$x, data_i$y)
  g_list[[i]] <- ggplot(data = data_i, aes(x, y)) +
    geom_point() +
    coord_fixed(xlim = new_lim$xlim, ylim = new_lim$ylim) 
}
grid.arrange(grobs = g_list, ncol = 2, respect=TRUE)

center adjusted

2。中心调整的地块,每个地块的xy比例正确,但在地块上不一致。

我希望现在更清楚了。当我专注于当前的问题并且忘记整个历史时,我的第一篇文章没有明确说明问题,这需要解释我们的要求。

@Mark彼得森的回答似乎解决了这个问题,我将进一步阅读代码进行验证。

谢谢!

编辑:为了给出一些背景信息,我在这里添加了真实数据中的图:

在一个图中包含所有海鸥的概览图,注意有一些异常值拉伸了图

the overview plots

这是一个方面图,它可以使所有内容保持一致。

facet

这是每个比例正确的单个图,而不是在图中对齐。

plots not adjusted

这个每个图都以质心为中心。我打算同时放大它们。唯一的问题是尺度在图表之间不一致。

enter image description here

编辑:我在我的数据上尝试了@Mark Peterson代码,它裁剪了一些点但是图表是一致的,可能是因为我的数据值更大,所以原始填充不够大。

Mark正在每个绘图的所有绘图中使用max xrange,因此每个绘图都具有相同的范围。我的代码尝试将每个绘图都适合他们的模式,但是将它们放置在具有一致比例的网格中将需要使用最大的画布缩小绘图,或填充最小的绘图。将每个绘图的范围设置为相同但实际上具有相似的效果,但实现起来要简单得多。

1 个答案:

答案 0 :(得分:3)

好吧,我认为我已经对你的要求做了最好的猜测,尽管我同意@MrFlick明确地分享数据会对此有很大的帮助。

如果你在同一个基本网格上拥有所有动物的简单数据,我猜你不会问(至少不是你的方式)。也就是说,鉴于这些数据:

set.seed(8675309)
df <-
  data.frame(
    x = runif(40, 1, 20)
    , y = runif(40, 100, 140)
    , ind = sample(LETTERS[1:4], 40, TRUE)
  )

这个直截了当的facet_grid有效:

ggplot(df , aes(x = x, y = y)) +
  geom_point() +
  facet_wrap(~ind) +
  coord_fixed()

给出这个:

enter image description here

但是,你说facet_wrap解决方案不起作用。所以,我猜你有数据,每个动物都在不同的网格中,如下所示(注意,在这里使用dplyr以及更多):

modDF <-
  df %>%
  mutate(x = x + as.numeric(ind)*10
         , y = y + as.numeric(ind)*20)

这意味着上面的代码(使用modDF代替df

ggplot(modDF, aes(x = x, y = y)) +
  geom_point() +
  facet_wrap(~ind) +
  coord_fixed()

给出了这个:

enter image description here

浪费了大量空间,看起来并不好看。所以,我你问的是如何处理这些数据。为此,我认为您需要做的是计算最大范围(在每个轴中),然后以每个人的数据为中心生成该范围。为此,我非常依赖dplyrgroup_by个人,并计算最小和最大x / y位置。然后,我计算了一些额外的列,以计算每个人的数据中点,范围的大小,然后范围应扩展到哪里设置为所需的最大宽度/高度,并以该个人为中心&# 39; s数据。请注意,我也在填充这些内容,以便在实现范围时设置expand = FALSE

getRanges <-
  modDF %>%
  group_by(ind) %>%
  summarise(
    minx = min(x)
    , maxx = max(x)
    , miny = min(y)
    , maxy = max(y)
  ) %>%
  mutate(
    # Find mid points for range setting
    midx = (maxx + minx)/2
    , midy = (maxy + miny)/2
    # Find size of all ranges
    , xrange = maxx - minx
    , yrange = maxy - miny
    # Set X lims the size of the biggest range, centered at the middle
    , xstart = midx - max(xrange)/2 - 0.5
    , xend = midx + max(xrange)/2 + 0.5
    # Set Y lims the size of the biggest range, centered at the middle
    , ystart = midy - max(yrange)/2 - 0.5
    , yend = midy + max(yrange)/2 + 0.5
    )

给出

     ind     minx     maxx     miny     maxy     midx     midy   xrange   yrange   xstart     xend   ystart     yend
  <fctr>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
1      A 14.91873 29.53871 120.0743 157.6944 22.22872 138.8844 14.61997 37.62010 14.17717 30.28027 119.5743 158.1944
2      B 22.50432 37.27647 153.5654 179.0589 29.89039 166.3122 14.77215 25.49352 21.83884 37.94195 147.0021 185.6222
3      C 32.15187 47.08845 165.9829 195.0261 39.62016 180.5045 14.93658 29.04320 31.56861 47.67171 161.1945 199.8146
4      D 44.49392 59.59702 192.7243 214.5523 52.04547 203.6383 15.10310 21.82806 43.99392 60.09702 184.3283 222.9484

然后,我循环遍历每个人,生成所需的绘图并将范围设置为为该个人计算的范围。 (您可以使用ggtitle代替facet_wrap,但我喜欢来自strip的{​​{1}}效果。)

facet_wrap

然后,我使用sepPlots <- lapply(levels(modDF$ind), function(thisInd){ thisRange <- filter(getRanges, ind == thisInd) modDF %>% filter(ind == thisInd) %>% ggplot(aes(x = x, y = y)) + geom_point() + coord_fixed( xlim = c(thisRange$xstart, thisRange$xend) , ylim = c(thisRange$ystart, thisRange$yend) , expand = FALSE ) + # ggtitle(thisInd) facet_wrap(~ind) }) 中的plot_grid将图表排列在一起。请注意,加载cowplot会设置主题。所以,我正在重置主题,因为我不是cowplot

中的主角
cowplot

给出:

enter image description here

从那里,您可以根据需要使用比例尺和轴标签。