Unequal horizontal adjustment to title lines in ggplot - r

I've got a plot with a fairly wide y-axis label, so I want to adjust the title to the left so that it's flush with the label rather than the axis (like this question) but the twist is that I have a multiline title. I've been using hjust, but it adjusts the two lines differently.
For example
ggplot(mtcars,aes(x=wt,y=mpg))+
geom_point()+
ggtitle("Figure: My long and winding title\nthat goes on and on and on") +
ylab("My long label") +
theme_bw() +
theme(plot.title = element_text(size=16, hjust=-.33, color="black", face="bold")) +
theme(axis.title.y = element_text(angle = 0, hjust = 1))
Gives
Is there a way to make the start of the two lines of the title flush after adjusting them horizontally?

You could use the following code. First create the plot and assign it to g, then turn g in to a grob with ggplotGrob. Consequently, manipulate the title left alignment in the layout part of the grob (from 5 to 2). And Finally plot the adapted grob.
g <- ggplot(mtcars,aes(x=wt,y=mpg)) +
geom_point() +
ggtitle("Figure: My long and winding title\nthat goes on and on and on") +
ylab("My long label") +
theme_bw() +
theme(plot.title = element_text(size=16, color="black", face="bold")) +
theme(axis.title.y = element_text(angle = 0, hjust = 1))
grob <- ggplotGrob(g)
# what is the current content
grob$layout$l[grob$layout$name == "title"]
[1] 5
grob$layout$l[grob$layout$name == "title"] <- 2
# plot the new grob
grid::grid.draw(grob)
yielding this plot:
Please let me know whether this is what you want.

Sorry I misunderstood your question.
I think you're just missing a space in the title.
ggtitle("Figure: My long and winding title\n that goes on and on and on")

Here You have it:
library(ggplot2)
library(grid)
library("gridExtra")
p<-ggplot(mtcars,aes(x=wt,y=mpg))+
geom_point()+
ggtitle("") +
ylab("My long label") +
theme_bw() +theme(axis.title.y = element_text(angle = 0, hjust = 1))
title.grob <- textGrob(
label = "Figure: My long and winding title\nthat goes on and on and on",
x = unit(0, "lines"),
y = unit(0, "lines"),
hjust = 0, vjust = 0,
gp = gpar(fontsize = 16))
p1 <- arrangeGrob(p, top = title.grob)
grid.draw(p1)`

hjust is the responsible of the white space.
Removing it remove the 2 white spaces on the second line.
library(ggplot2)
ggplot(mtcars,aes(x=wt,y=mpg))+
geom_point()+
ggtitle("Figure: My long and winding title\nthat goes on and on and on") +
ylab("My long label") +
theme_bw() +
theme(plot.title = element_text(size=16,
color="black", face="bold",
),
axis.title.y = element_text(angle = 0, hjust = 1))
EDIT 1:
If you want to automatically split the title in multiple line, you can use gsub. Here is one example where I split the title after 30 characters. (source)
long_title <- "Figure: My long and winding title that goes on and on and on"
ggplot(mtcars,aes(x=wt,y=mpg))+
geom_point()+
ggtitle(gsub('(.{1,30})(\\s|$)', '\\1\n', long_title)) +
ylab("My long label") +
theme_bw() +
theme(plot.title = element_text(size=16,
color="black", face="bold",
),
axis.title.y = element_text(angle = 0, hjust = 1))
Hope that help !

Related

How to align text outside of ggplot?

I wanted to make a barplot like the figure a of this one that I found in a publication with something like a table tab showing some information outside the plot using ggplot. I found this general approach of adding text outside of a plot using gridExtra.
However, my question is how to align the height of each row of the table tab to each bar of the barplot so they match?
Here is an example. I wanted to add the note as a table tab on the right of the barplot.
library(ggplot2)
library(gridExtra)
df <- data.frame(Model = c("Datsun 710","Duster 360","Hornet 4 Drive","Hornet
Sportabout","Mazda RX4 Wag","Merc 230","Merc 240D","Valiant"),
logFC = c(1.879,1.552,1.360,1.108,-2.407,-2.416,-2.670,-3.061),
Note = c("ModelA","ModelB","ModelC","ModelD","ModelE","ModelF","ModelG","ModelH"))
plot <- ggplot(df, aes(Model, logFC)) +
geom_bar(stat="identity") +
coord_flip() +
theme_bw() +
ggtitle("Data for cars") +
theme(plot.title = element_text(size = 18, hjust = 0.5))
tab <- as.data.frame(
c(Note = df$Note))
rownames(tab) <- NULL
p_tab <- tableGrob(unname(tab))
grid.arrange(plot, p_tab, ncol = 2)
Per Gregor's comment, this works for me:
plot <- ggplot(df, aes(Model, logFC, label=Note)) +
geom_bar(stat="identity") +
coord_flip(clip = "off") +
theme_bw() +
ggtitle("Data for cars") +
theme(plot.title = element_text(size = 18, hjust = 0.5))+
geom_text(y = 3,
hjust = 0,
size = 5) +
theme(plot.margin = unit(c(1,10,1,1), "lines"),
panel.border=element_blank(),
axis.line = element_line(),
panel.grid.major=element_blank(),
panel.grid.minor = element_blank())

Move subtitle to left direction (aligning to y axis area) in ggplot2

Given sample data and ggplot plotting code below:
df <- data.frame(Seller=c("Ad","Rt","Ra","Mo","Ao","Do"),
Avg_Cost=c(5.30,3.72,2.91,2.64,1.17,1.10), Num=c(6:1))
text <- "Real estate agents often refer to a home's curb appeal, the first impression
it makes on potential buyers. As a would-be seller, it's important to take as dispassionate
a look as possible at the outside of your home."
ggplot(df, aes(x=reorder(Seller, Num), y=Avg_Cost)) +
geom_bar(stat='identity') +
coord_flip() +
labs(
title = 'Costs of Selling a Home',
subtitle = stringr::str_wrap(text, 80)
) +
theme(
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0),
plot.margin = unit(c(0.1, 0, 0, 0), "cm")
)
Result:
I attempt to slightly adjust subtitle to left direction (as the red arrow shows), the final subtitle will be relocated in the area of black rectangle.
I've tried by adjusting values of hjust in plot.subtitle(), but I didn't get the chance of success. How could I achieve that? Thanks.
Reference:
https://r-charts.com/ggplot2/margins/
TBMK there is no easy out-of-the-box solution to achieve your desired result.
One option would be to create your title and subtitle as grid::textGrobs and to add them to your main plot via e.g. patchwork (or annotation_custom or ...) which allows to set the left margin for the subtitle, e.g. in my code below I used a margin of 5 mm on the left.
For the other params of the textGrobs I tried to replicate the default specs of theme_grey for the plot.title and plot.subtitle.
To add the grobs via patchwork you have to wrap them inside wrap_elements.
One drawback of this approach is that you have to set the (relative) height of the main plot (or the annotations) via plot_layout(heights = ...) according to your final plot size. So this requires some fiddling:
library(ggplot2)
library(grid)
library(patchwork)
pmain <- ggplot(df, aes(x = Avg_Cost, y = reorder(Seller, Num))) +
geom_bar(stat = "identity") +
theme(plot.margin = unit(c(0, 0, 0, 0), "cm"))
title <- grid::textGrob(
label = "Costs of Selling a Home", vjust = 1, y = unit(1, "npc"),
gp = gpar(fontsize = 11 * rel(1.2), lineheight = .9)
)
subtitle <- grid::textGrob(
label = stringr::str_wrap(text, 80), vjust = 1, y = unit(1, "npc"),
x = unit(0, "npc") + unit(5, "mm"), hjust = 0,
gp = gpar(fontsize = 11 * rel(1), lineheight = .9)
)
wrap_elements(panel = title) / wrap_elements(plot = subtitle) / pmain + #
plot_layout(heights = c(1, 3, 20)) &
theme(plot.margin = unit(c(.1, 0, 0, 0), "cm"))
Say p is OP's plot, simply do
p +
theme(plot.title.position = "plot")
Result:
Since ggplot2 v3.3.0 (2020-03-05) there are 2 changes that might be of interested here
there is a new theme argument called plot.title.position
no need for coord_flip() anymore, since direction of a plot is deducted from the aesthetic mappings
Relevant for this question is the 1. point.
Below is OP's plot, without coord_flip(), with geom_col() instead of geom_bar(..., stat = "identity") and switch of the aesthetics.
library(ggplot2)
p <- ggplot(df,
aes(x=Avg_Cost,
y=reorder(Seller, Num))
) +
geom_col() +
# geom_bar(stat='identity') +
# coord_flip() +
labs(
title = 'Costs of Selling a Home',
subtitle = stringr::str_wrap(text, 80)
) +
theme(
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0),
plot.margin = unit(c(0.1, 0, 0, 0), "cm")
)
Reference: https://www.tidyverse.org/blog/2020/03/ggplot2-3-3-0/#grab-bag
You can achieve the expected result using theme in ggplot2.
You just need to change the value of hjust.
plot.subtitle = element_text(hjust = -0.1)
It will give you the output like this:

Add text (or annotation) above (or below) the legend of a ggplot barplot with facet

I've literally spend a couple of days searching for the correct answer to this. I want to place additional text/annotation on the outside of the plot area--above or below the legend--on a geom_bar that has nested facets like below.
Some things I've tried:
annotate_custom, and annotate don't work because they add an annotation in every individual facet.
Grid.text worked (I was able to place the text correctly) but I was then unable to use ggsave (which is important) to save the completed plot.
I've also looked at answers that dealt with creating an annotation on a single facet of a multi-faceted plot. Those answers don't help because I want the text/annotation to be outside of the plotting area (above/below the legend) and not in the plotting area itself.
Thanks in advance for your help!
Here are some data:
library(ggplot2)
library(ggh4x) # for the facet_nested function
my.df<-data.frame("top.nest"=sample(c("Group.1","Group.2","Group.3"),26,rep=T),
"bottom.nest"=sample(c("Cat.1","Cat.2","Cat.3","Cat.4","Cat.5"),26,rep=T),
"my.teams"=c(LETTERS),
"quantity"=abs(round(rnorm(26,200,20))),
"my.factor"=sample(c("A","B","C"),26,rep=T))
my.plot<- ggplot(my.df, aes(my.teams, quantity, fill = my.factor)) +
geom_bar(stat = "identity") +
geom_col(width=1) +
scale_fill_manual(values=c("blue","red","green"), labels = c("A","B","C")) +
labs(title = "How do you add text/annotation either below or above the legend",
subtitle="of a facet barplot in ggplot?") +
theme(plot.title = element_text(hjust = 0.5, size=12),
plot.subtitle = element_text(hjust=0.5, size=12)) +
scale_y_continuous(expand = c(0, 0), limits=c(0,250)) +
facet_nested(~top.nest + bottom.nest, scales="free_x", space="free")
I've tried the following suggestion, which doesn't work for me. I added these three lines to the original code.
labs(tag = "XX") +
coord_cartesian(xlim = c(50, 350), ylim = c(10, 35), clip = "off") +
theme(plot.tag.position = c(.01, .95))
Whether I add it before, or after, the facet_nested command, the result is the same (see image below). My hunch is that is has to do with the fact that in the example provided in the first response to this question, the x variable is numeric. Mine is not.
EDIT: Okay, the code now works with the added 3 lines, as long as I remove the xlim=c() part. It works with a ylim=c(), or just remove all limits. It kind of makes sense given that the x-variable was not (as I earlier mentioned) numeric.
Here's the full code, and the successful plot!
my.plot<- ggplot(my.df, aes(my.teams, quantity, fill = my.factor)) +
geom_bar(stat = "identity") +
geom_col(width=1) +
scale_fill_manual(values=c("blue","red","green"), labels = c("A","B","C")) +
labs(title = "Successfully added text/annotation below the legend",
subtitle="of a facet barplot in ggplot") +
theme(plot.title = element_text(hjust = 0.5, size=12),
plot.subtitle = element_text(hjust=0.5, size=12)) +
scale_y_continuous(expand = c(0, 0), limits=c(0,250)) +
facet_nested(~top.nest + bottom.nest, scales="free_x", space="free") +
labs(tag = "XX") +
coord_cartesian(clip = "off") +
theme(plot.tag.position = c(.95, .6))
For completion, here is my final code. On the suggestion of Mr.Flick, I had initially tried using the answer found here. That gave me the second image in my original question above--the one without bars, but with random lines. I am assuming it was because the xlim code was throwing off my plot, given that my x-variable was not numeric. By removing the xlim part, I was able to get what I wanted.
my.plot<- ggplot(my.df, aes(my.teams, quantity, fill = my.factor)) +
geom_bar(stat = "identity") +
geom_col(width=1) +
scale_fill_manual(values=c("blue","red","green"), labels = c("A","B","C")) +
labs(title = "Successfully added text/annotation below the legend",
subtitle="of a facet barplot in ggplot",
tag ="My first tag line\nSecond tag line") +
theme(plot.title = element_text(hjust = 0.5, size=12),
plot.subtitle = element_text(hjust=0.5, size=12),
plot.margin = margin(1, 4, 1, 1, "lines"),
plot.tag.position = c(.91,.6),
plot.tag = element_text(hjust =0, size=9)) +
scale_y_continuous(expand = c(0, 0), limits=c(0,250)) +
facet_nested(~top.nest + bottom.nest, scales="free_x", space="free") +
coord_cartesian(clip = "off")

How can I adjust the vertical spacing of font and axis title in ggplot2? [duplicate]

I want to place the title inside the plot instead at the default top position.
Here is a simple code snippet
library(ggplot2)
df <- data.frame(x = c(1:10), y = rnorm(10, 1, 2))
ggplot(df, aes(x, y))+
geom_line() +
ggtitle("Demo") +
theme(plot.title = element_text(vjust = -3))
In the past I was able to do this by varying vjust value, but now it is not working. Any idea how to do this?
In the ggplot issue "vjust not working in v 2.0 for plot.title?", Hadley writes:
"All text elements now have a margin, which by default scale with the font
size in the theme. This leads to nicer spacing, particularly at large font
sizes. This means hacks with vjust and hjust no longer work. Instead,
use the margin() parameter of element_text()"
Play around with the t and b arguments in margin to adjust the title, e.g.:
ggplot(df, aes(x, y))+
geom_line() +
ggtitle("Demo") +
theme(plot.title = element_text(margin = margin(t = 10, b = -20)))
See ?margin for further arguments.
Note that you should use the margin argument for axis.title.x and axis.title.y as well:
ggplot() + ggtitle("this is title") + xlab("this is x") + ylab("this is y") +
theme(plot.title = element_text(margin = margin(b = -10)),
axis.title.x = element_text(margin = margin(t = -10)),
axis.title.y = element_text(margin = margin(r = -10)))

Adding a single legend with very long text to a ggplot2 facet_grid

Using R ggplot2 library, I an generating a facet_grid plot and I want to put a legend off to the right hand side of all the plots -NOT EACH INDIVIDUAL PLOT, but one legend that works for all the plots
g = ggplot(pd2, aes(x = p/1000, y=c/1000 )) +
geom_point(alpha=.1, colour="red") +
geom_smooth(method=lm) +
xlab("Ave P") +
ylab("Ave C)") +
labs(title = "Comparison ") +
theme(plot.title = element_text(face="bold",
size=14, colour="black", hjust = 0.5)) +
theme(axis.title = element_text(face="bold", size=12, colour="blue")) +
facet_grid(code ~ TheState) +
theme(strip.text.y = element_text(colour = "purple", angle = 0,
size = 9, hjust = 0.5, vjust = 0.5))
What I want to do is have a legend off to the right that is turned 90 degrees - because I want the legend to refer to very_long_code instead of just "code." I've tried all kinds of variations of legend, theme, and guide. Intuitively, I would like to say,
legend(text, angle=90, very_long_code, color="purple")
The next step would be to not use a single color, but to use a different color for each one that would correspond to colors in the strip.

Resources