矢量化行向量与R中数据帧的每一行的比较?

时间:2013-11-30 02:43:42

标签: r vector

假设我有一个数据框来自阅读以下文件Foo.csv

A,B,C
1,2,3
2,2,4
1,7,3

我想计算第一行和后续行之间匹配元素的数量。例如,第一行在一个位置与第二行匹配,并在两个位置与第三行匹配。以下是一些可以达到预期效果的代码。

foo = read.csv("Foo.csv")                      

numDiffs = rep(0,dim(foo)[1])                  
for (i in 2:dim(foo)[1]) {                     
   numDiffs[i] = sum(foo[i,] == foo[1,])       
}                                              
print(numDiffs)                                

我的问题是,可以通过矢量化来消除循环并可能缩短运行时间吗?我的第一次尝试是在下面,但它留下了错误,因为没有为此类比较定义==

colSums(foo == foo[1,])

4 个答案:

答案 0 :(得分:4)

> rowSums(sapply(foo, function(x) c(0,x[1] == x[2:nrow(foo)])))
[1] 0 1 2

答案 1 :(得分:4)

随着您的数据集变得越来越大,您可以通过以下方式获得更快的速度:

as.vector(c(0, rowSums(foo[rep(1, nrow(foo) - 1), ] == foo[-1, ])))
# [1] 0 1 2

基本思想是创建第一行的data.frame,使整个数据集的相同维度减去一行,并使用它来检查与剩余行的等效性。


删除我的原始更新,这里有一些基准测试。更改“N”以查看对不同data.frame尺寸的效果。来自@nacnudus的解决方案最佳。

set.seed(1)
N <- 10000000
mydf <- data.frame(matrix(sample(10, N, replace = TRUE), ncol = 10))
dim(mydf)
# [1] 1000000      10

fun1 <- function(data) rowSums(sapply(data, function(x) c(0,x[1] == x[2:nrow(data)])))
fun2 <- function(data) as.vector(c(0, rowSums(data[rep(1, nrow(data) - 1), ] == data[-1, ])))
fun3 <- function(data) {
  bar <- as.matrix(data)
  c(0, rowSums(t(t(bar[-1, ]) == bar[1, ])))
}

library(microbenchmark)

## On your original sample data
microbenchmark(fun1(foo), fun2(foo), fun3(foo))
# Unit: microseconds
#       expr     min       lq   median       uq     max neval
#  fun1(foo) 109.903 119.0975 122.5185 127.0085 228.785   100
#  fun2(foo) 333.984 354.5110 367.1260 375.0370 486.650   100
#  fun3(foo) 233.490 250.8090 264.7070 269.8390 518.295   100

## On the sample data created above--I don't want to run this 100 times!
system.time(fun1(mydf))
#    user  system elapsed 
#   15.53    0.06   15.60
system.time(fun2(mydf))
#    user  system elapsed 
#    2.05    0.01    2.06 
system.time(fun3(mydf))
#    user  system elapsed 
#    0.32    0.00    0.33 

HOWEVER ,如果Codoremifa要将代码更改为vapply而不是sapply,则该答案会获胜!从15秒到100万行的0.24秒。

fun4 <- function(data) {
  rowSums(vapply(data, function(x) c(0, x[1] == x[2:nrow(data)]), 
                 vector("numeric", length=nrow(data))))
} 

microbenchmark(fun3(mydf), fun4(mydf), times = 20)
# Unit: milliseconds
#        expr      min       lq   median       uq      max neval
#  fun3(mydf) 369.5957 422.9507 438.8742 462.6958 486.3757    20
#  fun4(mydf) 238.1093 316.9685 323.0659 328.0969 341.5154    20

答案 2 :(得分:4)

或使用矩阵比较的自动回收:

bar <- as.matrix(foo)
c(0, rowSums(t(t(bar[-1, ]) == bar[1, ])))
# [1] 0 1 2

t()有两次因为回收是列而不是行。

答案 3 :(得分:4)

呃,我不明白为什么你不能这样做..

c(foo[1,]) == foo
#         A     B     C
#[1,]  TRUE  TRUE  TRUE
#[2,] FALSE  TRUE FALSE
#[3,]  TRUE FALSE  TRUE

..甚至更好foo[1,,drop=TRUE] == foo ...

因此结果变成......

rowSums( c( foo[1,] ) == foo[-1,] )
#[1] 3 1 2

请记住,f[1,]仍然是data.frame。强制转换为向量,并根据您的需要定义==。这似乎比vapply在大数据框架上建议@AnandaMahto的答案要快一点。

基准

将此与@ AnandaMahto上面的答案中的fun3fun4进行比较,我发现在使用较大的data.frame,my.df时,速度有了很小的提升......

microbenchmark(fun3(mydf), fun4(mydf), fun6(mydf) , times = 20)
#Unit: milliseconds
#       expr      min       lq   median       uq      max neval
# fun3(mydf) 320.7485 344.9249 356.1657 365.7576 399.5334    20
# fun4(mydf) 299.6660 313.7105 319.1700 327.8196 555.4625    20
# fun6(mydf) 196.8244 241.4866 252.6311 258.8501 262.7968    20

fun6定义为......

fun6 <- function(data) rowSums( c( data[1,] ) == data )