R - 数据帧之间行的子集的有效比较

时间:2014-12-07 16:24:40

标签: r

谢谢你的帮助。

我需要检查另一个数据帧(df2)的行上数据帧(df1)每行元素的匹配总数。

数据帧具有不同的列数(例如,第一列中的5列与第二列中的列相比为6列)。行没有确切的形成规则(因此我无法通过组合分析找到这样做的方法)

此例程必须检查第一个数据帧中与第二个数据帧的所有行相关的所有行,从而产生按命中数出现的总数。

并非所有可能的金额都是有意义的。实际上我正在寻找一个特定的总数(我称之为#34;点击"在本文中)。

换句话说:每行df2大小"命中"的每一个子集的次数。可以在df1行中找到。

以下是一个例子:

> ### Example
> ### df1 and df2 here are regularly formed just for illustration purposes
>  
> require(combinat)
> 
> df1 <- as.data.frame(t(combn(6,5)))
> df2 <- as.data.frame(t(combn(7,6)))
> 
> df1
  V1 V2 V3 V4 V5
1  1  2  3  4  5
2  1  2  3  4  6
3  1  2  3  5  6
4  1  2  4  5  6
5  1  3  4  5  6
6  2  3  4  5  6
> 
> df2
  V1 V2 V3 V4 V5 V6
1  1  2  3  4  5  6
2  1  2  3  4  5  7
3  1  2  3  4  6  7
4  1  2  3  5  6  7
5  1  2  4  5  6  7
6  1  3  4  5  6  7
7  2  3  4  5  6  7
> 

在这个例子中,请注意,例如,df2的行#1中大小为5的子集可以在df1的行中找到6次。等等。

我试过这样的事情:

> ### Check how many times subsets of size "hits" from rows from df2 are found in rows of df1
> 
> myfn <- function(dfa,dfb,hits) {
+       sapply(c(1:dim(dfb)[1]),function(y) { sum(c(apply(dfa,1,function(x,i) { sum(x %in% dfb[i,]) },i=y))==hits) })
+   }
>   
> r1 <- myfn(df1,df2,5)
> 
> cbind(df2,"hits.eq.5" = r1)
  V1 V2 V3 V4 V5 V6 hits.eq.5
1  1  2  3  4  5  6         6
2  1  2  3  4  5  7         1
3  1  2  3  4  6  7         1
4  1  2  3  5  6  7         1
5  1  2  4  5  6  7         1
6  1  3  4  5  6  7         1
7  2  3  4  5  6  7         1

这似乎做了我需要的,但太慢了!我需要在大型数据帧(约200 K行)上使用此例程

我目前正在使用R 3.1.2 GUI 1.65 Mavericks build(6833)

任何人都可以提供更快或更聪明的方法吗?比你再次。

祝你好运, Vaccaro的

1 个答案:

答案 0 :(得分:1)

在数据帧上使用apply(...)非常低效。这是因为apply(...)将矩阵作为参数,因此如果传递数据帧,它将强制转换为矩阵。在您的示例中,每次调用apply(...)时都会将df1转换为矩阵,即nrow(df2)次。

此外,通过使用sapply(1:nrow(df2),...)dfb[i,],您正在使用数据框行索引,这也是非常低效的。最好先将所有内容转换为矩阵类,然后再使用apply(...)两次。

最后,没有理由使用对c(...)的调用。 apply(...)已经返回一个向量(在本例中),所以你只是招致另一个函数调用的开销而没有效果。

单独执行这些操作可将代码速度提高20倍。

set.seed(1)
nrows <- 100
df1 <- data.frame(matrix(sample(1:5,5*nrows,replace=TRUE),nc=5))
df2 <- data.frame(matrix(sample(1:6,6*nrows,replace=TRUE),nc=6))

myfn <- function(dfa,dfb,hits) {
  sapply(c(1:dim(dfb)[1]),function(y) { sum(c(apply(dfa,1,function(x,i) { sum(x %in% dfb[i,]) },i=y))==hits) })
}
myfn.2 <- function(dfa,dfb,hits) {
  ma <- as.matrix(dfa)
  mb <- as.matrix(dfb)
  apply(mb,1,function(y) { sum(apply(ma,1,function(x) { sum(x %in% y) })==hits) })
}

system.time(r1<-myfn(df1,df2,3))
#    user  system elapsed 
#    1.99    0.00    2.00 
system.time(r2<-myfn.2(df1,df2,3))
#    user  system elapsed 
#    0.09    0.00    0.10 
identical(r1,r2)
# [1] TRUE

还有另一种方法可以利用R在操作列表方面非常有效的事实。由于数据框只是一个向量列表,我们可以通过将行放入数据框列然后在其上使用sapply(..)来提高性能。这比myfn.2(...)更快,但只有约20%。

myfn.3 <-function(dfa,dfb,hits) {
  df1.t <- data.frame(t(dfa))   # rows into columns
  df2.t <- data.frame(t(dfb))
  sapply(df2.t,function(col2)sum(sapply(df1.t,function(col1)sum(col1 %in% col2)==hits)))
}
library(microbenchmark)
microbenchmark(myfn.2(df1,df2,5),myfn.3(df1,df2,5),times=10)
# Unit: milliseconds
#                 expr      min       lq   median       uq      max neval
#  myfn.2(df1, df2, 5) 92.84713 94.06418 96.41835 98.44738 99.88179    10
#  myfn.3(df1, df2, 5) 75.53468 77.44348 79.24123 82.28033 84.12457    10

如果你真的有一个55MM行的数据集,那么我认为你需要重新考虑这个问题。我不知道你想要完成什么,但这似乎是一种蛮力的方法。