根据多个条件匹配不同数据框中的行,而无需使用for循环

时间:2018-08-27 15:13:27

标签: r dataframe merge left-join matching

我的数据包含两个不同的数据框:

visits <- data.frame("visit_nr", "label", "degree", "code")
category <- data.frame("label", "degree", "group", "code1", "code2, "code3")

我想基于两个数据框之间“标签”,“度”和“代码”之间的匹配,为数据框“访问”中的每次访问分配一个组。 但是,如果来自数据框“类别”的“ code2”和“ code3”也列在数据框“访问”中,则只能将来自特定“ visit_nr”的行分配给特定组。这意味着要将一行分配给某个组,需要三行具有相同的“ visit_nr”,其中“ label”; “学位”和“代码”与以下任意一项匹配:

- "label", "degree", "code1"
- "label", "degree", "code2"
- "label", "degree", "code3" 

因为这些数据框都包含超过5万行,所以我想避免使用循环来完成此操作。

访问

visit_nr   | label | degree | code   |  Group
1601704801 |  171  |    1   | 354373 |   0
1601704801 |  171  |    1   | 200200 |   0
1601704801 |  171  |    1   | 973443 |   0
1601704801 |  171  |    1   | 475985 |   0
1601704801 |  171  |    1   | 994320 |   0

类别

label | degree | group | code1 | code2 | code3
 171  |   1    |   2   | 354373| 200200| 475985 
 171  |   1    |   3   | 354373| 200200| 998282
 171  |   1    |   1   | 354373| 200200| 0

预期输出:

visit_nr   | label | degree | code   |  Group 
1601704801 |  171  |    1   | 354373 |   2
1601704801 |  171  |    1   | 200200 |   2
1601704801 |  171  |    1   | 973443 |   2
1601704801 |  171  |    1   | 475985 |   2
1601704801 |  171  |    1   | 994320 |   2

2 个答案:

答案 0 :(得分:0)

Merge 2个表3次,然后像这样重新整理它们:

df1 <- merge(visits, category, by.x = c("label", "degree", "code"), by.y = c("label", "degree", "code1"), all.x = TRUE)
df2 <- merge(visits, category, by.x = c("label", "degree", "code"), by.y = c("label", "degree", "code2"), all.x = TRUE)
df3 <- merge(visits, category, by.x = c("label", "degree", "code"), by.y = c("label", "degree", "code3"), all.x = TRUE)
#change the column names using names(df) here to maintain consistency
df <- rbind(df1, df2, df3)

答案 1 :(得分:0)

还有另一种方法,可以将category从宽格式改写为长格式,与visits合并并计算可以找到多少个匹配代码:

library(data.table)
# reshape from wide to long format
lcat <- melt(setDT(category), measure.vars = patterns("^code"),
     value.name = "code")
# join and count
tmp <- lcat[setDT(visits), on = .(label, degree, code), nomatch = 0L][
  , .N, by = .(visit_nr, label, degree, group)][
    N == 3L]
tmp[]
     visit_nr label degree group N
1: 1601704801   171      1     2 3
# update join
visits[tmp, on = .(visit_nr, label, degree), Group := group, mult = "first"][]
visits[]
     visit_nr label degree   code Group
1: 1601704801   171      1 354373     2
2: 1601704801   171      1 200200     2
3: 1601704801   171      1 973443     2
4: 1601704801   171      1 475985     2
5: 1601704801   171      1 994320     2

编辑

comment中,OP公开了

  

并非数据框中的列code2code3中的所有行   category有一个值。也有只有code1有值的情况   与0不同,code2code3的值为0。   在特定的visit_nr中仅必须存在第一个代码的情况   将匹配的组分配给整个visit_nr

因此,简单检查是否有3个完全匹配的代码确实适用于示例数据集,但不适用于OP的生产数据集。

我认为可以通过两个修改来满足附加要求:

  1. 所有带有code == 0的行都从long中删除
  2. 如果tmp包含多个匹配项,则选择最高N的匹配项。如果有关系,which.max()选择遇到的第一个。

因此,代码变为:

library(data.table)
lcat <- melt(setDT(category), measure.vars = patterns("^code"),
             value.name = "code")[code != 0]
tmp <- lcat[setDT(visits), on = .(label, degree, code), nomatch = 0L][
  , .N, by = .(visit_nr, label, degree, group)][
    , .SD[which.max(N)], by = .(visit_nr, label, degree)]
visits[tmp, on = .(visit_nr, label, degree), Group := group]
visits[]
     visit_nr label degree   code Group
1: 1601704801   171      1 354373     2
2: 1601704801   171      1 200200     2
3: 1601704801   171      1 973443     2
4: 1601704801   171      1 475985     2
5: 1601704801   171      1 994320     2

数据

library(data.table)

visits <- fread("
visit_nr   | label | degree | code   |  Group
1601704801 |  171  |    1   | 354373 |   0
1601704801 |  171  |    1   | 200200 |   0
1601704801 |  171  |    1   | 973443 |   0
1601704801 |  171  |    1   | 475985 |   0
1601704801 |  171  |    1   | 994320 |   0
")

category <- fread("
label | degree | group | code1 | code2 | code3
 171  |   1    |   2   | 354373| 200200| 475985 
 171  |   1    |   3   | 354373| 200200| 998282
 171  |   1    |   1   | 354373| 200200| 0
")