user5327476
user5327476

Reputation:

Use mutate_at with nested ifelse

This will make values, which are not in columnA, NA given the conditions (using %>%).

mutate_at(vars(-columnA), funs(((function(x) {
if (is.logical(x))
  return(x)
else if (!is.na(as.numeric(x)))
  return(as.numeric(x))
else
  return(NA)
})(.))))

How can I achieve the same result using mutate_at and nested ifelse?

For example, this does not produce the same result:

mutate_at(vars(-columnA),funs(ifelse(is.logical(.),.,
ifelse(!is.na(as.numeric(.)),as.numeric(.),NA))))

Update (2018-1-5)

The intent of the question is confusing, in part, due to a misconception I had in regard to what was being passed to the function.

This is what I had intended to write:

mutate_at(vars(-columnA), funs(((function(x) {
  for(i in 1:length(x))
  {
    if(!is.na(as.numeric(x[i])) && !is.logical(x[i])) 
    {
      x[i] <- as.numeric(x[i]);
    }
    else if(!is.na(x[i]))
    {
      x[i] <- NA
    }
  }
  return(x)    
})(.))))

This is a better solution:

mutate_at(vars(-columnA), function(x) {
  if(is.logical(x)) 
      return(x)

  return(as.numeric(x))
})

ifelse may not be appropriate in this case, as it returns a value that is the same shape as the condition i.e., 1 logical element. In this case, is.logical(.), the result of the condition is of length 1, so the return value will be first element of the column that is passed to the function.

Update (2018-1-6)

Using ifelse, this will return columns that contain logical values or NA as-is and it will apply as.numeric to columns otherwise.

mutate_at(vars(-columnA),funs(
ifelse(. == TRUE | . == FALSE | is.na(.),.,as.numeric(.))))

Upvotes: 4

Views: 3596

Answers (1)

akrun
akrun

Reputation: 887098

The main issue is the

else if (!is.na(as.numeric(x)))
     return(as.numeric(x)) 

The if/else works on a vector of length 1. If the length of the vector/column where the function is applied is more than 1, it is better to use ifelse. In the above, the !is.na(as.numeric(x)) returns a logical vector of length more than 1 (assuming that the number of rows in the dataset is greater than 1). The way to make it work is to wrap with all/any (depending on what we need)

f1 <- function(x) {
    if (is.logical(x))
         return(x)
      else if (all(!is.na(as.numeric(x))))
         return(as.numeric(x))
          else
       return(x) #change if needed
}

df1 %>%
    mutate_all(f1)

data

set.seed(24)
df1 <- data.frame(col1 = sample(c(TRUE, FALSE), 10, replace = TRUE),
     col2 = c(1:8, "Good", 10), col3 = as.character(1:10),
 stringsAsFactors = FALSE)

Upvotes: 1

Related Questions