-2

Why do the following won't work?

ggplot(mtcars,aes(x=wt,y=mpg)) +
  geom_smooth({if (T) ("colour='red'")})

ggplot(mtcars,aes(x=wt,y=mpg)) +
  geom_smooth(ifelse(T,("colour='red'")))

ggplot(mtcars,aes(x=wt,y=mpg)) +
  geom_smooth(switch(T,("colour='red'")))

They all output this:

Error: `mapping` must be created by `aes()`

What would be a workaround?

Edit:

The proposed answers suggested to put the if statement after the named colour argument. The problem is that even if I leave color=NA or NULL or blank, it enters in conflict if aes(colour) is already defined, so it is not equivalent to not having the argument at all. And that is really the problem/workaround I'm trying to get at. For example:

ggplot(mtcars,aes(x=wt,y=mpg,colour=factor(cyl))) + geom_smooth(colour = NA) does not produce three colored lines (as it should if aes() would take over, indicating it's being overwritten)

Setting geom_smooth(color = NULL) gives: Error: Aesthetics must be either length 1 or the same as the data (80): colour

Setting geom_smooth(color = ) gives: Error in geom_smooth(color = ) : argument is missing, with no default

Note: Gregory also suggested duplicating the geom statement, which is a working workaround, but my question was ultimately about 'is there a way to avoid doing precisely this' (duplicating calls to geoms_) and trying to understand why we're not allowed to use if statements including arguments.

Edit 2:

My initial intention with this question was more theoretical as to why ggplot2 is coded in such a way that it does not allow conditional statements to include complete optional arguments in geoms (e.g., would that enter in conflict or create bugs in certain situations?). Yifu Yan originally proposed a promising answer to this question in saying that "color is a named argument, it must have a name", though I would have appreciated more elaboration/explanation on this.

My current understanding is that it might not be possible to do what I want without duplicating calls to geoms. However, an example of a function I would have been looking for (without duplicating calls to geoms) would be:

scatterfun <- function (Data,Predictor,Response,has.Groups,Group.variable) {
  ggplot(Data,aes(x={{Predictor}},y={{Response}})) +
    geom_smooth(switch(has.Groups + 1,("colour='red'"),
                       aes(colour={{Group.variable}})))
}

Works when has.Groups = TRUE

scatterfun(mtcars,wt,mpg,T,factor(cyl))

enter image description here

(As you can see I would like to keep the confidence bands)

But doesn't work when has.Groups = FALSE:

scatterfun(mtcars,wt,mpg,F,factor(cyl))

Error: `mapping` must be created by `aes()`

So with this question, I was attempting to better understand that part when I want to conditionally add a mono-colour to a geom statement (that is why I had isolated this section in the beginning of my question). Hope it's a bit more clear now.

Community
  • 1
  • 1
rempsyc
  • 785
  • 5
  • 24
  • what do you want to achieve? – Zhiqiang Wang Sep 10 '19 at 23:14
  • I'm making a function and would like to add required arguments only when necessary (see [my other post here](https://stackoverflow.com/questions/57875193/how-to-manually-set-colours-conditionally-in-aes-and-local-geom-xxx-in-ggp)) – rempsyc Sep 10 '19 at 23:16

2 Answers2

4

Scenario 1

If you want either three line or one lines:

condition <- TRUE
ggplot(mtcars,aes(x=wt,y=mpg,colour=factor(cyl))) + 
    {
        if ( condition ){
            geom_smooth() 
        } else {
            geom_smooth(color = "black")
        }
    }

enter image description here

condition <- FALSE
ggplot(mtcars,aes(x=wt,y=mpg,colour=factor(cyl))) + 
    {
        if ( condition ){
            geom_smooth() 
        } else {
            geom_smooth(color = "black")
        }
    }

enter image description here

Scenario 2

To be clear, you always want three separate regression lines for each the groups. But, meanwhile, you also want to create a conditional regression line for data regardless of the groups.

condition <- TRUE
ggplot(mtcars,aes(x=wt,y=mpg,colour=factor(cyl))) + 
    geom_smooth(se = FALSE) +
    geom_smooth(colour = if (condition) "black" else NA,se = FALSE)

enter image description here

library(ggplot2)
condition <- FALSE
ggplot(mtcars,aes(x=wt,y=mpg,colour=factor(cyl))) + 
    geom_smooth(se = FALSE) +
    geom_smooth(colour = if (condition) "black" else NA,se = FALSE)

enter image description here

yusuzech
  • 5,896
  • 1
  • 18
  • 33
  • But then did you try it with `if(FALSE)`? – rempsyc Sep 10 '19 at 23:14
  • 1
    You are right. Just added `else` to it. – yusuzech Sep 10 '19 at 23:17
  • Thanks for the `else` edit. But what if you want the else condition to be left blank/`NULL`/`NA`? (as for the example from my other post about mixing with `aes(colour=...)`. That's why I would like the whole argument to disappear when I don't need it... :3 – rempsyc Sep 10 '19 at 23:22
  • Then you can do `ggplot(mtcars,aes(x=wt,y=mpg)) + geom_smooth(color = if (condition) 'red' else NA,se = FALSE)`, `se` is set to `FALSE` in case you don't want the confidential interval to display. – yusuzech Sep 10 '19 at 23:26
  • The problem though is that even while putting `color=NA`, it enters in conflict if `aes(colour)` is already present, so it is not equivalent to not having the argument. And that is really the problem/workaround I'm trying to get at. E.g., ggplot(mtcars,aes(x=wt,y=mpg,colour=factor(cyl))) + geom_smooth(color = NA) – rempsyc Sep 10 '19 at 23:33
  • I don't get it what you mean by conflicts. Can you update your question with more specific example that represents the conflict? – yusuzech Sep 10 '19 at 23:36
  • Thanks for the suggestion @YifuYan, I've edited my question with specific examples that represent the conflict with `aes()` and specified the problem more precisely. – rempsyc Sep 10 '19 at 23:51
  • After reviewing your updated question, I'm still not sure what you want to achieve. The following statement is note TRUE: `ggplot(mtcars,aes(x=wt,y=mpg,colour=factor(cyl))) + geom_smooth(colour = NA) does not produce lines at all (as it should if aes() would take over)` because the line is generated but the color is transparent. If you still want the line, just set default color in `else` to any color, e.g. black. – yusuzech Sep 11 '19 at 01:40
  • Thanks for coming back to me @YifuYan and sorry for my lack of clarity. Indeed, the line is transparent, but if I put `geom_smooth(colour="black")` for example, the `group aes(colour=factor(cyl))` is overwritten and a **single** black line is drawn instead of **three** lanes colored by group. What I'm trying to achieve would be to be able to easily switch between a single coloured line and by-group colored lines (again, without duplicating calls to geoms). – rempsyc Sep 11 '19 at 02:41
  • I already changed my answer based on your comment. – yusuzech Sep 11 '19 at 03:20
  • What I would like is an **either or** function, an `ifelse()` that is *not* always three lines, but that is **either** three coloured lines by group **or** one overall mono-coloured line. I've made a second edit with a putative function to more clearly represent what I would be looking for. Though I liked the explanation in your original answer about colour being a named argument, I'd like to see that back in (maybe with more elaboration on this?). Thanks. – rempsyc Sep 11 '19 at 18:08
  • I just modified my answer based on your comment. – yusuzech Sep 11 '19 at 18:15
  • Thanks. If you 1) swap the order of scenarios (second one on top) and 2) put back your original explanation: "color is a named argument, it must have a name", as well as add some more explanations regarding that statement, I will accept your answer. – rempsyc Sep 12 '19 at 16:22
  • I've accepted your answer, but I was hoping for you to add back your original answer, "color is a named argument, it must have a name", as it is an important part of the answer in my opinion (as to why my original attempt didn't work) and I think will serve future readers. Just copy-paste it on top before scenario 1. Thanks. – rempsyc Sep 12 '19 at 19:46
1

You can create a standalone function that conditionally adds a red smooth or a default smooth.

library(ggplot2)

smooth_condition <- function (x) {
  return(
    if (x) {
      geom_smooth(color = 'red')
      } else {
      geom_smooth()
      }
    )
}

ggplot(mtcars, aes(x = wt, y = mpg)) +
  smooth_condition(TRUE)

enter image description here

ggplot(mtcars, aes(x = wt, y = mpg)) +
  smooth_condition(FALSE)

enter image description here

Gregory
  • 4,147
  • 7
  • 33
  • 44
  • 1
    There's an answer already. Yours does not add anything as is. Please edit and post an answer that differs from existing ones. – M-- Sep 10 '19 at 23:19
  • Thanks for the answer @Gregory. But what if you want the else condition to be left blank/NULL/NA?. I have problems when I do that. That's why I would like the whole argument to disappear when I don't need it. Do you see what I mean? – rempsyc Sep 10 '19 at 23:24
  • @RemPsyc I just updated my code to show how to add conditionally. – Gregory Sep 10 '19 at 23:35
  • @M-- I didn't mean to duplicate an existing answer. Must've been writing mine when it was submitted. I've updated my code to more precisely address OPs need, also making my answer different from Yifu Yan's. – Gregory Sep 10 '19 at 23:38
  • Thank you @Gregory (yes it happens that two people answer at the same time no worry). I edited my question for more clarity of the problem. Your new edited solution is indeed a "workaround" that also came up on my other post and that I'm currently using but I'm just really hoping to find an alternative that does not require duplicating geom calls (for use in a huge, complex function with many intertwined arguments). Is there really no way to accomplish this? – rempsyc Sep 10 '19 at 23:48
  • @RemPsyc You might prefer to define a standalone function for creating the smooth. Check out my revised answer and see if that works for you. – Gregory Sep 11 '19 at 00:00
  • @Gregory I didn't mean to say it was deliberate action. Just was pointing out. – M-- Sep 11 '19 at 00:08