2

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?

benson23
  • 16,369
  • 9
  • 19
  • 38
Chrissss12
  • 33
  • 4
  • 1
    `which.min()` takes a single argument. The pipe places the object on its left hand side as the first argument to the function on its right hand side. Therefore, the original first argument has nowhere to go. Hence the error. – Limey Feb 26 '22 at 15:20
  • 1
    I should have added that `dplyr::slice_min()` is a pipe-friendly function that will filter your `df` to return only those rows within each group that contain the minimum value of the specified variable within that group. See the online doc for details. – Limey Feb 26 '22 at 17:45

1 Answers1

6

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))
zephryl
  • 14,633
  • 3
  • 11
  • 30
  • This is very good! (+1) – Robert Long Feb 26 '22 at 15:44
  • I used the second solutions, which works perfectly fine. Now I have a additional question. How can I subset the Dataframe ir_OEX_data to only have the closest to the money options? – Chrissss12 Feb 26 '22 at 15:47
  • @Chrissss12 Not 100% sure I understand, but [this answer](https://stackoverflow.com/a/40835570/17303805) may help. If not, and you can't find a solution in a previous SO question, you could post this as a new question, [including data to help people understand your question](https://stackoverflow.com/a/5965451/17303805). – zephryl Feb 26 '22 at 17:28