ggplot how to control fonts on boxplot with stat - r

How do I control the font-family and size for text elements added to my boxplot:
Following the approach in this question, I have implemented the following code to show the number of observations:
library("ggplot2")
v_min <- -1
v_max <- 3.5
increm <- 0.5
y_limits <- c(v_min, v_max)
increms <- seq(v_min, v_max, increm)
counts <- function(x){
# you can experiment with 'adjust' and 'max-value' to find the perfect position
adjust <- 0.95
return(c(y = adjust * v_max, label = length(x)))
}
ggplot(d1, aes(x = ONRC_Hierarchy, y=lwpMeanRut_inc)) +
geom_boxplot(outlier.alpha = 0.2, outlier.size = 0.5) +
geom_hline(aes(yintercept=0), color="blue", linetype="dotted", size=1)+
stat_summary(fun.data = counts, geom = "text") +
scale_y_continuous(limits = y_limits, breaks = increms) +
xlab("") +
ylab("Rut Increment (mm/year)\n") +
theme_minimal() +
theme(
text = element_text(size = 10, family = "mono"),
axis.text.x=element_text(angle = -35, hjust = 0),
panel.grid.major.y = element_line(color = "lightgray",
size = 0.15,linetype = 2),
panel.grid.minor.y = element_blank(),
panel.grid.major.x = element_blank())
This solution works, as shown in the plot below, except that the font is different from the other graph elements. As you can see, I have tried to control this with the theme() statement, but it does not seem to work. Note, I deliberately used a small mono font to show the difference in the number of observations labels and the other graph elements.

Geom/stat fonts are set in the geom/stat layer, not in theme(). In this case, you can add family = "mono" as an argument to your stat_summary().
The size of fonts in geom_text/geom_label etc. is not on the same scale as element_text theme options, which are in points. You can read more about that here and here.

Related

ggplot2: combine fill and alpha legends

There are many questions out there pertaining to this topic, but none of the answers I have tried have worked for me so far.
I have a plot that is a heatmap with fill and alpha mapped to different values, i.e. different variables in my data create different colors and alpha values. I want to get a finished product here to see if this figure is worthwhile, so let's not discuss whether this is a good idea at the moment.
What I want to do is combine my fill and alpha legend such that I have the four different transparencies of blue, the four different transparencies of red, and for yellow. I can get those legends separately, or just one of them, but not two in one.
My best guess for code thus far has been
dummy <- data.frame(model=c(rep("X",23),rep("Y",23)),
longvarname=rep(c("CBH","NDMI","CovType","CH","CBD","NDVI_NF_750","Slope","TPI_Valley_1200", "TPI_Ridge_1200",
"TPI_Ridge_100","TPI_Valley_100", "TSHarv","Treat","RxBurn",
"TSTreat","TSRx","Deficit","SpecHumid","MaxRH","MinTemp","MaxTemp", "MaxGustDir", "MaxGustSpd"),2),
vargrp=rep(c(rep("Veg",6), rep("Topo",5), rep("Mgmt",5),rep("Clim",7)),2),
value=runif(46, min=0, max=1),
binary_slope=sample(c("negative","positive", "zero"), 46, replace=TRUE))
ggplot(dummy, aes(x=model, y=longvarname)) +
geom_tile(aes(fill=binary_slope, alpha=value))+
scale_alpha_binned(breaks=c(0.4, 0.6, 0.8, 1))+
facet_grid(vargrp~., scales='free_y', space="free_y")+
xlab("Model")+
ylab("Variable")+
scale_fill_manual(values=c("midnightblue","yellow1","red4"))+
# guides(fill=guide_legend(override.aes = list(fill=c(rep("#191970",4),
# rep("#FFEA00",4),
# rep("#8b0000",4)),
# alpha=rep(c(0.4,0.6,0.8,1),3))))+
theme(panel.background = element_blank(),
axis.text.x = element_text(angle = 45, hjust=1),
strip.text.y = element_blank(),
axis.ticks = element_blank())
The above code produces both legends which you can see in the example I attached. If you uncomment the guides() lines, the error I am getting is Error in [[<-.data.frame(*tmp*, i, value = c("#191970", "#191970", :
replacement has 12 rows, data has 3.
But most of my efforts have just resulted in only the fill legend at alpha=1. Another thought I had which I thought might get me there was in guides(), putting the alpha hex codes in front of each color hex code and then making alpha guide = "none", but no dice.
Thanks very much for your help!
Instead of making use of both fill and alpha one option would be to make use of just fill like so:
Add a column with your desired fill colors to your dataset using e.g. a left_join.
Manually compute your alpha levels using e.g. cut.
Adjust the transparency of th colors according to the alpha values using colorspace::adjust_transparency
Map the resulting colors on the fill aes and make use of scale_fill_identity. Add guide=guide_legend to get a legend.
library(ggplot2)
library(dplyr)
library(colorspace)
cols <- c(negative = "midnightblue", positive = "yellow1", zero = "red4")
cols <- tibble::enframe(cols, name = "binary_slope", value = "fill")
dummy <- left_join(dummy, cols, by = "binary_slope")
dummy <- mutate(dummy,
alpha = cut(value, breaks = c(0, 0.4, 0.6, 0.8, 1), labels = c(0.4, 0.6, 0.8, 1)),
alpha = as.numeric(as.character(alpha)),
fill = colorspace::adjust_transparency(fill, alpha)
)
ggplot(dummy, aes(x = model, y = longvarname)) +
geom_tile(aes(fill = fill)) +
scale_fill_identity(guide = guide_legend()) +
facet_grid(vargrp ~ ., scales = "free_y", space = "free_y") +
xlab("Model") +
ylab("Variable") +
theme(
panel.background = element_blank(),
axis.text.x = element_text(angle = 45, hjust = 1),
strip.text.y = element_blank(),
axis.ticks = element_blank()
)

Left-adjust (hjust = 0) vertical x axis labels on facets with free scale

I have decided to rephrase this question. (Editing would have taken more time and in my opinion would also not have helped the OP.)
How can one left-adjust (hjust = 0, i.e., in text direction) over facets, when scale = 'free_x'?
I don't really think that left-adjustment of x-labels is a very necessary thing to do (long labels generally being difficult to read, and right-adjusting probably the better choice) - but I find the problem interesting enough.
I tried with empty padding to the maximum character length, but this doesn't result in the same length for all strings. Also, setting axis.text.x = element.text(margin = margin()) doesn't help. Needless to say, hjust = 0 does not help, because it is adjusting within each facet.
library(ggplot2)
diamonds$cut_label <- paste("Super Dee-Duper", as.character(diamonds$cut))
ggplot(data = diamonds, aes(cut_label, carat)) +
facet_grid(~ cut, scales = "free_x") +
theme(axis.text.x = element_text(angle = 90))
The red arrows and dashed line indicate how the labels should adjust. hjust = 0 or margins or empty padding do not result in adjustment of those labels over all facets.
Data modification from this famous question
I tried with empty padding to the maximum character length, but this
doesn't result in the same length for all strings.
This caught my attention. Actually, it would result in the same length for all strings if you padded the labels with spaces, made them all the same length, and ensured the font family was non-proportionally spaced.
First, pad the labels with spaces such that all labels have the same length. I'm going to ustilise the str_pad function from the stringr package.
library(ggplot2)
data("diamonds")
diamonds$cut_label <- paste("Super Dee-Duper", as.character(diamonds$cut))
library(stringr)
diamonds$cut_label <- str_pad(diamonds$cut_label, side="right",
width=max(nchar(diamonds$cut_label)), pad=" ")
Then, you may need to load a non-proportionally-spaced font using the extrafont package.
library(extrafont)
font_import(pattern='consola') # Or any other of your choice.
Then, run the ggplot command and specify a proportionally spaced font using the family argument.
ggplot(data = diamonds, aes(cut_label, carat)) +
facet_grid(~cut, scales = "free_x") +
theme(axis.text.x = element_text(angle = 90, family="Consolas"))
One way, and possibly the most straight forward hack, would be to annotate outside the coordinates.
Disadvantage is that the parameters would need manual adjustments (y coordinate, and plot margin), and I don't see how to automate this.
library(ggplot2)
diamonds$cut_label <- paste("Super Dee-Duper", as.character(diamonds$cut))
ann_x <- data.frame(x = unique(diamonds$cut_label), y = -16, cut = unique(diamonds$cut))
ggplot(data = diamonds, aes(cut_label, carat)) +
facet_grid(~cut, scales = "free_x") +
geom_text(data = ann_x, aes(x, y, label = x), angle = 90, hjust = 0) +
theme(
axis.text.x = element_blank(),
plot.margin = margin(t = 0.1, r = 0.1, b = 2.2, l = 0.1, unit = "in")
) +
coord_cartesian(ylim = c(0, 14), clip = "off")
Created on 2020-03-14 by the reprex package (v0.3.0)
I'd approach this by making 2 plots, one of the plot area and one of the axis labels, then stick them together with a package like cowplot. You can use some theme settings to disguise the fact that the axis labels are actually made by a geom_text.
The first plot is fairly straightforward. For the second which becomes the axis labels, use dummy data with the same variables and adjust spacing how you want via text size and scale expansion. You'll probably also want to mess with the rel_heights argument in plot_grid to change the ratio of the two charts' heights.
library(ggplot2)
library(cowplot)
p1 <- ggplot(diamonds, aes(x = cut_label, y = carat)) +
facet_grid(cols = vars(cut), scales = "free_x") +
theme(axis.text.x = element_blank()) +
labs(x = NULL)
axis <- ggplot(dplyr::distinct(diamonds, cut_label, cut), aes(x = cut_label, y = 1)) +
geom_text(aes(label = cut_label), angle = 90, hjust = 0, size = 3.5) +
facet_grid(cols = vars(cut), scales = "free_x") +
scale_x_discrete(breaks = NULL) +
scale_y_continuous(expand = expansion(add = c(0.1, 1)), breaks = NULL) +
labs(y = NULL) +
theme(strip.text = element_blank(),
axis.text.x = element_blank(),
axis.ticks = element_blank(),
panel.background = element_blank())
plot_grid(p1, axis, ncol = 1, axis = "lr", align = "v")
We can edit the text grobs after generating the plot, using library(grid).
g <- ggplot(data = diamonds, aes(cut_label, carat)) +
facet_grid(~cut, scales = "free_x") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
gt <- cowplot::as_gtable(g)
axis_grobs <- which(grepl("axis-b", gt$layout$name))
labs <- levels(factor(diamonds$cut_label))[order(levels(diamonds$cut))]
for (i in seq_along(axis_grobs)) {
gt$grobs[axis_grobs[i]][[1]] <-
textGrob(labs[i], y = unit(0, "npc"), just = "left", rot = 90, gp = gpar(fontsize = 9))
}
grid.draw(gt)

How to manually edit a grid.arrange, ggplot_gtable and facet?

I'm ploting a Hydrograph but I additionally use facet_grid in R because I have objects with common features.
But when I use facet_grid the plot gets distorted, as shown in the figure below. How can I randerize this?
Note that it is not aligned properly, the scale of the y axis is scrambled, etc.
Among the adjustments I tried, I realized that it is possible to greatly improve this plot. I've created an image based on the above plot, some other attempts on how I'm trying and making some adjustments to paint to demonstrate what I'm trying to do.
Here's my code:
library(ggplot2)
library(grid)
library(gridExtra)
g1 <- ggplot(data_cet,
aes(x = Periodo,
y = Ind_plu)) +
geom_bar(stat = 'identity',
fill = "blue",
position = position_dodge()) +
ylab("Precip.") +
scale_y_reverse(labels = scales::comma) +
theme_bw() +
theme(axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank())
g2 <- ggplot(data_cet,
aes(x = Periodo,
y = Nivel,
colour = Bomba)) +
geom_line(aes(group = 1)) +
scale_color_manual(values = c("#0B775E", "#35274A", "#F2300F")) +
labs(colour = "Status CMB") +
facet_grid(data_cet$arranjo + data_cet$Bacia ~.) +
scale_x_date(breaks = datebreaks_m,
labels = date_format("%b/%y")) +
xlab('Período') + ylab('% Nível') +
theme_bw() +
theme(axis.text.x = element_text(face = "plain",
color = "black",
angle = 90),
axis.text.y = element_text(face = "plain",
color = "black"),
legend.title = element_blank(),
strip.background = element_blank(),
legend.position = "bottom")
g1 <- ggplot_gtable(ggplot_build(g1))
g2 <- ggplot_gtable(ggplot_build(g2))
maxWidth = unit.pmax(g1$widths[2:3], g2$widths[2:3])
g1$widths[2:3] <- maxWidth
g2$widths[2:3] <- maxWidth
plot_hyd <- grid.arrange(g1, g2, ncol = 1, heights = c(1, 3))
ggsave(file = "plot_hyd4.pdf", plot_hyd)
My dataset is too large, my apologize for not showing the dataset and dput().
You could add a widths = c(0.9, 1) to grid.arrange (fiddle with the first number some) to get your graphs to line up along the right side.
Otherwise, ggsave your file to a larger pdf. Your element_text objects, such as the legend, are absolute sizes, so if you scale up the pdf dimensions your graphs will look larger by comparison.
The exact values of widths and ggsave(width, height) are going to depend on you data, and unfortunately will take some trial and error. If you're using something like RStudio, I suggest fiddling with the grid.arrange call and finding the widths argument you like before calling ggsave. When you are ready to experiment with different ggsave width and height arguments, run it at a lower dpi the first few times so it processes more quickly.
Note that since you haven't included your data, I haven't tried to recreate this problem - this is just how I've solved this kind of issue in the past. If these suggestions don't work for you, let me know and I can use some built-in datasets to find another solution
Following the logic of the #Pintintended tip for the code. I adopted the layout_matrix argument.
>
plot_hyd <- grid.arrange(g1, g2,
layout_matrix = rbind(c(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,NA),
c(2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2),
c(2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2),
c(2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2),
c(2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2),
c(2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2),
c(2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2)))
#ggsave(file="plot_hyd4.jpeg",plot_hyd,width=13,height=16,dpi=200)

ggplot2 2D Density plot - the gradient fill is too smooth

I am having some difficulty with the ggplot2 package and the gradient fill. For my data with low number of data points, its gradient and density intensity doesn't really match. Here is an example:
The code I am using is:
pt <- read.xlsx("plots.xlsx", sheetName = "PT1_TB varseq", stringsAsFactors=FALSE)
ggplot(pt, aes(x=pt$BAF, y=pt$LogR) ) +
stat_density_2d(aes(fill = ..density..), geom = "raster", contour = FALSE) +
scale_fill_distiller(palette= "Spectral", direction=-1) +
scale_y_continuous(name="LogR", limits = c(-0.8, 0.6), breaks = seq(-0.8, 0.6, 0.2)) +
scale_x_continuous(name="BAF", breaks = seq(0, 0.8, 0.2)) +
theme(
legend.position='none',
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black")
) +
geom_point(aes(shape = factor("cyl")), size = 1) + scale_shape(solid = FALSE)
I would like the gradient to change more abruptly, for example, I would like to see more seperation in colors between points at (0;0.2) and (0.25;-0.2). Furthermore the yellow color in the middle where no points are should be blue.
While I am at it, does anybody know how remove the white gap between the axes and the actual plot?
Thanks in advance :)
It would help if you could provide a reproducible example. However, to drive the point in the comment by #RichardTelford home, here's an example which leverages the manipulate package to interactively set the h bandwidth parameters, in addition to n -- the number of grid points.
library(ggplot2)
library(manipulate)
manipulate(
ggplot(faithful, aes(x = eruptions, y = waiting)) +
geom_point() +
xlim(0.5, 6) +
ylim(40, 110) +
stat_density_2d(geom = "raster", aes(fill = ..density..), contour = F,
h = c(x_bandwidth, y_bandwidth),
n = grid_points) +
scale_fill_distiller(palette = "Spectral", direction = -1),
x_bandwidth = slider(0.1, 20, 1, step = 0.1),
y_bandwidth = slider(0.1, 20, 1, step = 0.1),
grid_points = slider(1, 100, 16)
)
So our plain-vanilla (default) plot looks like this:
We can interactively change the parameters using the pop-up menu accessible from the gear icon:

Changing line color when I have a geom_errorbar with ggplot

I have the following code:
library(ggplot2)
library(gridExtra)
data = data.frame(fit = c(9.8,15.4,17.6,21.6,10.8), lower = c(7.15,12.75,14.95,18.95,8.15), upper = c(12.44,18.04,20.24,24.24,13.44), factors = c(15,20,25,30,35), var = rep("Fator", 5))
gp <- ggplot(data, aes(x=factors, y=fit, ymax=upper, ymin=lower))
gp <- gp + geom_line(aes(group=var),size=1.2) +
geom_errorbar(width=.8, size=1, aes(colour='red')) +
geom_point(size=4, shape=21, fill="grey") +
labs(x = paste("\n",data$var[1],sep=""), y =paste("Values","\n",sep="")) +
theme(legend.position = 'none', axis.text = element_text(size = 11), plot.margin=unit(c(0.4,0.4,0.4,0.4), "cm"), axis.text.x = element_text(angle=45, hjust = 1, vjust = 1)) +
ylim((min(data$lower)), (max(data$upper)))
I want to change the line color after I have the ggplot object. I'm trying:
gp + scale_color_manual(values = "green")
but it change the error bar color and not the line color.
1)What should I do to change the line color?
2)How can I change the points color?
Thanks!
Try this:
gp$layers[[1]] <- NULL
gp + geom_line(aes(group = var),color = "green",size = 1.2)
A similar technique should work for the points layer. Technique was dredged up from my memories of a similar question.
I just looked at the contents of gp$layers manually to see which was which. I presume that the order will be the order in which they appear in your code, but I wouldn't necessarily rely on that.

Resources