如何在R中遍历列表并创建单独的数据帧

时间:2018-07-11 16:57:33

标签: r for-loop dataframe census

我正在尝试获取人口普查局有关整个美国按县迁移的数据。由于数据量大,人口普查要求您为数据导入指定一个“ regionin”(即州或县)。因此,我需要遍历所有状态的列表(通过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循环的新手,所以我不确定在哪里需要进行更改。 谢谢你的想法。

3 个答案:

答案 0 :(得分:2)

只需将您的过程转换为函数,然后传递到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`)

答案 1 :(得分:1)

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返回的数据帧。如果要将这些全部组合成一个数据帧,则可以使用如下代码:

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

如果要在每个状态下分别运行相同的代码,则可以只使用lapply或相关功能。从长远来看,这将比通过循环使用getassign更简单,而且更不容易出错。

答案 2 :(得分:0)

您现有的代码将创建一个名为migr的对象,并为其分配一个字符串,其中包含您要创建的data.frame的名称。然后,使用从普查中提取的data.frame覆盖migr对象。循环的每次迭代都会覆盖migr,这就是为什么仅保存循环的最后一次迭代中的数据,然后才将其保存为名为migr的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.frame列表可能比在全局环境中创建多个数据帧更容易。最简单的创建方法是使用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慢得多比其他两个)。