如何创建数据框列表?

How do I make a list of data frames?

提问人:Ben 提问时间:7/6/2013 最后编辑:moodymudskipperBen 更新时间:8/18/2022 访问量:370854

问:

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

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

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))
列表 数据帧 R-FAQ

评论

18赞 Gregor Thomas 2/21/2015
这是在几个答案中,但这里也值得有一个明显的评论:不要在里面使用。通过使用您创建并在您的全局环境中,您的数据框不是您想要的样子。=<-data.frame()<-y1y2
48赞 Ben 7/24/2015
看看 data.frame() 里面没有空格和 s 的乱七八糟的代码。我真是个新手。<-
10赞 Claus Wilke 12/11/2017
现在不是了。我刚刚编辑了您的问题以修复代码格式。如果您感到怀旧,请随时恢复。

答:

154赞 Peyton 7/6/2013 #1

这与您的问题无关,但您希望在函数调用中使用而不是在函数调用中使用。如果您使用 ,您最终会创建变量,并且在您工作的任何环境中:=<-<-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
13赞 Mark Miller 7/6/2013 #2

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

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

其中 返回包含第 2、3 和 4 个数据帧的列表。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赞 Gregor Thomas 11/22/2017
而不是 ,只需使用lapply(foo, get)mget(foo)
3赞 ML_for_now 6/22/2014 #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'

您可以使用此函数来获取和分配您选择的 DataFrame。

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 6/23/2014
是的,我知道,但出于某种原因,当我复制和粘贴时,一切都大写。 :(无论如何,小写的代码都可以工作。
5赞 Gregor Thomas 1/6/2016
我很好奇你为什么更愿意?如果您更喜欢函数语法,您甚至可以在不定义自定义函数的情况下进行操作。GETDF_FROMLIST(mylist, 1)mylist[[1]]"[["(mylist, 1)
4赞 Gregor Thomas 1/6/2016
你也可以简化你的函数定义,函数的整个主体可以只是 ,不需要分配一个中间变量。return(DF_LIST[[ITEM_LOC]])
26赞 Rich Scriven 6/24/2014 #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

评论

0赞 gghuffer 12/20/2022
正是这样的东西让我不想再使用 R。它看起来超级笨拙。
470赞 Gregor Thomas 6/24/2014 #5

其他答案告诉你,当你已经有一堆data.frames时,如何制作data.frames的列表,例如,、、......按顺序命名的数据帧是一个问题,将它们放在列表中是一个很好的解决方法,但最佳做法是首先避免一堆 data.frames 不在列表中d1d2

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

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


从头开始列出

永远不要创造,...,首先。创建一个包含元素的列表。d1d2d3dndn

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

在读取文件时,这很容易完成。也许你的目录中有文件。您的目标是一个名为 的 data.frames 列表。您需要的第一件事是包含所有文件名的向量。你可以用粘贴来构造它(例如,),但它可能更容易用于抓取所有适当的文件:.您可以使用正则表达式来匹配文件,如果您需要帮助,请在其他问题中阅读有关正则表达式的更多信息。通过这种方式,您可以抓取所有CSV文件,即使它们没有遵循良好的命名方案。或者,如果您需要从一堆 CSV 文件中挑选出某些 CSV 文件,则可以使用更高级的正则表达式模式。data1.csv, data2.csv, ...mydatamy_files = paste0("data", 1:5, ".csv")list.filesmy_files <- list.files(pattern = "\\.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.csvreadr::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)

特别是在这种情况下,您还应该考虑是否真的需要单独的数据框,或者带有“组”列的单个数据框是否也能正常工作?使用 or 对数据框“按组”执行操作非常容易。data.tabledplyr

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

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

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

如果您有以模式命名的数据框,例如 、 、 ,并且您希望它们出现在列表中,那么如果可以编写正则表达式来匹配名称,则可以使用它们。类似的东西df1df2df3get

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.

通常,用于获取多个对象并在命名列表中返回它们。它的对应项用于获取单个对象并返回它(不在列表中)。mgetget

将数据框列表合并为单个数据框

一个常见的任务是将数据框列表合并为一个大数据框。如果要将它们堆叠在一起,则可以使用一对,但对于数据帧列表,这里有三个不错的选择: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)

(同样,使用 或 for 列。cbinddplyr::bind_cols

要合并(联接)数据框列表,您可以看到以下答案。通常,这个想法是与(或其他一些连接函数)一起使用,将它们组合在一起。Reducemerge

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

使用它们可能很痛苦,而且几乎总是你实际上并不需要它们,但如果你这样做了,请尽你所能轻松地,然后你可以使用将所有列表项放入一个环境中,比如你的.listlist2env().GlobalEnv

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

将相似的数据放在列表中,因为您希望对每个数据帧执行类似的操作,而 、、 purrr和旧函数等函数可以很容易地做到这一点。人们轻松使用列表做事的例子遍布 SO。lapplysapplydo.callplyrl*ply

即使使用低级 for 循环,循环遍历列表的元素也比使用 构造变量名称和使用 访问对象要容易得多。调试也更简单。pasteget

考虑可扩展性。如果你真的只需要三个变量,那么使用 、 、 就可以了。但是,如果事实证明你真的需要 6 个,那就需要更多的打字了。下一次,当你需要 10 或 20 行代码时,你发现自己复制和粘贴了代码行,也许使用 find/replace 来更改为 ,你认为这不是编程应该有的样子。如果使用列表,则 3 个案例、30 个案例和 300 个案例之间的差异最多是一行代码---如果通过目录中有多少个文件自动检测您的案例数,则完全没有变化。d1d2d3d14d15.csv

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

总的来说,使用列表将引导你编写更干净、更易于阅读的代码,从而减少错误和混乱。

评论

5赞 derelict 1/15/2016
你推荐哪本书来介绍使用列表?
29赞 Gregor Thomas 2/10/2016
我建议阅读 Stack Overflow 上标有 both 和 的问题和答案。rlist
10赞 Gregor Thomas 3/7/2017
这是可能的,但要清楚地表明你正在创建一个列表,这很好!清晰的代码是一件好事。我看不出使用有什么好处。my_data <- list()my_data <- NULL
0赞 Matteo 8/20/2021
我正在执行,但元素不是数据框,而是列表,即 给。这是怎么回事,我该如何去奉献?请注意,这无济于事。dfs <- list.files(pattern = "^[0-9]") |> lapply(read.csv)dfsdfs[i] |> class()> [1] "list"dfs[i] |> class()> [1] "dataframe"dfs[i] <- dfs[i] |> as.data.frame()
1赞 Gregor Thomas 8/25/2021
@Matteo 如果是数据框列表,则将是一个数据框的列表。你需要用来提取一个单独的列表元素,将是一个数据框。有关更多上下文和解释,请参阅此常见问题解答dfsdfs[i][[dfs[[i]]
11赞 lmo 6/5/2016 #6

假设你有“大量”具有相似名称的 data.frames(这里是 d#,其中 # 是一些正整数),以下是对 @mark-miller 方法的轻微改进。它更简洁,返回一个 data.frame 的命名列表,其中列表中的每个名称都是相应原始 data.frame 的名称。

关键是与 一起使用。如果问题中提供的数据帧 d1 和 d2 是环境中唯一名称为 d# 的对象,则mgetls

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))
2赞 smerllo 5/22/2018 #7

很简单!这是我的建议:

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

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

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

所有这些都将给出相同的结果。

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

7赞 Loek van der Kallen 1/7/2020 #8

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

让我们从创建包含数据框的列表开始,如上所述:

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
5赞 D A Wells 7/29/2021 #9

for循环模拟

如果我有一个生成数据帧的循环,我会从空开始,并在生成数据帧时附加它们。forlist()

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

访问数据

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

或者,如果您希望从所有数据帧中获得相同的部分。sapply(dat_list, "[", "x")

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

1赞 user10917479 3/10/2022 #10

在 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

这在编译以后要按名称引用的列表时非常有用。