在每两个连续的列表元素上应用函数

时间:2018-02-05 13:07:51

标签: r mapply

我想知道是否有更有效的方法在列表的每两个连续元素上应用函数。 This question让我思考,我为用户发布的解决方案来自here

我认为使用Map/mapply somelist[-length(somelist)]somelist[-1]作为function(x, y)调用的参数是足够好的,但是有没有直接的方法,可能在一个更大/更新的包?

考虑这个例子(从上述问题中偷来的):

我有三个矩阵列表:

set.seed(1)
matlist <- list(M1 = matrix(sample(1:10, 4, replace = T), nrow = 2, ncol = 2),
                M2 = matrix(sample(1:10, 4, replace = T), nrow = 2, ncol = 2),
                M3 = matrix(sample(1:10, 4, replace = T), nrow = 2, ncol = 2)
                )

现在我要计算M1+M2M2+M3。提到的方法是

Map(`+`, matlist[-3], matlist[-1])

[[1]]
     [,1] [,2]
[1,]    5   11
[2,]   11   15

[[2]]
     [,1] [,2]
[1,]   12   13
[2,]    5   18

我要提供的apply系列的任何变体

xapply(matlist, `+`)

我知道我可以把自己写成一个小帮手,比如

xapply <- function(x, FUN){
  Map(FUN, x[-length(x)], x[-1])
}

但据我所知this classicsapply/lapply等利用for代码获得了优于C循环的性能优势,因此xapply上面的功能只是方便,而不是提升性能。

2 个答案:

答案 0 :(得分:2)

apply函数比循环更快是不正确的。有时他们不是。

如果想法不是使用索引而是使用整个对象方法,那么这里有一些方法,尽管问题中的Map方法似乎更好。

1)矩阵 rollsum将对窗口执行求和。它不适用于列表,但如果您在矩阵中编写转换函数,它将起作用:

library(magrittr)
library(zoo)

# convert between list and matrix where each row of matrix is one list component
list2mat <- function(x) t(sapply(x, c))
mat2list <- function(x, n) lapply(1:nrow(x), function(i) matrix(x[i, ], n))

nr <- nrow(matlist[[1]])
matlist %>% list2mat %>% rollsum(2) %>% mat2list(nr)

2)减少这是Reduce的尝试:

ans <- list()
invisible(Reduce(function(x, y) { ans <<- c(ans, list(x + y)); y }, matlist))

3)数组另一种方法是使用3d数组而不是列表。这样就可以使用aaplyrollsum来生成紧凑的单线程。

我们首先使用simplify2array将列表转换为3d数组,然后在该框架内使用数组a

library(plyr)
library(zoo)

a <- simplify2array(matlist) # convert matlist to array

aa <- aaply(a, 1:2, rollsum, 2)

# check
bb <- simplify2array(Map("+", matlist[-1], matlist[-3]))
identical(unname(aa), unname(bb))
## [1] TRUE

aaply基本上是一个幂等的应用,如果我们愿意置换(即广义转置),我们可以用普通的apply做到这一点 三维数组。这与上面的aaply行相同:

library(magrittr)
aa <- a %<% apply(1:2, rollsum, 2) %>% aperm(c(2, 3, 1))

这也有效:

aa <- a %>% aperm(3:1) %>% apply(2:3, rollsum, 2) %>% aperm(3:1)

答案 1 :(得分:0)

  1. 这是一种稍微不同的方法,尽管我不太喜欢。

    select distinct tmp.UserName from 
    (
      select distinct user_from as UserName from YourTable
      union 
      select distinct user_To as UserName from YourTable
    ) as tmp;
    
  2. 这是管道的一种变体,我认为更好,因为我喜欢管道。

     purr::map(seq_along(matlist)[-length(matlist)], 
           ~ reduce(list(matlist[[.]], matlist[[.+1]]), `+`))
    
  3. 不幸的是,它像原始的Map答案一样,给出了带有错误名称的列表。要摆脱错误的名称,您必须执行以下操作:

    matlist %>% 
       list(a = .[-length(.)], b = .[-1]) %>%
       .[-1] %>% 
       pmap( ~ .x + .y)
    

我认为删除名称是值得的,因为它们会误导人。