合并具有多个匹配项的数据帧时,仅选择第一行

时间:2016-06-10 13:22:45

标签: r join

我有两个数据框,"数据"和"得分",并希望将它们合并到" id"柱:

data = data.frame(id = c(1,2,3,4,5),
                  state = c("KS","MN","AL","FL","CA"))
scores = data.frame(id = c(1,1,1,2,2,3,3,3),
                    score = c(66,75,78,86,85,76,75,90))
merge(data, scores, by = "id")                  
semi_join(data, scores, by = "id")                  

"得分"数据,有" id"具有多个观察值,其中每个匹配在连接后获得一行。见?merge

  

如果有多个匹配项,则所有可能的匹配项各占一行。

但是,我希望只保留与scores表中第一个匹配相对应的行。

半连接本来不错,但我无法从右表中选择得分。

有什么建议吗?

4 个答案:

答案 0 :(得分:13)

使用data.table以及mult = "first"nomatch = 0L

require(data.table)
setDT(scores); setDT(data) # convert to data.tables by reference

scores[data, mult = "first", on = "id", nomatch=0L]
#    id score state
# 1:  1    66    KS
# 2:  2    86    MN
# 3:  3    76    AL

对于data id列的每一行,scores'中的匹配行找到id列,并保留第一个列(因为mult = "first")。如果没有匹配项,则会将其删除(因为nomatch = 0L)。

答案 1 :(得分:7)

以下是使用aggregatehead的基本R方法:

merge(data, aggregate(score ~ id, data=scores, head, 1), by="id") 

aggregate函数按ID分解分数数据,然后应用head从每个ID获取第一个观察值。由于aggregate返回data.frame,因此会直接将其合并到data.frame数据上。

可能更高效的是使用duplicated对得分data.frame进行子集化,这将获得与aggregate相同的结果,但会减少计算开销。

merge(data, scores[!duplicated(scores$id),], by="id")

答案 2 :(得分:5)

这是使用dplyr :: distinct的另一种方法。如果您想要保留“数据”中的所有行,即使没有匹配项,也很有用。

data = data.frame(id=c(1,2,3,4,5),
                  state=c("KS","MN","AL","FL","CA"))
scores = data.frame(id=c(1,1,1,2,2,3,3,3),
                    score=c(66,75,78,86,85,76,75,90))
data %>% dplyr::left_join(dplyr::distinct(scores, id, .keep_all = T))
# Joining, by = "id"
# id state score
# 1  1    KS    66
# 2  2    MN    86
# 3  3    AL    76
# 4  4    FL    NA
# 5  5    CA    NA

此外,如果要替换新data.frame中的NA,请尝试使用tidyr :: replace_na()函数。例如:

data %>% dplyr::left_join(dplyr::distinct(scores, id, .keep_all = T)) %>% tidyr::replace_na(replace = list("score"=0L))
# Joining, by = "id"
# id state score
# 1  1    KS    66
# 2  2    MN    86
# 3  3    AL    76
# 4  4    FL     0
# 5  5    CA     0

答案 3 :(得分:1)

base 中,可以使用match合并具有多个匹配项的数据帧时仅选择第一行

#Return also those which found no match
(tt <- cbind(data, score=scores[match(data$id, scores$id),"score"]))
#  id state score
#1  1    KS    66
#2  2    MN    86
#3  3    AL    76
#4  4    FL    NA
#5  5    CA    NA

#Return only those which found a match
tt[!is.na(tt$score),]
#  id state score
#1  1    KS    66
#2  2    MN    86
#3  3    AL    76