Сгруппировать по и найти ближайший номер

Данные представлены внизу страницы. У меня есть 2 фрейма данных df1 и df2.

df1:
ticker   Price
<chr>    <dbl>
SPY      200.00
AAPL     100.00

df2:
ticker  expiration   strike
<chr>    <dbl>       <dbl>
SPY      0621         180
SPY      0621         205
SPY      0719         180
SPY      0719         205
AAPL     0621          75
AAPL     0621         105
AAPL     0719          75
AAPL     0719         105

Оба фрейма данных содержат данные о запасах и имеют общий тикер в столбце. Я хотел бы сгруппировать df2 по 2 столбцам, а затем найти ближайший к столбцу Price страйк в df1.

Результат будет выглядеть примерно так.

df3 = df2 %>% group_by(ticker, expiration)%>% #which[abs(df1$Price - df2$strike) is closest to 0]

output:
ticker   expiration  strike
<chr>     <dbl>       <dbl>
SPY       0621         205
SPY       0719         205
AAPL      0621         105
AAPL      0719         105

Вот df1

structure(list(ticker = structure(2:1, .Label = c("AAPL", "SPY"
), class = "factor"), Price = c(200, 100)), class = "data.frame", row.names = c(NA, 
-2L))

Вот df2

structure(list(ticker = structure(c(2L, 2L, 2L, 2L, 1L, 1L, 1L, 
1L), .Label = c("AAPL", "SPY"), class = "factor"), expiration = c(621, 
621, 719, 719, 621, 621, 719, 719), strike = c(180, 205, 180, 
205, 75, 100, 75, 100)), class = "data.frame", row.names = c(NA, 
-8L))

Меня интересует ответ @akrun data.table. Однако я не получаю желаемого результата. 0719 для SPY отсутствует.

library(data.table)
setDT(df2)[, Price := strike][df1, on = .(ticker, Price), roll = -Inf]
ticker expiration strike Price
1:    SPY        621    205   200
2:   AAPL        621    100   100
3:   AAPL        719    100   100

person Jordan Wrong    schedule 05.06.2019    source источник
comment
Обратите внимание, что ожидаемый результат будет 100 вместо 105.   -  person akrun    schedule 05.06.2019


Ответы (3)


Мы можем использовать скользящее соединение после создания комбинации с unique элементами 'expiration' из второго набора данных.

library(data.table)
library(tidyr)
df1N <- crossing(df1, expiration = unique(df2$expiration))
setDT(df2)[, Price := strike][df1N, on = .(ticker, expiration, Price), roll = -Inf]
#    ticker expiration strike Price
#1:    SPY        621    205   200
#2:    SPY        719    205   200
#3:   AAPL        621    100   100
#4:   AAPL        719    100   100

Или выполните full_join, а затем slice на основе minimum abs абсолютной разницы между столбцами «Цена» и «страйк» после группировки по «тикеру», «истечению».

library(dplyr)
full_join(df1, df2) %>% 
    group_by(ticker, expiration) %>% 
    slice(which.min(abs(Price - strike)))
# A tibble: 4 x 4
# Groups:   ticker, expiration [4]
#  ticker Price expiration strike
#  <fct>  <dbl>      <dbl>  <dbl>
#1 AAPL     100        621    100
#2 AAPL     100        719    100
#3 SPY      200        621    205
#4 SPY      200        719    205
person akrun    schedule 05.06.2019
comment
Привет, Акрун. Меня действительно интересует способ data.table, однако я не получаю полного вывода. Похоже, 719 для SPY отсутствует. Я отредактировал результат в своем исходном сообщении. - person Jordan Wrong; 05.06.2019
comment
Привет, акрун, спасибо за весь цвет. Однако мне особенно нужно выбрать удар, который ближе всего к отметке 100, а это будет 105, а не 100. - person Jordan Wrong; 05.06.2019
comment
так жаль. Виноват. Большое спасибо!!! Это меня действительно беспокоило. - person Jordan Wrong; 05.06.2019

tidyverse ответ:

library(tidyverse)

df2 %>% 
  left_join(df1) %>%
  mutate(diff = abs(strike - Price)) %>%
  group_by(ticker, expiration) %>%
  top_n(-1, wt = diff) %>%
  select(-Price, -diff)

Выход:

Joining, by = "ticker"
# A tibble: 4 x 3
# Groups:   ticker, expiration [4]
  ticker expiration strike
  <fct>       <dbl>  <dbl>
1 SPY           621    205
2 SPY           719    205
3 AAPL          621    100
4 AAPL          719    100
person Marian Minar    schedule 05.06.2019
comment
Без проблем. Я рекомендую провести стресс-тестирование этих решений, настроив данные там, где есть ДВА ближайших значения. Что происходит с галстуком? Что бы вы хотели, чтобы произошло? Попробуйте сломать его, чтобы он стал сильнее. - person Marian Minar; 05.06.2019
comment
Очень мило с вашей стороны добавить немного цвета. Я могу добавить эту концепцию ко многим другим моим функциям! - person Jordan Wrong; 05.06.2019

Часто мне нравится использовать distinct() для выбора наименьшего или наибольшего значения для каждой группы (или любого другого результата arrange() на самом деле). Здесь я сначала упорядочиваю данные по абсолютной разнице strike и Price. Это очень быстро по сравнению с group_by(). По умолчанию distinct() выбирает первую строку для данной комбинации, и если мы используем .keep_all = TRUE, остальные столбцы сохраняются.

library(dplyr)

df2 %>% 
  left_join(df1) %>% 
  arrange(ticker, expiraton, abs(strike - Price)) %>% 
  distinct(ticker, expiraton, .keep_all = TRUE)
#> Joining, by = "ticker"
#>   ticker expiraton strike Price
#> 1   AAPL       621    100   100
#> 2   AAPL       719    100   100
#> 3    SPY       621    205   200
#> 4    SPY       719    205   200
person pasipasi    schedule 05.06.2019
comment
извиняюсь. плохое редактирование.Спасибо за помощь, паси. Это сработало отлично. - person Jordan Wrong; 05.06.2019