使用 API 调用向量化 R 循环

Vectorizing R Loop with API Call

我对向量化的概念比较陌生,想问一下社区是否有任何建议来改进我一直用来下载 bloomberg 的进程的 运行 时间 API 数据并将其绑定到矩阵。

目前,此过程在我的 API 调用中遍历每个单独的日期,这需要花费相当多的时间。我想知道我是否可以以“矢量化”方式执行此操作以便一次进行多次调用,然后绑定到数据帧,从而减少 运行 时间。 '''

#create fund names to feed through as param in loop below
fundList <- c("fund 1 on bloomberg",
"fund 2 on bloomberg",
"fund 3 on bloomberg",
"fund 4 on bloomberg",
"fund 5 on bloomberg",
"fund 6 on bloomberg",
"fund 7 on bloomberg",
)

#create datelist for params for loop
newDateList <- seq(as.Date(today()-1401),length=1401, by="days")
newDateListReformatted <- gsub("-","",newDateList)


#create df object and loop through bloomberg API, assign to dataframe object
df_total = data.frame()

for(fund in 1:length(fundList)){
  
  df_total = data.frame()
  
  for(b in 1:length(newDateListReformatted)){
    ovrd <- c("CUST_TRR_START_DT"=newDateListReformatted[b],"CUST_TRR_END_DT"=newDateListReformatted[b+1])
    print(ovrd)
    model <- bdp(fundList[fund],"CUST_TRR_RETURN_HOLDING_PER",overrides=ovrd)
    print(model)
    df <- data.frame(model)
    df1 <- data.frame(newDateListReformatted[b+1])
    df2 <- cbind(df,df1)
    df_total <- rbind(df_total,df2)
  }
  
  assign(fundList[fund],df_total)

}

'''

首先,循环移动到第一级的基金,遍历所有日期,并在移动到 fundList 中的下一个基金并再次遍历时间序列之前,一次一步地将行绑定到数据框。

按照我的想法,我会调用函数的多个日期参数向量,然后将它们“垂直”分配给 df_total 矩阵,一次分配的数量大于一个每个循环增加 运行 时间。或者,我可以调用每个单独的日期,但是跨多个基金进行调用并将它们“水平”分配给矩阵。

如有任何想法,我们将不胜感激。

矢量化包括创建一个函数,该函数可以有效地实现对每个输入的多个参数的处理。例如,可以使用循环 lapply(mtcars, mean) 或使用向量化函数 colMeans(mtcars) 来计算列的平均值。后者比使用循环 更有效,因为该函数针对输入进行了优化。

在 Whosebug 上,矢量化经常被误解为 readability 代码,因此使用 *apply 函数通常被认为是矢量化,而这些对于可读性更有用(它们本身没有)加速你的代码。

对于您的具体示例,您的瓶颈(和问题)部分来自对 bdp 的调用,部分来自使用 cbindrbindassign.
为了加速你的代码,我们首先需要知道函数是如何实现的。从文档中我们可以读到 fieldssecurities 接受多个参数。因此,这些参数是 vectorized,而 overrides 只接受覆盖字段的命名向量。这意味着我们可以通过一次提供所有字段和证券来消除代码中的外部循环。

接下来,为了通过迭代扩展 data.frame 来减少多次调用的开销,我们可以将中间结果存储在 list 中,一旦代码具有 运行。结合这些我们得到一个代码示例,例如下面的

n <- length(newDateListReformatted)
# Create override matrix (makes it easier to subset, but not strictly necessary
periods <- matrix(c(newDateListReformatted[-n], newDateListReformatted[-1]), ncol = 2, byrow = FALSE)
colnames(periods) <- c('CUST_TRR_START_DT', 'CUST_TRR_END_DT')
ovrds <- newDateListReformatted
models <- vector('list', n - 1)
for(i in seq_len(n - 1)){
  models[[i]] <- bdp(fundList, 
               'CUST_TRR_RETURN_HOLDING_PER', 
               overrides = periods[i, ]
               )
  # Add identifier columns
  models[[i]][,'CUST_TRR_START_DT'] <- periods[i, 1]
  models[[i]][,'CUST_TRR_END_DT'] <- periods[i, 2]
}
# Combine results in single data.frame (if wanted)
model <- do.call(rbind, models)

请注意,代码通过使用 do.call(rbind, models) 组合中间结果来完成,它给出了一个 data.frame,但是可以使用 dplyr 包中的 bind_rowsrbindlist 来自 data.table 包。

另请注意,我无法访问 bloomberg(目前),也无法测试我的代码是否存在拼写错误。