0

I'm trying to make a function that can take either a single group, or several groups, while using a single colour argument. However, there seems to be a problem in specifying colour both globally (in aes()) and locally (in geom_smooth()). Looks like aes() doesn't accept colour=NULL or just left empty colour=.

Plot without groups (works)

scatterfunction <- function (Data=mtcars,Predictor=mtcars$wt,Response=mtcars$mpg,what.Colour="purple") {
  library(ggplot2)
  ggplot(Data,aes(x=Predictor,y=Response)) +
    geom_smooth(method="lm",colour=what.Colour)
}
scatterfunction()

singlegroup

Plot with groups (works)

groupscatterfunction <- function (Data=mtcars,Predictor=mtcars$wt,Response=mtcars$mpg,Group.variable=factor(mtcars$cyl),what.Colour=c("purple", "yellow", "brown")) {
  library(ggplot2)
  ggplot(Data,aes(x=Predictor,y=Response,colour=Group.variable)) +
    geom_smooth(method="lm") +
    scale_color_manual(values=what.Colour)
}
groupscatterfunction()

3groups

Plot without groups, conditional (works when has.Groups=F)

conditionalscatterfunction <- function (Data=mtcars,Predictor=mtcars$wt,Response=mtcars$mpg,Group.variable=factor(mtcars$cyl),has.Groups=F,what.Colour="purple") {
  library(ggplot2)
  ggplot(Data,aes(x=Predictor,y=Response,colour= if(has.Groups==T) {Group.variable})) +
    geom_smooth(method="lm",colour= if(has.Groups==F){what.Colour}) +
    if (has.Groups==T) {scale_color_manual(values=what.Colour)}
}
conditionalscatterfunction()

singlegroup

Plot with groups, conditional (doesn't work when has.Groups=T)

conditionalscatterfunction(Data = mtcars,
                           Predictor = mtcars$wt,
                           Response = mtcars$mpg,
                           has.Groups = TRUE,
                           Group.variable = factor(mtcars$cyl),
                           what.Colour = c("purple", "yellow", "brown"))

Error: Aesthetics must be either length 1 or the same as the data (80): colour

Using the alternative switch() statement has worked for me before but not here:

conditionalscatterfunction <- function (Data=mtcars,Predictor=mtcars$wt,Response=mtcars$mpg,Group.variable=factor(mtcars$cyl),has.Groups=T,what.Colour=c("purple", "yellow", "brown")) {
  library(ggplot2)
  ggplot(Data,aes(x=Predictor,y=Response,colour= switch(has.Groups, Group.variable))) +
    geom_smooth(method="lm",colour= if(has.Groups==F){what.Colour}) +
    if (has.Groups==T) {scale_color_manual(values=what.Colour)}
}
conditionalscatterfunction()

Error: Aesthetics must be either length 1 or the same as the data (80): colour

It seems like as long as I add the "colour=" statement in aesthetics(), no matter if I leave it blank or = NULL, I get this error. What's its default when not explicitly called then?

I would prefer avoiding repeating the whole call again because I also have this issue with geom_points(), geom_shape(), etc., and I would need to repeat it for each combination of elements...

Question: How can I solve this problem?

rempsyc
  • 785
  • 5
  • 24
  • Just responding to your first paragraph, have you tried `colour = NA` instead of `colour = NULL`? – aosmith Sep 10 '19 at 16:52
  • Interesting, so it is indeed working as a standalone like `aes(colour=NA)` but not when included within a `switch()` or `ifelse()` statement... Maybe `aes()` just can't accept `switch()` or `ifelse()` statements at all? – rempsyc Sep 10 '19 at 17:14
  • 1
    It should be easier if you do data pre-processing using `dplyr` first and add another `color` column using if-else conditions.. Then you can reference the `color` column in `ggplot2`. – yusuzech Sep 10 '19 at 17:26
  • @YifuYan I don't know if that's what you wanted me to do, but I added this call in the function: `ifelse(has.Groups==T,(mtcars$colour.col = Group.variable),(mtcars$colour.col = what.Colour))`. I then changed for `aes(colour=mtcars$colour.col)`. Works if I remove the `colour=` argument in geom_smooth, but not otherwise... Did I do this right? – rempsyc Sep 10 '19 at 18:57

1 Answers1

1

There are many ways to skin this cat, but a simple method is to just pre-define the geom_smooth with an if {} else {}. E.g.:

conditionalscatterfunction <- function (Data, Predictor, Response, Group.variable, col = c("purple", "yellow", "brown")) {
  require(ggplot2)

  if (missing(Group.variable)) {
    smooth <- geom_smooth(method = "lm", color = col[1])
  } else {
    smooth <- geom_smooth(aes(color = {{Group.variable}}), method = "lm")
  }

  ggplot(Data, aes(x = {{Predictor}},y = {{Response}})) +
    smooth +
    scale_color_manual(values = col)
}

conditionalscatterfunction(mtcars, wt, mpg)
conditionalscatterfunction(mtcars, wt, mpg, factor(cyl))

Both work fine.

Axeman
  • 32,068
  • 8
  • 81
  • 94
  • Yes, nice, thank you @Axeman. However, as mentioned in my post, I'd here be duplicating the `geom_smooth` call... Isn't there a way to use the `ifelse()` call within the `geom_smooth` call itself? Else I'd need to repeat this for every other option and combination: `geom_points()`, `geom_jitter`, `geom_shape()`, `geom_line`, etc., which wouldn't respect the DRY principle right? Consider an additional `ifelse()` `has.points == TRUE` statement, which would require 2 duplicates for colour for each of T and F, resulting in 4 duplicates for a single call... Or I am getting this wrong? :3 – rempsyc Sep 10 '19 at 19:31
  • 1
    You can't easily do that, because when you define the group variable you aren't allowed to pass anything to `color` outside the `aes`. Passing `NULL` throws an error, and passing `NA` makes the line disappear. – Axeman Sep 10 '19 at 19:59
  • I see. So my actual function might be uber-long but I might not have a choice after all... Well then if it's the only way, that'll do! :) – rempsyc Sep 10 '19 at 20:06
  • 1
    It depends a bit on what is conditional. Some things you can define more easily on the fly. See e.g. https://stackoverflow.com/questions/22915337/if-else-condition-in-ggplot-to-add-an-extra-layer – Axeman Sep 10 '19 at 20:12