2

Looking at this post, I thought ifelse is vectorized in the sense that f(c(x1, x2, x3)) = c(f(x1), f(x2), f(x3)).

So, I thought if the code for z1 (provided below) will perform the following for each element of the vector y:

  • Test whether it is unity or not.
    • If YES, draw a random number from {1, 3, 5, 7, 9}.
    • If NO, draw a random number from {0, 2, 4, 6, 8}.

But, unfortunately it doesn't do that. It generates once for each case, and returns that very random number always.

Where exactly am I doing wrong? Or, is it actually the expected behaviour of ifelse?

Just to note, if I use this as a wrapper function inside sapply, I get the expected output z2 (in the sense that it is not deterministic as z1 where observing one occurrence of each case is enough), as you can see below.

y <- rbinom(n = 20,
            size = 1,
            prob = 0.5)

z1 <- ifelse(test = (y == 1),
             yes = sample(x = c(1, 3, 5, 7, 9),
                          size = 1),
             no = sample(x = c(0, 2, 4, 6, 8),
                         size = 1))

z2 <- sapply(X = y,
             FUN = function(w)
             {
               ifelse(test = (w == 1),
                      yes = sample(x = c(1, 3, 5, 7, 9),
                                   size = 1),
                      no = sample(x = c(0, 2, 4, 6, 8),
                                  size = 1))
             })

data.frame(y, z1, z2)
#>    y z1 z2
#> 1  0  2  2
#> 2  1  1  3
#> 3  1  1  9
#> 4  1  1  7
#> 5  0  2  0
#> 6  0  2  2
#> 7  1  1  7
#> 8  1  1  7
#> 9  0  2  0
#> 10 1  1  5
#> 11 0  2  0
#> 12 0  2  0
#> 13 0  2  6
#> 14 0  2  0
#> 15 0  2  2
#> 16 1  1  7
#> 17 1  1  7
#> 18 0  2  2
#> 19 0  2  2
#> 20 0  2  0

unique(x = z1[y == 1])
#> [1] 1

unique(x = z1[y == 0])
#> [1] 2

Created on 2019-03-13 by the reprex package (v0.2.1)

Any help will be appreciated.

yarnabrina
  • 1,561
  • 1
  • 10
  • 30

1 Answers1

6

ifelse isn't a function of one vector, it is a function of 3 vectors of the same length. The first vector, called test, is a boolean, the second vector yes and third vector no give the elements in the result, chosen item-by-item based on the test value.

A sample of size = 1 is a different size than test (unless the length of test is 1), so it will be recycled by ifelse (see note below). Instead, draw samples of the same size as test from the start:

ifelse(
   test = (y == 1),
   yes = sample(x = c(1, 3, 5, 7, 9), size = length(y), replace = TRUE),
   no = sample(x = c(0, 2, 4, 6, 8), size = lenght(y), replace = TRUE)
)

The vectors don't actually have to be of the same length. The help page ?ifelse explains: "If yes or no are too short, their elements are recycled." This is the behavior you observed with "It generates once for each case, and returns that very random number always.".

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
  • 2
    Just as a side note: I wouldn't say that "A sample of size = 1 is a different size than test, so it won't work with ifelse", because it's pretty common to have `yes` or `no` of size one. What should be outlined is that the value gets recycled, and that's why the OP obtained the same value for each group. – nicola Mar 13 '19 at 18:07
  • Thanks for the explanation.I've a question based on that. When you said that `ifelse` is a function of 3 vectors `test`, `yes` and `no`, do you mean that irrespective of the elements (`TRUE` or `FALSE`) of `test`, the other two (`yes` and `no`) gets calculated always, and the final result is then obtained by combining these two subject to the elements of `test`? – yarnabrina Mar 13 '19 at 18:12
  • 1
    Yes, with the exception of if `test` is *always* TRUE or *always* false. See the help page `?ifelse`: *"`yes` will be evaluated if and only if any element of test is `true`, and analogously for `no`."* For way more detail (and checks), see [Does ifelse really calculate both of its vectors every time?](https://stackoverflow.com/q/16275149/903061) – Gregor Thomas Mar 13 '19 at 18:14
  • 2
    Just to promote myself a bit, I answered a similar question (https://stackoverflow.com/questions/53520036/ordering-problems-when-using-mutate-with-ifelse-condition-to-date/53520746#53520746) without getting much (any to be fair) attention and explaining how the `ifelse` function works. – nicola Mar 13 '19 at 18:16
  • Thanks both of you, Gregor and @nicola for providing explanation and providing related references. – yarnabrina Mar 13 '19 at 18:22