我如何做一个数据帧的列表,我如何从列表中访问每个这些数据帧?

例如,如何将这些数据帧放入列表中?

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

当前回答

还可以使用[和[[]访问每个列表元素中的特定列和值。这里有几个例子。首先,使用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

其他回答

其他答案告诉你如何在你已经有了一堆data.frames的情况下创建一个data.frames的列表,例如,d1, d2, ....拥有按顺序命名的数据帧是个问题,将它们放在一个列表中是一个很好的解决方案,但最佳实践是避免在一开始就有一堆数据帧不在列表中。

其他答案提供了大量关于如何将数据帧分配给列表元素、访问它们等的细节。我们在这里也会讲到一点,但主要的观点是不要等到你有了一堆数据帧才把它们添加到列表中。从清单开始。

这个答案的其余部分将涵盖一些常见的情况,在这些情况下,您可能会忍不住创建顺序变量,并向您展示如何直接进入列表。如果您不熟悉R中的列表,您可能还想阅读在访问列表元素时[[和[]之间的区别是什么?


从一开始就列出

不要创造d1 d2 d3…在第一个地方。创建一个包含n个元素的列表d。

将多个文件读入数据帧列表

This is done pretty easily when reading in files. Maybe you've got files data1.csv, data2.csv, ... in a directory. Your goal is a list of data.frames called mydata. The first thing you need is a vector with all the file names. You can construct this with paste (e.g., my_files = paste0("data", 1:5, ".csv")), but it's probably easier to use list.files to grab all the appropriate files: my_files <- list.files(pattern = "\\.csv$"). You can use regular expressions to match the files, read more about regular expressions in other questions if you need help there. This way you can grab all CSV files even if they don't follow a nice naming scheme. Or you can use a fancier regex pattern if you need to pick certain CSV files out from a bunch of them.

在这一点上,大多数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个数据帧。使用复制并将它们放入列表中:

sim_list = replicate(n = 10,
                     expr = {data.frame(x = rnorm(50), y = rnorm(50))},
                     simplify = F)

特别是在这种情况下,您还应该考虑是否真的需要单独的数据帧,或者单个具有“group”列的数据帧也能工作吗?使用数据。表或dplyr是很容易做的事情“组”到一个数据帧。

我没有把我的数据放在列表中:(我下次会,但我现在能做什么?

如果它们是一个奇怪的分类(这是不寻常的),你可以简单地分配它们:

mylist <- list()
mylist[[1]] <- mtcars
mylist[[2]] <- data.frame(a = rnorm(50), b = runif(50))
...

如果你有一个以模式命名的数据帧,例如,df1, df2, df3,并且你想要它们在一个列表中,你可以通过编写一个正则表达式来匹配这些名称来获得它们。类似的

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和merge(或其他一些连接函数)来将它们组合在一起。

但我真的需要按顺序命名的变量

使用它们可能很痛苦,而且几乎总是不需要它们,但如果需要,请在列表中尽可能地使用它们,然后可以使用list2env()将所有列表项放入一个环境中,例如. globalenv。

为什么把数据放在列表中?

将相似的数据放在列表中,因为您希望对每个数据帧执行类似的操作,以及像lapply, sapply do这样的函数。Call、purrr包和旧的plyr l*ply函数可以很容易地做到这一点。人们用清单轻松做事的例子比比皆是。

即使使用低级的for循环,遍历列表中的元素也比使用paste构造变量名并使用get访问对象容易得多。调试也更容易。

Think of scalability. If you really only need three variables, it's fine to use d1, d2, d3. But then if it turns out you really need 6, that's a lot more typing. And next time, when you need 10 or 20, you find yourself copying and pasting lines of code, maybe using find/replace to change d14 to d15, and you're thinking this isn't how programming should be. If you use a list, the difference between 3 cases, 30 cases, and 300 cases is at most one line of code---no change at all if your number of cases is automatically detected by, e.g., how many .csv files are in your directory.

你可以命名一个列表的元素,以防你想使用数字索引以外的东西来访问你的数据帧(你可以使用两者,这不是一个异或的选择)。

总的来说,使用列表将使您编写更清晰、更易于阅读的代码,从而减少错误和混乱。

如果你有大量按顺序命名的数据帧,你可以像这样创建一个所需数据帧子集的列表:

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

我的地方。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

这与您的问题无关,但您希望在函数调用中使用=而不是<-。如果你使用<-,你最终会在你工作的环境中创建变量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

非常简单!以下是我的建议:

如果你想在你的工作空间中选择数据框架,试试这个:

Filter(function(x) is.data.frame(get(x)) , ls())

or

ls()[sapply(ls(), function(x) is.data.frame(get(x)))]

所有这些都会得到相同的结果。

你可以改变is.data.frame来检查其他类型的变量,比如is.function

在tidyverse中,可以使用函数lst()根据对象自动命名列表。

library(tibble)

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

lst(d1, d2)
# $d1
# y1 y2
# 1  1  4
# 2  2  5
# 3  3  6
# 
# $d2
# y1 y2
# 1  3  6
# 2  2  5
# 3  1  4

在编译稍后要按名称引用的列表时,这可能很有帮助。