【问题标题】:Match observations in two dataframes in R匹配 R 中两个数据框中的观察结果
【发布时间】:2017-12-25 13:50:25
【问题描述】:

我有两个数据框。我想使用一个数据框中的元素来搜索另一个数据框中的一列。我需要通过匹配来缩小这个数据框的范围。然后继续逐个元素地缩小范围。查看示例代码,它可以更好地解释。

df1    col1   

1      apples      
2      oranges     
3      apples    
4      banana  
5      grapes
6      mangoes
7      oranges
8      banana

df1 只有一列。同时 df2 有 2 列。 setID & col1

df2 setID   col1

1   1   apples      
2   1   oranges     
3   1   oranges
4   1   mangoes
5   1   grapes
6   1   banana  
7   1   banana
8   1   apples    
10  2   apples      
11  2   oranges     
12  2   apples    
13  2   banana  
14  2   grapes
15  2   mangoes
16  2   banana
17  2   oranges
18  3   apples      
19  3   banana  
20  3   oranges     
21  3   apples    
22  3   grapes
23  3   mangoes
24  3   oranges
25  3   banana
26  4   apples      
27  4   oranges     
28  4   apples    
29  4   grapes
30  4   grapes
31  4   oranges     
32  4   banana  
33  4   banana

如您所见,有一些重复的 setID。他们标记一组。集合的顺序很重要。请注意,df1$col1 不必与 df2 中的集合长度相同。它们也不必完全匹配。他们只需要足够接近即可。在这种情况下, df1$col1 最接近 df2$setID = 2 的匹配,只有最后两个元素乱序。它们不必完全匹配的原因是因为我想使用“键入时搜索”方法。我不想将 df1$col1 与 df2 上的 setID 匹配。我想通过逐个元素来缩小可能的集合。假设您一个一个地获取 df1 的元素,而不是一个完整的数据框。例如:

从 df2 中查找 df1$col1[1] 的匹配项,并将包含匹配项的所有集合保存到 tempdf。如果 df1$col1[1] 在同一个集合中多次找到匹配项,则无关紧要。如果至少找到一次,则该集合将被添加到 tempdf。

最后需要检索的是一个setID,它对应于匹配到df1的集合。在这种情况下,tempdf 将与 df2 相同,因为所有集合都包括“苹果”。鉴于第一个元素是匹配项,接下来将是 df1$col1[2] 与 tempdf 的匹配项。我猜 df1$col1[1:2] 来自 tempdf。这导致:

tempdf  setID   col1

1   1   apples      
2   1   oranges     
3   1   oranges
4   1   mangoes
5   1   grapes
6   1   banana  
7   1   banana
8   1   apples    
10  2   apples      
11  2   oranges     
12  2   apples    
13  2   banana  
14  2   grapes
15  2   mangoes
16  2   banana
17  2   oranges
26  4   apples      
27  4   oranges     
28  4   apples    
29  4   grapes
30  4   grapes
31  4   oranges     
32  4   banana  
33  4   banana

基本上 setID = 3 被省略。随着 df1 中的第三个元素继续进行,新的 tempdf 将仅包含 setID 2 和 4。一旦只剩下一个 setID,循环(我想解决这个问题)就会结束,在这种情况下 setID = 2。因此 setID = 2 将是被认为是 df1 的紧密匹配。

当然,请随时就比这个更好的方法提出建议。

【问题讨论】:

  • lapply(split(df2[, "col1", drop = FALSE], df2$setID), function(x) identical(df1, x))?
  • 如果df1 的行数少于df2 中的组,您的预期答案是什么?

标签: r dataframe


【解决方案1】:

您可能想查看“比较”包,它允许您比较允许不同的转换。

这里有几个例子可以考虑......

起始样本数据。注意setID == 4,它包含所有值,但顺序错误。

df1 <- data.frame(col1 = c("apples", "oranges", "apples", "banana"),
                  stringsAsFactors = FALSE)
df1
##      col1
## 1  apples
## 2 oranges
## 3  apples
## 4  banana

df2 <- structure(list(setID = c(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 
    4, 4, 4, 4), col1 = c("apples", "oranges", "apples", "banana", 
    "apples", "grapes", "oranges", "apples", "oranges", "grapes", 
    "banana", "banana", "apples", "apples", "banana", "oranges")), 
    .Names = c("setID", "col1"), 
    row.names = c("1", "2", "3", "4", "5", "6", "7", "8", 
    "9", "10", "11", "12", "13", "21", "31", "41"), class = "data.frame")
df2
##    setID    col1
## 1      1  apples
## 2      1 oranges
## 3      1  apples
## 4      1  banana
## 5      2  apples
## 6      2  grapes
## 7      2 oranges
## 8      2  apples
## 9      3 oranges
## 10     3  grapes
## 11     3  banana
## 12     3  banana
## 13     4  apples
## 21     4  apples
## 31     4  banana
## 41     4 oranges

加载“比较”并进行一些比较:

library(compare)
lapply(split(df2[, "col1", drop = FALSE], df2$setID), 
       function(x) compare(df1, x))
## $`1`
## TRUE
## 
## $`2`
## FALSE [FALSE]
## 
## $`3`
## FALSE [FALSE]
## 
## $`4`
## FALSE [FALSE]
## 

在比较之前允许所有转换(如果您只想允许某些转换,请参阅?compare 了解详细信息)。

lapply(split(df2[, "col1", drop = FALSE], df2$setID), 
       function(x) compare(df1, x, allowAll = TRUE))
## $`1`
## TRUE
## 
## $`2`
## FALSE [FALSE]
##   sorted
##   [col1] ignored case
##   renamed rows
##   [col1] ignored case
##   dropped row names
##   [col1] ignored case
## 
## $`3`
## FALSE [FALSE]
##   sorted
##   [col1] ignored case
##   renamed rows
##   [col1] ignored case
##   dropped row names
##   [col1] ignored case
## 
## $`4`
## TRUE
##   sorted
##   renamed rows
##   dropped row names
## 

【讨论】:

  • 这是一个很酷的答案。但是我可以问一下这是否是从 df1 逐行比较?因为它看起来很像比较整个列。在我的情况下,它需要逐行或一定数量的逐行。它不能一次比较 df1 的整个列。我将其放入数据框中以便于比较。但实际上,这些值将在传入时进行比较。我的目标是在不比较整个 df1 列的情况下找到匹配项。
  • @Nix,抱歉,您的评论不是很清楚。你会使用data.frame吗?
  • 将使用 data.frame。但这只是为了证明理论。在最终产品中,来自 df1(测试集)的集合将无法完整用于比较。测试集将一次填充一个元素。所以我想在填充“苹果”后立即开始搜索以缩小匹配集。随着“橙子”的出现,它的范围将进一步缩小。我能解释的最简单的方法是自动更正功能。您输入的字母越多,就越容易预测单词。
【解决方案2】:

使用基础 R:

split(df2,df2[,1])[by(df2[2],df2[1],function(x)all(x==df1))]
 $`1`
   setID    col1
 1     1  apples
 2     1 oranges
 3     1  apples
 4     1  banana

【讨论】:

    【解决方案3】:

    OP 已请求在df2 中查找setID 组,其中col1 中的值与df2 中的值完全相同

    为了完整起见,这里也是data.table的做法:

    library(data.table)
    tmp <- setDT(df2)[, all(col1 == df1$col1), by = setID][(V1)]
    tmp
    
       setID   V1
    1:     1 TRUE
    

    现在,OP 已请求返回匹配的行。这可以通过查找 setID 的匹配值来完成

    df2[setID %in% tmp$setID]
    
       setID    col1
    1:     1  apples
    2:     1 oranges
    3:     1  apples
    4:     1  banana
    

    或通过加入(可能在大型表上可能更快)

    df2[tmp, on = "setID", .SD]
    

    返回相同的结果。

    警告

    OP 提供的样本数据集表明df1 中的行数与df2 中每个setID 组中的行数相同。如果行数不同,OP未指定预期结果。

    【讨论】:

    • 我的错。我应该提到行数是否匹配。假设它没有。这就是为什么您完全匹配所有值的方法不起作用的原因。我已经更新了我的数据集以更好地解释。
    猜你喜欢
    • 2013-07-12
    • 2018-01-07
    • 2021-03-19
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    • 2023-03-25
    • 2012-12-18
    • 2014-12-16
    相关资源
    最近更新 更多