【问题标题】:Vectorize comparison of a row vector with every row of a dataframe in R?将行向量与R中数据帧的每一行进行矢量化比较?
【发布时间】:2013-11-30 02:43:41
【问题描述】:

假设我有一个来自读取以下文件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 个答案之下,我添加了这个评论......只要确保 foo[1,] 是一个向量并且它会起作用,即。 c(foo[1,]) == foof[1,,drop=TRUE] == foo.

标签: r vector


【解决方案1】:
> rowSums(sapply(foo, function(x) c(0,x[1] == x[2:nrow(foo)])))
[1] 0 1 2

【讨论】:

    【解决方案2】:

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

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

    t() 存在两次,因为回收是按列而不是按行的。

    【讨论】:

    • +1。我本可以发誓我尝试过这样的事情,但我一定错过了另一个t()
    • 我从这个answer中捏了一把。
    【解决方案3】:

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

    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 
    

    但是,如果 Codoremifa 将他们的代码更改为 vapply 而不是 sapply,则该答案获胜!在 100 万行上从 15 秒缩短到 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
    

    【讨论】:

    • 你打败了我! +1
    • @nacnudus,查看我的更新。看来 Codoremifa 最终还是可以胜出的!
    • +1。这通常是我的懒惰,我倾向于忽略vapply。接受的 +15 代表全部属于您。
    【解决方案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。强制转换为向量和 == 是根据您的需要定义的。这似乎比@AnandaMahto 在大数据框上建议的@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 )
    

    【讨论】:

    • +1,虽然最好能完成这个解决方案(用rowSums 包装)并提供一个基准。
    • @Arun 完成。它似乎有点快,但没有我原来想象的快 2 倍(我还没有完成rowSums)。
    猜你喜欢
    • 1970-01-01
    • 2021-03-24
    • 2014-01-18
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    相关资源
    最近更新 更多