Search code examples
rswitch-statementmagrittr

Why does this switch statement (case_else too) not work if I try to forward pipe it?


Why does this work:

 est <- unlist(column) %>%
    sample(i, replace = TRUE) %>%
    matrix(nrow = B, ncol = i) # %>%
  
  est <- switch(param,
           "mean" = rowMeans2(est, na.rm = TRUE),
           "sd" = rowSds(est, na.rm = TRUE),
           stop("Invalid `param` value. Valid values are 'mean' or 'sd.'")
           ) # %>%
  
  est <- mean(est)

But this does not:

  est <- unlist(column) %>%
    sample(i, replace = TRUE) %>%
    matrix(nrow = B, ncol = i) %>%
    switch(
      param,
      "mean" = rowMeans2(., na.rm = TRUE),
      "sd" = rowSds(., na.rm = TRUE),
      stop("Invalid `param` value. Valid values are 'mean' or 'sd.'")
    ) %>%
    mean()

The example using pipes gives the following error:

Error in switch(., param, mean = rowMeans2(., na.rm = TRUE), sd = rowSds(., : EXPR must be a length 1 vector

Also found case_when fails using pipes:

  case_when(
      param == "mean" ~ rowMeans2(na.rm = TRUE),
      param == "sd" ~ rowSds(na.rm = TRUE),
      TRUE ~ (stop("Invalid `param` value. Valid values are 'mean' or 'sd.'"))
    ) %>%
    mean()

With a different error:

Error in case_when(): ! Case 1 (.) must be a two-sided formula, not a double matrix.

Minimal code sample for repro:

# Sample data
set.seed(123)
column <- data.frame(X1 = rnorm(100))

# Parameters
i <- 5
B <- 100
param <- "mean"

# Load matrixStats
library(matrixStats)

# My function
myFunc <- function(i, column, B, param) {
  est <- unlist(column) %>%
    sample(i, replace = TRUE) %>%
    matrix(nrow = B, ncol = i) %>%
    switch(
      param,
      "mean" = rowMeans2(., na.rm = TRUE),
      "sd" = rowSds(., na.rm = TRUE),
      stop("Invalid `param` value. Valid values are 'mean' or 'sd.'")
    ) %>%
    mean()
  
  # Print the result
  print(est)
}

# Call the function for testing
myFunc(i, column, B, param)

Solution

  • You are calling switch inside the pipeline, so the previous output will take the place of its first argument EXPR. That's not a scalar, so that's an error, and it's also no longer the param which you intended to use. You can easily get around this by parsing param first, and you also only need to do this once (not for every value in the pipeline):

    myFunc <- function(i, column, B, param) {
      ## Parse once, outside pipeline
      paramfunc <- switch(
        param,
        "mean" = \(x) rowMeans2(x, na.rm = TRUE),
        "sd" = \(x) rowSds(x, na.rm = TRUE),
        stop("Invalid `param` value. Valid values are 'mean' or 'sd.'")
      )
      
      ## Insert above into pipeline
      est <- unlist(column) %>%
        sample(i, replace = TRUE) %>%
        matrix(nrow = B, ncol = i) %>%
        paramfunc() %>%
        mean()
      
      # Print the result
      print(est)
    }
    
    myFunc(i, column, B, param)
    #> 0.5044144