I'm plotting a correlation heatmap with x-axis on top by using switch_axis_position.
The x-axis labels are somewhat long, so I want it to be rotated by using angle=90 and align them by using hjust=0.
But this makes the labels too far from the x-axis and even gets them out of the plot area.
library(gtable)
library(cowplot)
library(grid)
heatmap<-ggplot(data=meltedh, aes(x=variable, y=X, fill=value))+
geom_tile(color="White")+
ylab("")+xlab("")+
scale_fill_gradient2(low="blue3", high="red3", mid="white",
midpoint=0,limit=c(-1,1), space="Lab", breaks=c(-0.5,0,0.5),
name="Correlation Coefficient")+
theme(legend.position="bottom",
axis.text.x=element_text(angle=90, hjust=0))
heatmap
ggdraw(switch_axis_position(heatmap,axis='x'))
How can I make this pretty? Any help would be great. Thanks.
Lucky for you I rather enjoy making up data.
So this might be what you want. I did the following things:
Played with hjust to get it close to looking okay
Padded the names with spaces to make them all the same length
Changed the font family to "mono", so the axis text would be aligned
library(gtable)
library(cowplot)
library(grid)
set.seed(1234)
cn <- c("Eastside","Pygrate","Tapeworm","Annerose","Bund",
"Mountain","Appalacia","Summer","Treasure","Riveria",
"Persia","Raggout","Bengal","Siam","Norman")
# Pad out the names with spaces to all be the same length
mxl <- max(nchar(cn))
fmt <- sprintf("%%-%ds",mxl) # the minus adds spaces to the string end
cn <- sprintf(fmt,cn)
rn <- rev(letters[1:16])
ddf <- expand.grid( x=rn, y=cn )
n <- nrow(ddf)
ddf$v <- runif(n,-1,-0.1)
nr <- n/length(cn)
ddf[ddf$y==cn[3],]$v <- runif(nr,0.1,0.8)
ddf[ddf$y==cn[8],]$v <- runif(nr,0.1,0.8)
ddf[ddf$y==cn[13],]$v <- runif(nr,0.1,0.8)
ddf[ddf$x %in% c("i","j","n","o"),]$v <- 0
meltedh <- data.frame(X=ddf$x,variable=ddf$y,value=ddf$v)
heatmap<-ggplot(data=meltedh, aes(x=variable, y=X, fill=value))+
geom_tile(color="White")+
ylab("")+xlab("")+
scale_fill_gradient2(low="blue3", high="red3", mid="white",
midpoint=0,limit=c(-1,1), space="Lab", breaks=c(-0.5,0,0.5),
name="Correlation Coefficient")+
theme(legend.position="bottom",
axis.text.x=element_text(angle=90, hjust=0.5,family="mono"))
heatmap
ggdraw(switch_axis_position(heatmap,axis='x'))
It yields this:
Related
I am using the ndodge function explained by #jan-glx here;
https://stackoverflow.com/a/60650595/13399047
However I could not figure out how to align the axis ticks aligned as for example;
I should probably use theme(axis.ticks.length=) but I am not sure how to do it in an even/odd way.
Please help!
As far as I am aware there is no build in way to do this in ggplot, though that might change when they rewrite the guide system.
It is neither pretty nor easy, but here is an example how you could do it by messing around in the gtable / grid.
library(ggplot2)
library(grid)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
g <- ggplot(diamonds, aes(cut, carat)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(n.dodge = 2))
# Convert to gtable
gt <- ggplotGrob(g)
# Grab bottom axis
is_axis <- grep("axis-b", gt$layout$name)
axisgrob <- gt$grobs[is_axis][[1]]
axis <- axisgrob$children$axis
# Grab tickmarks
is_ticks <- which(vapply(axis$grobs, inherits, logical(1), "polyline"))
ticks <- axis$grobs[[is_ticks]]
# Modify tickmarks
labelheight <- axis$heights[[2]] # First row of labels
modify <- which(seq_along(ticks$y) %% 4 == 0) - 1 # Change every the 3rd item in every quadruplet
ticks$y[modify] <- ticks$y[modify] - labelheight
# Insert ticks back into axis back into table
axis$grobs[[is_ticks]] <- ticks
axisgrob$children$axis <- axis
gt$grobs[[is_axis]] <- axisgrob
# Plot
grid.newpage()
grid.draw(gt)
Created on 2020-05-18 by the reprex package (v0.3.0)
Here is a solution using just ggplot2 stuff and not modifying any grobs. It requires ggplot2 3.0.0 and is based off https://stackoverflow.com/a/51312611/6615512
library(ggplot2)
data(diamonds)
diamonds$cut <- paste("Super Dee-Duper",as.character(diamonds$cut))
tick_min_pos_odd = -0.6
tick_min_pos_even = -0.4
custom_ticks = data.frame(cut = sort(unique(diamonds$cut)))
n_discrete_x_values = nrow(custom_ticks)
# Alternate tick lengths
custom_ticks$tick_min_pos = ifelse(1:n_discrete_x_values %% 2 == 0, tick_min_pos_odd, tick_min_pos_even)
ggplot(diamonds, aes(cut, carat)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
geom_linerange(data = custom_ticks, # The custom tickmarks
aes(x=cut, ymax=-0.25, ymin=tick_min_pos),
size=0.5, color='black',
inherit.aes = F) +
coord_cartesian(clip='off', ylim=c(0,NA)) + # Clip off makes it so the geoms can be drawn outside the plot
# ylim sets the y-axis from 0 to the max.
theme(plot.margin = margin(0,0,20,0), # Add some whitespace to the bottom of the plot
axis.title.x = element_text(vjust=-1.5), # nudge the x-axis title and text down a tad
axis.text.x = element_text(vjust=-1.5))
I was trying to make 2 separate plots which I want to present side by side in my poster (I need to make them separate and cannot make use of facet_wrap). One of the plots has several boxplots, while the second plot only has one. How can I manipulate the width of the boxplots such that the second boxplot is the same dimension as the width of any one of the individual boxplots in plot 1, when I put the two plots side by side? A reproducible example:
tvalues <- sample(1:10000,1200)
sex <- c(rep('M',600),rep('F',600))
region <- c('R1','R2','R3','R4','R5')
df1 <- data.frame(tvalues,sex,region)
tvalues2 <- sample(1:10000,200)
sex2 <- sample(c('M','F'),200,replace=T)
region2 <- 'R6'
df2 <- data.frame(tvalues2,sex2,region2)
p1 <- ggplot(data=df1,aes(x=region,y=tvalues,color=sex)) +
geom_boxplot(width=0.5)
p2 <- ggplot(data=df2,aes(x=region2,y=tvalues2,color=sex2)) +
geom_boxplot(width=0.5)
Plot 1
Plot2
I suggest to divide the width of boxes in the second plot by the number of categories of region in the first plot.
p2 <- ggplot(data=df2,aes(x=region2,y=tvalues2,color=sex2)) +
geom_boxplot(width=0.5/length(unique(df1$region)))
In case of a single boxplot like in the following example:
a<- data.frame(obs=rep("A", 50),
value=rnorm(50, 100, 50))
ggplot(a, aes(y=value))+
geom_boxplot()
Wide boxplot
We can establish a false x/y axis and establish an axis limit so the width option of geom_boxplot() determines the width of the box
ggplot(a, aes(y=value, x=0))+
geom_boxplot(width=0.7) +
xlim(-1,1)
Thinner boxplot
You can add the following to remove all x.axis text and ticks
theme(theme(axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank())
I'm trying to display three time series using facet_grid() and in order to save space, I'm reducing panel spacing between them. The problem is that their vertical axis overlap so I want to move it to the right only on the plot in the middle.
Since this seem impossible in ggplot2, what I'm trying to do is to render every axis and then remove it editing the gtable but so far I was not successful.
This is a minimal example:
library(ggplot2)
set.seed(123)
df <- data.frame(expand.grid(x = 1:150, type = letters[1:3]))
df$y <- df$x*0.016 + rnorm(150, sd = .5)
ggplot(df, aes(x, y)) + geom_line() +
facet_grid(type~.) +
theme(panel.spacing.y = unit(-3, "lines"), strip.text = element_blank()) +
scale_y_continuous(sec.axis = dup_axis(name = ""), name = "y")
Which produces this:
And I want to delete each axis text to get to this:
Thanks!
The solution was to assign a nullGrob() to the relevant elements of the gTable.
gt <- ggplotGrob(g)
t <- gt[["grobs"]][[8]][["children"]][[2]]
# Found those grobs by looking around the table.
gt[["grobs"]][[8]][["children"]][[2]] <- nullGrob()
gt[["grobs"]][[10]][["children"]][[2]] <- nullGrob()
gt[["grobs"]][[12]][["children"]][[2]] <- nullGrob()
grid.newpage()
grid.draw(gt)
I am trying to create a facet_grid() in ggplot() and am having issues with the margins of my plot. I am using grid.draw() for my final plot, and cannot figure out how to adjust the margins for printing. When I save my plot, it appears fine (see below). However, when I actually print my plot out to hard copy, half of the X&Y labels and my plot title are cut off.
I've attempted using par() to no avail. Here is a reproducible example, similar to my actual plot. I need to keep the panel <- off part because in my actual plot, I have plotted numbers above each bar and they get cut off by the facet sides for days at the beginning/end of each month. I'm thinking this might be the root of the issue, but I'm not really sure to be honest.
data(airquality)
library(stats)
library(ggplot2)
library(gtable)
library(gridExtra)
library(grid)
library(dplyr)
library(scales)
facet <- ggplot() +
geom_bar(data=airquality,aes(y=Wind,x=Day,fill=Temp),colour="black",stat="identity",position='stack') +
#theme_bw() +
facet_grid(~Month) +
theme(axis.title.x=element_text(face="bold",size=14),axis.title.y=element_text(face="bold",size=14),axis.text.x=element_text(face="bold",size=10),axis.text.y=element_text(face="bold",size=10))+
ylab("Wind") +
theme(panel.margin = unit(5, "mm"),panel.border=element_rect(color="black",fill=NA),panel.background = element_rect(fill="grey84"),plot.title = element_text (size=20,face="bold"),legend.position="right",panel.grid.minor=element_blank(),strip.text.x=element_text(size=12,face="bold"),strip.background=element_rect(fill=NA,colour="black"),legend.title=element_text(size=14,face="bold")) +
ggtitle("Test")
gt <- ggplot_gtable(ggplot_build(facet))
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
Thanks for any and all help! Please, let me know if you need any clarification or have any questions.
you have two options:
set some margins in the ggplot theme
assign a viewport of specific size to the plot/gtable, smaller than the device window by some margin. Here's an illustration of using both strategies at once
library(grid)
fig_size <- c(6, 4) # inches
margin <- unit(4, "line")
p <- ggplot() + theme(plot.background=element_rect(colour="red", size=2, fill="grey50"),
plot.margin = unit(1:4, "line"))
g <- ggplotGrob(p)
g$vp <- viewport(width = unit(fig_size[1], "in") - margin, height=unit(fig_size[2],"in")- margin)
ggsave("plot.pdf", g, width=fig_size[1], height=fig_size[2])
I have the following dual plot (from another SO question):
Here's the code that generates the plot:
library(ggplot2)
library(gtable)
df <- data.frame(x=c(5,2,7,3),
y=c("asdasxfqwe","a","b","c"),
facet=c(1,1,2,2))
# First plot (a bit of extra space between facets)
p <- ggplot(df, aes(x, y)) + facet_grid(~facet) +
geom_point() +
theme(panel.margin = unit(4, "lines"),
axis.text.y = element_text( hjust=0.5))
# get y-axis labels
g <- ggplotGrob(p)
axis <- gtable_filter(g, "axis-l")[["grobs"]][[1]][["children"]][["axis"]][,1]
# remove axis
g[["grobs"]][[4]][["children"]][["axis"]] <- NULL
# build plot & add axis to LHS of left facet
panels <- subset(g$layout, name == "panel")
g <- gtable_add_grob(g, grobs=axis, t = unique(panels$t),
l=tail(panels$l, -1)-1)
grid.newpage()
grid.draw(g)
As I understand, the empty space on the left is where the y-axis text used to be before it was moved using the gtable code. How to get rid of this empty space?
Upgraded comment:
Since you're editing the gtable, you can set the relevant width to something smaller,
g[["widths"]][3] <- list(unit(1, "line"))