数据框列表;添加一个名为数据框名称的新变量。

15

我有一个数据框的列表,最终想要将它们合并,并保留它们原始的数据框名称或列表索引的记录。这将允许我在所有行上进行子集等操作。为了实现这一点,我想给每个数据框添加一个新变量'id',其中包含它所属的数据框的名称/索引。

编辑:“在我的实际代码中,数据框变量是使用以下代码从多个文件中读取创建的,因此我没有实际名称,只有在'files.to.read'列表中的名称,我不确定它们是否会与数据框顺序对齐。”

mylist <- llply(files.to.read, read.csv)

有几种方法在几篇文章中得到了强调: Working-with-dataframes-in-a-list-drop-variables-add-new-onesUsing-lapply-with-changing-arguments

我尝试了两种类似的方法,第一种使用索引列表:

df1 <- data.frame(x=c(1:5),y=c(11:15))
df2 <- data.frame(x=c(1:5),y=c(11:15))
mylist <- list(df1,df2)

# Adds a new coloumn 'id' with a value of 5 to every row in every dataframe.
# I WANT to change the value based on the list index.
mylist1 <- lapply(mylist, 
    function(x){
        x$id <- 5
        return (x)
    }
)
#Example of what I WANT, instead of '5'.
#> mylist1
#[[1]]
  #x  y id
#1 1 11  1
#2 2 12  1
#3 3 13  1
#4 4 14  1
#5 5 15  1
#
#[[2]]
  #x  y id
#1 1 11  2
#2 2 12  2
#3 3 13  2
#4 4 14  2
#5 5 15  2

第二个尝试传递列表的names()。

# I WANT it to add a new coloumn 'id' with the name of the respective dataframe
# to every row in every dataframe.
mylist2 <- lapply(names(mylist), 
    function(x){
        portfolio.results[[x]]$id <- "dataframe name here"
        return (portfolio.results[[x]])
    }
)
#Example of what I WANT, instead of 'dataframe name here'.
# mylist2
#[[1]]
  #x  y id
#1 1 11  df1
#2 2 12  df1
#3 3 13  df1
#4 4 14  df1
#5 5 15  df1
#
#[[2]]
  #x  y id
#1 1 11  df2
#2 2 12  df2
#3 3 13  df2
#4 4 14  df2
#5 5 15  df2
但是names()函数在数据框列表上不起作用,它返回NULL。 在第一个示例中,我能否使用seq_along(mylist)。
有任何想法或更好的处理“与源ID合并”的方法吗? 编辑 - 下面添加了解决方案:我已经实现了一个解决方案,使用了Hadley的建议和Tommy的提示,大致如下。
files.to.read <- list.files(datafolder, pattern="\\_D.csv$", full.names=FALSE)
mylist <- llply(files.to.read, read.csv)
all <- do.call("rbind", mylist)
all$id <- rep(files.to.read, sapply(mylist, nrow))
我将文件.to.read向量用作每个数据框的ID。 我还改用了merge_recurse(),因为出于某种原因它非常慢。
 all <- merge_recurse(mylist)

谢谢大家。

5个回答

19

就我个人而言,我认为在折叠之后添加名称更容易:

df1 <- data.frame(x=c(1:5),y=c(11:15))
df2 <- data.frame(x=c(1:5),y=c(11:15))
mylist <- list(df1 = df1, df2 = df2)

all <- do.call("rbind", mylist)
all$id <- rep(names(mylist), sapply(mylist, nrow))

谢谢。我不确定折叠是否会保留顺序。我正在折叠列表以使用qplot()并通过$id指定子集。但是,折叠非常慢,因此我愿意尝试其他选项。 - Look Left
哇,我在实际代码中使用了rbind()而不是merge_recurse(),总时间从约10分钟减少到了10秒。 - Look Left

9

您的第一次尝试非常接近。通过使用索引而不是值,它将起作用。您的第二次尝试失败了,因为您没有给列表中的元素命名。

以下两种解决方案都利用了lapply可以向函数传递额外参数(mylist)的事实。

df1 <- data.frame(x=c(1:5),y=c(11:15))
df2 <- data.frame(x=c(1:5),y=c(11:15))
mylist <- list(df1=df1,df2=df2) # Name each data.frame!
# names(mylist) <- c("df1", "df2") # Alternative way of naming...

# Use indices - and pass in mylist
mylist1 <- lapply(seq_along(mylist), 
        function(i, x){
            x[[i]]$id <- i
            return (x[[i]])
        }, mylist
)

# Now the names work - but I pass in mylist instead of using portfolio.results.
mylist2 <- lapply(names(mylist), 
    function(n, x){
        x[[n]]$id <- n
        return (x[[n]])
    }, mylist
)

感谢回答两个问题。我编辑了我的问题,说明我没有名字,但实际上我有。将 files.to.read 转换为列表可以让我为每个列表元素分配相应的名称,前提是它们对齐正确...我看不到树木! - Look Left

2

names() 如果有名字就可以使用,但是你没有给它名字。这是一个无名列表。你需要使用数字索引:

> for(i in 1:length(mylist) ){ mylist[[i]] <- cbind(mylist[[i]], id=rep(i, nrow(mylist[[i]]) ) ) }
> mylist
[[1]]
  x  y id
1 1 11  1
2 2 12  1
3 3 13  1
4 4 14  1
5 5 15  1

[[2]]
  x  y id
1 1 11  2
2 2 12  2
3 3 13  2
4 4 14  2
5 5 15  2

谢谢。在Tommy、Hadley和你的帮助下,我已经创建了一个合适的解决方案。最终,我想要这些名称以便在后续过程中进行识别。 - Look Left

1

dlply函数来自plyr包,可能是一个解决方案:

library('plyr')
df1 <- data.frame(x=c(1:5),y=c(11:15))
df2 <- data.frame(x=c(1:5),y=c(11:15))
mylist <- list(df1 = df1, df2 = df2)

all <- ldply(mylist)

0
你还可以使用tidyverse,使用lst代替list,它会自动为列表命名,然后使用imap:

library(tidyverse)
mylist <- dplyr::lst(df1, df2)
purrr::imap(mylist, ~mutate(.x, id = .y))
# $df1
#   x  y  id
# 1 1 11 df1
# 2 2 12 df1
# 3 3 13 df1
# 4 4 14 df1
# 5 5 15 df1

# $df2
#   x  y  id
# 1 1 11 df2
# 2 2 12 df2
# 3 3 13 df2
# 4 4 14 df2
# 5 5 15 df2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接