如何制作数据框列表以及如何访问列表中的每个数据框?
例如,如何将这些数据框放入列表中?
d1 <- data.frame(y1 = c(1, 2, 3),
y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1),
y2 = c(6, 5, 4))
其他答案向您展示了当您已经有一堆data.frames 时如何制作一个 data.frames 列表,例如,、、...。按顺序命名的数据帧是一个问题,将它们放在一个列表中是一个很好的解决方法,但最佳做法是首先避免将一堆 data.frames 放在列表中。d1
d2
其他答案提供了大量关于如何将数据框分配给列表元素、访问它们等的详细信息。我们也会在这里介绍一下,但要点是不要等到你有一堆data.frames
将它们添加到列表中。从列表开始。
这个答案的其余部分将涵盖一些常见的情况,您可能会想创建顺序变量,并向您展示如何直接进入列表。如果您不熟悉 R 中的列表,您可能还想阅读访问列表元素和访问列表元素有什么区别?[[
[
.
永远不要创造d1
d2
d3
,...,dn
首先。创建一个d
包含n
元素的列表。
这在读取文件时很容易完成。也许您data1.csv, data2.csv, ...
在目录中有文件。您的目标是一个名为的 data.frames 列表mydata
。您需要的第一件事是包含所有文件名的向量。您可以使用粘贴(例如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
ordata.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
用于列。)
要合并(加入)数据框列表,您可以查看这些答案。通常,这个想法是使用Reduce
with merge
(或其他一些连接函数)将它们组合在一起。
将相似的数据放在列表中,因为您想对每个数据框做相似的事情,而像lapply
, sapply
do.call
, package和旧函数这样的purrr
plyr
l*ply
函数可以很容易地做到这一点。人们很容易用列表做事的例子到处都是。
即使您使用低级的 for 循环,循环列表的元素也比使用构造变量名paste
和访问对象要容易得多get
。也更容易调试。
想想可扩展性。如果你真的只需要三个变量,可以使用d1
, d2
, d3
. 但是如果事实证明你真的需要 6 个,那就需要更多的输入。下一次,当你需要 10 或 20 行代码时,你会发现自己在复制和粘贴代码行,可能使用 find/replace 来更改d14
为d15
,而你认为这不是编程应有的方式。如果您使用列表,则 3 例、30 例和 300 例之间的差异最多只有一行代码——如果您的案例数量是自动检测到的,例如,您的文件中有多少.csv
个文件,则根本没有变化目录。
您可以命名列表的元素,以防您想使用数字索引以外的东西来访问您的数据框(您可以同时使用两者,这不是 XOR 选择)。
总体而言,使用列表将导致您编写更清晰、更易于阅读的代码,从而减少错误和混乱。
这与您的问题无关,但您想使用=
而不是<-
在函数调用中。如果你使用<-
,你最终会在你工作的任何环境中y1
创建变量:y2
d1 <- data.frame(y1 <- c(1, 2, 3), y2 <- c(4, 5, 6))
y1
# [1] 1 2 3
y2
# [1] 4 5 6
这不会产生在数据框中创建列名的看似理想的效果:
d1
# y1....c.1..2..3. y2....c.4..5..6.
# 1 1 4
# 2 2 5
# 3 3 6
=
另一方面,运算符会将您的向量与 的参数相关联data.frame
。
至于您的问题,制作数据框列表很容易:
d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))
my.list <- list(d1, d2)
您可以像访问任何其他列表元素一样访问数据框:
my.list[[1]]
# y1 y2
# 1 1 4
# 2 2 5
# 3 3 6
您还可以使用 和 访问每个列表元素中的特定列和 [
值[[
。这里有几个例子。首先,我们只能访问列表中每个数据框的第一列lapply(ldf, "[", 1)
,其中1
表示列号。
ldf <- list(d1 = d1, d2 = d2) ## create a named list of your data frames
lapply(ldf, "[", 1)
# $d1
# y1
# 1 1
# 2 2
# 3 3
#
# $d2
# y1
# 1 3
# 2 2
# 3 1
同样,我们可以访问第二列中的第一个值
lapply(ldf, "[", 1, 2)
# $d1
# [1] 4
#
# $d2
# [1] 6
然后我们也可以直接访问列值,作为一个向量,[[
lapply(ldf, "[[", 1)
# $d1
# [1] 1 2 3
#
# $d2
# [1] 3 2 1
如果您有大量按顺序命名的数据框,您可以创建所需数据框子集的列表,如下所示:
d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))
my.list <- list(d1, d2, d3, d4)
my.list
my.list2 <- lapply(paste('d', seq(2,4,1), sep=''), get)
my.list2
其中my.list2
返回包含第 2、3 和 4 个数据帧的列表。
[[1]]
y1 y2
1 3 6
2 2 5
3 1 4
[[2]]
y1 y2
1 6 3
2 5 2
3 4 1
[[3]]
y1 y2
1 9 8
2 9 8
3 9 8
但是请注意,上述列表中的数据框不再命名。如果您想创建一个包含数据框子集的列表并希望保留它们的名称,您可以尝试以下操作:
list.function <- function() {
d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))
sapply(paste('d', seq(2,4,1), sep=''), get, environment(), simplify = FALSE)
}
my.list3 <- list.function()
my.list3
返回:
> my.list3
$d2
y1 y2
1 3 6
2 2 5
3 1 4
$d3
y1 y2
1 6 3
2 5 2
3 4 1
$d4
y1 y2
1 9 8
2 9 8
3 9 8
> str(my.list3)
List of 3
$ d2:'data.frame': 3 obs. of 2 variables:
..$ y1: num [1:3] 3 2 1
..$ y2: num [1:3] 6 5 4
$ d3:'data.frame': 3 obs. of 2 variables:
..$ y1: num [1:3] 6 5 4
..$ y2: num [1:3] 3 2 1
$ d4:'data.frame': 3 obs. of 2 variables:
..$ y1: num [1:3] 9 9 9
..$ y2: num [1:3] 8 8 8
> my.list3[[1]]
y1 y2
1 3 6
2 2 5
3 1 4
> my.list3$d4
y1 y2
1 9 8
2 9 8
3 9 8
假设您有“大量”具有相似名称的 data.frames(此处为 d#,其中 # 是某个正整数),以下是@mark-miller 方法的轻微改进。它更简洁,并返回一个命名的 data.frames 列表,其中列表中的每个名称都是对应的原始 data.frame 的名称。
关键是mget
配合使用ls
。如果问题中提供的数据框 d1 和 d2 是环境中唯一名称为 d# 的对象,则
my.list <- mget(ls(pattern="^d[0-9]+"))
这将返回
my.list
$d1
y1 y2
1 1 4
2 2 5
3 3 6
$d2
y1 y2
1 3 6
2 2 5
3 1 4
此方法利用了 中的模式参数ls
,它允许我们使用正则表达式对环境中的对象名称进行更精细的解析。正则表达式的替代方法"^d[0-9]+$"
是"^d\\d+$"
.
正如@gregor指出的那样,总体而言,最好设置数据构造过程,以便在开始时将 data.frames 放入命名列表中。
数据
d1 <- data.frame(y1 = c(1,2,3),y2 = c(4,5,6))
d2 <- data.frame(y1 = c(3,2,1),y2 = c(6,5,4))
我认为自己是一个完整的新手,但我认为我对此处未说明的原始子问题之一有一个非常简单的答案:访问数据帧或其中的一部分。
让我们首先使用如上所述的数据框创建列表:
d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))
my.list <- list(d1, d2)
然后,如果要访问其中一个数据框中的特定值,可以按顺序使用双括号。第一组让您进入数据框,第二组让您进入特定坐标:
my.list[[1]][[3,2]]
[1] 6
这可能有点晚了,但回到你的例子,我想我会稍微扩展一下答案。
D1 <- data.frame(Y1=c(1,2,3), Y2=c(4,5,6))
D2 <- data.frame(Y1=c(3,2,1), Y2=c(6,5,4))
D3 <- data.frame(Y1=c(6,5,4), Y2=c(3,2,1))
D4 <- data.frame(Y1=c(9,9,9), Y2=c(8,8,8))
然后你很容易地列出你的清单:
mylist <- list(D1,D2,D3,D4)
现在您有一个列表,但不是以旧方式访问列表,例如
mylist[[1]] # to access 'd1'
您可以使用此功能来获取和分配您选择的数据框。
GETDF_FROMLIST <- function(DF_LIST, ITEM_LOC){
DF_SELECTED <- DF_LIST[[ITEM_LOC]]
return(DF_SELECTED)
}
现在得到你想要的。
D1 <- GETDF_FROMLIST(mylist, 1)
D2 <- GETDF_FROMLIST(mylist, 2)
D3 <- GETDF_FROMLIST(mylist, 3)
D4 <- GETDF_FROMLIST(mylist, 4)
希望额外的一点帮助。
干杯!
for
循环模拟如果我有一个for
生成数据帧的循环,我从一个空开始,list()
并在生成数据帧时附加它们。
# Empty list
dat_list <- list()
for(i in 1:5){
# Generate dataframe
dat <- data.frame(x=rnorm(10), y=rnorm(10))
# Add to list
dat_list <- append(dat_list, list(dat))
}
请注意,它list(dat)
在我们的append()
调用中。
然后从我们使用的列表中获取第n
th 个数据帧dat_list[[n]]
。您可以以正常方式访问此数据框中的数据,例如dat_list[[2]]$x
.
或者,如果您想要所有数据帧中的相同部分sapply(dat_list, "[", "x")
。
请参阅@Gregor Thomas的答案,了解如何在没有for
循环的情况下执行此操作。
非常简单 !这是我的建议:
如果要在工作区中选择数据框,请尝试以下操作:
Filter(function(x) is.data.frame(get(x)) , ls())
或者
ls()[sapply(ls(), function(x) is.data.frame(get(x)))]
所有这些都会产生相同的结果。
您可以更改is.data.frame
以检查其他类型的变量,例如is.function