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

AGU*_*GUY 11 join r

我有两个数据框,"数据"和"分数",并希望将它们合并到"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")                  
Run Code Online (Sandbox Code Playgroud)

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

如果有多个匹配,则所有可能的匹配每个贡献一行.

但是,我想只保留与表中第一个匹配对应的行scores.

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

有什么建议?

Aru*_*run 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
Run Code Online (Sandbox Code Playgroud)

有关各行dataid列中,在匹配的行scores" id列中找到,并且仅在第一个被保留(因为mult = "first").如果没有匹配,则将其删除(因为nomatch = 0L).

  • 它工作正常,但效率很低.`data.table()`导致深层复制.关键参数将重新排序整个data.table(除非可以重用并且可能不合适,否则效率低下).`unique()`导致不必要的中间数据.而`on =`也允许一个干净的语法来查看它并理解连接列是什么(尽管它也可以用在keyed data.tables上).我建议阅读[二级索引](https://github.com/Rdatatable/data.table/wiki/Getting-started)插图. (4认同)

lmo*_*lmo 7

这是一个使用aggregate和的基本R方法head:

merge(data, aggregate(score ~ id, data=scores, head, 1), by="id") 
Run Code Online (Sandbox Code Playgroud)

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


可能更有效的是对分数data.frame进行子集化,使用duplicated它可以获得相同的结果aggregate,但会减少计算开销.

merge(data, scores[!duplicated(scores$id),], by="id")
Run Code Online (Sandbox Code Playgroud)


Hua*_*hen 6

这是使用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
Run Code Online (Sandbox Code Playgroud)

此外,如果要替换新的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
Run Code Online (Sandbox Code Playgroud)