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)
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).
I'm working with a plot analogous to the following:
ggplot(data=mtcars, aes(x=wt, y=mpg, color=carb)) +
geom_line() + facet_grid(gear ~ .) +
ggtitle(expression("Title")) +
labs(caption = "Sources: Compustat, Author's Calculations") +
theme(plot.title = element_text(size = 20, hjust = 0.5),
plot.caption=element_text(size=8, hjust=.5),
strip.background = element_blank(),
strip.text = element_blank(),
legend.title = element_blank())
I'm trying to do the following:
Insert a legend beneath each of the 3 facets, each legend specific to the facet above it.
Insert one plot title (as opposed to the same title above each facet).
Insert one caption beneath the final facet (as opposed to three captions beneath each facet).
I was able to reproduce this example on assigning a legend to each facet.
However, the plot title was placed above and the caption below each facet. Also, this example uses facet_wrap and not facet_grid.
Thank you in advance.
library(dplyr)
library(ggplot2)
tempgg <- mtcars %>%
group_by(gear) %>%
do(gg = {ggplot(data=., aes(x=wt, y=mpg, color=carb)) +
geom_point() +
labs(x = NULL) +
guides(color = guide_colorbar(title.position = "left")) +
theme(plot.title = element_text(size = 20, hjust = 0.5),
plot.caption=element_text(size=8, hjust=.5),
legend.position = "bottom")})
tempgg$gg[1][[1]] <- tempgg$gg[1][[1]] + labs(title = "Top title")
tempgg$gg[3][[1]] <- tempgg$gg[3][[1]] + labs(x = "Axis label", caption = "Bottom caption")
tempgg %>% gridExtra::grid.arrange(grobs = .$gg)
This isn't the most elegant way to do it. Each of the three grobs gets an equal space when you grid.arrange them, so the first and last ones are squished from the title and caption taking up space. You could add something like heights = c(3,2,3) inside the grid.arrange call, but you'd have to fiddle with each of the heights to get it to look right, and even then it would be a visual approximation, not exact.
To do it the more precise way, you'd need to look at the underlying gtables in each of the grobs. https://stackoverflow.com/users/471093/baptiste is the expert on that.
Update:
I used a #baptiste solution, which is still not particularly elegant, but gives you the same plot space for each panel. Use this snippet in place of the last line above.
tempggt <- tempgg %>% do(ggt = ggplot_gtable(ggplot_build(.$gg))) %>% .$ggt
gg1 <- tempggt[[1]]
gg2 <- tempggt[[2]]
gg3 <- tempggt[[3]]
gridExtra::grid.arrange(gridExtra::rbind.gtable(gg1, gg2, gg3))
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()