假设我有一个数据框来自阅读以下文件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,])
答案 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上面的答案中的fun3
和fun4
进行比较,我发现在使用较大的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 )