I have a legend at the bottom of a graph in ggplot2, on two lines. I need to increase the horizontal space between items.
Currently the code is:
p + theme(legend.key = element_blank(),
legend.position = "bottom",
legend.title = element_blank(),
legend.direction = "horizontal") +
guides(linetype = guide_legend(ncol = 3,keywidth=4))
But the resulting items are too close:
Any suggestion?
A bit of a hack:
#dummy data
df <- data.frame(x=1:20,
y=runif(20),
g=rep(c("a","long1","looonger1","xx"),5))
#suffix with spaces, make them same length
df$g <- substring(paste0(df$g," "),1,15)
#plot as usual
ggplot(df,aes(x,y,linetype=g)) +
geom_line() +
theme(legend.key = element_blank(),
legend.position = "bottom",
legend.title = element_blank(),
legend.direction = "horizontal") +
guides(linetype = guide_legend(ncol = 3,keywidth=4))
Related
guides = "collect" does its job but it locates the legend in a way that it overlaps the plot. I would like it to be placed right in the middle of the empty bottom right corner, but it appears to be difficult since theme(legend.position = c(X,Y)) does not work with patchwork
Illustration of the issue:
This is the code I have for assembling 4 graphs I have using patchwork. Pretty sure there is a more elegant way to use theme() but I am quite new to patchwork and it worked for me so far, except for the legend positioning.
A similar issue was resolved here but it does not seem to help in my case.
#first panel
s_wpanels_final <- (dots & theme_bw() & theme(axis.title.x =
element_blank(), panel.grid.minor.y = element_blank())) +
#second panel
(g_box_tmax & theme_bw() & theme(axis.text.x=element_blank(),
axis.ticks.x=element_blank(), panel.grid.minor.y = element_blank(),
axis.text.y=element_blank(), axis.ticks.y=element_blank(),
axis.title.x = element_blank(), legend.position = "none")) +
#third panel
(g_box_t0 & theme_bw() & theme(axis.text.x=element_blank(),
axis.ticks.x=element_blank(), panel.grid.minor.y = element_blank(),
axis.text.y=element_blank(), axis.ticks.y=element_blank(),
axis.title.x = element_blank(), legend.position = "none")) +
#fourth panel
(tmax_box & theme_bw() & theme(axis.text.x=element_blank(),
axis.ticks.x=element_blank(), axis.text.y=element_blank(),
axis.ticks.y=element_blank(), axis.title.y = element_blank(),
legend.position = "none")) +
guide_area()+
plot_layout(ncol=3, guides = "collect", widths=c(6,1,1), heights=c(6,1)) &
theme(legend.direction = "vertical", legend.box = "horizontal")
There are two issues with your code. First using + to glue your plots together and setting ncol=3 will place the guide_area in the second column of the second row. To center the legend I would suggest to use the design argument to specify the layout of the plot. Second, while the plot panels will adjust to the space set via the height and width arguments and the size of your plotting device, the legend will not, i.e. if the legend will not fit into the space given it will overlap with the surrounding panels. To fix that I would suggest to increase the widths of the second and third columns and the height of the second row. But as I said this also depends on the size of the plotting device.
Using some fake example plot based on mtcars(see below) let's first reproduce your issue:
library(ggplot2)
library(patchwork)
list(
dots,
g_box_tmax,
g_box_t0,
tmax_box,
guide_area()
) |>
wrap_plots() +
plot_layout(guides = "collect", widths = c(6, 1, 1), heights = c(6, 1), ncol = 3) &
theme(legend.direction = "vertical", legend.box = "horizontal")
However, specifying the layout via the design argument and increasing the height of the second row as well as the widths of the second and third columns works fine and centers the legend in the guide area:
design <-
"
ABC
DEE
"
list(
dots,
g_box_tmax,
g_box_t0,
tmax_box,
guide_area()
) |>
wrap_plots() +
plot_layout(guides = "collect", widths = c(6, 1.5, 1.5), heights = c(6, 1.5), design = design) &
theme(legend.direction = "vertical", legend.box = "horizontal")
PLOTS
dots <- ggplot(mtcars, aes(mpg, hp, color = factor(cyl), size = qsec)) +
geom_point() +
theme_bw() +
theme(
axis.title.x = element_blank(),
panel.grid.minor.y = element_blank()
)
g_box_tmax <- g_box_t0 <- ggplot(mtcars, aes(factor(cyl), hp, fill = factor(cyl))) +
geom_boxplot() +
theme_bw() +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(), panel.grid.minor.y = element_blank(),
axis.text.y = element_blank(), axis.ticks.y = element_blank(),
axis.title.x = element_blank(), legend.position = "none"
)
tmax_box <- ggplot(mtcars, aes(mpg, factor(cyl), fill = factor(cyl))) +
geom_boxplot() +
theme_bw() +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(), axis.text.y = element_blank(),
axis.ticks.y = element_blank(), axis.title.y = element_blank(),
legend.position = "none"
)
What causes the legend box (which is too big for the plot dimension) to be positioned there, is probably some quite clever patchwork code, and is related to guide_area (therefore my question title edit).
The below is a slightly unsatisfactory, but effective hack to modify the position. It's a bit of a trial and error. Simply give a negative margin to the legend box to the right and it will "drag" the box accordingly.
I've removed all the legend.position = "none" from your plots as this is not necessary with guides = "collect"
library(ggplot2)
library(patchwork)
p1 <- ggplot(iris) + geom_point(aes(Sepal.Length, Sepal.Width, color = Species, size = Petal.Length))
p2 <- ggplot(iris) + geom_point(aes(Sepal.Length, Sepal.Width, color = Species, size = Petal.Length))
p3 <- ggplot(iris) + geom_point(aes(Sepal.Length, Sepal.Width, color = Species, size = Petal.Length))
p4 <- ggplot(iris) + geom_point(aes(Sepal.Length, Sepal.Width, color = Species, size = Petal.Length))
p1 + p2 + p3 + p4 +
guide_area()+
plot_layout(ncol=3, guides = "collect", widths=c(6,1,1), heights=c(6,1)) &
theme(legend.direction = "vertical", legend.box = "horizontal",
legend.box.margin = margin(r = -1, unit = "in"))
For the following sample df
df = data.frame(x = c(2,3,4),y = c(4,5,6),group.a= c("1","1","2"),group.b = c("a","b","b"))
I want to just add a horizontal line in-between the y axis facet grids and after browsing different posts here I have tried using the panel.border = element_rect() argument however that gives me all four borders (top, right, bottom, left)
ggplot(df,aes(x=x,y=y)) + facet_grid(group.a~group.b) + theme_minimal() +
theme(legend.position = "bottom",
legend.title = element_blank(),
legend.direction = "horizontal",
legend.margin = margin(-20,0,0,0),
panel.grid = element_blank(),
panel.border = element_rect(color = "black", fill = NA, size = .5)
axis.text.x = element_blank(),
axis.line.y = element_line(size = .5),
axis.line.x = element_line(size = .5),
strip.placement = "outside")
Is there a way to just have the bottom and left border of the panel borders? Thanks!
There aren't any theme elements that fit the bill here; you would need custom annotations. Fortunately, annotation_custom applies to each panel, so this can be done with a couple of lines of code
library(ggplot2)
df = data.frame(x = c(2,3,4),y = c(4,5,6),
group.a= c("1","1","2"),group.b = c("a","b","b"))
ggplot(df,aes(x=x,y=y)) +
facet_grid(group.a~group.b) +
geom_point(col = "white") +
theme_minimal() +
annotation_custom(grid::linesGrob(y = c(0, 0), gp = grid::gpar(lwd = 3))) +
annotation_custom(grid::linesGrob(x = c(0, 0), gp = grid::gpar(lwd = 3))) +
theme(panel.grid = element_blank(),
strip.placement = "outside",
axis.text.x = element_blank())
I have below ggplot
library(ggplot2)
library(grid)
theme_set(theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.position=c(0,1),
legend.justification = c(0,1),
legend.box.margin = margin(5, l = 5, unit = 'mm'),
legend.box = 'horizontal'
))
ggplot(diamonds,aes(x,y,color=z))+
geom_point()+
scale_colour_gradient2('Time [min]',
low='lightgray',
mid='red3',
high='red4',
midpoint=15)
With this I am getting below plot,
However I wanted to horizontally align the colour definition in the legend, while colour title (i.e. Time [min]) should be on top of the colour definition. Is there any way to achieve this
By adding legend.direction = 'horizontal' and some guides,
ggplot(diamonds,aes(x,y,color=z))+
geom_point()+
scale_colour_gradient2('Time [min]',
low='lightgray',
mid='red3',
high='red4',
midpoint=15) +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.position=c(0,1),
legend.justification = c(0,1),
legend.box.margin = margin(5, l = 5, unit = 'mm'),
legend.box = 'horizontal',
legend.direction = 'horizontal'
) +
guides(colour = guide_colourbar(title.position="top", title.hjust = 0.5),
size = guide_legend(title.position="top", title.hjust = 0.5))
I have many subsets of data that I would like to apply functions to and then plot. I have tailored my ggplot code to be what I'd like, aesthetically, and don't want to fill my notebook with these same long lists of specifications for every independent plot. Is it possible to take something like the following blocks of ggplot text, where
p = [some generic ggplot]
p = p + guides(colour = guide_legend(title = "year_collected", ncol = 2, keywidth = 1, keyheight = 1))
p = p + theme_minimal() +
theme(panel.grid.major = element_blank(),
axis.title.y=element_text(margin=margin(0,20,0,0)),
text=element_text(size=8, color="black",family="Arial"),
panel.grid.minor = element_blank(),
#axis.ticks = element_blank(),
legend.key.size = unit(.1, "in"),
legend.spacing.x = unit(.01,"in"),
legend.position = "bottom",
plot.title = element_text(hjust = 0.5))
p$layers <- p$layers[-1]
and use the R function options to capture all of these specifications and additions?
Thanks!
Alternatively, you can feed any lines of ggplot code into a list (with items comma separated) and add that:
my_theme_lines <- list(theme_minimal(),
theme(panel.grid.major = element_blank(),
axis.title.y=element_text(margin=margin(0,20,0,0)),
text=element_text(size=8, color="black",family="Arial"),
panel.grid.minor = element_blank(),
#axis.ticks = element_blank(),
legend.key.size = unit(.1, "in"),
legend.spacing.x = unit(.01,"in"),
legend.position = "bottom",
plot.title = element_text(hjust = 0.5)))
ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
my_theme_lines
It's easy to define a custom ggplot theme, just use this template:
theme_custom <- function(){
#font <- "Georgia" # assign font family up front
theme_minimal() %+replace% #replace elements we want to change
# custom elements go here
theme(
)
}
Now with the question's settings.
theme_user3353556 <- function(){
font <- "Arial" # assign font family up front
theme_minimal() %+replace% #replace elements we want to change
# custom elements go here
theme(
panel.grid.major = element_blank(),
text = element_text(size = 8, color = "black"),
axis.title.y = element_text(margin = margin(0, 20, 0, 0)),
panel.grid.minor = element_blank(),
#axis.ticks = element_blank(),
legend.key.size = unit(0.1, "in"),
legend.spacing.x = unit(0.01,"in"),
legend.position = "bottom",
plot.title = element_text(hjust = 0.5)
)
}
ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
theme_user3353556()
I am trying to make a plot with no information beyond the data. No axes; no grid; no title; just the plot.
But I keep getting extra margins and padding that I can't remove.
library(ggplot2)
library(grid)
theme_bare <- theme(
axis.line = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
#axis.ticks.length = unit(0, "lines"), # Error
axis.ticks.margin = unit(c(0,0,0,0), "lines"),
legend.position = "none",
panel.background = element_rect(fill = "gray"),
panel.border = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.margin = unit(c(0,0,0,0), "lines"),
plot.background = element_rect(fill = "blue"),
plot.margin = unit(c(0,0,0,0), "lines")
)
ggplot() +
geom_area (data=economics, aes(x = date, y = unemploy), linetype=0) +
theme_bare
Produces this image:
What I want is this:
I can't figure out how to get rid of the blue and make the dark gray flush with the edges.
Could any one offer some advice?
Here is the way to plot only the panel region:
p <- ggplot() + geom_area (data=economics, aes(x = date, y = unemploy), linetype=0) +
scale_x_date(expand = c(0,0)) + scale_y_continuous(expand = c(0,0)) +
theme(line = element_blank(),
text = element_blank(),
title = element_blank())
gt <- ggplot_gtable(ggplot_build(p))
ge <- subset(gt$layout, name == "panel")
grid.draw(gt[ge$t:ge$b, ge$l:ge$r])
From ggplot2_2.0.0 you can use theme_void:
ggplot() +
geom_area(data = economics, aes(x = date, y = unemploy), linetype = 0) +
theme_void()
try
last_plot() + theme(axis.ticks.length = unit(0.001, "mm")) + labs(x=NULL, y=NULL)
you may want to file a bug for the 0 tick length.
If you just want to remove the grid in theme_bw(), you can use:
+ theme_bw() + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())