【问题标题】:Find the first rows in a data frame which meet a dynamic condition查找满足动态条件的数据框中的第一行
【发布时间】:2018-07-10 17:38:06
【问题描述】:

这里有一些示例代码:

library(quantmod)
library(dplyr)


stock.prices <- getSymbols(Symbols = 'AAPL', from = '2017-08-08', to = '2017-08-17', env = NULL)[,c(2,4)]
stock.dividends <- getDividends(Symbol = 'AAPL', from = '2017-08-08', to = '2017-08-17')

summary <- merge(stock.prices, stock.dividends)
summary <- data.frame(date=index(summary), coredata(summary))
summary <- mutate(summary, buy.price = ifelse(is.na(AAPL.div), NA, lag(AAPL.Close, 1)))
summary

它产生以下数据:

        date AAPL.High AAPL.Close AAPL.div lag.buy.price
1 2017-08-08    161.83     160.08       NA            NA
2 2017-08-09    161.27     161.06       NA            NA
3 2017-08-10    160.00     155.32     0.63        161.06
4 2017-08-11    158.57     157.48       NA            NA
5 2017-08-14    160.21     159.85       NA            NA
6 2017-08-15    162.20     161.60       NA            NA
7 2017-08-16    162.51     160.95       NA            NA

我想像这样附加一列:

        date AAPL.High AAPL.Close AAPL.div lag.buy.price    sell.date
1 2017-08-08    161.83     160.08       NA            NA           NA
2 2017-08-09    161.27     161.06       NA            NA           NA
3 2017-08-10    160.00     155.32     0.63        161.06   2017-08-15
4 2017-08-11    158.57     157.48       NA            NA           NA
5 2017-08-14    160.21     159.85       NA            NA           NA
6 2017-08-15    162.20     161.60       NA            NA           NA
7 2017-08-16    162.51     160.95       NA            NA           NA

这找到了我可以卖出以实现收支平衡的第一个日期...我在 2017 年 8 月 9 日买入股票,以便在第二天有资格获得股息。我支付每股161.06。收到股息后,我现在想以 >= 161.06 的价格出售。 2017-08-15 是我能做到这一点的第一天。

我可以运行一个 for 循环来实现这一点,但它看起来相当粗糙和低效。

有没有办法使用 dplyr 生成“sell.date”列?

【问题讨论】:

  • 我认为因为每个非NA 值都将与不同列中的所有后续值进行比较,所以在没有for 循环的情况下让它工作需要一些重要而脆弱的技巧。我怀疑for 循环将更加声明性和清晰性,从而使您(将来)或其他任何人(现在和永远)的可维护性变得更加简单。除非您发现代码性能是个问题,否则我建议您使用 for 循环。
  • 我认为单独使用dplyr 管道是不可能的。您需要的是一些类似于 SQL 中的窗口和框架功能的概念。 dplyr 不支持这些。我同意 for 循环是要走的路。

标签: r dplyr quantmod


【解决方案1】:

这应该可以让你到达那里:

library(quantmod)
library(tidyverse)


stock.prices <- getSymbols(Symbols = 'AAPL', from = '2017-08-08', to = '2017-08-17', env = NULL)[,c(2,4)]
stock.dividends <- getDividends(Symbol = 'AAPL', from = '2017-08-08', to = '2017-08-17')

summary <- merge(stock.prices, stock.dividends) %>% 
  as_tibble() %>% 
  rownames_to_column('date') %>% 
  coredata() %>% 
  mutate(buy.price = ifelse(is.na(AAPL.div), NA, lag(AAPL.Close, 1)))

new_summary <- summary %>% 
  rownames_to_column() %>%
  mutate(rowname = as.numeric(rowname),
         sell.date = map2_chr(rowname, buy.price, function(row, buy){
           if(is.na(row) | is.na(buy)){
             NA
          }else{
            data <- summary %>% 
              mutate(lt_buy = AAPL.High >= buy) %>% 
              filter(lt_buy == T, rowname > row) 

            min(data$date)
          }
        }))

首先,您需要将行号附加到数据框中。然后,您应该使用purrr::map 来迭代数据(我将您的library(dplyr) 更改为library(tidyverse) 以获得purrr)。 purrr::map2 接受两个向量输入(在这种情况下,您的 data.frame 的两列 - 我冒昧地切换到 tibble)并在这些输入上运行一个函数。我在那里写的匿名函数会过滤您的摘要tibble,以筛选超出输入日期的日期和高于买入价的价格。然后它返回满足该条件的最短日期。

我还对您的数据设置进行了一些更改,使其使用管道链和更多 tidy 类型的结构。

希望这会有所帮助!

【讨论】:

    【解决方案2】:
    df[is.na(df$AAPL.div),'AAPL.div'] <- 0
    
    sell.date <- 
    with(df, {
      bought <- date > as.Date('2017-08-09')
      date[which.max(bought & (AAPL.Close + cumsum(AAPL.div*bought)) > 161.06)]})
    sell.date     
    #[1] "2017-08-15"
    

    将此添加为列

    df$sell.date <- ifelse(is.na(df$lag.buy.price), NA, sell.date)
    
    df
    #          date AAPL.High AAPL.Close AAPL.div lag.buy.price  sell.date
    # 1: 2017-08-08    161.83     160.08     0.00            NA       <NA>
    # 2: 2017-08-09    161.27     161.06     0.00            NA       <NA>
    # 3: 2017-08-10    160.00     155.32     0.63        161.06 2017-08-15
    # 4: 2017-08-11    158.57     157.48     0.00            NA       <NA>
    # 5: 2017-08-14    160.21     159.85     0.00            NA       <NA>
    # 6: 2017-08-15    162.20     161.60     0.00            NA       <NA>
    # 7: 2017-08-16    162.51     160.95     0.00            NA       <NA>
    

    使用的数据

    library(data.table)
    df <- fread("
    a        date AAPL.High AAPL.Close AAPL.div lag.buy.price
    1 2017-08-08    161.83     160.08       NA            NA
    2 2017-08-09    161.27     161.06       NA            NA
    3 2017-08-10    160.00     155.32     0.63        161.06
    4 2017-08-11    158.57     157.48       NA            NA
    5 2017-08-14    160.21     159.85       NA            NA
    6 2017-08-15    162.20     161.60       NA            NA
    7 2017-08-16    162.51     160.95       NA            NA
    ")[, -1]
    

    【讨论】:

      【解决方案3】:

      这个解决方案并非完全没有 for 循环,但我猜你的意思是一个循环来比较每个值(该部分在这里被矢量化)。以防万一您观察到的红利不止一个,则需要使用此循环:

      summary$sell.date<-as.Date(rep(NA,7))
      
      
      for(i in 1:length(which(!is.na(summary$buy.price))))
      summary$sell.date[which(!is.na(summary$buy.price))[i]]<- summary[c(rep(FALSE,which(!is.na(summary$buy.price))[i]-1),(summary[which(!is.na(summary$buy.price))[i]:nrow(summary),"AAPL.High"]>summary[!is.na(summary$buy.price),"buy.price"][i])),"date"][1]
      

      它产生以下结果:

           date AAPL.High AAPL.Close AAPL.div buy.price  sell.date
      1 2017-08-08    161.83     160.08       NA        NA       <NA>
      2 2017-08-09    161.27     161.06       NA        NA       <NA>
      3 2017-08-10    160.00     155.32     0.63    161.06 2017-08-15
      4 2017-08-11    158.57     157.48       NA        NA       <NA>
      5 2017-08-14    160.21     159.85       NA        NA       <NA>
      6 2017-08-15    162.20     161.60       NA        NA       <NA>
      7 2017-08-16    162.51     160.95       NA        NA       <NA>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-20
        • 1970-01-01
        • 1970-01-01
        • 2018-05-16
        • 2021-02-01
        • 2013-10-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多