当一些图有图例而其他图没有时,在 ggplot2 中对齐多个图 [英] Align multiple plots in ggplot2 when some have legends and others don't

查看:32
本文介绍了当一些图有图例而其他图没有时,在 ggplot2 中对齐多个图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用了

我想要的是对齐 x 轴,并用空白填充缺失的图例.这可能吗?

提出的最优雅的解决方案是下面 Sandy Muspratt 提出的解决方案.

我实现了它,它在两个图形上运行得很好.

然后我尝试了三个,具有不同的图例大小,但它不再起作用了:

库(ggplot2)图书馆(重塑2)图书馆(gridExtra)x = seq(0, 10, length.out = 200)y1 = sin(x)y2 = cos(x)y3 = sin(x) * cos(x)y4 = sin(2*x) * cos(2*x)df1 <- data.frame(x, y1, y2)df1 <-melt(df1, id.vars = "x")g1 <- ggplot(df1, aes(x, value, color = variable)) + geom_line()g1 <- g1 + theme_bw()g1 <- g1 + 主题(legend.key = element_blank())g1 <- g1 + ggtitle("图 1", 副标题 = "有图例")df2 <- data.frame(x, y3)g2 <- ggplot(df2, aes(x, y3)) + geom_line()g2 <- g2 + theme_bw()g2 <- g2 + 主题(legend.key = element_blank())g2 <- g2 + ggtitle("图 2", 副标题 = "无图例")df3 <- data.frame(x, y3, y4)df3 <-melt(df3, id.vars = "x")g3 <- ggplot(df3, aes(x, value, color = variable)) + geom_line()g3 <- g3 + theme_bw()g3 <- g3 + 主题(legend.key = element_blank())g3 <- g3 + scale_color_discrete("这确实是一个很长的标题")g3 <- g3 + ggtitle("图 3", 副标题 = "有图例")gA <- ggplotGrob(g1)gB <- ggplotGrob(g2)gC <- ggplotGrob(g3)gB = gtable::gtable_add_cols(gB, sum(gC$widths[7:8]), 6)maxWidth <- grid::unit.pmax(gA$widths[2:5], gB$widths[2:5], gC$widths[2:5])gA$widths[2:5] <- maxWidthgB$widths[2:5] <- maxWidthgC$widths[2:5] <- maxWidthg <-排列格罗布(gA,gB,gC,ncol = 1)网格::grid.newpage()网格::grid.draw(g)

结果如下图:

我在这里找到的答案以及与该主题有关的其他问题的主要问题是人们玩"了很多向量myGrob$widths,而没有真正解释他们为什么这样做.我看到有人修改了 myGrob$widths[2:5] 其他 myGrob$widths[2:3] 并且我找不到任何解释这些列是什么的文档.

我的目标是创建一个通用函数,例如:

AlignPlots <- function(...) {# 检索要对齐的图列表plots.list <- list(...)# 初始化列表grobs.list <- list()widths.list <- list()# 收集每个图的每个grob的宽度max.nb.grobs <- 0最长的.grob <- NULLfor (i in 1:length(plots.list)){if (i != length(plots.list)) {plots.list[[i]] <- plots.list[[i]] + theme(axis.title.x = element_blank())}grobs.list[[i]] <- ggplotGrob(plots.list[[i]])current.grob.length <- 长度(grobs.list[[i]])如果(current.grob.length > max.nb.grobs){max.nb.grobs <- current.grob.length最长的.grob <- grobs.list[[i]]}widths.list[[i]] <- grobs.list[[i]]$widths[2:5]}# 获取最大宽度maxWidth <- do.call(grid::unit.pmax,widths.list)# 为每个grob分配最大宽度for (i in 1:length(grobs.list)){如果(长度(grobs.list[[i]])

解决方案

感谢 thisthat,已发布在评论(然后删除)中,我想出了以下通用解决方案.

我喜欢 Sandy Muspratt 的回答,鸡蛋包装似乎以一种非常优雅的方式完成了这项工作,但由于它是实验性的且易碎的",我更喜欢使用这种方法:

#' 垂直对齐绘图列表.#'#' 此函数对齐给定的绘图列表,以便 x 轴对齐.#' 它假设图共享相同范围的 x 数据.#'#' @param ... 要对齐的图列表.#' @param globalTitle 分配给新创建的图表的标题.#' @param keepTitles TRUE 如果你想保留每个人的头衔#' 阴谋.#' @param keepXAxisLegends TRUE 如果要保留每个的 x 轴标签#'个人情节.否则,除了图中的一个之外,它们都将被删除#' 在底部.#' @param nb.columns 生成的图的列数.#'#' @return 包含对齐图的 gtable.#' @例子#' g <- VAlignPlots(g1, g2, g3, globalTitle = "对齐测试")#' grid::grid.newpage()#' grid::grid.draw(g)VAlignPlots <- 函数(...,globalTitle = "",keepTitles = FALSE,keepXAxisLegends = FALSE,nb.columns = 1) {# 检索要对齐的图列表plots.list <- list(...)# 如果需要,删除单个图表标题如果(!keepTitles){plots.list <- lapply(plots.list, function(x) x <- x + ggtitle(""))plots.list[[1]] <- plots.list[[1]] + ggtitle(globalTitle)}# 删除所有图形上的 x 轴标签,除了最后一个,如果需要的话如果(!keepXAxisLegends){plots.list[1:(length(plots.list)-1)] <-lapply(plots.list[1:(length(plots.list)-1)],函数(x) x <- x + 主题(axis.title.x = element_blank()))}# 构建 grobs 列表grobs.list <- lapply(plots.list, ggplotGrob)# 获取最大宽度widths.list <- do.call(grid::unit.pmax, lapply(grobs.list, "[[", 'widths'))# 为所有grob分配最大宽度grobs.list <- lapply(grobs.list, function(x) {x[['widths']] = widths.listX})# 创建gtable并显示g <- grid.arrange(grobs = grobs.list, ncol = nb.columns)# 另一种方法是使用arrangeGrob 来创建表# 显示它#g <- do.call(arrangeGrob, c(grobs.list, ncol = nb.columns))退货(克)}

I have used the method indicated here to align graphs sharing the same abscissa.

But I can't make it work when some of my graphs have a legend and others don't.

Here is an example:

library(ggplot2)
library(reshape2)
library(gridExtra)

x = seq(0, 10, length.out = 200)
y1 = sin(x)
y2 = cos(x)
y3 = sin(x) * cos(x)

df1 <- data.frame(x, y1, y2)
df1 <- melt(df1, id.vars = "x")

g1 <- ggplot(df1, aes(x, value, color = variable)) + geom_line()
print(g1)

df2 <- data.frame(x, y3)
g2 <- ggplot(df2, aes(x, y3)) + geom_line()
print(g2)

gA <- ggplotGrob(g1)
gB <- ggplotGrob(g2)
maxWidth <- grid::unit.pmax(gA$widths[2:3], gB$widths[2:3])
gA$widths[2:3] <- maxWidth
gB$widths[2:3] <- maxWidth
g <- arrangeGrob(gA, gB, ncol = 1)
grid::grid.newpage()
grid::grid.draw(g)

Using this code, I have the following result:

What I would like is to have the x axis aligned and the missing legend being filled by a blank space. Is this possible?

Edit:

The most elegant solution proposed is the one by Sandy Muspratt below.

I implemented it and it works quite well with two graphs.

Then I tried with three, having different legend sizes, and it doesn't work anymore:

library(ggplot2)
library(reshape2)
library(gridExtra)

x = seq(0, 10, length.out = 200)
y1 = sin(x)
y2 = cos(x)
y3 = sin(x) * cos(x)
y4 = sin(2*x) * cos(2*x)

df1 <- data.frame(x, y1, y2)
df1 <- melt(df1, id.vars = "x")

g1 <- ggplot(df1, aes(x, value, color = variable)) + geom_line()
g1 <- g1 + theme_bw()
g1 <- g1 + theme(legend.key = element_blank())
g1 <- g1 + ggtitle("Graph 1", subtitle = "With legend")

df2 <- data.frame(x, y3)
g2 <- ggplot(df2, aes(x, y3)) + geom_line()
g2 <- g2 + theme_bw()
g2 <- g2 + theme(legend.key = element_blank())
g2 <- g2 + ggtitle("Graph 2", subtitle = "Without legend")

df3 <- data.frame(x, y3, y4)
df3 <- melt(df3, id.vars = "x")

g3 <- ggplot(df3, aes(x, value, color = variable)) + geom_line()
g3 <- g3 + theme_bw()
g3 <- g3 + theme(legend.key = element_blank())
g3 <- g3 + scale_color_discrete("This is indeed a very long title")
g3 <- g3 + ggtitle("Graph 3", subtitle = "With legend")

gA <- ggplotGrob(g1)
gB <- ggplotGrob(g2)
gC <- ggplotGrob(g3)

gB = gtable::gtable_add_cols(gB, sum(gC$widths[7:8]), 6)

maxWidth <- grid::unit.pmax(gA$widths[2:5], gB$widths[2:5], gC$widths[2:5])
gA$widths[2:5] <- maxWidth
gB$widths[2:5] <- maxWidth
gC$widths[2:5] <- maxWidth

g <- arrangeGrob(gA, gB, gC, ncol = 1)
grid::grid.newpage()
grid::grid.draw(g)

This results in the following figure:

My main problem with the answers found here and in other questions regarding the subject is that people "play" quite a lot with the vector myGrob$widths without actually explaining why they are doing it. I have seen people modify myGrob$widths[2:5] others myGrob$widths[2:3] and I just can't find any documentation explaining what those columns are.

My objective is to create a generic function such as:

AlignPlots <- function(...) {
  # Retrieve the list of plots to align
  plots.list <- list(...)

  # Initialize the lists
  grobs.list <- list()
  widths.list <- list()

  # Collect the widths for each grob of each plot
  max.nb.grobs <- 0
  longest.grob <- NULL
  for (i in 1:length(plots.list)){
    if (i != length(plots.list)) {
      plots.list[[i]] <- plots.list[[i]] + theme(axis.title.x = element_blank())
    }

    grobs.list[[i]] <- ggplotGrob(plots.list[[i]])
    current.grob.length <- length(grobs.list[[i]])
    if (current.grob.length > max.nb.grobs) {
      max.nb.grobs <- current.grob.length
      longest.grob <- grobs.list[[i]]
    }

    widths.list[[i]] <- grobs.list[[i]]$widths[2:5]
  }

  # Get the max width
  maxWidth <- do.call(grid::unit.pmax, widths.list)

  # Assign the max width to each grob
  for (i in 1:length(grobs.list)){
    if(length(grobs.list[[i]]) < max.nb.grobs) {
      grobs.list[[i]] <- gtable::gtable_add_cols(grobs.list[[i]],
                                                 sum(longest.grob$widths[7:8]),
                                                 6)
    }
    grobs.list[[i]]$widths[2:5] <- as.list(maxWidth)
  }

  # Generate the plot
  g <- do.call(arrangeGrob, c(grobs.list, ncol = 1))

  return(g)
}

解决方案

Thanks to this and that, posted in the comments (and then removed), I came up with the following general solution.

I like the answer from Sandy Muspratt and the egg package seems to do the job in a very elegant manner, but as it is "experimental and fragile", I preferred using this method:

#' Vertically align a list of plots.
#' 
#' This function aligns the given list of plots so that the x axis are aligned.
#' It assumes that the graphs share the same range of x data.
#'
#' @param ... The list of plots to align.
#' @param globalTitle The title to assign to the newly created graph.
#' @param keepTitles TRUE if you want to keep the titles of each individual
#' plot.
#' @param keepXAxisLegends TRUE if you want to keep the x axis labels of each
#' individual plot. Otherwise, they are all removed except the one of the graph
#' at the bottom.
#' @param nb.columns The number of columns of the generated graph.
#'
#' @return The gtable containing the aligned plots.
#' @examples
#' g <- VAlignPlots(g1, g2, g3, globalTitle = "Alignment test")
#' grid::grid.newpage()
#' grid::grid.draw(g)
VAlignPlots <- function(...,
                       globalTitle = "",
                       keepTitles = FALSE,
                       keepXAxisLegends = FALSE,
                       nb.columns = 1) {
  # Retrieve the list of plots to align
  plots.list <- list(...)

  # Remove the individual graph titles if requested
  if (!keepTitles) {
    plots.list <- lapply(plots.list, function(x) x <- x + ggtitle(""))
    plots.list[[1]] <- plots.list[[1]] + ggtitle(globalTitle)
  }

  # Remove the x axis labels on all graphs, except the last one, if requested
  if (!keepXAxisLegends) {
    plots.list[1:(length(plots.list)-1)] <-
      lapply(plots.list[1:(length(plots.list)-1)],
             function(x) x <- x + theme(axis.title.x = element_blank()))
  }

  # Builds the grobs list
  grobs.list <- lapply(plots.list, ggplotGrob)

  # Get the max width
  widths.list <- do.call(grid::unit.pmax, lapply(grobs.list, "[[", 'widths'))

  # Assign the max width to all grobs
  grobs.list <- lapply(grobs.list, function(x) {
    x[['widths']] = widths.list
    x})

  # Create the gtable and display it
  g <- grid.arrange(grobs = grobs.list, ncol = nb.columns)
  # An alternative is to use arrangeGrob that will create the table without
  # displaying it
  #g <- do.call(arrangeGrob, c(grobs.list, ncol = nb.columns))

  return(g)
}

这篇关于当一些图有图例而其他图没有时,在 ggplot2 中对齐多个图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆