Is it possible to make a theme that changes the shape of the legend icon/symbol that is used to identify the different color in a plot?
What I mean is that if you make a line plot:
library(ggplot2)
ggplot(iris, aes(x=Sepal.Length, y = Sepal.Width, color=Species)) +
geom_line()
You will see 'thin' lines identifying each different group. I don't prefer this.
What I would like is to always make my color identifiers nice solid squares/blocks like this:
ggplot(iris, aes(x=Sepal.Length, y = Sepal.Width, color=Species, fill=Species)) +
geom_bar(stat='identity')
I'm wondering if there is an option that I can add to a custom ggplot theme to enable this by default? I have some custom made themes for plots and I'd love to be able to add this in, as sometimes its really hard to make out the very faint lines or dots. A solid square is much more noticeable.
I don't think theme() allows you to override specific aesthetic parameters in the legend key symbols. It deals with the legend key's size, background, etc., while draw_key_XXX covers the key symbols' appearance.
However, if the concern is about re-typing, you can add guides(...) to your customized theme:
theme_customized <- function(...){
list(guides(color = guide_legend(override.aes = list(size = 10))),
# optional: the base theme you modify from
theme_light(),
# customized theme options for your theme
# (I'm using axis.text / panel.grid for illustration)
theme(axis.text = element_text(size = 15),
panel.grid.minor.y = element_blank()),
# any theme options that you may want to set
# on an ad hoc basis
theme(...))
}
Using the customized theme:
gridExtra::grid.arrange(
# with ggplot's default theme_grey
ggplot(iris, aes(x=Sepal.Length, y = Sepal.Width, color=Species)) +
geom_line(),
# customized theme with default options
ggplot(iris, aes(x=Sepal.Length, y = Sepal.Width, color=Species)) +
geom_line() +
theme_customized(),
# customized theme with additional adhoc specifications
ggplot(iris, aes(x=Sepal.Length, y = Sepal.Width, color=Species)) +
geom_line() +
theme_customized(axis.title = element_text(color = "blue"),
panel.background = element_rect(fill = "darkgrey")),
ncol = 1
)
Related
I am trying to combine multiple partial legends (i.e. for two different features of one plot) legends into one box. My real-life data is a plot made with geom_sf() with fill colors for polygons and a specific border highlighted made with geom_sf()with a line. Since the line is only one feature, it makes no sense to do a separate legend.
REPREX
data(iris)
ggplot(iris)+theme_classic()+
geom_point(aes(x=Petal.Length, y=Sepal.Length, color=Species, size=Sepal.Width))+
theme(legend.position=c(0.1,0.75),legend.background=element_rect(fill="white", color="black"), legend.spacing.y=unit(0,"cm"))
### Why doesn't ggplot draw a rectangle around the entire legend?
I managed to reduce the legend spacing, which already makes the plot cleaner, but ...
How can I forego the two separate legend boxes altogether and combine them in one box so that my plot looks cleaner?
I think you're looking for legend.box.background instead of legend.background:
ggplot(iris) +
theme_classic() +
geom_point(aes(x = Petal.Length, y = Sepal.Length,
color = Species, size = Sepal.Width)) +
theme(legend.position = c(0.1, 0.75),
legend.box.background = element_rect(fill = "white", color = "black"),
legend.spacing.y = unit(0,"cm"))
Following is the sample code for the random chart:
library(ggplot2)
ggplot(mtcars,aes(x=mpg,y=wt,color=factor(vs)))+geom_line()+theme(legend.position="top",legend.direction = "horizontal",legend.title=element_blank(),legend.key = element_blank())
and following is the output from above code:
My requirement: Instead of having two lines denoted by 0 and 1 as legend elements, I only want legend texts "0" and "1" colored as "red" and "skyblue" (same as color of lines) and remove the lines. Is this possible?
I've written a string legend guide in the ggh4x package, that you might find useful.
Example:
library(ggplot2)
library(ggh4x)
ggplot(mtcars,aes(x=mpg,y=wt,color=factor(vs))) +
guides(colour = "stringlegend") +
geom_line() +
theme(legend.position="top",
legend.direction = "horizontal",
legend.title=element_blank(),
legend.key = element_blank())
Created on 2021-03-19 by the reprex package (v0.3.0)
You can use the ggtext package which has element_markdown() function that allows text formatting
The ggtext package provides simple Markdown and HTML rendering for ggplot2. Under the hood, the package uses the gridtext package for the actual rendering, and consequently it is limited to the feature set provided by gridtext.
Support is provided for Markdown both in theme elements (plot titles, subtitles, captions, axis labels, legends, etc.) and in geoms (similar to geom_text()). In both cases, there are two alternatives, one for creating simple text labels and one for creating text boxes with word wrapping.
library(RColorBrewer)
library(ggplot2)
library(ggtext)
my_col <- RColorBrewer::brewer.pal(unique(mtcars$vs), 'Dark2')
p <- ggplot(mtcars, aes(x = mpg, y = wt, color = factor(vs))) +
geom_line() +
theme_bw(base_size = 16) +
theme(
legend.position = "top", legend.direction = "horizontal",
legend.title = element_blank(), legend.key = element_blank()
)
p +
scale_color_manual(
labels = paste(
"<span style='color:",
my_col,
"'>",
unique(mtcars$vs),
"</span>"
),
values = my_col
) +
theme(legend.text = element_markdown(size = 16)) +
guides(color = guide_legend(override.aes = list(linetype = c(NA, NA))))
Created on 2021-03-18 by the reprex package (v1.0.0)
teunbrands custom guide is awesome. Just for completeness sake, here the fake legend option.
Technically, a legend is a very specific type of plot annotation. One of the most frequently asked questions is "how do I annotate outside the plot area" - which you can almost always answer with "create a second plot and stitch it to the main plot". Same here.
It requires a couple of tricks, in particular use of geom_blank for scaling, and then correct use of theme elements, which you can either pass to each plot or globally (with &)
The disadvantage of this approach is that you need to fiddle around with the coordinates to get the position right. but this should not be too much of an issue, except if you need to automate this.
P.S. the colors you are showing are neither "steelblue" nor "red" ... ;)
library(ggplot2)
library(patchwork)
p_main <-
ggplot(mtcars, aes(x = mpg, y = wt, color = factor(vs))) +
geom_line()
# semi - manual - chose x and y depending on the position relative to the other plot
# for labels: I used as.character just to be very explicit
df_legend <- data.frame(x = c(22,24), y = 3, labels = as.character(0:1))
p_legend <-
ggplot(mtcars, aes(x = mpg, y = wt)) +
geom_blank() +
geom_text(data = df_legend, aes(x, y, label = labels, color = labels)) +
theme_void()
p_legend / p_main &
plot_layout(heights = c(1, 10)) &
theme(legend.position = "none")
Created on 2021-03-20 by the reprex package (v1.0.0)
Is there an elegant way in ggplot2 to make geom_text/geom_label inherit theme specifications like a base_family?
Or asked the other way round: Can I specify a theme that also applies to geom_text/geom_label?
Example:
I want text/labels to look exactly like the axis.text as specified in the theme...
Obviously I could add the specifications manually as optional arguments to geom_text, but I want it to inherit the specifications "automatically"...
library("ggplot2")
ggplot(mtcars, aes(x = mpg,
y = hp,
label = row.names(mtcars))) +
geom_point() +
geom_text() +
theme_minimal(base_family = "Courier")
Addition: A solution that works with ggrepel::geom_text_repel/geom_label_repel as well would be perfect...
You can
Setting Overall font
Firstly, depending on the system you will need to check which fonts are available. As I am running on Windows I am using the following:
install.packages("extrafont")
library(extrafont)
windowsFonts() # check which fonts are available
The theme_set function lets you specify the overall themes for ggplot. So therefore theme_set(theme_minimal(base_family = "Times New Roman")) lets you define the fonts for the plot.
Make Labels Inherit Font
To make the labels inherit this text, there are two things we need to use:
update_geom_defaults lets you update the geometry object styling for future plots in ggplot: http://ggplot2.tidyverse.org/reference/update_defaults.html
theme_get()$text$family extracts the font of the current global ggplot theme.
By combining these two, the label styles can be updated as follows:
# Change the settings
update_geom_defaults("text", list(colour = "grey20", family = theme_get()$text$family))
update_geom_defaults("text_repel", list(colour = "grey20", family = theme_get()$text$family))
Results
theme_set(theme_minimal(base_family = "Times New Roman"))
# Change the settings
update_geom_defaults("text", list(colour = "grey20", family = theme_get()$text$family))
# Basic Plot
ggplot(mtcars, aes(x = mpg,
y = hp,
label = row.names(mtcars))) +
geom_point() +
geom_text()
# works with ggrepel
update_geom_defaults("text_repel", list(colour = "grey20", family = theme_get()$text$family))
library(ggrepel)
ggplot(mtcars, aes(x = mpg,
y = hp,
label = row.names(mtcars))) +
geom_point() +
geom_text_repel()
I'm having issues with changing the size of the title, X-Y labels, X-Y axis text for my ggplot2. I'm using ggsave to save the plot as a jpg.
p <- ggplot()
p + theme(axis.title = element_text(size=30), axis.text.y = element_text(size=30),
axis.text.x = element_text(size=30))
but changing the sizes of these texts doesn't change anything on the plot. Would anyone know how to properly change the text sizes?
So I fixed the issue I was having so the changes I make to theme are not affecting the plot (I've tested with changing text color), however the size of the axis text still does not change.
p <- ggplot(d[d$user==i,], aes(x=date, y=url, group=user, label=user)) + geom_line() + geom_point() +
labs(list(title=i, x="Date and Time", y = "URL")) + # Plot labels
axis.POSIXct(1, at=seq(daterange[1], daterange[2], by="hour")) # Set x axis range to first and last date-time in data
p <- p + modifiedtheme
ggsave(plot = p, filename = "sample.jpg", height=2, width=6)
Here is a minimal, fully reproducible version of the problem (or lack of any problem, as comments have pointed out). Your own posted code appears to be correct, but maybe this example will help you solve whatever the real problem is:
library(ggplot2)
p1 = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, colour=Species)) +
geom_point()
p2 = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, colour=Species)) +
geom_point() +
theme(axis.title=element_text(size=30))
ggsave("figure1.jpg", plot=p1, height=3, width=4, units="in", dpi=150)
ggsave("figure2.jpg", plot=p2, height=3, width=4, units="in", dpi=150)
I tried to change the font to 10 for the labels of my bar plot in ggplot2 by doing something like this:
ggplot(data=file,aes(x=V1,y=V3,fill=V2)) +
geom_bar(stat="identity",position="dodge",colour="white") +
geom_text(aes(label=V2),position=position_dodge(width=0.9),
hjust=1.5,colour="white") +
theme_bw()+theme(element_text(size=10))
ggsave(filename="barplot.pdf",width=4,height=4)
but the resulting image has super big font size for the bar plot labels.
Then I thought of modifying in geom_text() with this:
geom_text(size=10,aes(label=V2),position=position_dodge(width=0.9),
hjust=1.5,colour="white")
The label font is even bigger...
I can change the size within geom_text to something like 3 and now it looks like font 10, similar to the axis labels.
I'm wondering what's going on? Does theme(text=element_text(size=10)) doesn't apply to labels?
And why size of 10 in geom_text() is different from that in theme(text=element_text()) ?
Here are a few options for changing text / label sizes
library(ggplot2)
# Example data using mtcars
a <- aggregate(mpg ~ vs + am , mtcars, function(i) round(mean(i)))
p <- ggplot(mtcars, aes(factor(vs), y=mpg, fill=factor(am))) +
geom_bar(stat="identity",position="dodge") +
geom_text(data = a, aes(label = mpg),
position = position_dodge(width=0.9), size=20)
The size in the geom_text changes the size of the geom_text labels.
p <- p + theme(axis.text = element_text(size = 15)) # changes axis labels
p <- p + theme(axis.title = element_text(size = 25)) # change axis titles
p <- p + theme(text = element_text(size = 10)) # this will change all text size
# (except geom_text)
For this And why size of 10 in geom_text() is different from that in theme(text=element_text()) ?
Yes, they are different. I did a quick manual check and they appear to be in the ratio of ~ (14/5) for geom_text sizes to theme sizes.
So a horrible fix for uniform sizes is to scale by this ratio
geom.text.size = 7
theme.size = (14/5) * geom.text.size
ggplot(mtcars, aes(factor(vs), y=mpg, fill=factor(am))) +
geom_bar(stat="identity",position="dodge") +
geom_text(data = a, aes(label = mpg),
position = position_dodge(width=0.9), size=geom.text.size) +
theme(axis.text = element_text(size = theme.size, colour="black"))
This of course doesn't explain why? and is a pita (and i assume there is a more sensible way to do this)
Take a look at the relevant entry in ggplot2's customization FAQ: https://ggplot2.tidyverse.org/articles/faq-customising.html#what-is-the-default-size-of-geom_text-and-how-can-i-change-the-font-size-of-geom_text
You can modify the default size of geom_text() by placing update_geom_defaults("text", list(size = X), where X is your choice of new size, at the beginning of your script.