【问题标题】:How do I tag rows with two variables that match rows in a second data frame? R如何使用与第二个数据框中的行匹配的两个变量来标记行? R
【发布时间】:2014-09-08 15:50:55
【问题描述】:

我有两个大型数据集 df1 和 df2。 df2 是 df1 的子集。我想将 df2 中存在的 df1 行标记为“是”,而将其他行标记为“否”。行不包含任何唯一变量值。我想匹配多个变量的值。但是,我使用 %in% 运算符得到了不正确的结果。我还尝试了一个嵌套的 for 循环,该循环遍历 df1 的每一行并将其与 df2 的每一行进行比较。它会产生正确的结果,但由于我的数据集的大小,它需要几天才能完成。

请看下面的例子:

df1 <- data.frame( Col1 = c( "d1", "d2", "d3", "d1"), 
               Col2 = c( "n1", "n2", "n1", "n2" ))
df2 <- data.frame( Col1 = c( "d1", "d2" ), Col2 = c( "n1", "n2" ))
df1$Res <- "No"
df1.len <- nrow( df1 )

for ( i in 1:df1.len ){
  if ( df1$Col1[i] %in% df2$Col1 & 
     df1$Col2[i] %in% df2$Col2 ){
  df1$Res[i] <- "Yes"
  }
}
df1
df2

输出:

df1 Col1 Col2 分辨率
1 d1 n1 是
2 d2 n2 是
3 d3 n1 否
4 d1 n2 是

df2
Col1 Col2
1 d1 n1
2 d2 n2

如您所见,df2 中不存在 df1 的第 4 行,但仍标记为“是”。任何帮助将不胜感激。我过去几天一直在看 SO 和其他论坛,但没有运气。谢谢。

【问题讨论】:

  • 应该很容易适应 here 的内容:“识别数据框 A 中不包含在数据框 B 中的记录”

标签: r


【解决方案1】:

这是一个使用 dplyr 的答案,它比基础 R 快得多。首先为每一行创建一个唯一 ID,然后根据比较 ID 的逻辑向量创建一个新变量。

require(dplyr)

df1 <- data.frame( Col1 = c( "d1", "d2", "d3", "d1"), 
               Col2 = c( "n1", "n2", "n1", "n2" ))
df2 <- data.frame( Col1 = c( "d1", "d2" ), Col2 = c( "n1", "n2" ))

df2 <- mutate(df2, Col3 = paste0(Col1, Col2))
df1 <- mutate(df1, 
  Col3 = paste0(Col1, Col2),
  Res = ifelse(Col3 %in% df2$Col3, "Yes", "No")
  )

使用 duplicated()(如 Ananda 的回答)在语法上很简单,但可能会很慢,具体取决于您的数据框有多少列。

【讨论】:

    【解决方案2】:

    我会这样做:

    duplicated(rbind(df2, df1))[-sequence(nrow(df2))]  ## Might not be reliable...
    # [1]  TRUE  TRUE FALSE FALSE
    

    或其中之一:

    interaction(df1) %in% interaction(df2)
    do.call(paste, df1) %in% do.call(paste, df2)
    

    如果需要,您可以轻松地将TRUE 更改为“是”,将FALSE 更改为“否”,但保留逻辑值可能更有用。

    【讨论】:

    • (现在我看到 Henrik 的副本几乎完全有这些选项。)
    • 如果 df1 有重复的行,但在 df2 中没有,你的第一个答案会给出错误的结果,对吗?
    • @MatthewPlourde,是的,我想是的。好电话。
    【解决方案3】:

    这提供了正确的答案,但如果您在 df1 和 df2 中的列具有不同的名称,则需要进行调整:

    df1 <- data.frame(Col1 = c("d1", "d2", "d3", "d1"), 
                      Col2 = c("n1", "n2", "n1", "n2"))
    df1.len <- nrow(df1)
    df1 <- cbind(df1, unique=seq(1:df1.len))
    
    df2 <- data.frame(Col1 = c("d1","d2"), Col2 = c("n1","n2"))
    df1$Res <- "No"
    
    match <- merge(df1,df2,by=c("Col1","Col2"))
    
    for (i in 1:df1.len){
    if (df1$unique[i] %in% match$unique) {
        df1$Res[i] <- "Yes"
      }
    }
    

    【讨论】:

    • 唐,谢谢您的回复。我的 df1 几乎有 100 万行,for 循环与 if 条件的组合需要很长时间。但是,我将创建唯一值的想法与 ifelse(它是一个矢量化函数,不需要 for 循环)结合起来以获得所需的结果。谢谢。
    【解决方案4】:

    另一种选择是使用split,例如,

    df1 <- data.frame(a=1:10, b=letters[1:10])
    df2 <- df1[1:5,]
    df1$in.df2 <- split(df1, rownames(df1)) %in% split(df2, rownames(df2))
    #     a b in.df2
    # 1   1 a   TRUE
    # 2   2 b   TRUE
    # 3   3 c   TRUE
    # 4   4 d   TRUE
    # 5   5 e   TRUE
    # 6   6 f  FALSE
    # 7   7 g  FALSE
    # 8   8 h  FALSE
    # 9   9 i  FALSE
    

    【讨论】:

    • 感谢您的回答。除非我遗漏了什么,否则这将不起作用,因为 df1 和 df2 中没有行名。我想为 df2 中的两个变量测试 df1 行。
    • 必须有行名。或者,您可以将 rownames(df) 替换为 seq(nrow(df))
    【解决方案5】:

    我的回答与 Ananda 的非常相似,只是不那么时髦,但如果您是 R 新手,可能会更有意义,从您的问题来看,我猜您是。

    df1 <- data.frame( Col1 = c( "d1", "d2", "d3", "d1"), 
                   Col2 = c( "n1", "n2", "n1", "n2" ))
    df2 <- data.frame( Col1 = c( "d1", "d2" ), Col2 = c( "n1", "n2" ))
    
    df1$col3 <- paste0(df1$Col1,df1$Col2) # create a composite of the row
    df2$col3 <- paste0(df2$Col1,df2$Col2) # repeat
    
    df1$col3 %in% df2$col3
    # [1]  TRUE  TRUE FALSE FALSE
    

    您不需要将 col3 附加到您正在使用的数据帧中,这正是我所做的。

    【讨论】:

    • 感谢您的解决方案。它非常有效。你在两个方面都是对的:我是 R 的新手,你的回答很有意义。我没有将粘贴的变量附加到 dfs。然后我使用 ifelse() 函数使用 %in% 条件的逻辑输出将 Yes/No 添加到 df1。我将很快上传我生成的代码。谢谢。
    猜你喜欢
    • 2013-09-08
    • 2022-01-07
    • 2011-06-20
    • 1970-01-01
    • 2019-08-06
    • 2018-11-12
    • 1970-01-01
    • 2014-03-16
    • 1970-01-01
    相关资源
    最近更新 更多