'Cannot use faceting for ggplot inside a function
I am trying to use ggplot inside a function, but I am unable to produce
the plots. Specifically, I would like to determine whether or not the plot
will use facet_grid() from the function call. This is my data:
mydf <- data.frame(
group = rep(c("g1", "g2"), each = 16, times = 1),
cluster = rep(c("c1", "c2"), each = 8, times = 2),
score1 = c(rnorm(n = 16, mean = 10, sd = 10), rnorm(n = 16, mean = 18, sd = 10)),
score2 = c(rnorm(n = 16, mean = 50, sd = 10), rnorm(n = 16, mean = 33, sd = 10))
)
Here is the function:
myFunc <- function(data, group = NULL, group2, var1, var2) {
# So we don't need quotation marks in function call
arguments <- as.list(match.call())
var1 = eval(arguments$var1, data)
var2 = eval(arguments$var2, data)
group2 = eval(arguments$cluster, data)
grouping = eval(arguments$group, data)
# Make this graph if no faceting needed
if (length(grouping) == 0) {
means <- aggregate(cbind(var1, var2) ~ group2, FUN = mean, data = data)
ggplot(data, aes(x = var1, y = var2, color = group2, label = group2)) +
stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
geom_text(alpha = 0.5, show.legend = FALSE) +
geom_text(data = means, aes(x = var1, y = var2, color = group2))
# Use faceting
} else if (length(grouping) > 0) {
means <- aggregate(cbind(var1, var2) ~ grouping + group2, FUN = mean, data = data)
# Plot
ggplot(data, aes(x = var1, y = var2, color = group2, label = group2)) +
stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
geom_text(alpha = 0.5, show.legend = FALSE) +
geom_text(data = means, aes(x = var1, y = var2, color = group2)) +
facet_grid(. ~ grouping)
}
}
I am calling the function like so:
myFunc(data = mydf, group = NULL, group2 = cluster, var1 = score1, var2 = score2)
myFunc(data = mydf, group = group, group2 = cluster, var1 = score1, var2 = score2)
Both calls give the following error respectively:
# Error 1
Error: Aesthetics must be either length 1 or the same as the data (32): x, y, colour, label
# Error 2
Error in combine_vars(data, params$plot_env, cols, drop = params$drop) :
At least one layer must contain all variables used for facetting
The expected output can be acquired by building the plots manually:
means <- aggregate(cbind(score1, score2) ~ group + cluster, FUN = mean, data = mydf)
# without facet
ggplot(mydf, aes(x = score1, y = score2, color = cluster, label = cluster)) +
stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
geom_text(alpha = 0.5, show.legend = FALSE) +
geom_text(data = means, aes(x = score1, y = score2, color = cluster))
# with facet
ggplot(mydf, aes(x = score1, y = score2, color = cluster, label = cluster)) +
stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
geom_text(alpha = 0.5, show.legend = FALSE) +
geom_text(data = means, aes(x = score1, y = score2, color = cluster)) +
facet_grid(. ~ group)
Solution 1:[1]
Here's the basic stat_ellipse plots with and without using facet_grid. I will let you add the frills. Here the column names are kept as strings so aes_string is used instead of aes, and formulas are passed to functions using as.formula.
myFunc <- function(df, var1, var2, group2, group1 = NULL) {
# Make this graph if no faceting needed
if (is.null(group1)) {
means_formula <- as.formula(paste(var1, "+", var2, "~", group2))
means <- aggregate(means_formula, FUN = mean, data = df)
p <- ggplot(df,
aes_string(x = var1, y = var2, color = group2, label = group2)) +
stat_ellipse(type = "norm", show.legend = FALSE,
geom = "polygon", alpha = 0.1)
}else{
means_formula <- as.formula(paste(var1,"+",var2,"~", group2,"+",group1))
means <- aggregate(means_formula, FUN = mean, data = df)
p <- ggplot(df,
aes_string(x = var1, y = var2, color = group2, label = group2)) +
stat_ellipse(type = "norm", show.legend = FALSE,
geom = "polygon", alpha = 0.1) +
facet_grid(as.formula(paste(".~ ",group1)))
}
print(p)
}
myFunc(df = mydf, var1 = "score1", var2 = "score2",
group2 = "cluster", group1 = NULL)
myFunc(df = mydf, var1 = "score1", var2 = "score2",
group2 = "cluster", group1 = "group")
Solution 2:[2]
First, you are assigning group2 to a nonexistent variable in scope of function, cluster. Replace: group2 = eval(arguments$cluster, data) with group2 = eval(arguments$group2, data).
Second, you need a dynamic facet_grid formula. Currently you are passing grouping which is not an actual field in dataset. However, since you are going without quotes in functional arguments, you will need to retrieve the string literal of function's argument, group, which can be achieved with deparse(substitute(...)) to return "group".
Consider adding at top near list of other function variables:
grpname = deparse(substitute(group))
Then replace you facet_grid with either a dynamic as.formula string concatenation or with reformulate:
facet_grid(as.formula(paste0(". ~ ", grpname)))
facet_grid(reformulate(grpname))
Of course, all can be dynamically run with quoted function args as shown by @P-robot.
Solution 3:[3]
- Use
aes_instead ofaes - Use
substitute( )around variable names inaes_( ) - Use
vars({{ }})around variable names infacet_grid
Reference: Hadley Wickam's ggplot2 book: https://ggplot2-book.org/programming.html\ Section 19.4.1 "Indirectly referring to variables"
library(ggplot2)
mydf <- data.frame(
group = rep(c("g1", "g2"), each = 16, times = 1),
cluster = rep(c("c1", "c2"), each = 8, times = 2),
score1 = c(rnorm(n = 16, mean = 10, sd = 10), rnorm(n = 16, mean = 18, sd = 10)),
score2 = c(rnorm(n = 16, mean = 50, sd = 10), rnorm(n = 16, mean = 33, sd = 10))
)
myFunc <- function(data, group = group, group2 = cluster, var1 = score1, var2 = score2) {
means <- aggregate(cbind(score1, score2) ~ group + cluster, FUN = mean, data = mydf)
ggplot(data, aes_(x = substitute(var1), y = substitute(var2),
color = substitute(group2), label = substitute(group2))) +
stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
geom_text(alpha = 0.5, show.legend = FALSE) +
geom_text(data = means, aes_(x = substitute(var1), y = substitute(var2),
color = substitute(group2))) +
facet_grid(cols = vars({{group}}))
}
myFunc(data = mydf, group = group, group2 = cluster, var1 = score1, var2 = score2)
A reprex of a ggplot2 function using facet_grid:
library(ggplot2)
func1 <- function(data, var1, var2) {
ggplot(data, aes_(x = substitute(var1))) +
geom_histogram(bins = 30) +
facet_grid(rows = vars({{var2}}))
}
func1(mtcars, mpg, am)
Created on 2022-03-26 by the reprex package (v2.0.1)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | p-robot |
| Solution 2 | Parfait |
| Solution 3 |

