我如何做一个数据帧的列表?

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

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

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))
342394 次浏览

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

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

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

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

 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)

希望这一点能有所帮助。

干杯!

还可以使用[[[访问每个列表元素中的特定列和值。这里有几个例子。首先,使用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,例如,d1d2, ....时,生成一个data.frames列表按顺序命名数据帧是一个问题,将它们放在列表中是一个很好的解决方案,但最佳实践是首先避免在列表中有一堆数据帧

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

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


从一开始就列出

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

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

这在读入文件时很容易做到。也许你在一个目录中有文件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.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)

特别是在这种情况下,您还应该考虑是否真的需要单独的数据帧,或者将单个数据帧与“;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(或其他一些连接函数)来将它们组合在一起。

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

使用它们可能很痛苦,而且几乎总是你实际上不需要它们,但如果你需要,为了方便,在list中尽你所能,然后你可以使用list2env()将所有列表项放入一个环境中,例如你的.GlobalEnv

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

将相似的数据放在列表中,因为你想对每个数据帧做类似的事情,而lapplysapply do.callpurrr和旧的plyr l*ply函数等函数可以很容易地做到这一点。人们用清单轻松做事的例子比比皆是。

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

想想可伸缩性。如果你真的只需要三个变量,可以使用d1d2d3。但如果你真的需要6个,那就需要更多的输入。下一次,当你需要10或20行代码时,你发现自己复制和粘贴了几行代码,可能使用find/replace将d14更改为d15,而你想到的是编程不应该是这样的。如果你使用一个列表,3个case、30个case和300个case之间的区别最多是一行代码——如果你的case的数量是由自动检测到的,例如,你的目录中有多少.csv文件,那么根本没有变化。

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

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

假设你有“大量”名称相似的data.frames(这里的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中的pattern参数,该参数允许我们使用正则表达式对环境中对象的名称进行更精细的解析。正则表达式"^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))

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

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

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

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

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

你可以改变is.data.frame来检查其他类型的变量,比如is.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))


my.list <- list(d1, d2)

然后,如果希望访问其中一个数据帧中的特定值,可以按顺序使用双括号。第一组让你进入数据帧,第二组让你到达特定的坐标:

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


[1] 6

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

注意,它在append()调用中是list(dat)

访问数据

. b1t4 . >访问数据

然后,为了从列表中获得nth数据帧,我们使用dat_list[[n]]。你可以用正常的方式访问这个数据框架中的数据,例如dat_list[[2]]$x

或者如果你想从你所有的数据框架sapply(dat_list, "[", "x")中得到相同的部分。

请参阅@Gregor托马斯在不使用for循环的情况下执行此操作的答案。

在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

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