代码之家  ›  专栏  ›  技术社区  ›  Nautica

如何优化包含列表附加的嵌套for循环?

  •  0
  • Nautica  · 技术社区  · 7 年前

    我正在从英国的公共卫生机构提取特定地理区域和特定指标的数据,使用他们为从他们的API中提取数据而开发的一个包,叫做 fingertipsR ,然后将它们插入到空列表中,其中该列表由包含表示每个指示器的列表的列表(地理位置)组成。

    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分钟的时间来执行-我想知道是否有任何简单的方法来优化这个代码-可能与 lapply purrr ?

    编辑:理想情况下,我希望每个地理区域的指标都在一个数据框中,因为它们共享相同的列。 Time period Value -之后我会处理的 unlist() 或者类似的事情——但是如果有人能在for循环中解决这个问题,我愿意接受建议。

    1 回复  |  直到 7 年前
        1
  •  2
  •   niko    7 年前

    这里有一个更浓缩的循环(大约需要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 尽可能少的次数:这些函数调用非常慢,不依赖于 geog 只有子集合可以。
    • 所以每一个 indicator 函数 指尖数据 调用一次,然后使用 VApple 已完成子集设置和查找最大值
    • 结果,输出的格式略有不同,但基本上包含相同的信息