Inverse of ggplotGrob?
I would say no. ggplotGrob
is a one-way street. grob objects are drawing primitives defined by grid. You can create arbitrary grobs from scratch. There's no general way to turn a random collection of grobs back into a function that would generate them (it's not invertible because it's not 1:1). Once you go grob, you never go back.
You could wrap a ggplot object in a custom class and overload the plot/print commands to do some custom grob manipulation, but that's probably even more hack-ish.
R: Is it possible to extract the original data from a gtable object created with grid.arrange and ggplot?
You can't; at this stage (namely, after ggplotGrob
) the data have been processed into graphical objects and the mapping isn't usually reversible, much like an omelette.
If you are desperate for getting some values back you can inspect individual grobs corresponding to the plotted points, e.g.
myobj$grobs[[2]]$grobs[[2]]$children[[3]][c('x','y')]
$x
[1] 0.525145067698259native 0.587040618955513native
[3] 0.525145067698259native 0.89651837524178native
[5] 0.819148936170213native 0.954545454545455native
[7] 0.474854932301741native 0.699226305609285native
[9] 0.648936170212766native 0.819148936170213native
[11] 0.470986460348163native
$y
[1] 0.233037353850445native 0.435264173310709native
[3] 0.425966388507938native 0.205143999442133native
[5] 0.0691638967016109native 0.12030171311685native
[7] 0.266741823760489native 0.143546175123777native
[9] 0.191197322237977native 0.0454545454545455native
[11] 0.339961879082309native
How to align grob with ggplot using ggplotGrob and annotation_custom?
You'd probably want to grab just the panel without axis, margins etc. before adding it as a custom annotation then. In example below, I made the red points larger so you can see that they overlap.
library(ggplot2)
p <- ggplot(mtcars, aes(wt, mpg)) +
geom_point(color = "red", size = 3)
grab_panel <- function(p) {
gt <- ggplotGrob(p)
layout <- gt$layout
is_panel <- which(layout$name == "panel")[[1]]
i <- layout$t[is_panel]
j <- layout$l[is_panel]
gt[i,j]
}
ggplot(mtcars, aes(wt, mpg)) +
annotation_custom(grob = grab_panel(p)) +
geom_point()
Created on 2021-03-20 by the reprex package (v1.0.0)
ggplot and grid: Find the relative x and y positions of a point in a ggplot grob
This is an old question, so an answer may no longer be relevant, but anyway ....
This is not straightforward, but it can be done with grid
editing tools. One needs to collect information along the way, and that makes the solution fiddly. This is very much a one-off solution. A lot depends on the specifics of the two ggplots. But maybe there is enough here for someone to use. There was insufficient information about the lines to be drawn; I'll draw two red lines: one from the centre of the crossbar of the first boxplot to the centre of the lower left tile of the heatmap; and one from the centre of the crossbar of the first boxplot to the next tile along in the heatmap.
Some points:
- Lines are to be drawn across different viewports. Normally, grobs are drawn within viewports, but there are a couple of ways to get lines across viewports. I'll use the
grid
functionsgrid.move.to()
andgrid.line.to()
. - The coordinates of grobs can be found to the structure of the grobs. That is,
one can extract the first boxplot, and look at its structure. The
structure will give the positions of segments for the whiskers, a
segment for the crossbar, and a polygon for the box. - Similarly, one can extract the heatmap, and the structure will give
the coordinates for the upper left corner of each rectangle (i.e.,
each tile) in the heatmap, and the width and height of each
rectangle. A bit of simple arithmetic will give the coordinates for
the centre of the tiles. - However, the coordinates for the rectangles are in terms of the
unrotated viewport. Some care is needed in selecting the relevant rectangles.
# Draw the plot
require(reshape2)
require(grid)
require(ggplot2)
set.seed(4321)
datamat <- matrix(rnorm(50), ncol=5)
cov_mat <- cov(datamat)
cov_mat[lower.tri(cov_mat)] <- NA
data_df <- melt(datamat)
cov_df <- melt(cov_mat)
plot_1 <- ggplot(data_df, aes(x=as.factor(Var2), y=value)) + geom_boxplot()
plot_2 <- ggplot(cov_df, aes(x=Var1, y=Var2, fill=value)) +
geom_tile() +
scale_fill_gradient(na.value="transparent") +
coord_fixed() +
theme(
legend.position="none",
plot.background = element_rect(fill = "transparent",colour = NA),
panel.grid=element_blank(),
panel.background=element_blank(),
panel.border = element_blank(),
plot.margin = unit(c(0, 0, 0, 0), "npc"),
axis.ticks=element_blank(),
axis.title=element_blank(),
axis.text=element_text(size=unit(0,"npc")))
cov_heatmap <- ggplotGrob(plot_2)
boxplot <- ggplotGrob(plot_1)
grid.newpage()
pushViewport(viewport(height=unit(sqrt(2* 0.4 ^2), 'npc'),
width=unit(sqrt(2* 0.4 ^2), 'npc'),
x=unit(0.5, 'npc'),
y=unit(0.63, 'npc'),
angle=-45,
clip="on",
name = "heatmap"))
grid.draw(cov_heatmap)
upViewport(0)
pushViewport(viewport(height=unit(0.5, 'npc'),
width=unit(1, 'npc'),
x=unit(0.5, 'npc'),
y=unit(0.25, 'npc'),
clip="on",
name = "boxplot"))
grid.draw(boxplot)
upViewport(0)
# So that grid can see all the grobs
grid.force()
# Get the names of the grobs
grid.ls()
The relevant bits are in sections to do with the panels. The name of the heatmap grob is:
geom_rect.rect.2
The names of the grobs that make up the first boxplot are (the numbers can be different):
geom_boxplot.gTree.40
GRID.segments.34
geom_crossbar.gTree.39
geom_polygon.polygon.37
GRID.segments.38
To get the coordinates of the rectangles in the heatmap.
names = grid.ls()$name
HMmatch = grep("geom_rect", names, value = TRUE)
hm = grid.get(HMmatch)
str(hm)
hm$x
hm$y
hm$width # heights are equal to the widths
hm$gp$fill
(Note that just
is set to "left", "top"
) The heatmap is a 5 X 5 grid of rectangles, but only the upper half are coloured, and thus visible in the plot. The coordinates for the two selected rectangles are: (0.045, 0.227) and (0.227, 0.409), and each rectangle has a width and height of 0.182
To get the coordinates of the relevant points in the first boxplot.
BPmatch = grep("geom_boxplot.gTree", names, value = TRUE)[-1]
box1 = grid.gget(BPmatch[1])
str(box1)
The x-coord of the whisker is 0.115, and the y-coord of the crossbar is .507
Now, to draw the lines across the two viewports. The lines are 'drawn' in the panel viewports, but the name of the heatmap panel viewport is the same as the name of the boxplot panel viewport. To overcome this difficulty, I seek the boxplot viewport, then push down to its panel viewport; similarly, I seek the heatmap viewport, then push down to its panel viewport.
## First Line (and points)
seekViewport("boxplot")
downViewport("panel.7-5-7-5")
grid.move.to(x = .115, y = .503, default.units = "native")
grid.points(x = .115, y = .503, default.units = "native",
size = unit(5, "mm"), pch = 16, gp=gpar(col = "red"))
seekViewport("heatmap")
downViewport("panel.7-5-7-5")
grid.line.to(x = 0.045 + .5*.182, y = 0.227 - .5*.182, default.units = "native", gp = gpar(col = "red", lwd = 2))
grid.points(x = 0.045 + .5*.182, y = 0.227 - .5*.182, default.units = "native",
size = unit(5, "mm"), pch = 16, gp=gpar(col = "red"))
## Second line (and points)
seekViewport("boxplot")
downViewport("panel.7-5-7-5")
grid.move.to(x = .115, y = .503, default.units = "native")
seekViewport("heatmap")
downViewport("panel.7-5-7-5")
grid.line.to(x = 0.227 + .5*.182, y = 0.409 - .5*.182, default.units = "native", gp = gpar(col = "red", lwd = 2))
grid.points(x = 0.227 + .5*.182, y = 0.409 - .5*.182, default.units = "native",
size = unit(5, "mm"), pch = 16, gp=gpar(col = "red"))
Enjoy.
Inverse of which
One-liner solution:
invwhich <- function(indices, totlength) is.element(seq_len(totlength), indices)
invwhich(c(2,5), 10)
[1] FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
ggplot and grob arrangement
I have never use the grobTree
function but replace it with a list and it works
gs = list(ggplotGrob(g1), ggplotGrob(g2), ggplotGrob(g3))
grid.arrange(grobs = gs, layout_matrix = lay)
this is the result, as you want I think
ggplot object value only available after printing it, how do I force it to evaluate
I think it lives in the aes_params
part until the plot gets printed.
library(ggplot2)
test_function <- function(test_arg){
ggplot() + annotate("text", x=0, y=0, label= test_arg)
}
result <- test_function("test")
result$layers[[1]]$aes_params$label
#> [1] "test"
Created on 2021-03-19 by the reprex package (v0.3.0)
Left align two graph edges (ggplot)
Try this,
gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)
grid.arrange(gA, gB, ncol=1)
Edit
Here's a more general solution (works with any number of plots) using a modified version of rbind.gtable
included in gridExtra
gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))
Related Topics
Replace All Values in a Matrix <0.1 with 0
Removing Multiple Columns from R Data.Table with Parameter for Columns to Remove
How to Set Fixed Continuous Colour Values in Ggplot2
How to Determine If You Have an Internet Connection in R
Creating Regular 15-Minute Time-Series from Irregular Time-Series
Add a Horizontal Line to Plot and Legend in Ggplot2
Ggplot2 Multiple Scales/Legends Per Aesthetic, Revisited
How to Draw a Nice Arrow in Ggplot2
How to Pass Dynamic Column Names in Dplyr into Custom Function
Import Data into R with an Unknown Number of Columns
How to Change Order of Array Dimensions
Apply a Ggplot-Function Per Group with Dplyr and Set Title Per Group
Replace a Value Na with the Value from Another Column in R
Lda with Topicmodels, How to See Which Topics Different Documents Belong To
In R Data.Table, How to Pass Variable Parameters to an Expression
Data.Table Join Then Add Columns to Existing Data.Frame Without Re-Copy
Ggplot2: Connecting Points in Polar Coordinates with a Straight Line 2