【问题标题】:how to loop through list and create separate dataframes in R如何遍历列表并在 R 中创建单独的数据框
【发布时间】:2018-07-12 21:19:38
【问题描述】:

我正在尝试获取人口普查局按县划分的整个美国的移民数据。由于数据的大小,人口普查要求您为数据导入指定“区域”(即州或县)。所以我需要遍历所有状态的列表(通过 fips 代码),以获取所有导入的数据。我需要的输出是每个状态的单独数据帧,然后我可以使用这些数据帧并将其组合成一个大数据帧。这是我编写的代码示例:

library(censusapi)

states <- c("01","02")
for(i in 1:length(states)) {
   region = str_glue("state:{states[i]}")
   migr = str_glue("migr2010_{states[i]}")
   migr <- getCensus(name = "acs/flows", vintage = 2010,
                     key = "*myAPIkey*",
                     vars = c("MOVEDNET", "MOVEDIN", "MOVEDOUT", "AGE"),
                     region = "county:*", regionin = region)
}

我想要得到的是每个名为“migr2010_01”、“migr2010_02”等的状态的单独数据框。我实际上得到的是一个名为“migr”的数据框,其中只有列表中最后一个状态的数据.我知道我的循环中有问题,但我不确定我需要在哪里进行更改,因为我是 R 循环的新手。 感谢您的任何想法。

【问题讨论】:

    标签: r for-loop dataframe census


    【解决方案1】:

    只需将您的流程变成一个函数并传递给lapply 或更好的sapply 用于命名列表(因为它输入一个字符向量)。重新考虑保存类似的结构,并且可能单独保存许多对象,但使用 一个 命名的数据框列表。避免不必要地淹没全球环境:

    library(stringr)
    library(censusapi)
    
    states <- c("01","02")
    
    get_census_data <- function(st)
       region = str_glue("state:{st}")
       migr = str_glue("migr2010_{st}")
    
       migr <- getCensus(name = "acs/flows", vintage = 2010,
                         key = "*myAPIkey*",
                         vars = c("MOVEDNET", "MOVEDIN", "MOVEDOUT", "AGE"),
                         region = "county:*", regionin = region)
    }
    
    df_list <- sapply(states, get_census_data, simplify=FALSE)
    # df_list <- setNames(lapply(states, get_census_data), states)   # EQUIVALENT CALL
    

    如果数据框存储在列表中而不是单独的对象中,则不会丢失数据框的功能:

    str(df_list$`01`)
    head(df_list$`01`)
    summary(df_list$`01`)
    
    dim(df_list$`02`)
    tail(df_list$`02`)
    table(df_list$`02`)
    

    【讨论】:

    • sapplysimplify=FALSE 一起使用而不是仅使用lapply 有什么好处吗?
    • sapply 默认有USE.NAMES = TRUElapply 的行为类似于USE.NAMES = FALSE
    • @GregSnow ... 我解释sapply 呈现一个命名列表,因为它需要一个字符向量作为输入。
    【解决方案2】:

    FAQ 7.21 部分回答了这个问题。该答案最重要的部分是它说使用列表更容易的结尾。

    您的代码可以转换为:

    library(censusapi)
    library(stringr)
    
    states <- c("01","02")
    migr.list <- lapply( states, function(x) {
       region = str_glue("state:{x}")
       migr = str_glue("migr2010_{x}")
       getCensus(name = "acs/flows", vintage = 2010,
                         key = "*myAPIkey*",
                         vars = c("MOVEDNET", "MOVEDIN", "MOVEDOUT", "AGE"),
                         region = "county:*", regionin = region)
    })
    names(migr.list) <- sprintf("migr2010_%s", states) # optional
    

    现在migr.list 将是一个列表对象,每个元素都是getCensus 返回的数据框。如果您想将这些全部组合成 1 个数据框,您可以使用如下代码:

    migr <- do.call(rbind, migr.list)
    

    如果您想在每个状态分别运行相同的代码,那么您可以使用lapply 或相关函数。从长远来看,这将比在循环中使用 getassign 更简单,更不容易出错。

    【讨论】:

      【解决方案3】:

      您现有的代码创建一个名为migr 的对象,并为其分配一个字符串,其中包含您要创建的data.frame 的名称。然后用从 Census 中提取的 data.frame 覆盖 migr 对象。循环的每次迭代都会覆盖migr,这就是为什么只保存循环最后一次迭代的数据,然后只保存为名为@9​​87654325@的data.frame。

      相反,您需要使用assign 命令将您从人口普查中提取的数据分配给存储在migr 中的值,如下所示:

      library(censusapi)
      
      states <- c("01","02")
      for(i in 1:length(states)) {
         region = str_glue("state:{states[i]}")
         migr = str_glue("migr2010_{states[i]}")
         assign(
           x = migr,
           value = getCensus(name = "acs/flows", vintage = 2010,
                             key = "*myAPIkey*",
                             vars = c("MOVEDNET", "MOVEDIN", "MOVEDOUT", "AGE"),
                             region = "county:*", regionin = region)
         )
      }
      

      编辑

      正如其他人所提到的,使用 data.frames 列表可能更容易,而不是在全局环境中创建多个。最简单的创建方法是使用lapply,如下:

       migr2010 <- lapply(
         paste0("state:", c("01", "02")),  # replaces region in the original
         getCensus,
         name = "acs/flows",
         vintage = 2010,
         key = "*myAPIkey*",
         vars = c("MOVEDNET", "MOVEDIN", "MOVEDOUT", "AGE"),
         region = "county:*"
         )
      

      然后,如果您想从中创建单个 data.frame,您可以使用 dplyr::bind_rows(migr2010)data.table::rbindlist(migr2010)do.call(rbind, migr2010)(尽管 do.call 比其他两个慢得多)。

      【讨论】:

      • 谢谢!我可以问为什么我必须为 migr 分配值但我不需要为 region 分配值吗?还是“regionin = region”在做同样的事情?
      • R 对region 做同样的事情——它在循环的每次迭代中都会覆盖它。但是,您只需要region 调用getCensus,因此您不需要保存region 的值以备后用。 (顺便说一句,如果我回答了你的问题,请将其标记为已回答。:))
      • 我强烈推荐 R for Data Science 以获得更好的解释:r4ds.had.co.nz/iteration.html
      • 这回答了问题,但这是一个坏主意,也是不好的做法,至少应该带有警告,其他解决方案要干净得多。
      • @Moody_Mudskipper 我对其进行了编辑以添加列表解决方案。我没有把它放在原件中,因为那不是他要求的。
      猜你喜欢
      • 2021-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-05
      相关资源
      最近更新 更多