0

Inside a function, I want to set a variable to either the corresponding argument, or a default value if that argument is NULL. The code is as follows:

  rcontrol <- ifelse(is.null(rcontrol),rpart.control(cp=0.001,minbucket=100,minsplit = 5),rcontrol)

It took me a long time to figure out that the rcontrol isn't the same as if it were defined in this way:

rcontrol <- rpart.control(cp=0.001,minbucket=100,minsplit = 5)

Can anyone please explain this bizarre behavior of ifelse?

Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
Catiger3331
  • 611
  • 1
  • 6
  • 18
  • 3
    You want `if(){} else{}`, not `ifelse`. If you look at `ifelse`, it "returns a value with the same shape as `test`" (its first argument), so if you pass it a NULL, you'll get nothing out. – Gregor Thomas May 10 '18 at 20:33
  • See, e.g., [is ifelse ever appropriate in a non-vectorized situation?](https://stackoverflow.com/q/8190279/903061) and [if-else vs ifelse with lists](https://stackoverflow.com/q/9449184/903061) – Gregor Thomas May 10 '18 at 20:35
  • @Gregor, do you think this is sufficiently similar to those links to be a duplicate? – Ben Bolker May 10 '18 at 20:36
  • 2
    @BenBolker I recently clarified the answer at the dupe I chose here to make it more applicable in these sorts of cases, but if you disagree I'm open to other suggestions... – joran May 10 '18 at 20:41
  • That's the one I was looking for. I just added the [tag:r-faq] tag to make it easier to find. – Gregor Thomas May 10 '18 at 20:44
  • for bonus points: is there a built-in idiom that lets you do `if (is.null(x)) x <- defaultValue` without repeating the parameter name? – Ben Bolker May 10 '18 at 20:48
  • @BenBolker I don't know of a general idiom other than setting default values for function arguments, which seems like it covers most of the times this would be useful... – Gregor Thomas May 11 '18 at 17:39

1 Answers1

3

The key is in ?ifelse:

‘ifelse’ returns a value with the same shape as ‘test’

(emphasis added). Since is.null(rcontrol) is a 1-element logical vector, what you get back is a 1-element thing (in this case the first element of rcontrol).

You are looking for either:

if (is.null(rcontrol)) { rcontrol <- rpart.control(cp=0.001,minbucket=100,minsplit = 5)  }

or

rcontrol <- if (is.null(rcontrol)) [...] else rcontrol

(in this case the first idiom seems more appropriate since you don't do anything to rcontrol if the test is false)

Ben Bolker
  • 211,554
  • 25
  • 370
  • 453