dismantling a ggplot with grid and gtable

2019-02-19 14:18发布

I'm struggling to build a dual-axis plot based on ggplot objects. At baptiste's suggestion, I have broken down the problem into smaller parts. The present issue is:

  1. how to remove all of the data from the grobs, while keeping the axis, the axis labels, the axis tickmarks, and the grid lines? By 'data' I mean the data associated with geom_line() and geom_points().

The reason for wanting to do this is that in the process of building a dual-axis plot, I ran into the following problem: the grid lines from one grob would overwrite the data from the other grob. By removing the data lines and points, there would be no overwriting.

Let me be clear: I do have workarounds that consist in adding a linetype to the aes() and setting scale_linetype_manual(values = c("solid", "blank") or, alternatively, sending the data 'off the grid', but I would like to post-process a plot object that has not been 'groomed' too much for the purpose at hand.

Below some code and figures.

# Data
df <- structure(list(Year = c(1950, 2013, 1950, 2013), Country = structure(c(1L, 
1L, 2L, 2L), .Label = c("France", "United States"), class = "factor"), 
Category = c("Hourly minimum wage", "Hourly minimum wage", 
"Hourly minimum wage", "Hourly minimum wage"), value = c(2.14, 
9.43, 3.84, 7.25), variable = c("France (2013 euros)", 
"France (2013 euros)", "United States (2013 dollars)", "United States (2013 dollars)"
), Unit = c("2013 euros", "2013 euros", "2013 dollars", "2013 dollars"
)), .Names = c("Year", "Country", "Category", "value", "variable", 
"Unit"), row.names = c(NA, 4L), class = "data.frame")

# Plot data with ggplot
library(ggplot2)
p <- ggplot(data = df, aes(x = Year, y = value, group = variable, colour = variable, shape = variable)) + 
geom_line(size = 2) + 
geom_point(size = 4) +
theme(panel.grid.major = element_line(size = 1, colour = "darkgreen"), 
      panel.grid.minor = element_line(size = 1, colour = "darkgreen", linetype = "dotted"))

# Manipulate grobs with gtable
library(gtable)
g <- ggplot_gtable(ggplot_build(p))
## Here remove the geom_line() and geom_point()
## g <- stripdata(g)  # pseudo-code!
grid.newpage()
grid.draw(g)

In the plot below, I would like the lines gone!

enter image description here

Edit: Following baptiste's suggestion, I attempt to remove the data layers. However, as BondedDust points out in the comments section, this breaks the ggplot object:

# Remove the two layers of data
p$layers[[1]] <- NULL
p$layers[[1]] <- NULL
g <- ggplot_gtable(ggplot_build(p))
## Error: No layers in plot

Removing the data from the ggplot object destroys it. One workaround I have used in applications is to 'send the data off the grid', e.g. multiplying each cell by -999999 and cuting off the display with + scale_y_continuous(limits = c(1, 10)), but I'd like to avoid this ugly hack, if feasible. I was hoping that the associated gtable would not be destroyed if I replaced every data point with NA or NULL, so that's why I was looking for a way to remove the data from inside the g object, rather than the p object.

After manipulation of the grobs (rather than hacking directly the ggplot object), the result of grid.draw(g) would be:

enter image description here

FYI, the second plot was obtained with the following workaround.

p <- ggplot(data = within(df, value <- -999999), aes(x = Year, y = value, group = variable, colour = variable, shape = variable)) + 
geom_line() + 
geom_point() +
theme(panel.grid.major = element_line(size = 1, colour = "darkgreen"), 
      panel.grid.minor = element_line(size = 1, colour = "darkgreen", linetype = "dotted")) +
scale_y_continuous(limits = c(1, 10))

1条回答
手持菜刀,她持情操
2楼-- · 2019-02-19 15:04

A more natural strategy would be to use invisible geom_blank layers, so that ggplot2 still trains the scales etc to build the plot but shows no data. Since you want to process already-formatted plots, however, you probably have to remove manually those grobs from the plot gTree. Here's an attempt,

library(gtable)
g <- ggplotGrob(p)

stripdata <- function(g){
  keep <- grepl("border|grill", 
                names(g[["grobs"]][[4]][["children"]]))
  g[["grobs"]][[4]][["children"]][!keep] <- NULL
  g
}

grid.newpage()
grid.draw(stripdata(g))
查看更多
登录 后发表回答