ggplot2 Create shaded area with gradient below curve - r

I would like to create the plot below using ggplot.
Does anyone know of any geom that create the shaded region below the line chart?
Thank you

I think you're just looking for geom_area. However, I thought it might be a useful exercise to see how close we can get to the graph you are trying to produce, using only ggplot:
Pretty close. Here's the code that produced it:
Data
library(ggplot2)
library(lubridate)
# Data points estimated from the plot in the question:
points <- data.frame(x = seq(as.Date("2019-10-01"), length.out = 7, by = "month"),
y = c(2, 2.5, 3.8, 5.4, 6, 8.5, 6.2))
# Interpolate the measured points with a spline to produce a nice curve:
spline_df <- as.data.frame(spline(points$x, points$y, n = 200, method = "nat"))
spline_df$x <- as.Date(spline_df$x, origin = as.Date("1970-01-01"))
spline_df <- spline_df[2:199, ]
# A data frame to produce a gradient effect over the filled area:
grad_df <- data.frame(yintercept = seq(0, 8, length.out = 200),
alpha = seq(0.3, 0, length.out = 200))
Labelling functions
# Turns dates into a format matching the question's x axis
xlabeller <- function(d) paste(toupper(month.abb[month(d)]), year(d), sep = "\n")
# Format the numbers as per the y axis on the OP's graph
ylabeller <- function(d) ifelse(nchar(d) == 1 & d != 0, paste0("0", d), d)
Plot
ggplot(points, aes(x, y)) +
geom_area(data = spline_df, fill = "#80C020", alpha = 0.35) +
geom_hline(data = grad_df, aes(yintercept = yintercept, alpha = alpha),
size = 2.5, colour = "white") +
geom_line(data = spline_df, colour = "#80C020", size = 1.2) +
geom_point(shape = 16, size = 4.5, colour = "#80C020") +
geom_point(shape = 16, size = 2.5, colour = "white") +
geom_hline(aes(yintercept = 2), alpha = 0.02) +
theme_bw() +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.border = element_blank(),
axis.line.x = element_line(),
text = element_text(size = 15),
plot.margin = margin(unit(c(20, 20, 20, 20), "pt")),
axis.ticks = element_blank(),
axis.text.y = element_text(margin = margin(0,15,0,0, unit = "pt"))) +
scale_alpha_identity() + labs(x="",y="") +
scale_y_continuous(limits = c(0, 10), breaks = 0:5 * 2, expand = c(0, 0),
labels = ylabeller) +
scale_x_date(breaks = "months", expand = c(0.02, 0), labels = xlabeller)

Related

Why does the top line of ggplot disappear when using xlim, ylim and scale_continuous to define breaks

I am using this piece of code to create a series of 4 point plots. I need the plots to go from x1 to x2 and y1 to y2. The length of the axes is always 10 with a single major break halfway and minor breaks at each unit.
When I get the scale and the breaks right I sometimes (not always) lose the top line on the box surrounding the map
The parts of the data set that are involved are
tag: an identifier used to label the points
lx, ly: the coordinates of the plot which range from 0 -20 in the dataset but each map does only a quarter of the area. So x goes from 0 to 10 or 10 to 20 and y goes from 0 to 10 or 10 to 20.
I tried this piece of code. I expect a point plot surrounded by a box
n ranges from 1:4
x1 = c(0, 10, 10, 0)
x2 = c(10, 20, 20, 10)
y1 = c(0, 0, 10, 10)
y2 = c(10, 10, 20, 20)
theme_set(theme_bw())
ggplot(onemap, aes(x = onemap$lx, y = onemap$ly)) + geom_point(size =
.3) +
xlim(c(x1[n], x2[n])) + ylim(c(y1[n], y2[n])) +
# coord_cartesian(expand = FALSE)
# scale_x_continuous(expand = c(0, 0), limits = c(0, NA)) +
scale_y_continuous(breaks = seq(y1[n], y2[n], 5),
minor_breaks = seq(y1[n], y2[n], 1)) +
scale_x_continuous(breaks = seq(x1[n], x2[n], 5),
minor_breaks = seq(x1[n], x2[n], 1)) +
labs(
x = element_blank(),
y = element_blank(),
title = hd,
subtitle = subheads
) +
theme(plot.title.position = "plot") +
theme(
plot.title = element_text(
size = 14,
face = "bold",
margin = margin(8, 0, 8, 0)
),
plot.subtitle = element_text(size = 10),
plot.margin = margin(
t = 1,
r = 2,
b = 1.5,
l = 2,
unit = "cm"
),
axis.ticks = element_blank(),
axis.text = element_text(size = 6),
axis.text.y = element_text(angle = 90)
) +
theme(
panel.grid.major = element_line(color = "gray30", linewidth = .25),
panel.grid.minor = element_line(
color = "gray30",
linewidth = .25,
linetype = "dashed"
),
panel.border = element_blank()
) +
geom_text_repel(
aes(label = tag),
box.padding = 0.01,
size = 2,
xlim = c(-Inf, Inf),
ylim = c(-Inf, Inf),
max.overlaps = 45,
segment.size = 2,
segment.color = "grey"
)
I worked out the problem. I should not have used xlim and ylim in the main body of ggplot. I should have used the limits in scale_x_continuous and scale_y_continuous as below.
scale_y_continuous(breaks = seq(y1[n], y2[n], 5), minor_breaks = seq(y1[n], y2[n], 1),limits=c(y1[n], y2[n])) +
scale_x_continuous(breaks = seq(x1[n], x2[n], 5), minor_breaks = seq(x1[n], x2[n], 1),limits=c(x1[n], x2[n])) +

How to display a data group as points and another one as confidence ellipse? Issues with ggplot and ggsave

I am new to R, and I am trying to generate scatter plots with two variables, with the values of each variable grouped into 4 classes.
In particular, I am trying to achieve the following:
Display two groups as data points, two groups as confidence ellipses
Generate and save scatter plots having the same dimensions in term of plot frame size and plot area (i.e., x-axis long 8 cm, y-axis long 6 cm.).
Below you can find a reproducible version (you just need to define the output for the png file) of the code that works, but it shows data points and confidence ellipses for all data:
library(ggplot2)
out_path = YOUR OUTPUT DIRECTORY
#data frame
gr1 <- (rep(paste('B-12-B-002'), 10))
gr2 <- (rep(paste('B-12-M-03'), 10))
gr3 <- (rep(paste('b-b-d-3'), 10))
gr4 <- (rep(paste('h-12-b-01'), 10))
Run_type <- c(gr1,gr2,gr3,gr4)
axial_ratio <- runif(40,0,1)
Solidity <- runif(40,0,1)
Convexity <- runif(40,0,1)
sel_data_all <- data.frame(Run_type,axial_ratio,Solidity,Convexity)
fill_colors <- c('red','blue','green','orange');
#Plot
one_plot = ggplot(sel_data_all,aes(x = axial_ratio,y = Solidity))+
geom_point(aes(x = axial_ratio,y = Solidity, fill = Run_type, shape = Run_type), color = "black", stroke = 1,
size = 5, alpha = 0.4)+
stat_ellipse(data = sel_data_all, aes(x = axial_ratio, y = Solidity, fill = Run_type,colour=Run_type),geom = "polygon",alpha = 0.4,type = "norm",level = 0.6,
show.legend = FALSE) + #, group=Run_type , data = subset(sel_data_all, Run_type %in% leg_keys_man[1:7]),
scale_shape_manual(values=c(21,21,23,23))+
scale_fill_manual(values = fill_colors)+
scale_color_manual(values = fill_colors)+
coord_fixed(ratio = 1)+
theme(legend.position="top", # write 'none' to hide the legend
legend.key = element_rect(fill = "white"), # Set background of the points in the legend
legend.title = element_blank(), # Remove legend title
panel.background=element_rect(fill = "white", colour="black"),
panel.grid.major=element_line(colour="lightgrey"),
panel.grid.minor=element_line(colour="lightgrey"),
axis.title.x = element_text(margin = margin(t = 10), size = 12,face = "bold"), # margin = margin(t = 10) vjust = 0
axis.title.y = element_text(margin = margin(r = 10), size = 12,face = "bold"), # margin = margin(r = 10) vjust = 2
axis.text = element_text(color = "black", size = 10), # To hide the text from a specific axis do: axis.text.y = element_blank()
axis.ticks.length=unit(-0.15, "cm"), # To hide the ticks from a specific axis do: axis.ticks.y = element_blank()
#plot.margin = margin(t = 0, r = 1, b = 0.5, l = 0.5, unit = "cm"), # define margine of the plot frame t = top, r = right, b = bottom, l = left
)
#expand_limits(x = 0, y = 0)+ #Force the origin of the plot to 0
#xlim(c(0,1))+
#ylim(c(0,1)) # or xlim, limit the axis to the values defined
show(one_plot)
# Save plots
ggsave(
filename=paste("Axial_ratio","_vs_","Solidity",".png",sep=""),
plot = one_plot,
device = "png",
path = out_path,
scale = 1,
width = 8, # Refers to the plot frame, not the area
height = 6, # Refers to the plot frame, not the area
units = "cm",
dpi = 300,
limitsize = FALSE,
bg = "white")
Unfortunately, after several days of trying and reading the R documentation and forums, I cannot achieve this.
For the first task, I tried subsetting the data by modifying the geom_point and stat_ellipse functions,
geom_point(data = subset(sel_data_all, Run_type %in% c('B-12-B-002','B-12-M-03')),aes(x = axial_ratio,y = Solidity, fill = Run_type, shape = Run_type), color = "black", stroke = 1,
size = 5, alpha = 0.4)+ #
stat_ellipse(data = subset(sel_data_all, Run_type %in% c('b-b-d-3','h-12-b-01')), aes(x = axial_ratio, y = Solidity, fill = Run_type,colour=Run_type),geom = "polygon",alpha = 0.4,type = "norm",level = 0.6,
show.legend = FALSE) + #
but I end up with a duplicate of the legend (in grey colour).
Like this.
For my second issue, with the working version of the script at the top of the message,
Here is the plot that shows in the "Plots" window in RStudio:
But this is what is saved in the output directory.
A final note about the second issue: the script presented here is actually inserted in a for loop that generates multiple scatter plots made by unique pairs of two variables, and the data frame provided here is only partial, to make it easier for you to help. Unfortunately, this is what ggsave generates:
axial ratio vs convexity
ves_pct vs axial ratio
Can anybody help?
Thank you in advance to everyone!
EDIT:
So, thanks to MarBlo (which I thank a lot), I managed to get almost what I want, but there is still yet something I cannot figure out.
This is the last version of the code, with some adaptation to better fit the reasoning:
library(tidyverse)
set.seed(123)
gr1 <- (rep(paste("B-12-B-002"), 10))
gr2 <- (rep(paste("B-12-M-03"), 10))
gr3 <- (rep(paste("b-b-d-3"), 10))
gr4 <- (rep(paste("h-12-b-01"), 10))
Sample_ID <- c(gr1, gr2, gr3, gr4)
axial_ratio <- runif(40, 0, 1)
Solidity <- runif(40, 0, 1)
Convexity <- runif(40, 0, 1)
sel_data_all <- data.frame(Sample_ID, axial_ratio, Solidity, Convexity)
fill_colors <- c("#5bd9ca",
"#1e99d6","#1e49d6","#f2581b80","#e8811280","#e3311280","#fc000080")
sel_data_all <- sel_data_all |> add_column(Run_type = c(
rep("MAG", 10), rep("PMAG", 10),
rep("MAG", 10), rep("PMAG", 10)), .before = "Sample_ID")
one_plot = ggplot(
data = sel_data_all |> dplyr::filter(Run_type == "PMAG"),
aes(x = axial_ratio, y = Solidity)
) +
# CONFIDENCE ELLIPSE
stat_ellipse(
data = sel_data_all |> dplyr::filter(Run_type == "MAG"),
aes(x = axial_ratio, y = Solidity,
fill = Sample_ID),
geom = "polygon", type = "norm",
level = 0.6,
colour = 'white', # ellipse border
) +
# DATA POINTS
geom_point(aes(colour = Sample_ID,
shape = Sample_ID),
stroke = 0.5,
size = 3,
) +
scale_color_manual(values = fill_colors[1:3]) + # of Data points
scale_shape_manual(values = c(21, 21, 23, 23,21,23,22)) + # of data points
scale_fill_manual(values = fill_colors[4:7]) + # of ellipses
coord_cartesian(xlim=c(0,1))+
#scale_x_continuous(expand = expansion(mult = c(0.001, 0.05)))+
coord_cartesian(ylim=c(0,1))+
#scale_y_continuous(expand = expansion(mult = c(0.001, 0.05)))+
# Theme
theme(
legend.position = "top",
legend.key.size = unit(5, 'mm'), #change legend key size
# legend.key.height = unit(1, 'cm'), #change legend key height
# legend.key.width = unit(1, 'cm'), #change legend key width
legend.text = element_text(size=8),
legend.key = element_rect(fill = "white", colour = 'white'),
legend.background = element_rect(fill = "transparent"),
legend.title = element_blank(),
panel.background = element_rect(fill = "white", colour = "black"),
panel.grid.major = element_line(colour = "lightgrey"),
panel.grid.minor = element_line(colour = "lightgrey"),
axis.title.x = element_text(vjust = -1, size = 12, face = "bold"),
axis.title.y = element_text(vjust = 4, size = 12, face = "bold"),
axis.text = element_text(color = "black", size = 10),
axis.ticks.length = unit(-0.15, "cm"),
plot.margin = margin(t = 2, # Top margin
r = 4, # Right margin
b = 4, # Bottom margin
l = 4, # Left margin
unit = "mm"),
)+
guides(colour = guide_legend(nrow=2, byrow=TRUE)+
coord_fixed(ratio = 1))
ggsave(
filename=paste("snap",".png",sep=""),
plot = one_plot,
device = "png",
path = here::here(),
width = 8, # Refers to the plot frame, not the area
height = 8, # Refers to the plot frame, not the area
units = "cm",
#dpi = 300,
#limitsize = FALSE,
bg = "white")
Here is the saved plot
What I need, are the data points filled with the colour currently used for their border, and the border of all data points in black.
I tried to move around the aesthetics, but I ended up with the duplicate legend and more confusion.
Thanks in advance again for your help.
I have taken your data and added a variable called group which makes filtering in ggplot easier.
If you define x and y in ggplot(..,aes()) you do not have to define it again in geom_point.
In geom_point you give already a color to Run_type , the variable from which the legend should be made up. Because you use in geom_ellipse a different subset of the DF the legend would be updated and make again 4 legend entries instead of 2 for the variables only. color = Run_type can therefore be skipped.
I have added set.seed() which ensures that results are being comparable, although random numbers are generated for making up the DF.
library(tidyverse)
set.seed(123)
gr1 <- (rep(paste("B-12-B-002"), 10))
gr2 <- (rep(paste("B-12-M-03"), 10))
gr3 <- (rep(paste("b-b-d-3"), 10))
gr4 <- (rep(paste("h-12-b-01"), 10))
Run_type <- c(gr1, gr2, gr3, gr4)
axial_ratio <- runif(40, 0, 1)
Solidity <- runif(40, 0, 1)
Convexity <- runif(40, 0, 1)
sel_data_all <- data.frame(Run_type, axial_ratio, Solidity, Convexity)
fill_colors <- c("red", "blue", "green", "orange")
df <- sel_data_all |> mutate(group = c(
rep("Data", 10), rep("Conf", 10),
rep("Data", 10), rep("Conf", 10)
))
ggplot(
data = df |> dplyr::filter(group == "Data"),
aes(x = axial_ratio, y = Solidity)
) +
geom_point(aes(color = Run_type, shape = Run_type),
stroke = 1,
size = 5, alpha = 0.4
) +
stat_ellipse(
data = df |> dplyr::filter(group != "Data"),
aes(
x = axial_ratio, y = Solidity,
fill = Run_type
),
geom = "polygon", alpha = 0.4, type = "norm", level = 0.6,
show.legend = FALSE
) +
scale_shape_manual(values = c(21, 21, 23, 23)) +
scale_fill_manual(values = fill_colors) +
scale_color_manual(values = fill_colors) +
coord_fixed(ratio = 1) +
theme(
legend.position = "top",
legend.key = element_rect(fill = "white"),
legend.title = element_blank(),
panel.background = element_rect(fill = "white", colour = "black"),
panel.grid.major = element_line(colour = "lightgrey"),
panel.grid.minor = element_line(colour = "lightgrey"),
axis.title.x = element_text(margin = margin(t = 10), size = 12, face = "bold"),
axis.title.y = element_text(margin = margin(r = 10), size = 12, face = "bold"),
axis.text = element_text(color = "black", size = 10),
axis.ticks.length = unit(-0.15, "cm"),
)
The plot was then saved with equal width and height.
ggsave(
filename=paste("Axial_ratio","_vs_","Solidity",".png",sep=""),
plot = last_plot(),
device = "png",
path = here::here(),
width = 8, # Refers to the plot frame, not the area
height = 8, # Refers to the plot frame, not the area
units = "cm",
#dpi = 300,
#limitsize = FALSE,
bg = "white")
the saved png looks like this.
NEW EDIT
My understanding now is that you want two colors for geom_points and 2 different colors for stat_ellipse.
So the following will show a new attempt. If this is the right answer, I will erase most from above, to make this post better readable.
The ggsave -issue I regard as solved.
I have defined two different color-sets; one for geom_point and one for stat_ellipse. (There are 3 and 4 colors defined, although later in scale_color_manual and scale_fill_manual only 2 colors are needed.)
As for both geom_point and stat_ellipse a different DF is used, ggplot is called without any data or aes. Both will be defined individually when geom_point and stat_ellipse are called.
For stat_ellipse fill is used and for geom_point color is used as aes.
If you want to leave the one or the other legend out, you may use
show.legend = F in the respective geom.
xlim and ylim define axis limits.
guides() and theme_bw() make sure that the dark background of legend.key is erased.
I have tried to make the theme aa bit more concise.
library(tidyverse)
set.seed(123)
gr1 <- (rep(paste("B-12-B-002"), 10))
gr2 <- (rep(paste("B-12-M-03"), 10))
gr3 <- (rep(paste("b-b-d-3"), 10))
gr4 <- (rep(paste("h-12-b-01"), 10))
Sample_ID <- c(gr1, gr2, gr3, gr4)
axial_ratio <- runif(40, 0, 1)
Solidity <- runif(40, 0, 1)
Convexity <- runif(40, 0, 1)
sel_data_all <- data.frame(Sample_ID, axial_ratio, Solidity, Convexity)
fill_colors_points <- c("#5bd9ca", "#1e99d6", "#1e49d6")
fill_colors_ellipse <- c("#f2581b80", "#e8811280", "#e3311280", "#fc000080")
sel_data_all <- sel_data_all |> mutate(Run_type = c(
rep("MAG", 10), rep("PMAG", 10),
rep("MAG", 10), rep("PMAG", 10)
))
ggplot() +
stat_ellipse(
data = sel_data_all |> dplyr::filter(Run_type == "MAG"),
aes(
x = axial_ratio, y = Solidity,
fill = Sample_ID
),
geom = "polygon", type = "norm",
level = 0.6, show.legend = T
) +
geom_point(
data = sel_data_all |> dplyr::filter(Run_type == "PMAG"),
aes(
x = axial_ratio, y = Solidity,
color = Sample_ID,
shape = Sample_ID
),
stroke = 0.5, size = 3,
) +
scale_color_manual(values = fill_colors_points[1:2]) + # of Data points
scale_fill_manual(values = fill_colors_ellipse[1:2]) + # of ellipses
xlim(0,1) + ylim(0,1) +
guides(color = guide_legend(override.aes = list(fill = NA))) +
theme_bw() +
theme(
legend.position = "top",
legend.key.size = unit(5, "mm"), # change legend key size
legend.text = element_text(size = 8),
legend.title = element_blank(),
panel.background = element_rect(fill = "white", colour = "black"),
panel.grid = element_line(colour = "lightgrey"),
axis.title.x = element_text(vjust = -1, size = 12, face = "bold"),
axis.title.y = element_text(vjust = 4, size = 12, face = "bold"),
axis.text = element_text(color = "black", size = 10),
axis.ticks.length = unit(-0.15, "cm"),
) +
coord_fixed(ratio = 1)

Strip plot with many columns

How can I replicate a plot like the one below using ggplot?
It is basically a set of strip plots, bound side-by-side. I came across it as an image in a publication, so I don't have the code that made it.
My data source (not plotted) would be something like a list of vectors, similar to those below, each of which can be of a different length.
my_data = list(a=c(1,18,90), b=c(1,5,7,8,80), c=c(1,6), d=c(1,22,35,300))
(That said, using a different data structure would be fine too - e.g. a dataframe with some NA entries)
Thanks for any help!
This seems pretty close:
library(tidyverse)
set.seed(123)
IDs <- letters[1:16]
values <- sample(c(rep(NA, 100000), seq(1e-10, 0, 1e-10)), size = 1600, replace = TRUE)
test_data <- list(IDs = IDs, values = values)
df <- as.data.frame(test_data)
ggplot(df, aes(x = IDs, y = values)) +
geom_point(shape = 95, size = 10) +
geom_vline(xintercept = seq(0.5, 16.5, 1), colour = "grey75") +
theme_classic(base_size = 20) +
scale_x_discrete(expand = c(0.035, 0.035)) +
scale_y_log10(expand = c(0.001, 0.001),
breaks = c(0.0001, 0.001, 0.01, 0.1, 0),
labels = expression(10^-4, 10^-3, 10^-2, 10^-1, 10^0)) +
theme(axis.title = element_blank(),
axis.ticks.x = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, size = 2))
Edit
Here is a more suitable alternative for the data in your example:
library(tidyverse)
my_data = list(a=c(1,18,90), b=c(1,5,7,8,80), c=c(1,6), d=c(1,22,35,300))
df <- stack(my_data)
ggplot(df, aes(x = ind, y = values)) +
geom_errorbarh(aes(xmin = as.numeric(ind) + 0.45,
xmax = as.numeric(ind) - 0.45),
height = 0) +
geom_vline(xintercept = seq(0.5, 16.5, 1), colour = "grey75") +
theme_classic(base_size = 20) +
scale_x_discrete(expand = c(0.125, 0.125)) + # alter these numbers to suit
theme(axis.title = element_blank(),
axis.ticks.x = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, size = 2))
Created on 2021-08-31 by the reprex package (v2.0.1)

Create a polygon within a graph in ggplot (R)

I am trying to create a polygon on one of my graphs I create in ggplot2 in order to denote a "compositional field". I understand that I should be using the geom_polygon function, but I was unable to successfully input this function. The code I have for my graph is below (less geom_polyon). I included a photo of what I am trying to do on my graph. Essentially, I want to create a polyogon between (x = 8, y = 0.8) and (x = 100, y = 2). Any help is greatly appreciate, thank you.
Image
[1]: https://i.stack.imgur.com/MR8pj.png
Graph script
ggplot(data = ZonesPd, aes(x = Pd_ppm, y = Cu.Pd)) +
geom_point(aes(color = Mineralized.Zone, size = Mineralized.Zone), alpha = 0.5) +
scale_x_log10(limit = c(1e-3, 1e3), expand = c(0, 0)) +
scale_y_log10(limit = c(1e2, 1e5), expand = c(0, 0)) +
scale_size_manual(values = c(0.5, 0.5, 3)) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.ticks.length = unit(-0.24, "cm"),
axis.text.x = element_text(size = 15, color = "black", margin = margin(0.5, 0.5, 0.5, 0.5,
"cm")),
axis.text.y = element_text(size = 15, color = "black", margin = margin(0.5, 0.5, 0.5, 0.5,
"cm")),
axis.title = element_text(size = 15),
aspect.ratio = 0.75,
legend.position = c(0.8, 0.15),
legend.text = element_text(size = 15),
legend.title = element_blank())
As suggested by r2evans in the comments, geom_rect is the way to go here:
library(ggplot2)
ZonesPd <- data.frame(Pd_ppm <- 10^rnorm(100), Cu.Pd <- 10^rnorm(100),
Mineralized.Zone=sample(gl(5,20)))
ggplot() +
geom_point(data = ZonesPd, aes(x = Pd_ppm, y = Cu.Pd, color = Mineralized.Zone, size = Mineralized.Zone), alpha = 0.5) +
scale_x_log10() + scale_y_log10() +
geom_rect(aes(xmin=8, xmax=100, ymin=0.8, ymax=8))
If you are happy with your plot and just want to add a rectangle with corners at (x = 8, y = 0.8) and (x = 100, y = 2), try adding:
+ geom_polygon(data = data.frame(x = c(8, 8, 100, 100), y = c(.8, 2, 2, .8)),
aes(x = x, y = y, alpha = 0.5))

Is it possible to avoid axis label overlapping by ggrepel?

I am drawing heatmap with ggplot2. Several ticks on y axis need to be labeled. However,some of them are too close and overlap. I know ggrepel could separate text labels, but currently I have not worked out for my problem.
My code is as following. Any suggestion is welcome. Thanks.
Code:
df <- data.frame()
for (i in 1:50){
tmp_df <- data.frame(cell=paste0("cell", i),
gene=paste0("gene", 1:100), exp = rnorm(100), ident = i %% 5)
df<-rbind(df, tmp_df)
}
labelRow=rep("", 100)
for (i in c(2, 5, 7, 11, 19, 23)){
labelRow[i] <- paste0("gene", i)
}
library(ggplot2)
heatmap <- ggplot(data = df, mapping = aes(x = cell, y = gene, fill = exp)) +
geom_tile() +
scale_fill_gradient2(name = "Expression") +
scale_y_discrete(position = "right", labels = labelRow) +
facet_grid(facets = ~ident,
drop = TRUE,
space = "free",
scales = "free", switch = "x") +
scale_x_discrete(expand = c(0, 0), drop = TRUE) +
theme(axis.line = element_blank(),
axis.ticks = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_text(),
axis.title.x = element_blank(),
axis.text.x = element_blank(),
strip.text.x = element_text(angle = -90))
heatmap
For these kinds of problems, I prefer to draw the axis as a separate plot and then combine. It takes a bit of fiddling but allows you to draw pretty much any axis you want.
In my solution, I'm using the functions get_legend(), align_plots(), and plot_grid() from the cowplot package. Disclaimer: I'm the package author.
library(ggplot2)
library(cowplot); theme_set(theme_gray()) # undo cowplot theme setting
library(ggrepel)
df<-data.frame()
for (i in 1:50){
tmp_df <- data.frame(cell=paste0("cell", i),
gene=paste0("gene", 1:100), exp=rnorm(100), ident=i%%5)
df<-rbind(df, tmp_df)
}
labelRow <- rep("", 100)
genes <- c(2, 5, 7, 11, 19, 23)
labelRow[genes] <- paste0("gene ", genes)
# make the heatmap plot
heatmap <- ggplot(data = df, mapping = aes(x = cell,y = gene, fill = exp)) +
geom_tile() +
scale_fill_gradient2(name = "Expression") +
scale_x_discrete(expand = c(0, 0), drop = TRUE) +
facet_grid(facets = ~ident,
drop = TRUE,
space = "free",
scales = "free", switch = "x") +
theme(axis.line = element_blank(),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
strip.text.x = element_text(angle = -90),
legend.justification = "left",
plot.margin = margin(5.5, 0, 5.5, 5.5, "pt"))
# make the axis plot
axis <- ggplot(data.frame(y = 1:100,
gene = labelRow),
aes(x = 0, y = y, label = gene)) +
geom_text_repel(min.segment.length = grid::unit(0, "pt"),
color = "grey30", ## ggplot2 theme_grey() axis text
size = 0.8*11/.pt ## ggplot2 theme_grey() axis text
) +
scale_x_continuous(limits = c(0, 1), expand = c(0, 0),
breaks = NULL, labels = NULL, name = NULL) +
scale_y_continuous(limits = c(0.5, 100.5), expand = c(0, 0),
breaks = NULL, labels = NULL, name = NULL) +
theme(panel.background = element_blank(),
plot.margin = margin(0, 0, 0, 0, "pt"))
# align and combine
aligned <- align_plots(heatmap + theme(legend.position = "none"), axis, align = "h", axis = "tb")
aligned <- append(aligned, list(get_legend(heatmap)))
plot_grid(plotlist = aligned, nrow = 1, rel_widths = c(5, .5, .7))

Resources