3

I created a user function that computes Hedges' c() bias correction for g (Hedges, 1981). It is directly based on metafor::.cmicalc() function from library(metafor). It does this:

hedges_c <- function(df) {
  return(exp(lgamma(df / 2) - log(sqrt(df / 2)) - lgamma((df - 1)/2)))
}

When applied to a vector containing values <= 1, lgamma() generates a warning, because lgamma(0) (as well as any negative value) generates NaN. So, my solution (and also what metafor::.cmicalc() does) was including an ifelse() statement:

hedges_c <- function(df) {
  cdf <- ifelse(df <= 1, 
                NA,
                exp(lgamma(df / 2) - log(sqrt(df / 2)) - lgamma((df - 1)/2)))
  return(cdf)
}

But, and here is the problem I don't seem to find the solution to, it still generates the warnings(), even if all the values are correctly rendered as NA.

Example:

hedges_c(c(0, 0, 20, 14, 0, 0, 0, 0))
#[1]      NA      NA 0.9619445 0.9452877      NA      NA      NA      NA
#Warning messages:
#1: In ifelse(df <= 1, NA, exp(lgamma(df/2) - log(sqrt(df/2)) - lgamma((df -  :
#  value out of range in 'lgamma'
#(...)

I understand (e.g., from this answer) that the third (FALSE) argument of ifelse() is evaluated even when the condition is TRUE (and inversely if I change the order of the condition and the arguments)... But I simply don't know how to solve that (except maybe for hiding warnings before and after...).

(Note: I also tried dplyr::case_when(), but it is exactly the same problem.)

zx8754
  • 52,746
  • 12
  • 114
  • 209
iNyar
  • 1,916
  • 1
  • 17
  • 31
  • 3
    Did you try to use `if(...) { ... } else { ... }` instead of `ifelse(...,...,...)`? – Heikki Nov 23 '17 at 22:34
  • @Heikki: Yes, but then it does not work correctly when applied on a vector. `ifelse()` allows it to be vectorized. – iNyar Nov 23 '17 at 23:37

2 Answers2

3

I'm pretty sure the values are calculated regardless, and just subset out by ifelse. You could always just apply your function to the valid values and make the rest NA:

hedges_c <- function(df) {
  ss <- df >= 1

  hc <- function(x) exp(lgamma(x / 2) - log(sqrt(x / 2)) - lgamma((x - 1)/2))

  df[ss]  <- hc(df[ss])
  df[!ss] <- NA
  df
}

hedges_c(c(0, 0, 20, 14, 0, 0, 0, 0))
#[1]        NA        NA 0.9619445 0.9452877        NA        NA        NA        NA
thelatemail
  • 91,185
  • 12
  • 128
  • 188
  • As an answer to your comment to my answer, I believe you are right, I remember having read the source of `ifelse` and I have the impression that it creates both vectors, the `yes` and the `no`, and then subsets. I'm not sure and right now don't have the time but will return to this tomorrow. – Rui Barradas Nov 23 '17 at 22:44
  • I tested with `casewhen` and it also computes all values – moodymudskipper Nov 23 '17 at 22:45
  • @Moody_Mudskipper - yep, `case_when` just creates a big list of all the logical outcomes of each comparison and then loops over them looking for the first `TRUE` value. That's why the remainder cases are always `TRUE ~ ...` You can pretty much approximate `case_when` by `cbind`-ing each of the comparisons and calling `max.col(result, "first")` – thelatemail Nov 23 '17 at 23:03
  • You and I were right, the last lines of `ifelse` do subset the vector `ans`. – Rui Barradas Nov 24 '17 at 12:07
3

Try using a different approach. Something like the following.

hedges_c <- function(df) {
  cdf <- rep(NA, length(df))
  inx <- df > 1
  cdf[inx] <- exp(lgamma(df[inx] / 2) - log(sqrt(df[inx] / 2)) - lgamma((df[inx] - 1)/2))
  return(cdf)
}

hedges_c(c(0, 0, 20, 14, 0, 0, 0, 0))
#[1]        NA        NA 0.9619445 0.9452877        NA        NA        NA
#[8]        NA

And the warnings are gone.

Rui Barradas
  • 70,273
  • 8
  • 34
  • 66