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

Apply function over every two consecutive list elements

我想知道是否有更有效的方法对列表的每两个连续元素应用一个函数。 got me thinking, and the solution I posted for the user came from here.

我认为使用 Map/mapplysomelist[-length(somelist)]somelist[-1] 作为 function(x, y) 调用的参数就足够了,但是有没有直接的方法,也许在 bigger/newer 软件包之一中实施?

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

我有一个包含三个矩阵的列表:

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])
}

但据我了解 sapply/lapply 等通过使用 C 代码获得了优于 for 循环的性能优势,因此 xapply 上面的功能只是为了方便,而不是提高性能。

应用函数不一定比循环快。有时他们不是。

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

1) 矩阵 rollsum 将对 windows 执行求和。它不适用于列表,但如果您编写与矩阵之间的转换函数,它会起作用:

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 这是对 Reduce:

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

3) array 另一种方法是使用 3d 数组而不是列表。这导致使用 aaplyrollsum 的紧凑单行。

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

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 来做到这一点 3d 数组。也就是说,这与上面的 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. 这是一种略有不同的方法,尽管我不太喜欢它。

     purr::map(seq_along(matlist)[-length(matlist)], 
           ~ reduce(list(matlist[[.]], matlist[[.+1]]), `+`))
    
  2. 这里有一个带烟斗的变体,我觉得更好,只是因为我喜欢烟斗。

    matlist %>% 
       list(a = .[-length(.)], b = .[-1]) %>%
       .[-1] %>% 
       pmap( ~ .x + .y)
    
  3. 不幸的是,就像原来的 Map 答案一样,它给出了一个包含错误名称的列表。要摆脱您必须做的错误名称:

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

我认为去掉这些名称是值得的,因为它们具有误导性。