I am creating a grob from a ggplot using ggplotGrob and then adding it as a background layer in a complex ggplot construction using annotation_custom since the performance is much improved in a facet_wrap plot with a large dataset. However I am unable to align the underlying grob correctly with the ggplot.
This simple example shows the issue I am trying to solve.
library(ggplot2)
p <- ggplot(mtcars, aes(wt, mpg)) +
geom_point(color = "red")
ggplot(mtcars, aes(wt, mpg)) +
annotation_custom(grob = ggplotGrob(p)) +
geom_point()
I want the plot with red points to be perfectly underneath the plot with black points.
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)
I want to have a horizontal bar chart, but without legend. When I run the script below without coord_flip(), no legend shows. But when I run it with the coord_flip() argument, the legend appaers. I think this might be a bug. Does anyone know a different code with which I can skip the legend?
df <- aggregate(formula=mpg~cyl, data=mtcars, FUN=sum)
fig <- ggplot(df, aes(x=cyl, y=mpg, fill=cyl, label=mpg)) +
geom_col() +
coord_flip() +
theme(legend.position="none") +
theme_bw()
fig
I am trying to display the xvar median as a dotted line & show it in the legend. Here's my code:
require(ggplot2)
require(scales)
medians_mtcars <- data.frame("wt.median"=median(mtcars$wt))
# legend shows but linetype is wrong (solid)
p <- ggplot(mtcars, aes(wt, mpg))
p <- p + geom_point()
p <- p + geom_vline(aes(xintercept=wt.median, linetype="dotted"),
data=medians_mtcars, show_guide=TRUE)
p
I also tried:
# linetype is correct but legend does not show
p <- ggplot(mtcars, aes(wt, mpg))
p <- p + geom_point()
p <- p + geom_vline(aes(xintercept=wt.median),
data=medians_mtcars, show_guide=TRUE, linetype="dotted")
p
Would have liked to post the plot images, but haven't crossed the reputation threshold yet.
There were 2 other posts on this forum that comes close to this topic but does not offer a solution to this problem:
Add vline to existing plot and have it appear in ggplot2 legend?
;
Incorrect linetype in legend, ggplot2 in R
I am using ggplot2 version 1.0.0
What am I doing wrong ?
Thanks in advance
If you need to show linetype in legend and also change it then inside aes() you can just write name for that linetype (as you have only one line) and then change linetype with scale_linetype_manual().
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
geom_vline(aes(xintercept=wt.median, linetype="media"),
data=medians_mtcars, show_guide=TRUE)+
scale_linetype_manual(values="dotted")
If you really want to type linetype in aes() and also get correct legend then you should use scale_linetype_identity() with argument guide="legend".
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
geom_vline(aes(xintercept=wt.median, linetype="dotted"),
data=medians_mtcars,show_guide=TRUE)+
scale_linetype_identity(guide="legend")
Ok, I'm stumped on a home-brew ggplot.
What I would like to do is have a three row, one column faceted plot with a different y-axis label for each facet. The units of the y-axis are all the same. This would be the most convenient, but googling tells me it may not be possible.
Alternatively, I found this solution using grid.arrange, which seems like it will work. However, I want to keep a legend only for one plot and remove it from the other two, but maintain the spacing as if it were still there so that everything lines up nice. Someone had the same problem a few years ago, but the suggested solution is depreciated and I can't sort out how to make it work in modern ggplot.
Any help is appreciated! Using facets would be easiest!
Edited to add copy of plot after using user20560's gridArrange solution below. Very nearly there, just would like to get back the box around the top and bottom facet panels!
I have assumed (possibly wrongly) that you are wanting to add separate y-axis titles rather than axis labels. [If it is the labels you want different you can use the scales argument in facet_grid]
There will be a ggplot way to do this but here are a couple of ways you could tweak the grobs yourself.
So using mtcars dataset as example
library(ggplot2)
library(grid)
library(gridExtra)
One way
p <- ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() +
facet_grid(gear ~ .)
# change the y axis labels manually
g <- ggplotGrob(p)
yax <- which(g$layout$name=="ylab")
# define y-axis labels
g[["grobs"]][[yax]]$label <- c("aa","bb", "cc")
# position of labels (ive just manually specified)
g[["grobs"]][[yax]]$y <- grid::unit(seq(0.15, 0.85, length=3),"npc")
grid::grid.draw(g)
Or using grid.arrange
# Create a plot for each level of grouping variable and y-axis label
p1 <- ggplot(mtcars[mtcars$gear==3, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="aa") + theme_bw()
p2 <- ggplot(mtcars[mtcars$gear==4, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="bb") + theme_bw()
p3 <- ggplot(mtcars[mtcars$gear==5, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="cc") + theme_bw()
# remove legends from two of the plots
g1 <- ggplotGrob(p1)
g1[["grobs"]][[which(g1$layout$name=="guide-box")]][["grobs"]] <- NULL
g3 <- ggplotGrob(p3)
g3[["grobs"]][[which(g3$layout$name=="guide-box")]][["grobs"]] <- NULL
gridExtra::grid.arrange(g1,p2,g3)
If it is the axis titles you want to add I should ask why you want a different titles - can the facet strip text not do?
Following the comments by Axeman and aosmith (thank you), here's a way to do this using the facet labels using ggplot2 version 2.2.0
library(ggplot2) # From sessionInfo(): ggplot2_2.2.0
ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() +
facet_grid(gear ~ ., switch = 'y') +
theme( axis.title.y = element_blank(), # remove the default y-axis title, "wt"
strip.background = element_rect(fill = 'transparent'), # replace the strip backgrounds with transparent
strip.placement = 'outside', # put the facet strips on the outside
strip.text.y = element_text(angle=180)) # rotate the y-axis text (optional)
# (see ?ggplot2::theme for a list of theme elements (args to theme()))
I know this is an old post, but after finding it, I could not get #user20560's response to work.
I've edited #user20560's grid.extra approach as follows:
library(ggplot2)
library(gridExtra)
library(grid)
# Create a plot for each level of grouping variable and y-axis label
p1 <- ggplot(mtcars[mtcars$gear==3, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="aa") + theme_bw()
p2 <- ggplot(mtcars[mtcars$gear==4, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="bb") + theme_bw()
p3 <- ggplot(mtcars[mtcars$gear==5, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="cc") + theme_bw()
# get the legend as a grob
legend <- ggplotGrob(p1)
legend <- legend$grobs[[which(legend$layout$name=="guide-box")]]
lheight <- sum(legend$height)
lwidth <- sum(legend$width)
# remove the legend from all the plots
p1 <- p1 + theme(legend.position = 'none')
p2 <- p2 + theme(legend.position = 'none')
p3 <- p3 + theme(legend.position = 'none')
# force the layout to the right side
layoutMat <- matrix(c(1,2,3,4,4,4),ncol = 2)
grid.arrange(p1,p2,p3,legend, layout_matrix = layoutMat, ncol = 2,
widths = grid::unit.c(unit(1,'npc') - lwidth, lwidth))
This example is somewhat specific to this particular layout. There is a more general approach on the ggplot2 wiki.
I too had trouble getting the first approach in the answer of user20560 (above) to work. This is probably because the internals of ggplot2 have evolved, and there is no guarantee that these internals should stay the same. In any case, here is a version that currently works:
library(ggplot2) # From sessionInfo(): ggplot2_2.1.0
library(grid)
p <- ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() + facet_grid(gear ~ .)
g <- ggplotGrob(p)
yax <- which(g$layout$name == "ylab")
g[["grobs"]][[yax]]$children[[1]]$label <- c('fo','bar','foobar')
g[["grobs"]][[yax]]$children[[1]]$y <- grid::unit(seq(0.15, 0.85, length=3), "npc")
grid.draw(g)
Note that this is the approach that keeps the facets and does not repeat the x-axes.
Let's say that I would prefer geom_point to use circles (pch=1) instead of solid dots (pch=16) by default. You can change the shape of the markers by passing a shape argument to geom_point, e.g.
ggplot(diamonds, aes(depth, carat, colour=cut)) + geom_point(shape=1)
ggplot(diamonds, aes(depth, carat, colour=cut)) + geom_point(shape=16)
but I can't figure out how to change the default behaviour.
Geom (and stat) default can be updated directly:
update_geom_defaults("point", list(shape = 1))
ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point()
One way to do it (although I don't really like it) is to make your own geom_point function. E.g.
geom_point2 <- function(...) geom_point(shape = 1, ...)
Then just use as normal:
ggplot(diamonds, aes(depth, carat, colour=cut)) + geom_point2()
Or if you want you can override the function geom_point():
geom_point <- function(...) {
ggplot2::geom_point(shape = 1, ...)
}
This might be considered bad practice but it works. Then you don't have to change how you plot:
ggplot(diamonds, aes(depth, carat, colour=cut)) + geom_point()