The goal
I'd like to have a function that processes some input X
and produces a gglot
graph using geom_point
.
That function should allow to map columns of X
to various aesthetics inside aes()
(via arguments .shapefac
, .colfac
, etc.), but also allow to set e.g. point colour and shape manually (e.g. colour = "tomato"
), outside of aes()
.
The problem
If .shapefac
or .colfac
arguments are provided, the aesthetics get overridden by the specification of shape
and colour
, respectively. This is explained e.g. here.
What I've tried
One workaround would be to remove the shape
and colour
arguments, and allow them to be specified using ...
in geom_point
. This works, but needs some advanced R knowledge for a user of such a function.
The question
Does anybody know if there is
a way to change the default setting for the
shape
andcolour
arguments outside ofaes()
ingeom_point
?a way to override the specification of the
shape
andcolour
arguments, if an aesthetic is used?another way to reach the goal stated above, without using
...
?
Any hints or explanations are much appreciated!
The following function and plots hopefully illustrate the problem.
define function
library(mlbench)
data("Ionosphere")
cplot <- function(X, v = 1:ncol(X), .shapefac = NULL, .colfac = NULL, shape = 21, colour = "black",
center = TRUE, scale = FALSE, x = 1, y = 2, plot = TRUE) {
library(ggplot2)
# some code processing X to Y
d.pca <- prcomp(X, center = center, scale. = scale)
Y <- data.frame(X, d.pca$x)
v <- round(100 * (d.pca$sdev^2 / sum(d.pca$sdev^2)), 2)
# plot PCA
p <- ggplot(Y, aes_string(x = paste0("PC",x), y = paste0("PC",y)))
geom_point(aes_string(shape = .shapefac, colour = .colfac),
shape = shape, color = colour)
labs(x = paste0("PC ", x, " (", v[x], "%)"),
y = paste0("PC ", x, " (", v[y], "%)"))
theme_bw()
if (plot) print(p)
invisible(p)
}
setting colour and shape arguments outside aes()
This works as it should.
cplot(Ionosphere, v = 3:34, colour = "tomato", shape = 4)
setting shape inside aes()
when shape outside aes()
has a default of 21
The default shape = 21
outside aes()
overrides the shape aesthetic.
cplot(Ionosphere, v = 3:34, .shapefac = "Class")
setting colour inside aes()
when colour outside aes()
has a default of "black"
The default colour = "black"
outside aes()
overrides the colour aesthetic.
cplot(Ionosphere, v = 3:34, .colfac = "Class")
failed trials to set default shape and colour to NULL
or NA
# results in an empty plot (shape = NA)
cplot(Ionosphere, v = 3:34, .shapefac = "Class", shape = NA)
#> Warning: Removed 351 rows containing missing values (geom_point).
# results in an error
cplot(Ionosphere, v = 3:34, .shapefac = "Class", shape = NULL)
#> Error: Aesthetics must be either length 1 or the same as the data (351): shape
Created on 2021-09-20 by the reprex package (v2.0.1)
CodePudding user response:
One option to achieve your desired result may look like so:
- If the aesthetics are provided set the color and/or shape params to
NULL
- Make use of
modifyList
to construct a list of arguments to be passed togeom_point
which includes the mapping and the non-NULL parameters. Making usemodifyList
will drop anyNULL
. - Make use of
do.call
to callgeom_point
with the list of arguments.
Note: I slightly changed your function to select only numeric columns for the PCA.
library(mlbench)
library(ggplot2)
data(Ionosphere)
cplot <- function(X, .shapefac = NULL, .colfac = NULL, shape = 21, colour = "black",
center = TRUE, scale = FALSE, x = 1, y = 2, plot = TRUE) {
col_numeric <- unlist(lapply(X, is.numeric))
# some code processing X to Y
d.pca <- prcomp(X[, col_numeric], center = center, scale. = scale)
Y <- data.frame(X, d.pca$x)
v <- round(100 * (d.pca$sdev^2 / sum(d.pca$sdev^2)), 2)
colour <- if (is.null(.colfac)) colour
shape <- if (is.null(.shapefac)) shape
mapping <- aes_string(shape = .shapefac, colour = .colfac)
args <- modifyList(list(mapping = mapping), list(color = colour, shape = shape))
geom <- do.call("geom_point", args)
p <- ggplot(Y, aes_string(x = paste0("PC", x), y = paste0("PC", y)))
geom
labs(
x = paste0("PC ", x, " (", v[x], "%)"),
y = paste0("PC ", x, " (", v[y], "%)")
)
theme_bw()
if (plot) print(p)
invisible(p)
}
cplot(Ionosphere, colour = "tomato", shape = 4)
cplot(Ionosphere, .shapefac = "Class")
cplot(Ionosphere, .colfac = "Class")
cplot(Ionosphere, .colfac = "Class", .shapefac = "Class")
cplot(Ionosphere, .shapefac = "Class", shape = NULL)