【问题标题】:How to store loop output of each iteration to data frame如何将每次迭代的循环输出存储到数据帧
【发布时间】:2017-10-17 18:39:48
【问题描述】:

好的,我有一个循环来计算股票价格系列的年化/累积回报。

我希望对许多文件做同样的事情。所以做了一个循环来这样做。

首先是一些虚拟数据:

    # Create dummy data
    # Use lubridate to change timestamp to date format
    # Use dplyr to arrange by ascending order
    # Use fread from data.table to read .csv to data frame
    require(lubridate)
    require(data.table)
    require(dplyr)
    MSFT <- fread("https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=MSFT&outputsize=full&apikey=6RSYX9BPXKZVXUS9&datatype=csv")
    MSFT$timestamp <- ymd(MSFT$timestamp)
    MSFT <- arrange(MSFT,timestamp)
    AAPL <- fread("https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=AAPL&outputsize=full&apikey=6RSYX9BPXKZVXUS9&datatype=csv")
    AAPL$timestamp <- ymd(AAPL$timestamp)
    AAPL <- arrange(AAPL,timestamp)
    NFLX <- fread("https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=NFLX&outputsize=full&apikey=6RSYX9BPXKZVXUS9&datatype=csv")
    NFLX$timestamp <- ymd(NFLX$timestamp)
    NFLX <- arrange(NFLX,timestamp)
    TSLA <- fread("https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=TSLA&outputsize=full&apikey=6RSYX9BPXKZVXUS9&datatype=csv")
    TSLA$timestamp <- ymd(TSLA$timestamp)
    TSLA <- arrange(TSLA,timestamp)

    # Place data frames in a list
    df.list <- list(MSFT,AAPL,NFLX,TSLA)

# Specify file names
    file.names <- c("MSFT","AAPL","NFLX","TSLA")

现在准备数据。

接下来我要计算每个系列的累计和年化回报。我将它放在一个函数中,然后用循环调用该函数:

    # Create function for performing commands.
    genAnnualized = function(x){
      next.file <- data.frame(df.list[[1]],stringsAsFactors=FALSE)
      next.name <- paste0(file.names[i])
      new.df <- data.frame(next.file)

      # Calculate annualized return 
      # Make prices vector
      prices <- new.df[, "close", drop = FALSE]

      # Denote n the number of time periods:
      n <- nrow(prices)

      # Calculate close to close returns
      # lead in with rep,NA,1 to maintain length of vector comparible to data frame
      close_ret <- c(rep(NA, 1),(prices[2:n, 1] - prices[1:(n-1), 1])/prices[1:(n-1), 1])
      close_ret[1] <- 0

      # Compute continuously  returns (log returns)
      close_ccret <- log(prices[2:n, 1]) - log(prices[1:(n-1), 1])

      # Compute gross returns
      close_gret <- 1 + close_ret   # use close to close ret

      # Compute future values
      close_fv <- cumprod(close_gret)

      # Obtain first and last values
      ret.last <- tail(close_fv, n=1)
      ret.first <- head(close_fv, n=1)
      cum.ret <- (ret.last-ret.first)/ret.first

      # Get First And Last row to calculate time between
      ret.first.row <- head(new.df$timestamp, n=1)
      ret.last.row <- tail(new.df$timestamp, n=1)

     # Time diff 
     #trading.years.between <- as.numeric(difftime(as.Date(ret.last.row), as.Date(ret.first.row), unit="weeks"))/52.25

      # Find time diff
      ret.time <- ret.last.row - ret.first.row
      ret.trading.years.between <- ret.time/365   #252 trading days or 365 
      ret.trading.years.between <- as.numeric(ret.trading.years.between, units="days")   # Extract numerical value from time difference 'Time difference of 2837.208 days'
      # Annualized return
      # (1 + % diff of final) / (last balance to the power of 1/time first and last balance) -1
      ret.annual.return <- (1+cum.ret) ^ (1/ret.trading.years.between) -1

      ########## Store annualized and cumulative return in data frame for each iteration #########
      # Store file name as a row name :: next.name variable
      # Store final annualized return :: cret.annual.return
      # Store final cumulative return :: cum.ret
      output.df <- cbind(cum.ret,ret.annual.return)
      rownames(output.df) <- next.name

##################################################################

      # Sanity check, use PerformanceAnalytics for annualized return
      # TTR for returns
      # Calculate Close-to-Close returns
      require(TTR)
      require(PerformanceAnalytics)
      new.df$clret <- ROC(new.df$close, type = c("discrete"))
      new.df$clret[1] <- 0
      # Make time series object of returns and date
      require(xts)
      xts1 = xts(new.df$clret, order.by=as.Date(new.df$timestamp, format="%m/%d/%Y")) 
      Return.annualized(xts1)
      Return.cumulative(xts1, geometric=TRUE)
    }

并调用函数循环遍历数据框列表中的每个数据框:

for (i in 1:length(df.list)){
  tryCatch({
    genAnnualized(df.list[[i]])
  }, error = function(e) { print(paste("i =", i, "failed:")) })
}

这应该是一个可重现的例子。

在每次迭代中,我希望将每个系列的累积和年化收益与数据集的名称一起存储(以便以后识别)。 我在我的函数中尝试使用以下内容:

  output.df <- cbind(cum.ret,ret.annual.return)
  rownames(output.df) <- next.name

我正在指定名称:

file.names <- c("MSFT","AAPL","NFLX","TSLA")

然后在调用它的函数中:

next.name <- paste0(file.names[i])

我希望粘贴文件名,以便在数据框中标记我的最终输出。

我认为在命名每行或列名时可能需要重复两次。这样它就可以标记累积回报和年化回报。

我认为有一个大致的想法,但已经为此苦苦挣扎了几个星期,因此寻求一些帮助。

基本上使用输出数据框,然后我可以将其组织成四分位数等以进行进一步分析

【问题讨论】:

  • 我总是通过将值分配给循环内的数据框或列表来处理这些情况,即 alist[]
  • 您在函数的第一行有一个硬编码的df.list[[1]] - 这似乎不正确。此外,您的函数应该 return() 一些东西 - 现在它正在返回 Return.cumulative(xts1, geometric=TRUE),但 Return.annualized(xts1) 已计算但未使用或返回。
  • 我建议编写你的函数,这样它就需要一个数据框和一个名称作为参数,并在没有“意识到”它在循环内的情况下对它们进行操作 - 没有对 i 的引用或函数内的df.list。然后你可以,正如 CCurtis 建议的那样,做类似results = list(); for (i in seq_along(df_list)) results[[i]] = genAnnualized(dat = df.list[[i]], name = file.names[i])
  • 是的,我改成了 df.list[[i]] 来完成这项工作。阅读 return() 并看看它现在是如何工作的!

标签: r function loops for-loop


【解决方案1】:

我最简单的解决方案是为每个file.names 评估rbind 一个数据框,并将此行命名为与相应文件的名称相同。 为了清楚起见,我删除了 cmets(并放了一些我的)。

'genAnnualized' = function(df_list) {

  next.file <- data.frame(df_list, stringsAsFactors=FALSE) # Put the parameter of the function here
  next.name <- paste0(file.names[i])
  new.df <- data.frame(next.file)

  prices <- new.df[, "close", drop = FALSE]
  n <- nrow(prices)

  close_ret <- c(rep(NA, 1),(prices[2:n, 1] - prices[1:(n-1), 1])/prices[1:(n-1), 1])
  close_ret[1] <- 0
  close_ccret <- log(prices[2:n, 1]) - log(prices[1:(n-1), 1])
  close_gret <- 1 + close_ret  
  close_fv <- cumprod(close_gret)
  ret.last <- tail(close_fv, n=1)
  ret.first <- head(close_fv, n=1)
  cum.ret <- (ret.last-ret.first)/ret.first
  ret.first.row <- head(new.df$timestamp, n=1)
  ret.last.row <- tail(new.df$timestamp, n=1)

  ret.time <- ret.last.row - ret.first.row
  ret.trading.years.between <- ret.time/365  
  ret.trading.years.between <- as.numeric(ret.trading.years.between, units="days")  

  ret.annual.return <- (1+cum.ret) ^ (1/ret.trading.years.between) -1

  output.df <- cbind(cum.ret,ret.annual.return)
  rownames(output.df) <- next.name

  ##################################################################

  new.df$clret <- TTR::ROC(new.df$close, type = c("discrete"))
  new.df$clret[1] <- 0
  xts1 = xts::xts(new.df$clret, order.by=as.Date(new.df$timestamp, format="%m/%d/%Y")) 

  # Create the output of the function : a named data.frame 
  out_df <- data.frame("Annualized Return" = PerformanceAnalytics::Return.annualized(xts1),
                       "Cumulative Return" = PerformanceAnalytics::Return.cumulative(xts1, geometric=TRUE))
  return(out_df)
}

# Initialize the output dataframe to which we will rowbind the results
cum_ret <- data.frame()

for (i in 1:length(df.list)){
    temp <- genAnnualized(df.list[[i]] )
    rownames(temp) <- file.names[i]
    cum_ret <- rbind.data.frame(cum_ret, temp)
}

这给出了一个数据框,其命名行数等于 df.list 中的文件和 2 列的年化和累积回报。

> cum_ret
     Annualized.Return Cumulative.Return
MSFT       -0.02279597        -0.3361359
AAPL        0.02039616         0.4314812
NFLX        0.17454862        10.8991045
TSLA        0.44666765        13.8233571

【讨论】:

  • 谢谢。我现在看到了它的结构。本质上,我在想我需要将每个迭代存储在函数内部的同一个 df 中,并继续在那里构建它。但是,我看到我们只放置了该迭代的最终结果,然后使用 return()。 return(df) 有什么作用?以及它如何与调用函数的循环交互? return simple 是否会忽略函数中的所有其他内容,返回我们的输出,这是我们在循环中调用函数时唯一获取的内容?
猜你喜欢
  • 1970-01-01
  • 2021-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-17
  • 1970-01-01
相关资源
最近更新 更多