是否可以在数据之前先绘制轴线?

Tje*_*ebo 9 r ggplot2 grob

这是我之前的问题的后续问题,我正在寻找一种解决方案,先绘制轴,然后绘制数据。答案适用于该特定问题和示例,但它提出了一个更一般的问题,即如何更改底层 grobs 的绘图顺序。首先是轴,然后是数据。

非常类似于面板网格 grob 可以绘制在顶部或不绘制的方式。

面板网格和轴格布显然是不同的——轴更多地作为引导对象而不是“简单”格布。(轴是用 绘制的ggplot2:::draw_axis(),而面板网格是作为ggplot2:::Layout对象的一部分构建的)。

我想这就是为什么在顶部绘制轴的原因,我想知道是否可以更改绘制顺序。

# An example to play with 

library(ggplot2)
df <- data.frame(var = "", val = 0)

ggplot(df) + 
  geom_point(aes(val, var), color = "red", size = 10) +
  scale_x_continuous(
    expand = c(0, 0),
    limits = c(0,1)
  ) +
  coord_cartesian(clip = "off") +
  theme_classic() 
Run Code Online (Sandbox Code Playgroud)

PS我不会接受甚至不赞成创建假轴的答案,因为这不是我正在寻找或试图理解的。

use*_*650 8

Aggplot可以用它的 表示gtable。grobs 的位置由layout元素给出,“z 列用于定义 grobs 的绘制顺序”

z该值panel,其中包含点GROB,然后可以增加,所以它是最后绘制。

所以如果p是你的情节那么

g <- ggplotGrob(p) ;
g$layout[g$layout$name == "panel", "z"] <-  max(g$layout$z) + 1L
grid::grid.draw(g)
Run Code Online (Sandbox Code Playgroud)

然而,正如评论中所指出的,这会改变轴的外观,这可能是由于面板被绘制在一些轴上。

但是在来自dww 的令人兴奋的新消息中

如果我们添加theme(panel.background = element_rect(fill = NA))到图中,轴不再部分被遮挡。这既证明这是导致轴线变细的原因,也提供了合理的解决方法,前提是您不需要彩色面板背景。

  • 如果我们将“theme(panel.background = element_rect(fill = NA))”添加到绘图中,轴将不再被部分遮挡。这既证明了这是轴线较细的原因,又提供了合理的解决方法,前提是您不需要彩色面板背景。 (3认同)

All*_*ron 6

由于您正在寻找更多“在绘制级别”的解决方案,那么开始的地方是询问“首先如何绘制 ggplot?”。答案可以在printggplot 对象的方法中找到:

ggplot2:::print.ggplot
#> function (x, newpage = is.null(vp), vp = NULL, ...) 
#> {
#>     set_last_plot(x)
#>    if (newpage) 
#>         grid.newpage()
#>     grDevices::recordGraphics(requireNamespace("ggplot2", 
#>         quietly = TRUE), list(), getNamespace("ggplot2"))
#>     data <- ggplot_build(x)
#>     gtable <- ggplot_gtable(data)
#>     if (is.null(vp)) {
#>         grid.draw(gtable)
#>     }
#>     else {
#>         if (is.character(vp)) 
#>             seekViewport(vp)
#>         else pushViewport(vp)
#>         grid.draw(gtable)
#>         upViewport()
#>     }
#>     invisible(x)
#> }
Run Code Online (Sandbox Code Playgroud)

在那里您可以看到 ggplot 实际上是通过调用ggplot_buildggplot 对象绘制的,然后ggplot_gtableggplot_build.

困难在于面板及其背景、网格线和数据被创建为一个独特的 grob 树。然后将其作为单个实体嵌套在由ggplot_build. 轴线绘制在该面板的“顶部”。如果先绘制这些线,它们的一部分粗细将被面板过度绘制。正如 user20650 的回答中提到的,如果您不需要您的绘图具有背景颜色,这不是问题。

据我所知,除非您自己将轴线添加为 grob,否则没有本地方式将轴线作为面板的一部分包含在内。

以下小功能套件允许您获取绘图对象,从中删除轴线并将轴线添加到面板中:

get_axis_grobs <- function(p_table)
{
  axes <- grep("axis", p_table$layout$name)
  axes[sapply(p_table$grobs[axes], function(x) class(x)[1] == "absoluteGrob")]
}

remove_lines_from_axis <- function(axis_grob)
{
  axis_grob$children[[grep("polyline", names(axis_grob$children))]] <- zeroGrob()
  axis_grob
}

remove_all_axis_lines <- function(p_table)
{
  axes <- get_axis_grobs(p_table)
  for(i in axes) p_table$grobs[[i]] <- remove_lines_from_axis(p_table$grobs[[i]])
  p_table
}

get_panel_grob <- function(p_table)
{
  p_table$grobs[[grep("panel", p_table$layout$name)]]
}

add_axis_lines_to_panel <- function(panel)
{
  old_order <- panel$childrenOrder
  panel <- grid::addGrob(panel, grid::linesGrob(x = unit(c(0, 0), "npc")))
  panel <- grid::addGrob(panel, grid::linesGrob(y = unit(c(0, 0), "npc")))
  panel$childrenOrder <- c(old_order[1], 
                           setdiff(panel$childrenOrder, old_order),
                           old_order[2:length(old_order)])
  panel
}
Run Code Online (Sandbox Code Playgroud)

这些现在都可以协调成一个单一的功能,使整个过程更容易:

underplot_axes <- function(p)
{
  p_built <- ggplot_build(p)
  p_table <- ggplot_gtable(p_built)
  p_table <- remove_all_axis_lines(p_table)
  p_table$grobs[[grep("panel", p_table$layout$name)]] <-
    add_axis_lines_to_panel(get_panel_grob(p_table))
  grid::grid.newpage()
  grid::grid.draw(p_table)
  invisible(p_table)
}
Run Code Online (Sandbox Code Playgroud)

现在你可以调用underplot_axesggplot 对象。我稍微修改了您的示例以创建一个灰色背景面板,以便我们可以更清楚地看到发生了什么:

library(ggplot2)

df <- data.frame(var = "", val = 0)

p <- ggplot(df) + 
  geom_point(aes(val, var), color = "red", size = 10) +
  scale_x_continuous(
    expand = c(0, 0),
    limits = c(0,1)
  ) +
  coord_cartesian(clip = "off") +
  theme_classic() +
  theme(panel.background = element_rect(fill = "gray90"))

p
Run Code Online (Sandbox Code Playgroud)

underplot_axes <- function(p)
{
  p_built <- ggplot_build(p)
  p_table <- ggplot_gtable(p_built)
  p_table <- remove_all_axis_lines(p_table)
  p_table$grobs[[grep("panel", p_table$layout$name)]] <-
    add_axis_lines_to_panel(get_panel_grob(p_table))
  grid::grid.newpage()
  grid::grid.draw(p_table)
  invisible(p_table)
}
Run Code Online (Sandbox Code Playgroud)

reprex 包(v0.3.0)于 2021 年 5 月 7 日创建

现在,您可能会认为这是“创建假轴”,但我认为它更像是将轴线从 Grob 树中的一个位置“移动”到另一个位置。遗憾的是,该选项似乎没有内置到 ggplot 中,但我也可以看到,它需要对 ggplot 的构造方式进行重大改革以允许该选项。