Сгруппировать и найти ближайший номер - PullRequest
2 голосов
/ 05 июня 2019

Данные приведены внизу страницы.У меня есть 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

Ответы [ 3 ]

2 голосов
/ 05 июня 2019

A 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
2 голосов
/ 05 июня 2019

Мы можем использовать скользящее соединение после создания комбинации с 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 на основе абсолютной разницы min imum 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
1 голос
/ 05 июня 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
...