我如何在R中按行名称合并超过2个数据框?

36

我从4个数据框中收集数据,并希望按行名称合并它们。我正在寻找一种有效的方法来完成这个任务。以下是我拥有的数据的简化版本。

df1           <- data.frame(N= sample(seq(9, 27, 0.5), 40, replace= T),
                            P= sample(seq(0.3, 4, 0.1), 40, replace= T),
                            C= sample(seq(400, 500, 1), 40, replace= T))
df2           <- data.frame(origin= sample(c("A", "B", "C", "D", "E"), 40,
                                           replace= T),
                            foo1= sample(c(T, F), 40, replace= T),
                            X= sample(seq(145600, 148300, 100), 40, replace= T),
                            Y= sample(seq(349800, 398600, 100), 40, replace= T))
df3           <- matrix(sample(seq(0, 1, 0.01), 40), 40, 100)
df4           <- matrix(sample(seq(0, 1, 0.01), 40), 40, 100)
rownames(df1) <- paste("P", sprintf("%02d", c(1:40)), sep= "")
rownames(df2) <- rownames(df1)
rownames(df3) <- rownames(df1)
rownames(df4) <- rownames(df1)

这通常是我会做的:

# merge df1 and df2
dat           <- merge(df1, df2, by= "row.names", all.x= F, all.y= F) #merge
rownames(dat) <- dat$Row.names #reset rownames
dat$Row.names <- NULL  #remove added rownames col

# merge dat and df3
dat           <- merge(dat, df3, by= "row.names", all.x= F, all.y= F) #merge
rownames(dat) <- dat$Row.names #reset rownames
dat$Row.names <- NULL  #remove added rownames col

# merge dat and df4
dat           <- merge(dat, df4, by= "row.names", all.x= F, all.y= F) #merge
rownames(dat) <- dat$Row.names #reset rownames
dat$Row.names <- NULL #remove added rownames col

正如您所看到的,这需要大量的代码。我的问题是是否可以用更简单的方法实现相同的结果。我已经尝试过(没有成功):更新:现在这个方法可行了!

MyMerge       <- function(x, y){
  df            <- merge(x, y, by= "row.names", all.x= F, all.y= F)
  rownames(df)  <- df$Row.names
  df$Row.names  <- NULL
  return(df)
}
dat           <- Reduce(MyMerge, list(df1, df2, df3, df4))

1
你所说的“without success”具体是指什么?请更加明确,包括错误信息。更好的做法是提供可重现的示例。 - Paul Hiemstra
1.) 如果行名称对于您的数据结构非常重要,以至于您通过这些名称进行合并,为什么不为data.frame添加一个真正的列呢?这样可以节省大部分编码工作。 2.) 即使您保留它们,也可以通过查看merge参数by.xby.y来节省大量编码。 3.) 使用df$Row.Names <- NULL可以从data.frame中删除一列。 4.) Reduce方法实际上应该有效,我也想知道为什么会失败。 - Beasterfield
然而,在这种设置中,仅保留交叉行名 all.x = Fall.y = F。是否可能保留 df1 的所有行,但排除其他 df 中不在 %in% rownames(df1) 中的行,即 all.x = T, all.y = F - Hans Roelofsen
好的,我已经解决了最后一个问题。只需在MyMerge函数中调整all.x = T,all.y = T即可。感谢@Paul和@Beasterfield的帮助。 - Hans Roelofsen
我已经为这个问题点赞了,所以你现在应该有足够的声望来发布你的答案作为一个“回答”(这是鼓励的),而不是作为对你的问题的编辑。 - Ben Bolker
显示剩余2条评论
4个回答

51

plyrjoin_all 函数可能会达到您想要的效果。但是所有输入必须是数据框,并且行名称将会被添加为一列。

require(plyr)

df3 <- data.frame(df3)
df4 <- data.frame(df4)

df1$rn <- rownames(df1)
df2$rn <- rownames(df2)
df3$rn <- rownames(df3)
df4$rn <- rownames(df4)

df <- join_all(list(df1,df2,df3,df4), by = 'rn', type = 'full')

type参数应该有所帮助,即使行名称不同也不匹配 如果您不想要行名称:


如果您不需要行名称:

df$rn <- NULL

15

在编辑您的函数时,我想出了一种函数,可以通过特定列键(列名)合并更多数据框。所得到的数据帧包括所有合并的数据框的变量(如果你只想保留公共变量(不包括NA),使用:all.x= FALSE, all.y= FALSE)。

MyMerge <- function(x, y){
  df <- merge(x, y, by= "name of the common column", all.x= TRUE, all.y= TRUE)
  return(df)
}
new.df <- Reduce(MyMerge, list(df1, df2, df3, df4))

1
这个函数很好,有没有办法让它重命名列名,并为常见的列名提供不同的列名? - Chirag
合并两个数据框后,我通常使用函数“fix()”,它允许您编辑数据库 - 因此您也可以重命名列。 - Roxana Adam

8

我一直在寻找相同的功能。在试用了这里和其他地方提供的几个选项后,最容易的方法是:

cbind.data.frame( df1,df2,df3,df4....)

6
只有当每个数据框中的行顺序相同时,这才有效。 - Matt

7

只需三行代码即可得到完全相同的结果:

dat2 <- cbind(df1, df2, df3, df4)
colnames(dat2)[-(1:7)] <- paste(paste('V', rep(1:100, 2),sep = ''),
                            rep(c('x', 'y'), each = 100), sep = c('.'))
all.equal(dat,dat2)    

啊,我明白了为什么你会遇到这么多麻烦。使用旧的for循环肯定可以解决问题。也许还有更聪明的解决方案。

rn <- rownames(df1)
l <- list(df1, df2, df3, df4)
dat <- l[[1]]
for(i in 2:length(l)) {
  dat <- merge(dat, l[[i]],  by= "row.names", all.x= F, all.y= F) [,-1]
  rownames(dat) <- rn
}

嗨,感谢您的回复。我明白它是如何工作的。然而,在我的示例数据中,我承认我没有清楚地表达出来,我希望这也适用于rownames不同的情况。因此,在这个例子中,rownames是相等的,但是当行被洗牌时,或者例如df2有更多或更少的行时,处理仍然应该起作用。这就是为什么我选择了merge的原因。 - Hans Roelofsen

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