我想合并两个数据框,保留其中一个的原始行顺序(在下面的示例中为df.2
)。
以下是一些示例数据(来自class
列的所有值都在两个数据框中定义):
df.1 <- data.frame(class = c(1, 2, 3), prob = c(0.5, 0.7, 0.3))
df.2 <- data.frame(object = c('A', 'B', 'D', 'F', 'C'), class = c(2, 1, 2, 3, 1))
如果我这样做:
merge(df.2, df.1)
输出是:
class object prob
1 1 B 0.5
2 1 C 0.5
3 2 A 0.7
4 2 D 0.7
5 3 F 0.3
如果我添加sort = FALSE
:
merge(df.2, df.1, sort = F)
结果是:
class object prob
1 2 A 0.7
2 2 D 0.7
3 1 B 0.5
4 1 C 0.5
5 3 F 0.3
但我想要的是:
class object prob
1 2 A 0.7
2 1 B 0.5
3 2 D 0.7
4 3 F 0.3
5 1 C 0.5
答案 0 :(得分:45)
您只需要创建一个变量,该变量在df.2中给出行号。然后,一旦合并了数据,就可以根据此变量对新数据集进行排序。这是一个例子:
df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
df.2$id <- 1:nrow(df.2)
out <- merge(df.2,df.1, by = "class")
out[order(out$id), ]
答案 1 :(得分:26)
查看plyr包中的join函数。它就像合并,但它允许您保持其中一个数据集的行顺序。总的来说,它比合并更灵活。
使用您的示例数据,我们将使用join
,如下所示:
> join(df.2,df.1)
Joining by: class
object class prob
1 A 2 0.7
2 B 1 0.5
3 D 2 0.7
4 F 3 0.3
5 C 1 0.5
以下是一些描述合并函数修复的链接,用于保持行顺序:
http://www.r-statistics.com/2012/01/merging-two-data-frame-objects-while-preserving-the-rows-order/
答案 2 :(得分:11)
从data.table v1.9.5+开始,您可以:
require(data.table) # v1.9.5+
setDT(df.1)[df.2, on="class"]
通过在class
中为df.1
中的每一行找出匹配的行并提取相应的列,对列df.2
执行联接。
答案 3 :(得分:5)
为了完整起见,在连接中更新也会保留原始行顺序。如果只有几列要追加,这可能是Arun's data.table
answer的替代选择:
library(data.table)
setDT(df.2)[df.1, on = "class", prob := i.prob][]
object class prob 1: A 2 0.7 2: B 1 0.5 3: D 2 0.7 4: F 3 0.3 5: C 1 0.5
此处,df.2
正在加入df.1
并获得一个新列prob
,该列从df.1
的匹配行中复制。
答案 4 :(得分:3)
accepted answer提出了一种在使用merge
时保持订单的手动方式,这种方式大部分时间都有效,但需要不必要的手工操作。这个解决方案来自How to ddply() without sorting?,它处理了保持秩序的问题,但处于拆分应用组合环境中:
一段时间之前就出现在plyr邮件列表上了(由@kohske提出),这是Peter Meilstrup针对有限案例提供的解决方案:
#Peter's version used a function gensym to
# create the col name, but I couldn't track down
# what package it was in.
keeping.order <- function(data, fn, ...) {
col <- ".sortColumn"
data[,col] <- 1:nrow(data)
out <- fn(data, ...)
if (!col %in% colnames(out)) stop("Ordering column not preserved by function")
out <- out[order(out[,col]),]
out[,col] <- NULL
out
}
现在您可以使用此通用keeping.order
函数来保持merge
调用的原始行顺序:
df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
keeping.order(df.2, merge, y=df.1, by = "class")
根据要求,这将产生:
> keeping.order(df.2, merge, y=df.1, by = "class")
class object id prob
3 2 A 1 0.7
1 1 B 2 0.5
4 2 D 3 0.7
5 3 F 4 0.3
2 1 C 5 0.5
所以keeping.order
在接受的答案中有效地自动化了方法。
答案 5 :(得分:1)
感谢@PAC,我想出了类似的东西:
merge_sameord = function(x, y, ...) {
UseMethod('merge_sameord')
}
merge_sameord.data.frame = function(x, y, ...) {
rstr = paste(sample(c(0:9, letters, LETTERS), 12, replace=TRUE), collapse='')
x[, rstr] = 1:nrow(x)
res = merge(x, y, all.x=TRUE, sort=FALSE, ...)
res = res[order(res[, rstr]), ]
res[, rstr] = NULL
res
}
这假定您希望保留第一个数据帧的顺序,并且合并的数据帧将具有与第一个数据帧相同的行数。它将为您提供干净的数据框,而无需额外的列。
答案 6 :(得分:1)
In this specific case you could us factor
for a compact base solution:
df.2$prob = factor(df.2$class,labels=df.1$prob)
df.2
# object class prob
# 1 A 2 0.7
# 2 B 1 0.5
# 3 D 2 0.7
# 4 F 3 0.3
# 5 C 1 0.5
Not a general solution however, it works if:
left_join
1 is not negotiable, for the rest we can do:
df.3 <- df.2 # deal with 2.
df.1b <- df.1[order(df.1$class),] # deal with 3
df.1b <- df.1b[df.1$class %in% df.2$class,] # deal with 4.
df.3$prob = factor(df.3$class,labels=df.1b$prob)
df.3 <- df3[!is.na(df.3$prob),] # deal with 5. if you want an `inner join`
df.3$prob <- as.numeric(as.character(df.3$prob)) # deal with 6.
答案 7 :(得分:0)
在几种用例中,一个简单的子集就可以完成:
# Use the key variable as row.names
row.names(df.1) = df.1$key
# Sort df.1 so that it's rows match df.2
df.3 = df.1[df.2$key, ]
# Create a data.frame with cariables from df.1 and (the sorted) df.2
df.4 = cbind(df.1, df.3)
此代码将保留df.2及其顺序,并仅添加来自df.1的匹配数据
如果仅要添加一个变量,则不需要cbind()
信息:
row.names(df.1) = df.1$key
df.2$data = df.1[df.2$key, "data"]
答案 8 :(得分:0)
作为软件包开发者,您希望尽可能少地依赖其他软件包。尤其是tidyverse函数,这种变化对于程序包开发人员恕我直言。
下面是一个快速实现,可以不使用导入dplyr
来使用dplyr
包的联接功能。它保持原始排序(按OP的要求),并且不会将连接列移到最前面(这是merge()
的另一个烦人的事情)。
left_join <- function(x, y, ...) {
merge_exec(x = x, y = y, all.x = TRUE, ...)
}
right_join <- function(x, y, ...) {
merge_exec(x = x, y = y, all.y = TRUE, ...)
}
inner_join <- function(x, y, ...) {
merge_exec(x = x, y = y, all = TRUE, ...)
}
full_join <- function(x, y, ...) {
merge_exec(x = x, y = y, ...)
}
# workhorse:
merge_exec <- function(x, y, ...) {
# set index
x$join_id_ <- 1:nrow(x)
# do the join
joined <- merge(x = x, y = y, sort = FALSE, ...)
# get suffices (yes, I prefer this over suffixes)
if ("suffixes" %in% names(list(...))) {
suffixes <- list(...)$suffixes
} else {
suffixes <- c("", "")
}
# get columns names in right order, so the 'by' column won't be forced first
cols <- unique(c(colnames(x),
paste0(colnames(x), suffixes[1]),
colnames(y),
paste0(colnames(y), suffixes[2])))
# get the original row and column index
joined[order(joined$join_id),
cols[cols %in% colnames(joined) & cols != "join_id_"]]
}
答案 9 :(得分:0)
评分最高的答案不会产生原始海报所希望的结果,即第1列中的“类别”。如果OP允许切换df.2中的列顺序,则可能是基数R不合并一个-行答案:
df.1 <- data.frame(class = c(1, 2, 3), prob = c(0.5, 0.7, 0.3))
df.2 <- data.frame(class = c(2, 1, 2, 3, 1), object = c('A', 'B', 'D', 'F', 'C'))
cbind(df.2, df.1[match(df.2$class, df.1$class), -1, drop = FALSE])
我碰巧喜欢row.names中描述的信息。可以完全复制OP期望结果的完整单线是
data.frame(cbind(df.2, df.1[match(df.2$class, df.1$class), -1, drop = FALSE]),
row.names = NULL)
我同意https://stackoverflow.com/users/4575331/ms-berends的观点,即程序包开发人员对另一个程序包(或“ verse”)的依赖性越小越好,因为开发路径会随着时间的流逝而经常出现分歧。
注意:如果df.1$class
中有重复项,则上面的单行不起作用。在没有'outer'
合并和循环的情况下,可以克服此问题,或更普遍地,在Berend女士的巧妙的合并后加扰代码中可以解决此问题。
答案 10 :(得分:-1)
基地可能有更有效的方式。这对函数来说相当简单。
varorder <- names(mydata) # --- Merge
mydata <- merge(mydata, otherData, by="commonVar")
restOfvars <- names(mydata[!(names(mydata) %in% varorder)])
mydata[c(varorder,restOfvars)]