I'm having some issues with ggplot2 and y axis tick marks - if someone can provide any input I'd really appreciate it.
I'm trying to create a 'stacked' plot with independent y axis for publication I'm working on. The idea is to have N plots stacked with a common X axis but distinct Y axes for each subplot while making it seem like a single contiguous plot.
I would like inverted tick marks on the x axis (for the bottom most subplot) and all the y axes. Problem is while the tick marks show up on the yaxis breifly in the plot generation they seem to be overwritten at the last stage and become invisible. Its such a minor thing and I could just leave them off but I'd really like to know what is going on for my own sanity...
Below is some sample code that should reproduce the problem and here is an imgur link highlighting the plot style and missing tick marks.
On a tangent, if anyone knows how to customize the axis.line.y.right / axis.line.x.top without using a dummy 'second axis' let me know (it seems a very verbose way of doing something that should be simple).
Thanks for your help
ylim=c(-5,5)
xlim=c(3,12)
ybreaks=c(-2,2)
base <- ggplot() +
theme_bw() +
scale_y_continuous(limits=ylim, breaks=ybreaks) +
scale_x_continuous(limits=xlim) +
labs(x="", y="") +
theme( panel.grid.major=element_line("gray78",0.5,1),
panel.border=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_text(margin=unit(c(0,3,0,0), "mm")))
bottom <- base + xlab("xlab") +
theme( axis.ticks.length.y=unit(-2,"mm"),
axis.ticks.length.x=unit(-2,"mm"),
axis.line.y=element_line(),
axis.line.x=element_line(),
plot.margin=unit(c(0,5,5,5),"mm"),
axis.text.x=element_text(margin=unit(c(3,0,0,0), "mm")))
middle <- base +
theme( axis.ticks.length.y=unit(-2,"mm"),
axis.ticks.length.x=unit(0,"mm"),
axis.line.y=element_line(),
axis.line.x=element_line(linetype=3),
plot.margin=unit(c(0,5,0,5),"mm"),
axis.title=element_blank())
top <- base +
theme( axis.ticks.length.y=unit(-2,"mm"),
axis.ticks.length.x=unit(0,"mm"),
axis.line.y=element_line(),
axis.line.x=element_line(linetype=3),
plot.margin=unit(c(5,5,0,5),"mm"),
axis.title=element_blank())
ggarrange(top,middle,bottom,ncol=1)
The easiest and more ggplot-y way would be to use facets.
I recommend using labs(x=NULL, y=NULL), because using = '' actually is drawing something.
I am removing the facet strips in the plot, but I generally think your graph may be slightly less confusing when you keep the labels and also keep a bit of distance between those graphs.
In order to add the dashed lines between your facets, you could simply add annotations, e.g., with annotate(geom = 'segment')
library(ggplot2)
ylim=c(-5,5)
xlim=c(3,12)
ybreaks=c(-2,2)
ggplot(data.frame(facet = letters[1:3])) +
theme_bw() +
annotate(geom = 'segment', x = -Inf, xend = Inf, y = -Inf, yend = -Inf, linetype = 3)+
scale_y_continuous(limits=ylim, breaks=ybreaks) +
scale_x_continuous(limits=xlim) +
labs(x=NULL, y=NULL) +
theme( panel.grid.major=element_line("gray78",0.5,1),
panel.border=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_text(margin=unit(c(0,3,0,0), "mm")),
panel.spacing = unit(0, "lines"),
strip.background = element_blank(),
strip.text = element_blank(),axis.line.y=element_line(),
axis.line.x=element_line(),
axis.ticks.length.y=unit(-2,"mm"),
axis.ticks.length.x=unit(-2,"mm"),
plot.margin=unit(c(b = 5, t = 5, r = 5, l = 5),"mm")) +
facet_wrap(~facet, nrow = 3)
Created on 2020-02-22 by the reprex package (v0.3.0)
I've got three subplots I want to put together into one plot, and faceting would be a natural way to do it. However, one of these subplots would be easier/more natural to read with a reversed x-axis (whereas I'd like to leave the others alone). Is there a way to accomplish this using facet_grid() or facet_wrap()?
The other alternative I've considered is grid.arrange(), and the chief problem I've run into there is getting it to align the subplots based on plot area (inside the axes), rather than based on the edges of the images. (My axis titles and labels are not the same size, so the default behavior is fairly ugly.)
Edited to add a MWE with some data for context. Here, since larger is "better" for the beta and R-squared subplots, it would be more natural to reverse the axis for the p subplot. (In this case it would probably also be better to add the log transform to that scale, but my real problem doesn't need to get that fancy.)
df <- data.frame(z=c(rep("R-squared",15),rep("p",15),rep("beta",15)),
x=c(runif(15),exp(-runif(15,1,10)),rnorm(15,1,0.5)),
y=rep(letters[1:15],3))
plot <- ggplot(df) + geom_point(aes(x=x,y=y)) + facet_grid(.~z, scales="free_x", switch="x")
Here's a solution using patchwork
library(ggplot2)
library(dplyr)
df <- data.frame(z=c(rep("R-squared",15),rep("p",15),rep("beta",15)),
x=c(runif(15),exp(-runif(15,1,10)),rnorm(15,1,0.5)),
y=rep(letters[1:15],3))
p1 <- ggplot(filter(df, z == "beta"), aes(x, y)) +
geom_point()
p2 <- ggplot(filter(df, z == "p"), aes(x, y)) +
geom_point() +
scale_x_reverse() +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
p3 <- ggplot(filter(df, z == "R-squared"), aes(x, y)) +
geom_point() +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
#devtools::install_github("thomasp85/patchwork")
library(patchwork)
p1 + p2 + p3
I am trying to produce a circular "heatmap" in R, and found a solution with coord_polar, and how to distribute the labels around the plot.
My problem is that the labels around the plot seem to be centred and the long names are overlapping the plot. I can't use hjust and vjust to align the text to the edge of the plot.
My code and a subset of my data:
library(reshape)
library(ggplot2)
data <- data.frame(id=c("S_subsp_houtenae_str_ATCC_BAA-1581","S_Heidelberg_S_1_7","S_Haifa_S_11_3","S_Infantis_S_2_3","S_Newport_S_1_4","S_Bredeney_S_1_3","S_Saint_Paul_S_1_5","S_Bovismorbificans_S_3_8","S_Saintpaul_str_SARA26","S_London_S_6_7","S_Mbandaka_S_7_5","S_Corvallis_S_5_6","S_San_Diego_S_9_5","S_Javiana_str_10721"),
A.C2=c(0,0,0,0,0,0,0,0,0,0,0,2,0,0),Col156=c(0,0,0,0,0,4,0,0,0,0,0,0,0,0),
ColRNAI=c(0,8,0,0,8,8,8,0,8,0,0,0,0,0),FIB=c(0,0,0,0,10,0,0,10,10,0,0,0,0,0),
FII=c(0,0,0,0,0,0,0,12,12,0,0,0,0,0),HI2=c(0,15,0,0,15,15,0,0,0,0,0,0,0,0),
HI2A=c(0,15,0,0,15,15,0,0,0,0,0,0,0,0),I1=c(0,17,17,17,0,0,0,0,0,0,0,17,17,0),
I2=c(0,0,0,0,0,0,0,0,0,0,0,18,18,18),N=c(0,0,0,0,0,0,0,19,19,19,19,0,0,0),
P=c(20,20,20,20,20,20,20,0,0,0,0,0,0,0),Q1=c(0,22,0,0,22,0,0,0,0,0,0,22,0,0))
data <- transform(data,id=factor(id,levels=unique(id)))
data.m <- melt(data)
data.m$var2 = as.numeric(data.m$variable) + 15
y_labels = levels(data.m$variable)
y_breaks = seq_along(y_labels) + 15
sequence_length = length(unique(data.m$id))
first_sequence = c(1:(sequence_length%/%2))
second_sequence = c((sequence_length%/%2+1):sequence_length)
first_angles =c(90 - 180/length(first_sequence) * first_sequence)
second_angles = c(-90 - 180/length(second_sequence) * second_sequence)
Palette <- c("#f1f1f1","#302013","#614126","#58DB41","#638A5C","#62D585","#579134","#B8DD95","#9ED84D","#4B6FC8","#2A344D","#47689B","#315CEE","#D9AB68","#E09B33","#FE9E2A","#D97B0C","#6A2F45","#A02A77","#E1C73E","#D16F60","#C13420","#DA435C","#E20338","#000000","#999999")
p = ggplot(data.m, aes(x=id, y=var2, fill=factor(value))) +
geom_tile(colour="white") +
scale_fill_manual(values=Palette) +
scale_y_discrete(breaks=y_breaks, labels=y_labels) +
theme(panel.background=element_blank(),
axis.title=element_blank(),
panel.grid=element_blank(),
axis.text.x=element_text(angle= c(first_angles,second_angles),size=8),
axis.ticks=element_blank(),
axis.text.y=element_blank(),
legend.position="none")
p = p + coord_polar()
plot(p)
I've had similar issues in coord_polar() with labels not responding to either hjust= or vjust= and therefore not aligning as I'd like.
The solution to this, shown here https://stackoverflow.com/a/28846989/4340137, is to use geom_text() to manually label the data.
The example at the link provided does everything you need. Unfortunately, I just can't get it working quickly with your more complicated data structure and SO won't let me leave this as a comment.
Someone else may be able to edit to include the exact code.
In RStudio, when I run the following and zoom, all the labels are outside the circle except the longest one, which may mean the plot margin at the top is too tight (or you might consider shortening the name or using \n for a new line). I changed the axis.text.y argument to theme. I also couldn't get the odd legend in the top left to go away. Even so, the inserted plot suffers from the overlap problem you described.
ggplot(data.m, aes(x=id, y=var2, fill=factor(value))) +
geom_tile(colour="white") +
scale_fill_manual(values=Palette) +
scale_y_discrete(breaks=y_breaks, labels=y_labels) +
theme(panel.background=element_blank(), axis.title=element_blank(), panel.grid=element_blank(),
axis.text.x=element_text(angle= c(first_angles,second_angles),size=8, vjust=-1), # vjust=-1
axis.ticks=element_blank(), legend.position="none",
axis.text.y=element_text(vjust = -2), legend.position="none") +
coord_polar()
The y-axis title appears too close to the axis text.
ggplot(mpg, aes(cty, hwy)) + geom_point()
I have tried changing the value of many parameters with theme() but none seems to help.
From ggplot2 2.0.0 you can use the margin = argument of element_text() to change the distance between the axis title and the numbers. Set the values of the margin on top, right, bottom, and left side of the element.
ggplot(mpg, aes(cty, hwy)) + geom_point()+
theme(axis.title.y = element_text(margin = margin(t = 0, r = 20, b = 0, l = 0)))
margin can also be used for other element_text elements (see ?theme), such as axis.text.x, axis.text.y and title.
addition
in order to set the margin for axis titles when the axis has a different position (e.g., with scale_x_...(position = "top"), you'll need a different theme setting - e.g. axis.title.x.top. See https://github.com/tidyverse/ggplot2/issues/4343.
Based on this forum post: https://groups.google.com/forum/#!topic/ggplot2/mK9DR3dKIBU
Sounds like the easiest thing to do is to add a line break (\n) before your x axis, and after your y axis labels. Seems a lot easier (although dumber) than the solutions posted above.
ggplot(mpg, aes(cty, hwy)) +
geom_point() +
xlab("\nYour_x_Label") + ylab("Your_y_Label\n")
A solution that offers more fine-grained control than \n but is less cumbersome than adding margins is to use vjust in the theme function.
For adjusting the position on the y-axis or (x-axis) to add space, this often requires using a positive value for vjust (y-axis) or a negative value for vjust (x-axis) as in theme(axis.title.y = element_text(vjust = 2)). See a fully worked example below.
# load patchwork to show plots side-by-side
library(patchwork)
library(ggplot2)
# Plot A: just for comparison, moving titles *inward*
p1 <- ggplot(mpg, aes(cty, hwy)) +
geom_point() +
theme_gray() +
theme(
axis.title.y = element_text(vjust = -3),
axis.title.x = element_text(vjust = +3)
)
# Plot B: what we want, moving titles *outward*
p2 <- ggplot(mpg, aes(cty, hwy)) +
geom_point() +
theme_gray() +
theme(
axis.title.y = element_text(vjust = +3),
axis.title.x = element_text(vjust = -0.75)
)
# show plots side-by-side with patchwork package
p1 + p2 +
plot_annotation(tag_levels = "A")
For some reason the margin argument suggested by Didzis Elferts did not work for me. So, I used a different hack that is more flexible than adding an empty line but needs giving up the axis ticks.
myplot + theme(axis.ticks.x = element_blank(), axis.ticks.length.x = unit(3.25, "cm")
I guess, one can add the tick marks manually with geom_segment. Another possibility might be [ggalt::annotation_ticks][1]but I didn't bother trying either (note the current version of ggalt on CRAN (0.4) does not support this function, the one on github (0.6) does).