如何制作数据框列表?

时间:2013-07-06 02:16:31

标签: 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))

8 个答案:

答案 0 :(得分:300)

当你已经拥有一堆data.frames时,其他答案会向你显示如何制作data.frames列表,例如d1d2,....顺序命名数据框是一个问题,将它们放在一个列表中是一个很好的修复,但最好的做法是避免让一堆data.frames不在列表中首先。

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

本答案的其余部分将介绍一些您可能想要创建顺序变量的常见情况,并向您展示如何直接进入列表。如果您对R中的列表不熟悉,则可能还需要阅读What's the difference between [[ and [ in accessing elements of a list?


从头开始列出

首先,不要创建d1 d2 d3,...,dn。使用d元素创建列表n

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

在阅读文件时,这很容易完成。也许您在目录中有文件data1.csv, data2.csv, ...。您的目标是名为mydata的data.frames列表。你需要的第一件事是一个包含所有文件名的向量。您可以使用粘贴(例如,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)

在这种情况下,您还应该考虑是否确实需要单独的数据帧,或者是否需要一个带有&#34;组的单个数据帧&#34;专栏工作也一样吗?使用data.tabledplyr它很容易做到&#34;按组&#34;到数据框。

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

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

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
# they will be faster and can also add id columns to identify
# which list item they came from. They can also fill in
# missing values if some data frames have more columns than others
big_data = data.table::rbindlist(df_list)
big_data = dplyr::bind_rows(df_list)

(同样地使用cbinddplyr::bind_cols作为列。)

要合并(加入)数据框列表,您可以看到these answers。通常,我的想法是使用Reducemerge(或其他一些加入函数)将它们组合在一起。

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

在列表中放入类似的数据,因为您希望对每个数据框执行类似的操作,以及lapplysapply do.callthe purrr package和旧{{{{ {1}} plyr函数可以轻松完成此操作。使用列表轻松处理事务的人的例子都是SO。

即使使用forly for循环,循环列表元素也比使用l*ply构造变量名更容易,并使用paste访问对象。也更容易调试。

考虑可伸缩性。如果您真的只需要三个变量,那么使用getd1d2就可以了。但如果事实证明你确实需要6,那就更多了。下次,当您需要10或20时,您会发现自己正在复制和粘贴代码行,可能使用查找/替换将d3更改为d14,并且您正在考虑这不是编程应该如何。如果你使用一个列表,3个案例,30个案例和300个案例之间的差异最多只有一行代码 - 如果你的案例数量被自动检测到,例如,有多少{{1}文件在您的目录中。

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

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

答案 1 :(得分:121)

这与您的问题无关,但您希望在函数调用中使用=而不是<-。如果您使用<-,则最终会在您正在使用的任何环境中创建变量y1y2

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

答案 2 :(得分:17)

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

答案 3 :(得分: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返回包含第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

答案 4 :(得分:9)

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

关键是mgetls一起使用。如果问题中提供的数据框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 points out,设置数据构建过程会更好,以便在开始时将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))

答案 5 :(得分: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'

您可以使用此功能获取&amp;分配您选择的数据框。

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)

希望额外的帮助。

干杯!

答案 6 :(得分: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

答案 7 :(得分:-1)

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 个数据帧,我们使用 dat_list[[n]]。您可以以正常方式访问此数据框中的数据,例如dat_list[[2]]$x

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

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