其他答案向您展示如何在您已经一堆 data.frames 时制作 data.frames 列表,例如,d1、@987654325 @, .... 按顺序命名的数据帧是个问题,将它们放在一个列表中是一个很好的解决方法,但最佳做法是避免将一堆 data.frames 不在列表中首先。
其他答案提供了大量关于如何将数据框分配给列表元素、访问它们等的详细信息。我们也会在这里稍微介绍一下,但要点是说不要等到你有一堆data.frames 才将它们添加到列表中。从列表开始。
此答案的其余部分将涵盖一些您可能会想创建顺序变量的常见情况,并向您展示如何直接进入列表。如果您不熟悉 R 中的列表,您可能还想阅读 What's the difference between [[ and [ in accessing elements of a list?。
从头开始列出
首先不要创建d1 d2 d3, ..., dn。使用 n 元素创建一个列表 d。
将多个文件读入数据框列表
这在读入文件时很容易完成。也许您的目录中有文件data1.csv, data2.csv, ...。您的目标是一个名为mydata 的data.frames 列表。您需要的第一件事是包含所有文件名的向量。您可以使用粘贴(例如,my_files = paste0("data", 1:5, ".csv"))来构建它,但使用list.files 获取所有适当的文件可能更容易:my_files <- list.files(pattern = "\\.csv$")。您可以使用正则表达式来匹配文件,如果您需要帮助,请在其他问题中阅读有关正则表达式的更多信息。通过这种方式,您可以获取所有 CSV 文件,即使它们没有遵循良好的命名方案。或者,如果您需要从一堆 CSV 文件中挑选出某些 CSV 文件,您可以使用更高级的正则表达式模式。
此时,大多数 R 初学者都会使用 for 循环,这并没有什么问题,它工作得很好。
my_data <- list()
for (i in seq_along(my_files)) {
my_data[[i]] <- read.csv(file = my_files[i])
}
更类似于 R 的方式是使用 lapply,这是上述方法的快捷方式
my_data <- lapply(my_files, read.csv)
当然,可以酌情用其他数据导入功能代替read.csv。 readr::read_csv 或 data.table::fread 会更快,或者您可能还需要针对不同文件类型的不同函数。
无论哪种方式,命名列表元素以匹配文件都很方便
names(my_data) <- gsub("\\.csv$", "", my_files)
# or, if you prefer the consistent syntax of stringr
names(my_data) <- stringr::str_replace(my_files, pattern = ".csv", replacement = "")
将数据框拆分为数据框列表
这非常简单,基本函数split() 为您完成。您可以按一列(或多列)数据进行拆分,也可以按您想要的任何其他内容进行拆分
mt_list = split(mtcars, f = mtcars$cyl)
# This gives a list of three data frames, one for each value of cyl
这也是一种将数据框分解为多个片段以进行交叉验证的好方法。也许您想将 mtcars 拆分为训练、测试和验证部分。
groups = sample(c("train", "test", "validate"),
size = nrow(mtcars), replace = TRUE)
mt_split = split(mtcars, f = groups)
# and mt_split has appropriate names already!
模拟数据框列表
也许你在模拟数据,像这样:
my_sim_data = data.frame(x = rnorm(50), y = rnorm(50))
但是谁只做一次模拟呢?你想这样做 100 次,1000 次,更多!但是您不希望工作区中有 10,000 个数据框。使用replicate 并将它们放在一个列表中:
sim_list = replicate(n = 10,
expr = {data.frame(x = rnorm(50), y = rnorm(50))},
simplify = F)
尤其是在这种情况下,您还应该考虑是否真的需要单独的数据框,或者带有“组”列的单个数据框也可以工作?使用 data.table 或 dplyr 可以很容易地“按组”对数据框执行操作。
我没有把我的数据放在一个列表中 :( 下次我会的,但我现在能做什么?
如果它们是奇怪的分类(这是不寻常的),您可以简单地分配它们:
mylist <- list()
mylist[[1]] <- mtcars
mylist[[2]] <- data.frame(a = rnorm(50), b = runif(50))
...
如果您有以模式命名的数据框,例如df1、df2、df3,并且您希望将它们放在一个列表中,您可以get它们,如果您可以编写一个正则表达式来匹配名字。类似的东西
df_list = mget(ls(pattern = "df[0-9]"))
# this would match any object with "df" followed by a digit in its name
# you can test what objects will be got by just running the
ls(pattern = "df[0-9]")
# part and adjusting the pattern until it gets the right objects.
通常,mget 用于获取多个对象并在命名列表中返回它们。其对应的get 用于获取单个对象并将其返回(不在列表中)。
将数据框列表组合成单个数据框
一个常见的任务是将一系列数据框组合成一个大数据框。如果您想将它们堆叠在一起,您可以使用rbind 来表示它们,但是对于数据框列表,这里有三个不错的选择:
# base option - slower but not extra dependencies
big_data = do.call(what = rbind, args = df_list)
# data table and dplyr have nice functions for this that
# - are much faster
# - add id columns to identify the source
# - fill in missing values if some data frames have more columns than others
# see their help pages for details
big_data = data.table::rbindlist(df_list)
big_data = dplyr::bind_rows(df_list)
(类似地使用cbind 或dplyr::bind_cols 表示列。)
要合并(加入)数据框列表,您可以查看these answers。通常,这个想法是使用 Reduce 和 merge(或其他一些连接函数)将它们组合在一起。
为什么要把数据放在一个列表中?
将相似的数据放在列表中,因为您想对每个数据框执行相似的操作,而 lapply、sapplydo.call、the purrr package 和旧的 plyrl*ply 等函数可以实现很容易做到这一点。用列表轻松做事的例子比比皆是。
即使您使用低级的 for 循环,循环列表的元素也比使用 paste 构造变量名并使用 get 访问对象要容易得多。也更容易调试。
考虑可扩展性。如果你真的只需要三个变量,使用d1、d2、d3就可以了。但是如果事实证明你真的需要 6 个,那就需要更多的输入。下一次,当您需要 10 或 20 行代码时,您会发现自己在复制和粘贴代码行,可能使用 find/replace 将 d14 更改为 d15,而您认为 这不是编程方式应该是。如果使用列表,则 3 例、30 例和 300 例之间的差异最多只有一行代码——如果您的案例数量是自动检测的,例如有多少 .csv 文件,则根本没有变化在您的目录中。
您可以命名列表的元素,以防您想使用数字索引以外的其他内容来访问您的数据框(您可以同时使用这两种方法,这不是 XOR 选择)。
总体而言,使用列表将导致您编写更清晰、更易于阅读的代码,从而减少错误和混乱。