7
votes

switch() accepts as it's first argument

"EXPR an expression evaluating to a number or a character string."

however, can it be coerced to working with a logical? If so, am I doing something else wrong in this code?

I have a column containing logical values in a data frame, and I want to write a new column containing values from the existing data in the data frame based on the logical parameters:

exampleCurrent <- data.frame(value = c(5.5, 4.5, 4, 2.9, 2),
                         off = as.logical(c("F", "F", "T", "T", "F")),
                         extremeValue = as.logical(c("F", "F", "F", "F", "T")),
                         eclMinWork = c(5, 5.3, 5, 4.7, 3),
                         eclMinOff = c(4, 3.2, 3, 4, 3))

I would like to get to this:

exampleWanted <- data.frame(value = c(5.5, 4.5, 4, 2.9, 2),
                        off = as.logical(c("F", "F", "T", "T", "F")),
                        extremeValue = as.logical(c("F", "F", "F", "F", "T")),
                        eclMinWork = c(5, 5.3, 5, 4.7, 4),
                        eclMinOff = c(4, 3.2, 3, 4, 3),
                        output = c(5, 4.5, 3, 2.9, 3))

The rules for selecting a number are:

  1. Check off. If off is FALSE, select from either value or eclMinWork. If off is TRUE, select from either value or eclMinOff
  2. Check extremeValue. If extreneValue = FALSE, select the smaller of value and the field in step 1. If extremeValue = TRUE, select the value from the field in step 1.

I have successfully written an ifelse() that performs, though I am wondering if I can use switch instead.

exampleGenerated <- cbind(exampleCurrent, bestCase =
                          switch(exampleCurrent$off,
                                 FALSE = ifelse(exampleCurrent$value<exampleCurrent$eclMinWork,exampleCurrent$value, exampleCurrent$eclMinWork),
                                 TRUE = ifelse(exampleCurrent$value<exampleCurrent$eclMinOff,exampleCurrent$value, exampleCurrent$eclMinOff)))

The above throws an error, I am assuming as FALSE is not a character, and is not (on the face of it) a numeric or character:

Error: unexpected '=' in: switch(exampleCurrent$off, FALSE ="

However, my attempts at wrapping as.numeric and as.character around the variables have also failed. Is there a way to do it, or am I missing a fundamental mistake in my code?

1
I don't think that even if you will convert it to character, you will be able to pass an ifelse statement within switch - David Arenburg
@DavidArenburg, that may be true, though if I replace the ifelse() with a straight numerical value, it still doesn't work :( - DaveRGP
Take a look here - David Arenburg
@DavidArenburg, if I understand correctly, you're suggesting to use as.character around off, and then "" on false and true. Afraid I still haven't succeeded: exampleGenerated <- cbind(exampleCurrent, bestCase = switch(as.character(exampleCurrent$off), "FALSE" = "A", "TRUE" = "B")) - DaveRGP
That's because switch can't accept a vector longer than 1 (try reading the error message). In order for your example to work, you''ll have to loop it, i.e., sapply(exampleCurrent$off, function(x) switch(as.character(x), "FALSE" = "A", "TRUE" = "B")). But I'd just go with @Svens nice solution. - David Arenburg

1 Answers

7
votes

You don't need switch for this task. It is easier with ifelse and pmin:

tmp <- with(exampleCurrent, ifelse(off, eclMinOff, eclMinWork))
transform(exampleCurrent, 
          bestCase = ifelse(extremeValue, tmp, pmin(value, tmp)))

#   value   off extremeValue eclMinWork eclMinOff bestCase
# 1   5.5 FALSE        FALSE        5.0       4.0      5.0
# 2   4.5 FALSE        FALSE        5.3       3.2      4.5
# 3   4.0  TRUE        FALSE        5.0       3.0      3.0
# 4   2.9  TRUE        FALSE        4.7       4.0      2.9
# 5   2.0 FALSE         TRUE        3.0       3.0      3.0