I can't get the answer to this question to work.
What both me and that user want is to add axis ticks and labels to all columns when using facet_grid().
Display y-axis for each subplot when faceting
When I run the reproducable example and the solution (after adding abc=as.data.frame(abc) to fix the initial error) I receive an error message
Error in gtable_add_grob(g, grobs = list(segmentsGrob(1, 0, 1, 1),
segmentsGrob(1, : Not all inputs have either length 1 or same
length same as 'grobs
I made my own reproducible example because the original one is ehhm, a bit odd :-). It results in the same error message
require(ggplot2)
require(reshape)
require(grid)
require(gtable)
data(iris)
iris$category=rep(letters[1:4],length.out=150)
plot1=ggplot(data=iris,aes(x=1,y=Sepal.Width))+geom_boxplot()+facet_grid(Species~category)
The answer should be this:
g <- ggplotGrob(plot1)
require(gtable)
axis <- gtable_filter(g, "axis-l")[["grobs"]][[1]][["children"]][["axis"]][,2]
segment <- segmentsGrob(1,0,1,1)
panels <- subset(g$layout, name == "panel")
g <- gtable_add_grob(g, grobs=list(axis, axis), name="ticks",
t = unique(panels$t), l=tail(panels$l, -1)-1)
g <- gtable_add_grob(g, grobs=list(segmentsGrob(1,0,1,1),
segmentsGrob(1,0,1,1)),
t = unique(panels$t), l=tail(panels$l, -1)-1,
name="segments")
The answer you refer to does not apply to your situation.
To get nice placement of the tick marks and tick mark labels, I would add columns to the gtable to take the axis material. The new columns have the same width as the original y axis.
You might want to add more margin space between the panels. Do so with theme(panel.margin.x = unit(1, "lines")).
require(ggplot2)
require(grid)
require(gtable)
data(iris)
iris$category = rep(letters[1:4], length.out = 150)
plot1 = ggplot(data = iris, aes(x = 1, y = Sepal.Width))+
geom_boxplot()+
facet_grid(Species~category)
# Get the ggplot grob
g <- ggplotGrob(plot1)
# Get the yaxis
yaxis <- gtable_filter(g, "axis-l")
# Get the width of the y axis
Widths = yaxis$widths
# Add columns to the gtable to the left of the panels,
# with a width equal to yaxis width
panels <- g$layout[grepl("panel", g$layout$name), ]
pos = rev(unique(panels$l)[-1] - 1)
for(i in pos) g = gtable_add_cols(g, Widths, i)
# Add y axes to the new columns
panels <- g$layout[grepl("panel", g$layout$name), ]
posx = rev(unique(panels$l)[-1] - 1)
posy = unique(panels$t)
g = gtable_add_grob(g, rep(list(yaxis), length(posx)),
t = rep(min(posy), length(posx)), b = rep(max(posy), length(posx)), l = posx)
# Draw it
grid.newpage()
grid.draw(g)
Alternatively, place the axis in a viewport of the same width as the original y axis, but with right justification. Then, add the resulting grob to the existing margin columns between the panels, adjusting the width of those columns to suit.
require(ggplot2)
require(grid)
require(gtable)
data(iris)
iris$category = rep(letters[1:4], length.out = 150)
plot1 = ggplot(data = iris, aes(x = 1, y = Sepal.Width))+
geom_boxplot() +
facet_grid(Species ~ category )
# Get the ggplot grob
g <- ggplotGrob(plot1)
# Get the yaxis
axis <- gtable_filter(g, "axis-l")
# Get the width of the y axis
Widths = axis$width
# Place the axis into a viewport,
# of width equal to the original yaxis material,
# and positioned to be right justified
axis$vp = viewport(x = unit(1, "npc"), width = Widths, just = "right")
# Add y axes to the existing margin columns between the panels
panels <- g$layout[grepl("panel", g$layout$name), ]
posx = unique(panels$l)[-1] - 1
posy = unique(panels$t)
g = gtable_add_grob(g, rep(list(axis), length(posx)),
t = rep(min(posy), length(posx)), b = rep(max(posy), length(posx)), l = posx)
# Increase the width of the margin columns
g$widths[posx] <- unit(25, "pt")
# Or increase width of the panel margins in the original construction of plot1
# Draw it
grid.newpage()
grid.draw(g)
This is what I came up (using ggplot2_2.1.0):
g <- ggplotGrob(plot1)
axis <- gtable_filter(g, "axis-l")
newG <- gtable_add_grob(g, list(axis, axis, axis),
t = rep(4, 3), b = rep(8, 3), l = c(5, 7, 9))
grid.draw(newG)
..Which looks like this:
This is the process I went through:
g <- ggplotGrob(plot1) Create a gtable.
print(g) Look over the elements of the gtable...I'm looking for the names of the grobs that I want to mess around with. Here, it is the three grobs called "axis-l".
axis <- gtable_filter(g, "axis-l") I select my three grobs from the larger gtable object, g, and save them in a gtable called axis. Note that gtable_filter is actually selecting the grobs, not filtering them from g.
gtable_show_layout(g) Look over the layout of g so I can figure out where I want to put axis in relationship to the overall plot.
gtable_add_grob, etc. Now that I know where I'm going with it, I can append the original plot with axis.
I think that those steps are a pretty common workflow when it comes to gtable. Of course you'll have other stuff that you may what to mess around with. For example, the space that is given for all but the left-most y axis labels is not sufficient in this case. So maybe just:
newG$widths[c(5, 7, 9)] <- grid:::unit.list(axis$widths) # you won't need to wrap this in grid
grid.draw(newG)
Related
I have a grid composed of several ggplots and want to add an x axis, where axis ticks and annotations are added between the plots. I could not came up with a better solution than to create a custom plot for the axis and adding it below with arrangeGrob. But they do not align with the plots (I draw arrows where the numbers should be). Also there is a large white space below which I don't want.
I will also need an analogue for the y-axis.
library(ggplot2)
library(gridExtra)
library(ggpubr)
library(grid)
# Create a grid with several ggplots
p <-
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
theme_transparent() +
theme(plot.background = element_rect(color = "black"))
main.plot <- arrangeGrob(p, p, p, p, p, p, p, p, ncol = 4, nrow = 2)
# grid.draw(main.plot)
# Now add an x axis to the main plot
x.breaks <- c(0, 1, 2.5, 8, 10)
p.axis <- ggplot() +
ylim(-0.1, 0) +
xlim(1, length(x.breaks)) +
ggpubr::theme_transparent()
for (i in seq_along(x.breaks)) {
p.axis <- p.axis +
geom_text(aes_(x = i, y = -0.01, label = as.character(x.breaks[i])), color = "red")
}
# p.axis
final.plot <- arrangeGrob(main.plot, p.axis, nrow = 2)
grid.draw(final.plot)
Any help appreciated.
Note: In the code below, I assume each plot in your grid has equal width / height, & used equally spaced label positions. If that's not the case, you'll have to adjust the positions yourself.
Adding x-axis to main.plot:
library(gtable)
# create additional row below main plot
# height may vary, depending on your actual plot dimensions
main.plot.x <- gtable_add_rows(main.plot, heights = unit(20, "points"))
# optional: check results to verify position of the new row
dev.off(); gtable_show_layout(main.plot.x)
# create x-axis labels as a text grob
x.axis.grob <- textGrob(label = x.breaks,
x = unit(seq(0, 1, length.out = length(x.breaks)), "npc"),
y = unit(0.75, "npc"),
just = "top")
# insert text grob
main.plot.x <- gtable_add_grob(main.plot.x,
x.axis.grob,
t = nrow(main.plot.x),
l = 1,
r = ncol(main.plot.x),
clip = "off")
# check results
dev.off(); grid.draw(main.plot.x)
You can do the same for the y-axis:
# create additional col
main.plot.xy <- gtable_add_cols(main.plot.x, widths = unit(20, "points"), pos = 0)
# create y-axis labels as a text grob
y.breaks <- c("a", "b", "c") # placeholder, since this wasn't specified in the question
y.axis.grob <- textGrob(label = y.breaks,
x = unit(0.75, "npc"),
y = unit(seq(0, 1, length.out = length(y.breaks)), "npc"),
just = "right")
# add text grob into main plot's gtable
main.plot.xy <- gtable_add_grob(main.plot.xy,
y.axis.grob,
t = 1,
l = 1,
b = nrow(main.plot.xy) - 1,
clip = "off")
# check results
dev.off(); grid.draw(main.plot.xy)
(Note that the above order of x-axis followed by y-axis should not be switched blindly. If you are adding rows / columns, it's good habit to use gtable_show_layout() frequently to check the latest gtable object dimensions, & ensure that you are inserting new grobs into the right cells.)
Finally, let's add some buffer on all sides, so that the labels & plot borders don't get cut off:
final.plot <- gtable_add_padding(main.plot.xy,
padding = unit(20, "points"))
dev.off(); grid.draw(final.plot)
I am having a problem to increase the size and add a label for x axis when I use grid.arrange.
I asked a question here how can I make my data side by side barplot with dots which the answer is sufficient and I accepted it.
At the end of the code, I should glue three parts together like this
library(gridExtra)
gg1 <- ggplot_gtable(ggplot_build(g1))
gg2 <- ggplot_gtable(ggplot_build(g2))
gg.mid <- ggplot_gtable(ggplot_build(g.mid))
grid.arrange(gg1,gg.mid,gg2,ncol=3,widths=c(4/9,1/9,4/9))
I want to add a Label for it but I could not find a way to do this. I also searched and I found only one related post
Universal x axis label and legend at bottom using grid.arrange
and I tried to assigned my grid.arrangeto a variable and then
p <- grid.arrange(gg1,gg.mid,gg2,ncol=3,widths=c(4/9,1/9,4/9))
p <- arrangeGrob(p, textGrob("my label", gp=gpar(fontsize=12)))
print(p)
but the problem is not solved. Any idea how to add such a label for it?
g1 <- g.mid <- g2 <- ggplot()
grid.arrange(g1,g.mid,g2,ncol=3,widths=c(4/9,1/9,4/9),
bottom=textGrob("x axis title", gp=gpar(fontsize=22)))
Edit: perhaps the easiest way to control margins is to wrap the grob in a 3x3 gtable,
titleGrob <- function(label, margin=unit(c(b=5, l=0, t=2, r=0), "line"), ..., debug=FALSE){
library(gtable)
tg <- textGrob(label, ...)
w <- grobWidth(tg)
h <- grobHeight(tg)
g <- gtable("title",
widths = unit.c(margin[2], w, margin[4]),
heights = unit.c(margin[3], h, margin[1]), respect = FALSE)
if(debug){
rg <- rectGrob()
pos <- expand.grid(x=1:3, y=1:3)[-5,]
g <- gtable_add_grob(g, list(rg, rg, rg, rg, rg, rg, rg, rg), t=pos$y, l=pos$x)
}
gtable_add_grob(g, tg, t=2, l = 2)
}
grid.arrange(g1,g.mid,g2,ncol=3,widths=c(4/9,1/9,4/9),
bottom=titleGrob("x axis title", gp=gpar(fontsize=22), debug=FALSE))
With the code found at https://gist.github.com/low-decarie/5886616
A dual dendodogram tile plot can be produced:
dual_dendogram_tile_plot(as.matrix(USArrests), main="USA Arrests")
The problem: Align the vertical dendogram with the tile plot area.
(and/or improve the alignment of the horizontal dendogram)
This question relates to:
left align two graph edges (ggplot)
Specifying ggplot2 panel width
Plot correlation matrix into a graph
Here's an example to align more basic grobs,
library(ggplot2)
library(grid)
library(gtable)
p <- qplot(1,1)
g <- ggplotGrob(p)
panel_id <- g$layout[g$layout$name == "panel",c("t","l")]
g <- gtable_add_cols(g, unit(1,"cm"))
g <- gtable_add_grob(g, rectGrob(gp=gpar(fill="red")),
t = panel_id$t, l = ncol(g))
g <- gtable_add_rows(g, unit(1,"in"), 0)
g <- gtable_add_grob(g, rectGrob(gp=gpar(fill="blue")),
t = 1, l = panel_id$l)
grid.newpage()
grid.draw(g)
and with your grobs
The answer from #baptiste helped me get a better understanding of the gtable structure and how to modify it. Below I post just my modified variant as code snippet for (my own) reuse.
It is using find_panel() to get the panel extent, and pipes %>% the modifications directly into grid.draw. The piping considerably simplified playing around with the gtable_* functions, as it allows to easily uncomment single lines and check the effect on the final plot.
library(ggplot2)
library(grid)
library(gtable)
library(dplyr)
p <- ggplot(tribble(~x,~y,~a,~b,
1, 1, "a1","b1",
1, 1, "a2","b1",
1, 1, "a2","b2"),
aes(x=x,y=y)) +
geom_point() +
facet_grid(vars(a),vars(b))
g <- ggplotGrob(p)
panels_extent <- g %>% find_panel()
g %>%
# Add red box to the very right, by appending a column and then filling it
gtable_add_cols(widths = unit(1,"cm"), pos = -1) %>%
gtable_add_grob(rectGrob(gp=gpar(fill="red")),
t = panels_extent$t, b = panels_extent$b,
l = -1, r = -1) %>%
# Add green box to the top, by prepending a row and then filling it
# Note the green box extends horizontally over the first panel as well
# as the space in between.
gtable_add_rows(heights = unit(1,"cm"), pos = 0) %>%
gtable_add_grob(rectGrob(gp=gpar(fill="green")),
t = 1, b = 1,
l = panels_extent$l, r = panels_extent$l+1) %>%
{grid.newpage();grid.draw(.)}
Stagger axis labels, new feature in ggplot2
I'm new at R and following the answer of #Sandy Muspratt to #spindoctor is a bit daunting. The answer works great for the y-axis labels. I tried some editing. For example I changed:
index <- which(g$layout$name == "axis-l") # Which grob
to:
index <- which(g$layout$name == "axis-b") # Which grob
But the x-axis labels remained as they were, unstaggered. Could you please indicate how to modify the code so that it works for the x-axis labels as well?
# Get the grob
g <- ggplotGrob(out.plot)
# Get the y axis
index <- which(g$layout$name == "axis-l") # Which grob
yaxis <- g$grobs[[index]]
# Get the ticks (labels and marks)
ticks <- yaxis$children[[2]]
# Get the labels
ticksL <- ticks$grobs[[1]]
# Make the edit
ticksL$children[[1]]$x <- rep(unit.c(unit(c(1,0,-1),"npc")), 27)
# Put the edited labels back into the plot
ticks$grobs[[1]] <- ticksL
yaxis$children[[2]] <- ticks
g$grobs[[index]] <- yaxis
# Make the relevant column a little wider
g$widths[3] <- unit(2.5, "cm")
# Draw the plot
grid.newpage()
grid.draw(g)
The output of the TableGrob is as follows:
>g
TableGrob (6 x 5) "layout": 8 grobs
z cells name grob
1 0 (1-6,1-5) background rect[plot.background..rect.507]
2 3 (3-3,3-3) axis-l absoluteGrob[GRID.absoluteGrob.498]
3 1 (4-4,3-3) spacer zeroGrob[NULL]
4 2 (3-3,4-4) panel gTree[GRID.gTree.484]
5 4 (4-4,4-4) axis-b absoluteGrob[GRID.absoluteGrob.491]
6 5 (5-5,4-4) xlab titleGrob[axis.title.x..titleGrob.501]
7 6 (3-3,2-2) ylab titleGrob[axis.title.y..titleGrob.504]
8 7 (2-2,4-4) title zeroGrob[plot.title..zeroGrob.505]
I tried identifying relevant x,y-axis values but navigating this structure is a bit foreign to me. Any suggestions or comments or resources to avoid time wasted guessing are greatly appreciated.
You just need to change height not width in this case. Also in editGrob you need to pass the y slot not the x since you are changing the coordinates wrt the y-axis not left to right on the x.
I kept everything the same but commented out the things I changed and put my changes directly under.
# Libraries
library(ggplot2)
library(gtable)
library(grid)
library(stringi)
# fake data
set.seed(12345)
var <- stri_rand_strings(81, 4, pattern = '[HrhEgeIdiFtf]')
var1 <- rnorm(81, mean = 175, sd = 75)
out <- data.frame(var, var1)
out$var <- factor(out$var, levels = out$var[order(out$var1, decreasing = FALSE)])
# Plot
# out.plot <- ggplot(out, aes(x = var, y = var1)) + geom_point() + coord_flip()
out.plot <- ggplot(out, aes(x = var1, y = var)) + geom_point() + coord_flip()
# Get the ggplot grob
g = ggplotGrob(out.plot)
# Get a hierarchical list of component grobs
grid.ls(grid.force(g))
# make the relevant column a little wider
# g$widths[3] = unit(2.5, "cm")
g$heights[4] = unit(1, "cm")
# The edit
g = editGrob(grid.force(g),
gPath("axis-b", "axis", "axis", "GRID.text"),
# x = unit(c(-1, 0, 1), "npc"),
y = unit(c(1, 0, -1), "npc"), ## or c(-1,0,1) etc to change order
grep = TRUE)
# Draw the plot
grid.newpage()
grid.draw(g)
I have created a graph in ggplot2 that looks like this:
depth = c(1.6,2.6,3.6, 4.6,5.6,6.6,7.6,8.6)
ri <- c(0.790143779,1.485888068,2.682375391,1.728120227,0.948414515,71.43308158,4.416120653,0.125458801)
df = data.frame(depth,ri)
library(ggplot2)
m <- qplot(ri, depth, data=df)
m +
scale_x_log10("Richardson Number",breaks = c(0.1,0.25,0.5,1,5,10, 50)) +
scale_y_reverse("Depth (m)")
This is the output:
What I am trying to do is have the x-axis along the top, and also include a geom_line like the one I have added here (manually in Paint).
I understand that it is difficult to get ggplot2 to move the axes around, and I have tried to reproduce this graph in ggvis but I am unable to get the log10 scale I need. Is there any way I can use R to create the graph I am aiming for?
Updated solution
As of ggplot 2.2.0, axes can be drawn on the top of the panel (and/or on the right of the panel)
library(ggplot2)
depth = c(1.6,2.6,3.6, 4.6,5.6,6.6,7.6,8.6)
ri <- c(0.790143779,1.485888068,2.682375391,1.728120227,0.948414515,71.43308158,4.416120653,0.125458801)
df = data.frame(depth,ri)
m <- qplot(ri, depth, data=df) +
scale_x_log10("Richardson Number",breaks = c(0.1,0.25,0.5,1,5,10, 50),
position = "top") +
scale_y_reverse("Depth (m)")+
geom_path()
Original solution The original, after a little updating to ggplot version2.2.0.
Axes can be moved around using gtable functions. Adapting code from #Walter's answer here, the basic idea is to: get the axis (the axis text and the tick marks); reverse the axis text and tick marks; add a new row to the gtable layout immediately above the plot panel; insert the modified axis into the new row.
library(ggplot2)
library(gtable)
library(grid)
depth = c(1.6,2.6,3.6, 4.6,5.6,6.6,7.6,8.6)
ri <- c(0.790143779,1.485888068,2.682375391,1.728120227,0.948414515,71.43308158,4.416120653,0.125458801)
df = data.frame(depth,ri)
m <- qplot(ri, depth, data=df) +
scale_x_log10("Richardson Number",breaks = c(0.1,0.25,0.5,1,5,10, 50)) +
scale_y_reverse("Depth (m)")+
geom_path()
# Get ggplot grob
g1 <- ggplotGrob(m)
## Get the position of the plot panel in g1
pp <- c(subset(g1$layout, name == "panel", se = t:r))
# Title grobs have margins.
# The margins need to be swapped.
# Function to swap margins -
# taken from the cowplot package:
# https://github.com/wilkelab/cowplot/blob/master/R/switch_axis.R
vinvert_title_grob <- function(grob) {
heights <- grob$heights
grob$heights[1] <- heights[3]
grob$heights[3] <- heights[1]
grob$vp[[1]]$layout$heights[1] <- heights[3]
grob$vp[[1]]$layout$heights[3] <- heights[1]
grob$children[[1]]$hjust <- 1 - grob$children[[1]]$hjust
grob$children[[1]]$vjust <- 1 - grob$children[[1]]$vjust
grob$children[[1]]$y <- unit(1, "npc") - grob$children[[1]]$y
grob
}
# Get xlab and swap margins
index <- which(g1$layout$name == "xlab-b")
xlab <- g1$grobs[[index]]
xlab <- vinvert_title_grob(xlab)
# Put xlab at the top of g1
g1 <- gtable_add_rows(g1, g1$heights[g1$layout[index, ]$t], pp$t-1)
g1 <- gtable_add_grob(g1, xlab, pp$t, pp$l, pp$t, pp$r, clip = "off", name="topxlab")
# Get x axis (axis line, tick marks and tick mark labels)
index <- which(g1$layout$name == "axis-b")
xaxis <- g1$grobs[[index]]
# Swap axis ticks and tick mark labels
ticks <- xaxis$children[[2]]
ticks$heights <- rev(ticks$heights)
ticks$grobs <- rev(ticks$grobs)
# Move tick marks
# Get tick mark length
plot_theme <- function(p) {
plyr::defaults(p$theme, theme_get())
}
tml <- plot_theme(m)$axis.ticks.length # Tick mark length
ticks$grobs[[2]]$y <- ticks$grobs[[2]]$y - unit(1, "npc") + tml
# Swap tick mark labels' margins and justifications
ticks$grobs[[1]] <- vinvert_title_grob(ticks$grobs[[1]])
# Put ticks and tick mark labels back into xaxis
xaxis$children[[2]] <- ticks
# Add axis to top of g1
g1 <- gtable_add_rows(g1, g1$heights[g1$layout[index, ]$t], pp$t)
g1 <- gtable_add_grob(g1, xaxis, pp$t+1, pp$l, pp$t+1, pp$r, clip = "off", name = "axis-t")
# Remove original x axis and xlab
g1 = g1[-c(9,10), ]
# Draw it
grid.newpage()
grid.draw(g1)