【问题标题】:Check if each row of a data frame is contained in another data frame检查数据帧的每一行是否包含在另一个数据帧中
【发布时间】:2014-03-26 21:21:17
【问题描述】:

我写了以下函数,它可以工作。但是,当df1 有 1700 行,df2 有 70000 行时,速度非常慢。有没有办法提高效率?

rowcheck <- function(df1, df2){
         apply(df1, 1, function(x) any(apply(df2, 1, function(y) all(y==x))))
}

我写这个函数的一个例子是:我想检查 df1 中的每一行是否包含在 df2 中:

df1=data.frame(a=c(1:3),b=c("a","b","c"))
df2=data.frame(a=c(1:6),b=rep(c("a","b","c"),2))

对于 df1 的每一行,我想检查它是否作为一行包含在 df2 中。我想将函数返回为长度为 nrow(df1) 的逻辑向量。

感谢您的帮助。

【问题讨论】:

  • 也许你可以试试all(y %in% x)?我建议您提供一个简单的示例来说明您的问题。 stackoverflow.com/questions/5963269/…
  • dplyr 包有一个 setdiff 方法用于 data.frame,您可能会对其进行调查。
  • 否则我会用 u
  • @droopy u%in%v 适用于一维 u 和每个元素也是一维的向量 v。但是,当 u 是向量时,比如 u=(x1,x2),并且 v 是 m x 2 维的矩阵或数据框,%in% 不能正常工作。我是否缺少 %in% 的内容?
  • 是的,因为我的解决方案正是 Richard 复制并粘贴到函数中的解决方案

标签: r dataframe


【解决方案1】:

一种方法是将行粘贴在一起,并将它们与%in% 进行比较。根据要求,结果是一个长度为nrow(df1) 的逻辑向量。

do.call(paste0, df1) %in% do.call(paste0, df2)
# [1] TRUE TRUE TRUE

【讨论】:

  • 感谢@RichardScriven。所以基本上你将列维度减少到 1,所以你可以使用 %in%。所以我猜 %in% 只适用于一维变量检查。
  • 这是一个完美而干净的解决方案!谢谢!
【解决方案2】:

试试:

Filter(function(x) x > 0, which(duplicated(rbind(df2, df1))) - nrow(df2))

它会告诉您df1 中的哪些行号出现在df2 中。如果您想要 Richard Scriven 的回答中的逻辑原子向量,请尝试

duplicated(rbind(df2, df1))[-seq_len(nrow(df2))]

它也更快,因为它使用内部 C 函数 duplicated(我的是 rowcheck2

> microbenchmark(rowcheck(df1, df2), rowcheck2(df1, df2))
 Unit: milliseconds
                expr      min       lq   median       uq       max neval
  rowcheck(df1, df2) 2.045210 2.169182 2.328296 3.539328 13.971517   100
  rowcheck2(df1, df2) 1.046207 1.112395 1.243390 1.727921  7.442499   100

【讨论】:

  • 如果我没记错的话,如果df1中有重复值,这不会失败吗? OP 似乎没有任何迹象表明 df1 不会有重复项。
【解决方案3】:

只是想在这个查询上给我两分钱。基于 plyr 的解决方案:

nrow(match_df(df2, df1))

.. 将对照 df2(相对于所有列)检查 df1 的每一行,并为您提供 df2 中包含的 df1 的行数。

【讨论】:

    【解决方案4】:

    基于merge的解决方案是:

    # simulate data
    options(stringsAsFactors = FALSE)
    set.seed(1)
    n1 <- 400L
    n2 <- 1000L
    df1 <- data.frame(a = sample.int(20L, n1, TRUE) ,
                      b = sample(letters, n1, TRUE))
    df2 <- data.frame(a = sample.int(20L, n2, TRUE),
                      b = sample(letters, n2, TRUE))
    df2 <- df2[!duplicated(df2), ]
    
    # the new function
    row_check_new <- function(x, y){
      # are there columns in x that are not in y or vice versa?
      if(length(union(colnames(x), colnames(y))) > length(colnames(x)))
        return(logical(NROW(x)))
      dum <- transform(x, row_id_dummy = 1:NROW(x))
      dum$row_id_dummy %in% merge(dum, y)$row_id_dummy
    }
    
    # it yields the same
    rowcheck <- function(df1, df2)
      apply(df1, 1, function(x) any(apply(df2, 1, function(y) all(y==x))))
    
    all.equal(rowcheck(df1, df2), row_check_new(df1, df2))
    #R> [1] TRUE
    
    # but is much faster
    bench::mark(old = rowcheck(df1, df2), new = row_check_new(df1, df2))
    #R> # A tibble: 2 x 13
    #R>   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
    #R>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
    #R> 1 old        322.56ms 327.26ms      3.06    11.4MB    18.3      2    12
    #R> 2 new          1.25ms   1.31ms    736.     222.8KB     6.00   368     3
    

    这适用于df1 中的重复项。 Rich Scriven 的解决方案更快。在某些极端情况下,基于merge 的解决方案更可取,因为 Rich Scriven 的解决方案会给出不正确的答案。例如,考虑以下带有整数的示例

    df1 <- data.frame(x1 = 11, x2 = 1)
    df2 <- data.frame(x1 = 1, x2 = 11)
    
    do.call(paste0, df1) %in% do.call(paste0, df2)
    #R> [1] TRUE
    rowcheck(df1, df2)
    #R> [1] FALSE
    row_check_new(df1, df2)
    #R> [1] FALSE
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-20
      • 2022-01-19
      • 1970-01-01
      • 2017-09-12
      • 1970-01-01
      • 2022-11-28
      • 2017-08-18
      • 1970-01-01
      相关资源
      最近更新 更多