ggplot geom_text font size control - r

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.

Related

Can you vary text size within the same ggplot2 axis?

I'd like to use ggplot2 to make a plot where the axis text size varies between labels- for example, larger font every five ticks with smaller font for the intervening ticks. I looked at using minor_breaks in scale_x_continuous, but I couldn't find a way to label the minor breaks.
The best I've got to work so far is a modification from this answer where I use bquote to pass an expression for the axis labels:
label_span <- 1:40
ShrinkIf <- Vectorize(function(val) {
if (val %% 5 == 0) return(as.character(val))
return(bquote(scriptstyle(.(as.character(val)))))
})
x_labels <- ShrinkIf(label_span)
x_labels <- purrr::invoke(expression, x_labels)
ggplot(mtcars, aes(x = mpg , y = hp)) +
geom_point() +
scale_x_continuous(breaks = label_span, labels = x_labels)
Is there a better way to go about this, or maybe a way with a little more control of the label size (or even font choice / text decoration, etc)? Thanks in advance for your help!
You can create a vector of text sizes and add it using element_text()
library(ggplot2)
ggplot(mtcars, aes(x = mpg, y = hp)) +
geom_point() +
theme(
axis.text.x = element_text(size = rep(c(12,24), 3))
)

Change size (width) of plot in ggplot

I am a newbie attempting to change the width of a ggplot, so that I am can arrange different plots(heatmap and dotplot) in the same figure. However, after hours of trying to reduce the width of the dotplot, I am about to give up.
Code for heatmap (maybe not relevant):
heatmap_GO_NES_1<-ggplot(data=long_frame_GO_NES_1) +
geom_tile(mapping = aes(
x = factor(timepoint,levels = c("6h","12h","24h")),
y =bio_process,fill = NES)) +
ylab(label="Biological process") +
theme(axis.title.x=element_blank()) +
scale_fill_gradient(low="red",high="green")+
facet_grid( group ~. , scales="free",space="free")+
theme(axis.text.x = element_text(angle = 90))+
theme(strip.text.y = element_text(size = 8))
heatmap_GO_NES_1
Code for dotplot:
dot_GO_NES_1<- ggplot(data=long_frame_GO_NES_2)+
geom_count(mapping=aes(x=timepoint, y =bio_process, size=setsize))+
theme(axis.title.x=element_blank(), axis.text.x=element_blank(),
axis.ticks.x=element_blank(),axis.title.y=element_blank(),
axis.text.y=element_blank(),axis.ticks.y=element_blank())
dot_GO_NES_1
Code for figure:
plot_grid(heatmap_GO_NES_1,dot_GO_NES_1)
Obviously, the dotplot is stealing all the figure space, so that my heatmap does not show up in the figure.
TL;DR - you need to use the rel_widths= argument of plot_grid(). Let me illustrate with an example using mtcars:
# Plots to display
p1 <- ggplot(mtcars, aes(mpg, disp)) + geom_point()
p2 <- ggplot(mtcars, aes(x='X', y=disp)) + geom_point(aes(size=cyl))
Here are the plots, where you see p2 is like your plot... should not be too wide or it looks ridiculous. This is the default behavior of plot_grid(), which makes both plots the same width/relative size:
plot_grid(p1,p2)
Adjust the relative width of the plots using rel_widths=:
plot_grid(p1,p2, rel_widths=c(1,0.3))

How to set automatic label position based on box height

In a previous question, I asked about moving the label position of a barplot outside of the bar if the bar was too small. I was provided this following example:
library(ggplot2)
options(scipen=2)
dataset <- data.frame(Riserva_Riv_Fine_Periodo = 1:10 * 10^6 + 1,
Anno = 1:10)
ggplot(data = dataset,
aes(x = Anno,
y = Riserva_Riv_Fine_Periodo)) +
geom_bar(stat = "identity",
width=0.8,
position="dodge") +
geom_text(aes( y = Riserva_Riv_Fine_Periodo,
label = round(Riserva_Riv_Fine_Periodo, 0),
angle=90,
hjust= ifelse(Riserva_Riv_Fine_Periodo < 3000000, -0.1, 1.2)),
col="red",
size=4,
position = position_dodge(0.9))
And I obtain this graph:
The problem with the example is that the value at which the label is moved must be hard-coded into the plot, and an ifelse statement is used to reposition the label. Is there a way to automatically extract the value to cut?
A slightly better option might be to base the test and the positioning of the labels on the height of the bar relative to the height of the highest bar. That way, the cutoff value and label-shift are scaled to the actual vertical range of the plot. For example:
ydiff = max(dataset$Riserva_Riv_Fine_Periodo)
ggplot(dataset, aes(x = Anno, y = Riserva_Riv_Fine_Periodo)) +
geom_bar(stat = "identity", width=0.8) +
geom_text(aes(label = round(Riserva_Riv_Fine_Periodo, 0), angle=90,
y = ifelse(Riserva_Riv_Fine_Periodo < 0.3*ydiff,
Riserva_Riv_Fine_Periodo + 0.1*ydiff,
Riserva_Riv_Fine_Periodo - 0.1*ydiff)),
col="red", size=4)
You would still need to tweak the fractional cutoff in the test condition (I've used 0.3 in this case), depending on the physical size at which you render the plot. But you could package the code into a function to make the any manual adjustments a bit easier.
It's probably possible to automate this by determining the actual sizes of the various grobs that make up the plot and setting the condition and the positioning based on those sizes, but I'm not sure how to do that.
Just as an editorial comment, a plot with labels inside some bars and above others risks confusing the visual mapping of magnitudes to bar heights. I think it would be better to find a way to shrink, abbreviate, recode, or otherwise tweak the labels so that they contain the information you want to convey while being able to have all the labels inside the bars. Maybe something like this:
library(scales)
ggplot(dataset, aes(x = Anno, y = Riserva_Riv_Fine_Periodo/1000)) +
geom_col(width=0.8, fill="grey30") +
geom_text(aes(label = format(Riserva_Riv_Fine_Periodo/1000, big.mark=",", digits=0),
y = 0.5*Riserva_Riv_Fine_Periodo/1000),
col="white", size=3) +
scale_y_continuous(label=dollar, expand=c(0,1e2)) +
theme_classic() +
labs(y="Riserva (thousands)")
Or maybe go with a line plot instead of bars:
ggplot(dataset, aes(Anno, Riserva_Riv_Fine_Periodo/1e3)) +
geom_line(linetype="11", size=0.3, colour="grey50") +
geom_text(aes(label=format(Riserva_Riv_Fine_Periodo/1e3, big.mark=",", digits=0)),
size=3) +
theme_classic() +
scale_y_continuous(label=dollar, expand=c(0,1e2)) +
expand_limits(y=0) +
labs(y="Riserva (thousands)")

Removing ggplot legend symbol while retaining label

Example code and figure:
data <- data.frame( ID = c(LETTERS[1:26], paste0("A",LETTERS[1:26])),
Group = rep(c("Control","Treatment"),26),
x = rnorm(52,50,20),
y = rnorm(52,50,10))
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
theme(legend.text = element_text(color=c("blue","red")))
What I'm trying to solve is removing the legend symbols (the "a") and coloring the Group labels (Control and Treatment) as they appear in the plot (Blue and Red respectively).
I've tried:
geom_text(show_guide = F)
But that just removes the legend entirely.
To keep it simple I could just use annotate...but wondering if there's a legend specific solution.
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8, show_guide=F) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
annotate("text",label="Control", color="blue",x=20,y=80,size=8) +
annotate("text",label="Treatment", color="Red",x=23,y=77,size=8)
Another option is to use point markers (instead of the letter "a") as the legend symbols, which you can do with the following workaround:
Remove the geom_text legend.
Add a "dummy" point geom and set the point marker size to NA, so no points are actually plotted, but a legend will be generated.
Override the size of the point markers in the legend, so that point markers will appear in the legend key to distinguish each group.
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8, show.legend=FALSE) +
geom_point(size=NA) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
labs(colour="") +
guides(colour=guide_legend(override.aes=list(size=4)))
Beginning with ggplot2 2.3.2, you can specify the glyph used in the legend using the argument key_glyph:
ggplot(data, aes(x=x, y=y, label=ID, color=Group)) +
geom_text(size=8, key_glyph="point") +
scale_color_manual(values=c("blue", "red")) +
labs(color=NULL) +
theme_classic()
For a full list of glyphs, refer to the ggplot2 documentation for draw_key. Credit to R Data Berlin for alerting me to this simple solution. Emil Hvitfeldt also has a nice blog post showcasing the options.
As a quick fix you can tweak the legend key, by hard coding the info you want, although around the other way - keep the key and remove the label.
library(grid)
GeomText$draw_key <- function (data, params, size) {
txt <- ifelse(data$colour=="blue", "Control", "Treatment")
# change x=0 and left justify
textGrob(txt, 0, 0.5,
just="left",
gp = gpar(col = alpha(data$colour, data$alpha),
fontfamily = data$family,
fontface = data$fontface,
# also added 0.5 to reduce size
fontsize = data$size * .pt* 0.5))
}
And when you plot you suppress the legend labels, and make legend key a bit wider to fit text.
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
theme(legend.text = element_blank(),
legend.key.width = unit(1.5, "cm"))

Adjust width of panel labels on ggplot with faceting

I am having trouble with excessively wide panel labels on my ggplot2 faceted plot.
Here is the code that I used to generate the plot:
png(paste("/directory/", hgnc_symbol, "_", curr_gene, ".png", sep=""),
width=4, height=3, units="in", pointsize=1, res=300)
print({barplot <-
ggplot(curr_data, aes(x = condition, y = tpm, fill=condition)) +
geom_boxplot(outlier.colour=NA, lwd=0.2, color="grey18") +
stat_boxplot(geom ='errorbar', color="grey18") +
geom_jitter(size=0.8) +
facet_wrap(~target_id) +
guides(fill=FALSE) +
theme_bw() +
labs(title=paste(hgnc_symbol, "_", curr_gene, sep="")) +
labs(x="condition") + labs(y="TPM") +
theme(text = element_text(size=5), strip.background=element_rect(size = 1),
axis.text.x = element_text(angle = 90, hjust = 1, size=4.5))})
dev.off()
The plot comes out looking like this:
As you can see, the background of the panel labels is so wide, that the plots themselves are barely visible. The points plotted on the graph are also much larger than I expected them to be.
The odd thing is that I used this same exact code to produce this following plot (which looks good) just a few days ago:
What is causing this difference, and how can I fix the problem?
In ggplot, text is defined in pts, which are absolute units of measure. The plot panel is relative in size and scales according to the dimensions you specify when you save your plot. If the dimensions are small, then the text will be large relative to the panel areas. If the dimensions are large, then the text will be small relative to the panel areas. See examples below:
ggplot(diamonds, aes(x = x, y =y)) + geom_point() + facet_wrap(~ clarity)
ggsave("filepath//4x3.png", width=4, height = 3)
ggsave("filepath//8x6.png", width=8, height = 6)
The 4 x 3 in plot:
The 8 x 6 in plot:
You can edit the grobs to exert finer control on the plot dimensions (example).

Resources