3

I have a vector. For example,

a = c(5,-5)

I am wanting to perform different functions on the value of a depending on whether a>0 or not

For simplicity, let's say the function is as follows

output = ifelse(a>0, log(1+a), 0)

This code returns the desired values in that output is equal to

1.791759 0.000000

However, it also shows the warning:

Warning message:
In log(1+a) : NaNs produced

I was hoping someone might be able to explain why this warning is displaying despite the fact that log(1+a) would only ever be called when a>0 (which would not produce an NaN). Is this a weird quirk of ifelse or am I doing something wrong.

I will also note that this warning does not occur when both elements of a are less than 0 (e.g. a=c(-5,-5))

zx8754
  • 52,746
  • 12
  • 114
  • 209
Cam
  • 158
  • 1
  • 9
  • I don't think you're doing something wrong. If you look at the examples in the docs `?ifelse`, you'll see there's a similar example with `sqrt()`. And besides, the output is as expected. – Val Jul 14 '17 at 07:56
  • 2
    The `ifelse` evaluates the whole "yes" expression `log(1+a)` (if any part of the test is true) and then only picks those elemens where the test is true. But due to the evaluation it returns a warning. – talat Jul 14 '17 at 07:57
  • @docendodiscimus my bad – JAD Jul 14 '17 at 08:08
  • If you want to avoid the warning, you can use `log(replace(a, a <= 0, 0) + 1)` – talat Jul 14 '17 at 08:18
  • Alternatively you could do `log(ifelse(a>0, 1+a, 1))` – JAD Jul 14 '17 at 08:35
  • Thanks for those ideas. Unfortunately, the specific equation I am using is a bit more complicated than I posted. Specifically, `ifelse(a>0, log(1+a), -(log(1+a)+(log(1+a))^2))`. I don't think I can adapt your solutions to that specific equation. I know it isn't good practice, but to avoid generating thousands of errors when I run this a large number of times, I am simply now using suppressWarnings(log(1+a)) – Cam Jul 16 '17 at 01:28

1 Answers1

3

This is a quirky thing about the implementation of ifelse.

If we look at the function, we can see the part that is responsible for the actual output:

> ifelse
function (test, yes, no) 
{
    if (is.atomic(test)) {
        if (typeof(test) != "logical") 
            storage.mode(test) <- "logical"
        if (length(test) == 1 && is.null(attributes(test))) {
            if (is.na(test)) 
                return(NA)
            else if (test) {
                if (length(yes) == 1 && is.null(attributes(yes))) 
                  return(yes)
            }
            else if (length(no) == 1 && is.null(attributes(no))) 
                return(no)
        }
    }
    else test <- if (isS4(test)) 
        methods::as(test, "logical")
    else as.logical(test)
    ans <- test
    ok <- !(nas <- is.na(test))
    if (any(test[ok])) 
        ans[test & ok] <- rep(yes, length.out = length(ans))[test & 
            ok]
    if (any(!test[ok])) 
        ans[!test & ok] <- rep(no, length.out = length(ans))[!test & 
            ok]
    ans[nas] <- NA
    ans
}

This is the relevant part:

    ans <- test
    ok <- !(nas <- is.na(test))
    if (any(test[ok])) 
        ans[test & ok] <- rep(yes, length.out = length(ans))[test & 
            ok]
    if (any(!test[ok])) 
        ans[!test & ok] <- rep(no, length.out = length(ans))[!test & 
            ok]
    ans[nas] <- NA
    ans

The boolean result from test is stored in ans. Then there are some checks about whether there are na results, which is irrelevant here. Then the result vector is created based on the booleans. But look at the way that is done.

For the TRUE results:

ans[test & ok] <- rep(yes, length.out = length(ans))[test & ok]

yes is evaluated and repeated so that it matches the output length, and then subsetted to take the items that were TRUE in the test.

The point where the warning is generated is right here. ifelse does evaluate log(-5 + 1), generating the warning, but then excludes it from the result because test = FALSE.

Note that if all entries are FALSE, the if statement if (any(test[ok])) prevents the execution of that part, so there is no evaluation of the yes argument and no warnings.

JAD
  • 2,035
  • 4
  • 21
  • 35