【问题标题】:Apply function over every two consecutive list elements对每两个连续的列表元素应用函数
【发布时间】:2018-02-05 13:07:51
【问题描述】:

我想知道是否有一种更有效的方法可以对列表的每两个连续元素应用一个函数。 This question引起了我的思考,我为用户发布的解决方案来自here

我认为使用Map/mapplysomelist[-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 等通过使用C 代码获得了优于for 循环的性能优势,因此上面的xapply 函数只是方便,不会提高性能.

【问题讨论】:

  • 你创建的函数有什么问题。正确编写 forsapply/lapply 不会有太大的性能优势
  • 它本身没有问题,我只是想知道这种循环 xx+1 的方式是否已经以任何优化的方式实现了。
  • 请注意Map 已经属于apply 家族,只需调用mapply
  • 是的,我只是使用Map 来避免默认的SIMPLIFY = TRUE

标签: r mapply


【解决方案1】:

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 做到这一点 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)

【讨论】:

  • 感谢您的努力,这是对可能解决方案的一个很好的概述!
【解决方案2】:
  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. 不幸的是,就像原始地图答案一样,它给出了一个包含错误名称的列表。要摆脱错误名称,您必须这样做:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 2014-09-24
    相关资源
    最近更新 更多