检查数据帧的每一行是否包含在另一个数据帧中

时间:2014-03-26 21:21:18

标签: r dataframe

我写了以下函数,它有效。但是,如果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)的逻辑向量。

感谢您的帮助。

4 个答案:

答案 0 :(得分:11)

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

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

答案 1 :(得分:6)

尝试:

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

它会告诉您df1df2中出现了哪些行号。如果你想要一个逻辑的原子向量,如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

答案 2 :(得分:1)

只是想对这个问题给出两分钱。基于plyr的解决方案:

nrow(match_df(df2, df1))

..将针对df2(相对于所有列)检查df1的每一行,并给出df2中包含的df1的行数。

答案 3 :(得分:0)

基于 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