如何制作数据框列表?

Ben*_*Ben 171 r list dataframe r-faq

如何制作数据框列表以及如何从列表中访问每个数据框?

例如,如何将这些数据框放在列表中?

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))
Run Code Online (Sandbox Code Playgroud)

Gre*_*gor 321

其他答案告诉你如何做data.frames列表时,你已经有一堆data.frames,例如,d1,d2,...有顺序命名的数据帧是一个问题,并且把它们放在一个列表是一个很好的修复,但最好的做法是避免让一堆data.frames首先不在列表中.

其他答案提供了大量有关如何将数据框分配给列表元素,访问它们等的详细信息.我们也会在这里稍微介绍一下,但重点是说不要等到你有一堆data.frames将它们添加到列表中.从列表开始.

本答案的其余部分将介绍一些您可能想要创建顺序变量的常见情况,并向您展示如何直接进入列表.如果您对R中的列表不熟悉,您可能还想阅读列表中元素访问元素之间的区别是什么?[[[.


列表从一开始

首先,不要创造d1 d2 d3.......创建一个dn包含3个元素的列表.

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

在阅读文件时,这很容易完成.也许你d在目录中有文件.您的目标是调用的data.frames列表n.你需要的第一件事是一个包含所有文件名的向量.你可以使用paste(例如data1.csv, data2.csv, ...)来构造它,但它可能更容易mydata用来获取所有适当的文件:my_files = paste0("data", 1:5, ".csv").您可以使用正则表达式来匹配文件,如果您需要帮助,请在其他问题中阅读有关正则表达式的更多信息.

在这一点上,大多数R初学者将使用list.files循环,并且没有任何问题,它可以正常工作.

my_data <- list()
for (i in seq_along(my_files)) {
    my_data[[i]] <- read.csv(file = my_files[i])
}
Run Code Online (Sandbox Code Playgroud)

更像R的方法是使用my_files <- list.files(pattern = "\\.csv$"),这是上述的快捷方式

my_data <- lapply(my_files, read.csv)
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,命名列表元素以匹配文件都很方便

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 = "")
Run Code Online (Sandbox Code Playgroud)

将数据帧拆分为数据帧列表

这非常简单,基本功能for为您完成.您可以按数据的一列(或多列)或您想要的任何其他内容进行拆分

mt_list = split(mtcars, f = mtcars$cyl)
# This gives a list of three data frames, one for each value of cyl
Run Code Online (Sandbox Code Playgroud)

这也是将数据框分成多个部分以进行交叉验证的好方法.也许你想lapply分成训练,测试和验证部分.

groups = sample(c("train", "test", "validate"),
                size = nrow(mtcars), replace = TRUE)
mt_split = split(mtcars, f = groups)
# and mt_split has appropriate names already!
Run Code Online (Sandbox Code Playgroud)

模拟数据帧列表

也许你正在模拟数据,如下所示:

my_sim_data = data.frame(x = rnorm(50), y = rnorm(50))
Run Code Online (Sandbox Code Playgroud)

但谁只进行一次模拟?你想这样做100次,1000次,更多!但是,您希望工作区中有10,000个数据框.使用read.csv并将它们放入列表中:

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

在这种情况下,您还应该考虑是否确实需要单独的数据框,或者具有"组"列的单个数据框是否也能正常工作?使用readr::read_csv或者data.table::fread很容易"按组"对数据框执行操作.

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

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

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

如果你有一个模式命名的数据帧,例如split(),mtcars,replicate,和你希望他们在列表中,你可以data.table他们,如果你可以写一个正则表达式匹配的姓名.就像是

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.
Run Code Online (Sandbox Code Playgroud)

通常,dplyr用于获取多个对象并将其返回到命名列表中.它的对应物df1用于获取单个对象并将其返回(不在列表中).

将数据帧列表组合到单个数据帧中

一个常见的任务是将数据帧列表组合成一个大数据帧.如果你想将它们叠加在一起,你可以使用df2它们,但是对于数据帧列表,这里有三个不错的选择:

# 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)
Run Code Online (Sandbox Code Playgroud)

(类似地使用df3get用于列.)

要合并(加入)数据框列表,您可以看到这些答案.通常情况下,这个想法是用mgetget(或其他主机加入功能),让他们在一起.

为什么要将数据放在列表中?

放入列表类似的数据,因为你想要做类似的事情,每个数据帧,而像功能rbind,cbind dplyr::bind_cols,Reduce,和老merge lapply功能可以很容易地做到这一点.使用列表轻松处理事务的人的例子都是SO.

即使你使用lowly for循环,循环遍历列表的元素要比用它构造变量名sapply和访问对象容易得多do.call.也更容易调试.

考虑可扩展性.如果你真的只需要三个变量,它的优良使用purrr,plyr,l*ply.但是如果事实证明你真的需要6,那就更多了.而下一次,当你需要10或20,你发现自己复制和粘贴代码行,也许用查找/替换改变pasteget了,你在想这是编程不应该是怎么样.如果您使用列表,3个案例,30个案例和300个案例之间的差异最多只有一行代码---如果您自动检测到案例数量,例如,d1您的文件数量是多少,则根本没有变化目录.

您可以命名列表的元素,以防您想使用数字索引以外的其他东西来访问您的数据框(并且您可以同时使用这两个,这不是XOR选择).

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

  • 我建议在Stack Overflow上阅读用`r`和`list`标记的问题和答案. (15认同)
  • 这是可能的,但是`my_data < - list()`清楚地表明你正在创建一个列表,这很好!清晰的代码是一件好事.我认为使用`my_data < - NULL`没有任何好处. (6认同)
  • 我同意,关于你所说的,但就像我说的那样,你可以逃避命名文件的阶段.`names(my_data)< - gsub("\\.csv $","",my_files)`;)但是我尊重你的建议,因为我从新手那里学到了很多东西,我真的很感激:) (3认同)
  • 您推荐使用哪本书来处理列表? (2认同)
  • @Gregor我想补充一点,我们可以通过分配`my_data < - NULL`而不是`my_data < - list()'来避免将列表元素命名为匹配文件!:) (2认同)

Pey*_*ton 126

这与您的问题无关,但您想要使用=而不是<-在函数调用中.如果您使用<-,您将最终创建变量,y1y2在您正在使用的任何环境中:

d1 <- data.frame(y1 <- c(1, 2, 3), y2 <- c(4, 5, 6))
y1
# [1] 1 2 3
y2
# [1] 4 5 6
Run Code Online (Sandbox Code Playgroud)

这不会产生在数据框中创建列名的看似期望的效果:

d1
#   y1....c.1..2..3. y2....c.4..5..6.
# 1                1                4
# 2                2                5
# 3                3                6
Run Code Online (Sandbox Code Playgroud)

=运营商,在另一方面,将您的向量与参数相关联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)
Run Code Online (Sandbox Code Playgroud)

您可以像访问任何其他列表元素一样访问数据框:

my.list[[1]]
#   y1 y2
# 1  1  4
# 2  2  5
# 3  3  6
Run Code Online (Sandbox Code Playgroud)


Ric*_*ven 19

您也可以访问特定的列和值在每个列表元素与 [[[.这里有几个例子.首先,我们只能访问列表中每个数据框的第一列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
Run Code Online (Sandbox Code Playgroud)

同样,我们可以访问第二列中的第一个值

lapply(ldf, "[", 1, 2)
# $d1
# [1] 4
# 
# $d2
# [1] 6
Run Code Online (Sandbox Code Playgroud)

然后我们也可以直接访问列值,作为向量,用 [[

lapply(ldf, "[[", 1)
# $d1
# [1] 1 2 3
#
# $d2
# [1] 3 2 1
Run Code Online (Sandbox Code Playgroud)


Mar*_*ler 13

如果您有大量按顺序命名的数据框,则可以创建所需数据框子集的列表,如下所示:

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
Run Code Online (Sandbox Code Playgroud)

where 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
Run Code Online (Sandbox Code Playgroud)

但请注意,上面列表中的数据框不再被命名.如果要创建包含数据框子集的列表并希望保留其名称,可以尝试以下操作:

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
Run Code Online (Sandbox Code Playgroud)

返回:

> 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
Run Code Online (Sandbox Code Playgroud)

  • 而不是`lapply(foo,get)`,只需使用`mget(foo)` (2认同)

lmo*_*lmo 9

作为给定,你有一个"大"数字的具有相似名称的数据帧(这里是d#,其中#是一些正整数),以下是@ mark-miller方法的略微改进.它更简洁并返回一个命名的data.frames列表,其中列表中的每个名称都是相应原始data.frame的名称.

关键是mget与...一起使用ls.如果问题中提供的数据框d1和d2是环境中唯一名称为d#的对象,那么

my.list <- mget(ls(pattern="^d[0-9]+"))
Run Code Online (Sandbox Code Playgroud)

哪会回来

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
Run Code Online (Sandbox Code Playgroud)

此方法利用了pattern参数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))
Run Code Online (Sandbox Code Playgroud)


小智 7

我认为自己是一个完全的新手,但我认为我对此处未说明的原始子问题之一有一个非常简单的答案:访问数据帧或其部分。

让我们首先使用如上所述的数据框创建列表:

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)
Run Code Online (Sandbox Code Playgroud)

然后,如果您想访问其中一个数据帧中的特定值,可以通过顺序使用双括号来实现。第一组让您进入数据框,第二组让您进入特定坐标:

my.list[[1]][3, 2]

[1] 6
Run Code Online (Sandbox Code Playgroud)


D A*_*lls 5

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))
}
Run Code Online (Sandbox Code Playgroud)

请注意,它list(dat)在我们的append()通话中。

访问数据

n然后从我们使用的列表中获取第一个数据帧dat_list[[n]]。您可以以正常方式访问此数据框中的数据,例如dat_list[[2]]$x

或者,如果您想要所有数据帧中的相同部分sapply(dat_list, "[", "x")

请参阅@Gregor Thomas的答案,了解如何在没有for循环的情况下执行此操作。