如何制作数据帧列表?


185

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

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

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

13
这是几个答案,但在这里也有一个可见的注释:=不要<-在inside 使用data.frame()。通过使用<-您的创建y1y2在全局环境中,您的数据框架不是您想要的那样。
格雷戈尔·托马斯

37
看看<-data.frame()中没有空格和s的那堆混乱的代码。我真是个新手。

5
不再。我刚刚编辑了您的问题,以修复代码格式。如果您怀旧,请随时还原。
克劳斯·威尔克,

Answers:


133

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

343

其他答案告诉你如何做data.frames列表时,你已经有一堆data.frames,例如,d1d2,...有顺序命名的数据帧是一个问题,并且把它们放在一个列表是一个很好的解决方法,但是最佳实践是避免一堆data.frames不在列表中。

其他答案给予充足的如何分配数据帧的细节到列表中的元素,访问他们,等我们将介绍的是,这里有点太,但要点是说不要等到你有一大堆的一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文件,则可以使用更高级的正则表达式模式。

在这一点上,大多数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_csvdata.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)

特别是在这种情况下,您还应该考虑您是否真的需要单独的数据帧,或者具有“ group”列的单个数据帧也可以工作吗?使用data.tabledplyr“按组”对数据帧进行操作非常容易。

我没有将数据放入列表中:(下一次我会,但是现在我该怎么办?

如果它们是奇数分类(这是不寻常的),则可以简单地分配它们:

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

如果你有一个模式命名,数据帧例如df1df2df3,和你希望他们在列表中,你可以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)

(类似地使用cbinddplyr::bind_cols用于列。)

要合并(加入)数据帧列表,您可以看到以下答案。通常,此想法是Reducemerge(或其他一些加入函数)一起使用以使它们在一起。

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

放入列表类似的数据,因为你想要做类似的事情,每个数据帧,以及功能,如lapplysapply do.callpurrr,和老plyr l*ply功能可以很容易地做到这一点。人们很容易用列表来做事,例如SO。

即使您使用低级的for循环,也要比使用构造变量名paste和使用来访问对象要容易得多get。调试也很容易。

考虑可伸缩性。如果你真的只需要三个变量,它的优良使用d1d2d3。但是,如果事实证明您确实需要6,则需要输入更多内容。下次,当您需要10或20时,您发现自己正在复制和粘贴代码行,也许使用find / replace更改d14d15,并且您认为这不是编程应有的方式。如果使用列表,则3个案例,30个案例和300个案例之间的差异最多是一行代码-如果自动检测到案例数量(例如,.csv您的文件数为多少),则根本不会更改目录。

您可以命名列表中的元素,以防万一您想使用数字索引以外的其他方式来访问数据框(并且您可以使用两者,这不是XOR选择)。

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


2
您推荐哪本书涵盖使用列表?
遗弃

15
我建议阅读在Stack Overflow上同时标记有r和的问题和答案list
格里戈尔·托马斯

2
@Gregor我想补充一点,我们可以通过简单地分配my_data <- NULL而不是`my_data <-list()' 来避免命名列表元素以匹配文件!:)
丹尼尔(Daniel)

6
可以,但是要my_data <- list()明确您正在创建列表,这很好!清晰的代码是一件好事。我认为使用my_data <- NULL替代方法没有任何优势。
格雷戈尔·托马斯

3
我同意您的意见,但是就像我说的那样,这样做可以避免文件命名的麻烦。names(my_data) <- gsub("\\.csv$", "", my_files) ;)<br>但是,我确实尊重您的建议,因为我作为新手向他们学习了很多东西,我也非常感激:)
Daniel

21

您也可以访问特定的列和值在每个列表元素与 [[[。这里有几个例子。首先,我们只能使用来访问列表中每个数据帧的第一列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

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

其中my.list2返回包含第二,第三和第四数据帧的列表。

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

2
代替lapply(foo, get)使用,而是使用mget(foo)
Gregor Thomas

9

假设您有大量的具有相似名称的data.frame(此处为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

该方法利用了中的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))

3

这可能有点晚了,但回到您的示例,我想我会稍微扩展一下答案。

 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)

希望更多的帮助。

干杯!


2
是的,我知道,但是由于某些原因,当我复制粘贴时,一切都达到了上限。:(在任何情况下的代码小写的作品。
ML_for_now

4
我很好奇,你为什么会喜欢GETDF_FROMLIST(mylist, 1)mylist[[1]]?如果您更喜欢函数语法,甚至可以在"[["(mylist, 1)不定义自定义函数的情况下进行操作。
Gregor Thomas

4
您还可以简化函数定义,整个函数可以是return(DF_LIST[[ITEM_LOC]]),而无需分配中间变量。
格雷戈尔·托马斯

1

很简单 !这是我的建议:

如果要在工作区中选择数据框,请尝试以下操作:

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

要么

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

所有这些都会产生相同的结果。

您可以更改is.data.frame以检查其他类型的变量,例如is.function


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

my.list <- list(d1, d2)

然后,如果要访问其中一个数据帧中的特定值,可以依次使用双括号来进行访问。第一组将您带入数据框,第二组将您带入特定坐标:

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

[1] 6
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.