Search code examples
rmagrittr

Why is a command not working in a pipe operator in R, but outside the pipe it works perfectly fine?


My dataframe includes options data. I want to find the closest to the money option for every trading date. Unfortunately

ir_OEX_data %>% group_by(quotedate) %>% which.min(abs(moneyness_call  - 1))

leads to the following error:

Error in which.min(., abs(ir_OEX_data$moneyness_call - 1)) : unused argument (abs(ir_OEX_data$moneyness_call - 1))

But when I run solely:

 which.min(abs(ir_OEX_data$moneyness_call  - 1))

The command works perfectly fine.

What is my mistake here?


Solution

  • Problem: not all functions are pipe-friendly

    {magrittr} pipes work best with functions written to be "pipe-friendly." These generally take a dataframe as a first argument, and may use data masking to let you refer to columns within that dataframe without prefixing them. e.g., many {dplyr} verbs are pipe-friendly.

    which.min isn't pipe-friendly. Your code,

    ir_OEX_data %>% group_by(quotedate) %>% which.min(abs(moneyness_call  - 1))
    

    is actually equivalent to

    which.min(
      group_by(ir_OEX_data, quotedate), 
      abs(moneyness_call  - 1)
    )
    

    but which.min expects only one argument, so throws an error.

    Solution 1: the exposition pipe (%$%)

    There are a few ways to deal with this. One is the {magrittr} exposition pipe, %$%, which makes your column names available to the next function without passing the data:

    library(magrittr)
    library(dplyr)
    
    ir_OEX_data %>% 
      group_by(quotedate) %$% 
      which.min(abs(moneyness_call  - 1))
    

    Solution 2: use inside a pipe-friendly function

    If you wanted to add the result of which.min to your dataset, you'd just need to use it inside summarize or mutate:

    ir_OEX_data %>% 
      group_by(quotedate) %>% 
      summarize(call_which_min = which.min(abs(moneyness_call  - 1)))
    

    Solution 3: write a pipe-friendly wrapper

    You can also put a non-friendly function in a pipe-friendly wrapper. This would probably be overkill here, but can be useful in more complex cases.

    which_min_pipe <- function(.data, x) {
      .data %>% summarize(out = which.min({{ x }})) %>% pull(out)
    }
    
    ir_OEX_data %>% 
      group_by(quotedate) %>% 
      which_min_pipe(abs(moneyness_call - 1))