How to modify axis labels within ggplot labs() - r

Say I want to modify a ggplot axis label with the str_to_title() function.
library(tidyverse)
mtcars %>%
ggplot(aes(x = wt, y = mpg)) +
geom_point() +
labs(x = ~str_to_title(.x))
Rather than my x-axis being labeled 'Wt' it will be labeled 'str_to_title(.x)'. Is there a way to apply functions within the labs() function?

labs doesn't do programmatic NSE like many other components of ggplot2. One option is to define the columns programmatically, use aes_ and as.name (or other ways too) and it'll work.
library(ggplot2)
library(stringr) # str_to_title
xx <- "wt"; yy <- "mpg"
ggplot(mtcars, aes_(x = as.name(xx), y = as.name(yy))) +
geom_point() +
labs(x = str_to_title(xx))

Related

Use tidy evaluation when passing an expression as a character to ggplot2::aes()

I am trying to convert my use of ggplot2 in functions to using tidy evaluation so as to avoid the warning messages that are evaluated. I particular, I have extensively used aes_string() and these need to be converted to aes(). I can handle cases when just the name of a column is passed as a characater. However, I have been unable to work how to deal with the case when the character is a mathematical expression.
Here is a small reproducible example of the problem that I an trying to solve.
library(ggplot2)
set.seed(1)
dat <- data.frame(x=rnorm(100),y=rnorm(100))
xvar <- 'x+100'
yvar <- 'y'
#This works but uses the deprecated aes_string
ggplot(dat,aes_string(x=xvar,y=yvar))
#This works
ggplot(dat, ggplot2::aes(x=x+100, y=.data[[yvar]])) + geom_point()
#This does not work
ggplot(dat, aes(x={{xvar}}, y=.data[[yvar]])) + geom_point()
My question is what tidy evaluation techniques do I need to employ to use xvar to specify the x variable as is possible with aes_string()?
You could use eval(str2expression()):
library(ggplot2)
ggplot(dat, aes(x = eval(str2expression(xvar)), y = eval(str2expression(yvar)))) +
geom_point() +
labs(x = xvar, y = yvar)
Or using the analogous rlang functions:
library(rlang)
ggplot(dat, aes(x = eval_tidy(parse_expr(xvar)), y = eval_tidy(parse_expr(yvar)))) +
geom_point() +
labs(x = xvar, y = yvar)
Note you’ll want to manually set the axis labels using labs(); otherwise you’ll end up with e.g. "eval(str2expression(xvar))" for the x axis.
It may need parse_expr/eval
library(ggplot2)
ggplot(dat, aes(x=eval(rlang::parse_expr(xvar)), y=.data[[yvar]])) +
geom_point() +
xlab(xvar)
-output
Or another option would be to interpolate and do the eval/parse
eval(parse(text = glue::glue("ggplot(dat, aes(x = {xvar}, y = {yvar}))",
"+ geom_point()")))

different titles for each element after using facet_wrap()

After setting ncol = 1 in the facet_wrap() function, I'm trying to use ggtitle() function inside the facet_wrap() function to set a different title for each graph created (there are only two of them).
ggplot(df, aes(x, y)) +
geom_point() +
facet_wrap(~ var, ncol = 1) +
ggtitle(function(x) paste("Title for", df$title[df$var == x]))
I'm trying to use the value of the "title" column of the dataframe, where the value of the "var" column matches the value of the current plot's var.
But I get this error:
Error in as.character(x$label) :
cannot coerce type 'closure' to vector of type 'character'
How can I set different titles for each graph in ggplot2 using the facet_wrap() function with ncol=1?
Thanks, Ido
Here is something that might give you what you want. My comment above still stands, but this may be more what you are looking for.
library(ggplot2)
library(dplyr)
df <- mtcars %>%
mutate(strip_title = paste(cyl, "Cylinders"))
ggplot(df, aes(x = mpg, y = wt)) +
geom_point() +
facet_wrap(~strip_title, ncol = 1)

Change axes label and scale using ggplot and patchwork in R

(I am trying to make this question as short and concise as possible, as other related answers may be tough for the non-savvy like myself.)
With the following code in mind, is it possible to have both y-axes on the same scale (that of the graph with the highest y-limit), and to have independent labels for each of the axes (namely the y-axes)? I tried to use facet_wrap but haven't so far been able to succeed as Layer 1 is missing)
library(ggplot2)
library(patchwork)
d <- cars
d$Obs <- c(1:50)
f1 <- function(a) {
ggplot(data=d, aes_string(x="Obs", y=a)) +
geom_line() +
labs(x="Observation",y="Speed/Distance")
}
f1("speed") + f1("dist")
You could add two additional arguments to your function, one for the axis label and one for your desired limits.
library(ggplot2)
library(patchwork)
d <- cars
d$Obs <- c(1:50)
f1 <- function(a, y_lab) {
ggplot(data = d, aes_string(x = "Obs", y = a)) +
geom_line() +
scale_y_continuous(limits = range(c(d$speed, d$dist))) +
labs(x = "Observation", y = y_lab)
}
f1("speed", "Speed") + f1("dist", "Distance")
Reshape wide-to-long, then use facet. Instead of having different y-axis labels we will have facet labels:
library(ggplot2)
library(tidyr)
pivot_longer(d, 1:2, names_to = "grp") %>%
ggplot(aes(x = Obs, y = value)) +
geom_line() +
facet_wrap(vars(grp))

How do I pass multiple arguments through an R function to ggplot, both aesthetics and scale format?

How do I pass multiple arguments through to my ggplot function?
Here is an example of the plot I want to automate.
library(ggplot2)
library(scales)
p <- ggplot(diamonds, aes(x=cut, y=price) ) +
geom_boxplot() +
scale_y_continuous(labels = dollar)
p
But I want to graph multiple different variables and use the appropriate scale e.g. price, depth etc, some are in dollars.
So I made a function
myfunction <- function(var1,var2){
p <- ggplot(diamonds, aes(x=cut, y= var1) ) +
geom_boxplot() +
scale_y_continuous(labels = var2)
p
return(p)
}
When I test the function, it doesn't work. Both arguments cause different errors on their own.
myfunction("price","dollar")
For var1 I get:
Error: Discrete value supplied to continuous scale
and var2:
Error in f(..., self = self) : Breaks and labels are different lengths
Question 1. Why doesn't that work? This is the most important question for me.
I then wish to make multiple graphs, which I can do with a for loop, but I keep hearing I should do it with apply. Here's what I tried.
Question 2. How would you make the multiple graphs work with apply?
FirstPlotData <- c("price","dollar")
SecondPlotData <- c("depth", "comma")
plotMetaData <- data.frame(FirstPlotData,SecondPlotData)
lapply doesn't work for me with multiple arguments. Can it pass multiple arguments?
lapply(plotMetaData, function(avar,bvar)myfunction(avar, bvar))
Would mapply work? How?
mapply(mytestfunction,plotMetaData[1,],plotMetaDataList[2,])
Thanks in advance. I note that I could do the multiple graphs with facet, but for my more complex example, with hiding outliers, scaling, and also doing stats, then doing the multiple plots and putting in a {cowplot} grid seems easier.
Try this
library(ggplot2)
library(scales)
library(rlang) # for sym
myfunction <- function(var1,var2){
p <- ggplot(diamonds, aes(x=cut, y= !! sym(var1)) ) +
geom_boxplot() +
scale_y_continuous(labels = get(var2))
p
return(p)
}
myfunction('price','dollar')
You probably want aes_string. This function has been designed to make programming with ggplot easier (similar ideas have also been applied to dplyr commands). The following works:
library(tidyverse)
data(diamonds)
myfunction <- function(var1){
p <- ggplot(diamonds, aes_string(x="cut", y= var1) ) +
geom_boxplot()
p
return(p)
}
myfunction("price")
Why?
contrast the following:
# works
ggplot(diamonds, aes(x=cut, y= price) ) + geom_boxplot()
# these 2 are equivalent, but do not work
ggplot(diamonds, aes(x=cut, y= "price") ) + geom_boxplot()
var1 = "price"
ggplot(diamonds, aes(x=cut, y= var1) ) + geom_boxplot()
# these 2 are equivalent, both works but inputs are strings
ggplot(diamonds, aes_string(x="cut", y= "price") ) + geom_boxplot()
var1 = "price"
ggplot(diamonds, aes_string(x="cut", y= var1) ) + geom_boxplot()
Using apply?
For this purpose I would be inclined to use loops (others are welcome to disagree). If you are set on using an apply approach then you probably want apply as lapply, mapply, vapply and sapply are list-, multivariate-, vector- and simple-apply respectively.
A more ggplot way of doing this now, is using .data pronoun.
library(ggplot2)
myfunction <- function(var1, var2) {
p <- ggplot(diamonds, aes(x = cut, y = .data[[var1]])) +
geom_boxplot() +
scale_y_continuous(
labels = getFromNamespace(x = var2, ns = "scales")
)
p
return(p)
}
myfunction("price", "dollar")
myfunction("price", "comma")
Then to create multiple plots with these function by passing multiple arguments, a better and tidier approach is using map functions from the {purrr}
plots <- purrr::map2(
.x = c("price", "price"),
.y = c("dollar", "comma"),
.f = myfunction
)
So, plots[[1]] contains the 1st plot with var1 = "price" and var2 = "dollar" and plots[[2]] contains the 2nd plot with var1 = "price" and var2 = "comma".

Individually change x labels using expressions in ggplot2 boxplot with facet_grid in R

I want to individually change the x labels of my ggplot2 boxplot when using a facet_grid. I made the following simple example:
library(ggplot2)
data1 <- InsectSprays
data1$group <- "group 1"
data2 <- InsectSprays
data2$group <- "group 2"
plotData <- rbind(data1, data2)
ggplot(plotData, aes(x=spray, y=count, fill=spray))+
guides(fill=FALSE) +
facet_grid(. ~ group) +
geom_boxplot()
I want to change the labels on the x axis (A, B, C,...), but individually for the two groups. One way changing the labels would be, using:
scale_x_discrete(labels=c("label 1", "label 2", ...))
but this would change the labels in both groups to the same values. At the end I also need to be able to use expressions for the labels. Is there any way to achieve what I want?
EDIT:
There is a very simple way to solve my problem (thanks #Axeman). By using:
scale_x_discrete(labels=c('A' = expression(beta)))
I can change the labels. In my example this would change both groups, but for me it is possible to rename the labels to individual labels beforehand and than use this trick to use expressions for the labels.
plotData$x <- interaction(plotData$spray, plotData$group)
plotData$x <- factor(plotData$x, labels = paste('labels', 1:12))
ggplot(plotData, aes(x=x, y=count, fill=spray))+
geom_boxplot(show.legend = FALSE) +
facet_grid(. ~ group, scales = 'free')
I would have expected the following to work, but it doesn't!
ggplot(plotData, aes(x=interaction(spray, group), y=count, fill=spray))+
geom_boxplot(show.legend = FALSE) +
facet_grid(. ~ group, scales = 'free') +
scale_x_discrete(labels = paste('labels', 1:12))

Resources