Map/mapply 包含两个列表的所有可能组合

Map/mapply with all possible combinations of two lists

我刚开始使用 R 并申请,我正在尝试从网站下载一组 .csv 文件。

我想下载危地马拉 (GT)、萨尔瓦多 (SV) 和洪都拉斯 (HN) 这三个国家的 2004 年和 2005 年(作为示例;实际上我想要更多年)。

我可以 运行 按国家/地区执行以下操作:

years = c(2004, 2005)    
Map(download.file, url = paste0("https://www.colef.mx/emif/datasets/basesdeDatos/sur/", years, "/DEUAGT%20S1%20", years, ".csv"), 
          destfile = paste0(raw_data, years, ".csv") )

这将使我获得 2004 年和 2005 年的危地马拉数据库,因为危地马拉基地由 URL 中的 "DEAUGT" 定义。洪都拉斯和 El Salvatorian 数据库分别是 "DEAUHN""DEAUSV"

但是因为我正在努力学习,所以我想把所有的东西都放在“一个运行”中。所以我尝试了:

countries = c("GT", "HN", "SV")
years = c(2004, 2005, 2007, 2009:2019)

Map(possibly(download.file, otherwise = NA), url = paste0("https://www.colef.mx/emif/datasets/basesdeDatos/sur/", years, "/DEUA", countries, "%20", years, ".csv"), 
              destfile = paste0(raw_data, countries, years,".csv"))

但是我没有下载我想要的 6 个文件(三个国家,两年),而是下载了 2 个文件。

我在这里找到的各种帖子都注意到了,在 RStudio 社区中注意到 Map/mapply 没有 运行 通过列表 "countries""years" 的所有可能组合,而是逐点(或类似的东西)。

我在不同的设置中找到了各种建议,但 none 特别容易,而且有些事情告诉我有一个简单的解决方案。使用 expand.grid 创建数据框而不是列表列表。

您可以使用以下解决方案。如果我们使用 purrr::walk2 代替 purrr::map2 会更好,因为我们调用 download.file 是为了它的副作用,所以 walk2 是更好的选择:

library(purrr)

# First we create a data frame of all combinations of countries and years
comb <- expand.grid(countries, years)

# Then I wrap `download.file` with possibly for error handling
poss_download <- possibly(download.file, otherwise = NA)

# Then I apply our function on every combination of countries and years 
# in a row-wise operation

walk2(comb$Var1, comb$Var2, ~ {
  url = paste0("https://www.colef.mx/emif/datasets/basesdeDatos/sur/", .y, "/DEUA", .x, "%20", .y, ".csv")
  destfile = paste0(raw_data, .x, .y,".csv")
  poss_download(url, destfile)
})

这是这个问题的基本 R 解决方案。

  • 而不是 paste0 我使用了 sprintf 函数,根据文档“returns 包含文本和变量值的格式化组合的字符向量”。我将 %d 用于 integer/numeric 值(2 次代表年份),使用 %s 代表字符串(一次用于国家),应该注意的是我们必须提供尽可能多的变量,以便它们并入它们的位置以形成长度为 one
  • 的单个字符串
  • 然后我用 tryCatch 代替 purrr::possibly 来处理可能的错误
  • 最后我使用mapplyMap同时迭代两个向量urldestfile
comb <- expand.grid(countries, years)

url <- sprintf("https://www.colef.mx/emif/datasets/basesdeDatos/sur/%d/DEUA%s%d.csv", comb$Var2, comb$Var1, comb$Var2)

destfile = paste0(raw_data, comb$Var1, comb$Var2,".csv")

mapply(function(x, y) {
  tryCatch(download.file(url, destfile),
           error = function(e) {
             NA
           })
}, url, destfile)