如何优化涉及列表附件的嵌套for循环?

时间:2019-01-02 11:46:16

标签: r for-loop lapply purrr

我正在使用英国开发的用于从API提取fingertipsR API数据包的过程中,从英国的公共卫生机构提取特定地理区域和特定指标的数据,然后将其插入空白列表,该列表由包含代表每个指标的列表的列表(地理区域)组成。

geog <- c("E38000220", "E38000046", "E38000144", "E38000191", "E38000210", 
"E38000038", "E38000164", "E38000195", "E38000078", "E38000139", 
"E38000166", "E38000211", "E38000147", "E38000183", "E38000028", 
"E38000053", "E38000126", "E38000153", "E38000173", "E38000175"
)
indicators <- c(241, 92588, 90672, 90692, 90697, 90698, 90701, 90702, 91238, 
90690, 90694, 93245, 93246, 93244, 93247, 93248, 93049, 93047, 
90700)

## install.packages("fingertipsR"); library(fingertipsR)
library(dplyr)

list <- list()

start <- Sys.time()
for (geog_group in geog) {
    for (indicator_number in indicators) {
    list[[geog_group]][[as.character(indicator_number)]] <- fingertips_data(IndicatorID = indicator_number, AreaTypeID = c(152, 153, 154)) %>% 
      filter(AreaCode == geog_group, TimeperiodSortable == max(TimeperiodSortable)) %>% 
      select(Timeperiod, Value) %>% distinct()
  }
}
end <- Sys.time()
end-start

在我的笔记本电脑上,这大约需要15分钟才能执行-我想知道是否有任何简便的方法可以优化此代码-可能使用lapplypurrr

编辑:理想情况下,我希望每个地理区域的指标都在一个数据框中,因为它们共享相同的列Time periodValue-我将在{{之后处理1}}或类似的东西-但如果有人有办法在for循环内解决该问题,我欢迎您提出建议。

1 个答案:

答案 0 :(得分:1)

这是一个更紧凑的循环(大约需要25秒)

result_list <- list(length(indicators))
for (k in seq_along(indicators)) {
  ind   <- indicators[k]
  # load the data once per indicator
  tmpDF <- fingertips_data(IndicatorID = ind, AreaTypeID = 152:154)
  # retrieve the rows corresp. to max per geog
  out <- t(vapply(seq_along(geog), function (s) {
    row_geog <- which(.subset2(tmpDF, which(names(tmpDF) == 'AreaCode')) == geog[s])
    row_max <- which.max(.subset2(tmpDF, which(names(tmpDF) == 'TimeperiodSortable'))[row_geog])
    res <- tmpDF[row_geog,c("Timeperiod","Value")][row_max,]
    res <- c(Timeperiod = res$Timeperiod, Value = res$Value)
    if (length(res) == 0) res <- c(Timeperiod = NA_character_, Value = NA_character_)
    return (res)
  }, character(2)))
  # save result for indicator[k]
  result_list[[k]] <- data.frame(indicator = ind, geog, 
                                 Timeperiod = out[,1], 
                                 Value = as.numeric(out[,2]),
                                 stringsAsFactors = FALSE)  
}

我对fingertipsR并不真正熟悉,但是似乎可以完成工作(如果我错了,请纠正我),这是结果的第一个要素:

head(result_list[[1]])
# indicator      geog Timeperiod    Value
# 1       241 E38000220    2017/18 8.214912
# 2       241 E38000046    2017/18 7.907130
# 3       241 E38000144    2017/18 9.139239
# 4       241 E38000191    2017/18 8.891195
# 5       241 E38000210    2017/18 8.311592
# 6       241 E38000038    2017/18 6.653444

更改

以下是我对您的版本所做的更改:

  • 我只编写了一个for循环(严格来说,在我使用vapply时,我的版本中还有两个循环)对指标进行了迭代。主要原因不是甚至没有更少的循环(它本身已经足够激励),而是尽可能少地调用函数fingertips_data:这些函数调用非常慢,并且不依赖于{{1 }},只有子设置可以。
  • 因此,对于每个geog,函数indicator被调用一次,然后使用fingertips_data进行子设置并找到最大值
  • 因此,输出的格式略有不同,但本质上包含相同的信息