How to use subscripts in ggplot2 legends [R] - r

Can I use subscripts in ggplot2 legends? I see this question on greek letters in legends and elsewhere, but I can't figure out how to adapt it.
I thought that using expression(), which works in axis labels, would do the trick. But my attempt below fails. Thanks!
library(ggplot2)
temp <- data.frame(a = rep(1:4, each = 100), b = rnorm(4 * 100), c = 1 + rnorm(4 * 100))
names(temp)[2:3] <- c("expression(b[1])", "expression(c[1])")
temp.m <- melt(temp, id.vars = "a")
ggplot(temp.m, aes(x = value, linetype = variable)) + geom_density() + facet_wrap(~ a)

The following should work (remove your line with names(temp) <-...):
ggplot(temp.m, aes(x = value, linetype = variable)) +
geom_density() + facet_wrap(~ a) +
scale_linetype_discrete(breaks=levels(temp.m$variable),
labels=c(expression(b[1]), expression(c[1])))
See help(scale_linetype_discrete) for available customization (e.g. legend title via name=).

If you want to incorporate Greek symbols etc. into the major tick labels, use an unevaluated expression.
For a bar graph, i did the following:
library(ggplot2)
data <- data.frame(names=tolower(LETTERS[1:4]),mean_p=runif(4))
p <- ggplot(data,aes(x=names,y=mean_p))
p <- p + geom_bar(colour="black",fill="white")
p <- p + xlab("expressions") + scale_y_continuous(expression(paste("Wacky Data")))
p <- p + scale_x_discrete(labels=c(a=expression(paste(Delta^2)),
b=expression(paste(q^n)),
c=expression(log(z)),
d=expression(paste(omega / (x + 13)^2))))
p

Related

Handle ggplot2 axis text face programmatically

(x-posted to community.rstudio.com)
I'm wondering if it's possible to change the axis text in ggplot2 programatically or if there is some native way to do this in ggplot2. In this reprex, the idea is that I want to bold the axis text of a variable y that has an absolute value of x over 1.5. I can add it in manually via theme(), and that works fine:
library(ggplot2)
library(dplyr)
library(forcats)
set.seed(2939)
df <- data.frame(x = rnorm(15), y = paste0("y", 1:15), group = rep(1:3, 5))
df <- mutate(df, big_number = abs(x) > 1.5, face = ifelse(big_number, "bold",
"plain"))
p <- ggplot(df, aes(x = x, y = fct_inorder(y), col = big_number)) + geom_point() +
theme(axis.text.y = element_text(face = df$face))
p
Plot 1 with no facets
But if I facet it by group, y gets reordered and ggplot2 has no idea how face is connected to df and thus y, so it just bolds in the same order as the first plot.
p + facet_grid(group ~ .)
Plot 2 with facets
And it's worse if I use a different scale for each.
p + facet_grid(group ~ ., scales = "free")
Plot 3 with facets and different scales
What do you think? Is there a general way to handle this that would work consistently here?
Idea: Don't change theme, change y-axis labels. Create a call for every y with if/else condition and parse it with parse.
Not the most elegant solution (using for loop), but works (need loop as bquote doesn't work with ifelse). I always get confused when trying to work with multiple expressions (more on that here).
Code:
# Create data
library(tidyverse)
set.seed(2939)
df <- data.frame(x = rnorm(15), y = paste0("y", 1:15), group = rep(1:3, 5)) %>%
mutate(yF = fct_inorder(y),
big_number = abs(x) > 1.5)
# Expressions for y-axis
# ifelse doesn't work
# ifelse(df$big_number, bquote(bold(1)), bquote(plain(2)))
yExp <- c() # Ignore terrible way of concatenating
for(i in 1:nrow(df)) {
if (df$big_number[i]) {
yExp <- c(yExp, bquote(bold(.(as.character(df$yF[i])))))
} else {
yExp <- c(yExp, bquote(plain(.(as.character(df$yF[i])))))
}
}
# Plot with facets
ggplot(df, aes(x, yF, col = big_number)) +
geom_point() +
scale_y_discrete(breaks = levels(df$yF),
labels = parse(text = yExp)) +
facet_grid(group ~ ., scales = "free")
Result:
Inspired by #PoGibas, I also used a function in scale_y_discrete(), which works, too.
bold_labels <- function(breaks) {
big_nums <- filter(df, y %in% breaks) %>%
pull(big_number)
labels <- purrr::map2(
breaks, big_nums,
~ if (.y) bquote(bold(.(.x))) else bquote(plain(.(.x)))
)
parse(text = labels)
}
ggplot(df, aes(x, fct_inorder(y), col = big_number)) +
geom_point() +
scale_y_discrete(labels = bold_labels) +
facet_grid(group ~ ., scales = "free")

r ggplot use plotmath expressions dynamically

I want to change axis labels dynamically using ggplot. The code below is a simple version of what I'd like to do. It correctly displays a degree symbol in the y axis.The commented out ylab lines of code are what I'd like to do but fail. I want to create the plotmath code, assign it to a variable (e.g. yLabel) and then have ggplot interpret it.
library(data.table)
library(ggplot2)
DT <- data.table(timeStamp=c(1:12), ColN1=runif(12, 0, 10))
DT.long <- data.table::melt(
DT, id.vars = c("timeStamp"))
yLabel <- "Temperature~(~degree~F)"
yLabel1 <- expression("Temperature~(~degree~F)")
p <- ggplot(data = DT.long, aes(x = timeStamp, y = value)) +
xlab("Time") +
# ylab( expression(paste("Value is ", yLabel,","))) +
# ylab(yLabel) +
# ylab(yLabel1) +
ylab(Temperature~(~degree~F)) +
scale_y_continuous() +
theme_bw() +
geom_line()
print(p)
Use bquote
Here is your dynamic component
temp <- 12
Assign it to the label using
ylab(bquote(Temperature ~is ~ .(temp) ~(degree~F)))
Or to address your additional question below
V = "Temperature is ("~degree~"F)"
W = "depth is ("~degree~"C)"
ggplot(data = DT.long, aes(x = timeStamp, y = value)) +
xlab("Time") +
ylab(bquote(.(V)))
ggplot(data = DT.long, aes(x = timeStamp, y = value)) +
xlab("Time") +
ylab(bquote(.(W)))
I had the same issue with dynamically modifying an axis label and B Williams answer helped me.
The solution:
dynamic <- "dynamic text"
# Put the dynamic text in its right context
str <- sprintf("Static label text [%s]", dynamic)
# Use multiplication "*" rather than "~" to build the expression to avoid unnecessary space
complete_label <- bquote(.(str)[subscript]*"/L")
To verify that it works:
library(ggplot2)
ggplot() + labs(x = complete_label)

How to underline text in a plot title or label? (ggplot2)

Please pardon my ignorance if this is a simple question, but I can't seem to figure out how to underline any part of a plot title. I'm using ggplot2.
The best I could find was
annotate("segment") done by hand, and I have created a toy plot to illustrate its method.
df <- data.frame(x = 1:10, y = 1:10)
rngx <- 0.5 * range(df$x)[2] # store mid-point of plot based on x-axis value
rngy <- 0.5 * range(df$y)[2] # stores mid-point of y-axis for use in ggplot
ggplot(df, aes(x = x, y = y)) +
geom_point() +
ggtitle("Oh how I wish for ..." ) +
ggplot2::annotate("text", x = rngx, y = max(df$y) + 1, label = "underlining!", color = "red") +
# create underline:
ggplot2::annotate("segment", x = rngx-0.8, xend = rngx + 0.8, y= 10.1, yend=10.1)
uses bquote(underline() with base R
pertains to lines over and under nodes on a graph
uses plotmath and offers a workaround, but it didn't help
Try this:
ggplot(df, aes(x = x, y = y)) + geom_point() +
ggtitle(expression(paste("Oh how I wish for ", underline(underlining))))
Alternatively, as BondedDust points out in the comments, you can avoid the paste() call entirely, but watch out for the for:
ggplot(df, aes(x = x, y = y)) + geom_point() +
ggtitle(expression(Oh~how~I~wish~'for'~underline(underlining)))
Or another, even shorter approach suggested by baptiste that doesn't use expression, paste(), or the many tildes:
ggplot(df, aes(x = x, y = y)) + geom_point() +
ggtitle(~"Oh how I wish for "*underline(underlining))

Align x axes of box plot and line plot using ggplot

Im trying to align the x-axes of a bar plot and line plot in one window frame using ggplot. Here is the fake data I'm trying to do it with.
library(ggplot2)
library(gridExtra)
m <- as.data.frame(matrix(0, ncol = 2, nrow = 27))
colnames(m) <- c("x", "y")
for( i in 1:nrow(m))
{
m$x[i] <- i
m$y[i] <- ((i*2) + 3)
}
My_plot <- (ggplot(data = m, aes(x = x, y = y)) + theme_bw())
Line_plot <- My_plot + geom_line()
Bar_plot <- My_plot + geom_bar(stat = "identity")
grid.arrange(Line_plot, Bar_plot)
Thank you for your help.
#eipi10 answers this particular case, but in general you also need to equalize the plot widths. If, for example, the y labels on one of the plots take up more space than on the other, even if you use the same axis on each plot, they will not line up when passed to grid.arrange:
axis <- scale_x_continuous(limits=range(m$x))
Line_plot <- ggplot(data = m, aes(x = x, y = y)) + theme_bw() + axis + geom_line()
m2 <- within(m, y <- y * 1e7)
Bar_plot <- ggplot(data = m2, aes(x = x, y = y)) + theme_bw() + axis + geom_bar(stat = "identity")
grid.arrange(Line_plot, Bar_plot)
In this case, you have to equalize the plot widths:
Line_plot <- ggplot_gtable(ggplot_build(Line_plot))
Bar_plot <- ggplot_gtable(ggplot_build(Bar_plot))
Bar_plot$widths <-Line_plot$widths
grid.arrange(Line_plot, Bar_plot)
The gridlines on the x axes will be aligned if you use scale_x_continuous to force ggplot to use limits you specify.
My_plot <- ggplot(data = m, aes(x = x, y = y)) + theme_bw() +
scale_x_continuous(limits=range(m$x))
Now, when you add the layers, the axes will share the common scaling.

Different breaks per facet in ggplot2 histogram

A ggplot2-challenged latticist needs help: What's the syntax to request variable per-facet breaks in a histogram?
library(ggplot2)
d = data.frame(x=c(rnorm(100,10,0.1),rnorm(100,20,0.1)),par=rep(letters[1:2],each=100))
# Note: breaks have different length by par
breaks = list(a=seq(9,11,by=0.1),b=seq(19,21,by=0.2))
ggplot(d, aes(x=x) ) +
geom_histogram() + ### Here the ~breaks should be added
facet_wrap(~ par, scales="free")
As pointed out by jucor, here some more solutions.
On special request, and to show why I am not a great ggplot fan, the lattice version
library(lattice)
d = data.frame(x=c(rnorm(100,10,0.1),rnorm(100,20,0.1)),par=rep(letters[1:2],each=100))
# Note: breaks have different length by par
myBreaks = list(a=seq(8,12,by=0.1),b=seq(18,22,by=0.2))
histogram(~x|par,data=d,
panel = function(x,breaks,...){
# I don't know of a generic way to get the
# grouping variable with histogram, so
# this is not very generic
par = levels(d$par)[which.packet()]
breaks = myBreaks[[par]]
panel.histogram(x,breaks=breaks,...)
},
breaks=NULL, # important to force per-panel compute
scales=list(x=list(relation="free")))
Here is one alternative:
hls <- mapply(function(x, b) geom_histogram(data = x, breaks = b),
dlply(d, .(par)), myBreaks)
ggplot(d, aes(x=x)) + hls + facet_wrap(~par, scales = "free_x")
If you need to shrink the range of x, then
hls <- mapply(function(x, b) {
rng <- range(x$x)
bb <- c(rng[1], b[rng[1] <= b & b <= rng[2]], rng[2])
geom_histogram(data = x, breaks = bb, colour = "white")
}, dlply(d, .(par)), myBreaks)
ggplot(d, aes(x=x)) + hls + facet_wrap(~par, scales = "free_x")
I don't think that it is possible to give different break points in each facet.
As workaround you can make two plots and then with grid.arrange() function from library gridExtra put them together. To set break points in geom_histogram() use binwidth= and set one value for width of bin.
p1<-ggplot(subset(d,par=="a"), aes(x=x) ) +
geom_histogram(binwidth=0.1) +
facet_wrap(~ par)
p2<-ggplot(subset(d,par=="b"), aes(x=x) ) +
geom_histogram(binwidth=0.2) +
facet_wrap(~ par)
library(gridExtra)
grid.arrange(p1,p2,ncol=2)
Following on from Didzis example:
ggplot(dat=d, aes(x=x, y=..ncount..)) +
geom_histogram(data = d[d$par == "a",], binwidth=0.1) +
geom_histogram(data = d[d$par == "b",], binwidth=0.01) +
facet_grid(.~ par, scales="free")
EDIT: This works for more levels but of course there are already better solutions
# More facets
d <- data.frame(x=c(rnorm(200,10,0.1),rnorm(200,20,0.1)),par=rep(letters[1:4],each=100))
# vector of binwidths same length as number of facets - need a nicer way to calculate these
my.width=c(0.5,0.25,0.1,0.01)
out<-lapply(1:length(my.width),function(.i) data.frame(par=levels(d$par)[.i],ggplot2:::bin(d$x[d$par==levels(d$par)[.i]],binwidth=my.width[.i])))
my.df<-do.call(rbind , out)
ggplot(my.df) + geom_histogram(aes(x, y = density, width = width), stat = "identity") + facet_wrap(~par,scales="free")
from https://groups.google.com/forum/?fromgroups=#!searchin/ggplot2/bin$20histogram$20by$20facet/ggplot2/xlqRIFPP-zE/CgfigIkgAAkJ
It is not, strictly speaking, possible to give different breaks in the different facets. But you can get the same effect by having a different layer for each facet (much as in user20650's answer), but mostly automating the multiple geom_histogram calls:
d <- data.frame(x=c(rnorm(100,10,0.1),rnorm(100,20,0.1)),
par=rep(letters[1:2],each=100))
breaks <- list(a=seq(9,11,by=0.1),b=seq(19,21,by=0.2))
ggplot(d, aes(x=x)) +
mapply(function(d, b) {geom_histogram(data=d, breaks=b)},
split(d, d$par), breaks) +
facet_wrap(~ par, scales="free_x")
The mapply call creates a list of geom_histograms which can be added to the plot. The tricky part is that you have to manually split the data (split(d, d$par)) into the data that goes into each facet.

Resources