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

how to loop through list and create separate dataframes in R

我正在尝试按县提取整个美国人口普查局的移民数据。由于数据的大小,人口普查要求您为数据导入指定 "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 循环的新手。 感谢您的任何想法。

您现有的代码会创建一个名为 migr 的对象,并为其分配一个字符串,其中包含您要创建的 data.frame 的名称。然后用从人口普查中提取的 data.frame 覆盖 migr 对象。循环的每次迭代,你覆盖 migr,这就是为什么只保存循环的最后一次迭代的数据,然后只作为一个名为 migr.[=23 的 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比其他两个慢很多)。

只需将您的过程转换为一个函数并传递给 lapply 或更好的 sapply 以获取命名列表(因为它输入了一个字符向量)。重新考虑保存类似的结构,并可能分别保存许多对象,但使用 one 命名的数据帧列表。避免不必要地淹没全球环境:

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`)

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 循环更简单且更不容易出错。