将循环替换为“apply”系列的一个功能

时间:2018-01-14 12:35:14

标签: r loops apply

我在列表中有data.frames,通常,当我想要居中数据时,我使用循环(如下例所示)。我想使用“apply”系列的一些功能,但我无法弄清楚如何编写代码。

我的数据示例:

env <- list (data.frame(a=c(-1.08, -1.07, -1.07),
                        b=c( 4.61,  4.59,  4.59),
                        c=c( 3.46,  3.56,  3.52)),
             data.frame(a=c( 3.93,  3.94,  3.92),
                        b=c(-6.69, -6.72, -6.68),
                        c=c( 3.04,  3.08,  3.03)))

我将使用的值将它们置于中心位置:

d <- c(a=10.20, b=-10.91, c=11.89)

我常用的循环类型:

for(i in 1:length(env)) {
    env[[i]][, 1] <- env[[i]][, 1] - d[1]
    env[[i]][, 2] <- env[[i]][, 2] - d[2]
    env[[i]][, 3] <- env[[i]][, 3] - d[3]
}

有没有办法使用“apply”系列的函数来完成我在上面循环中做的同样的事情?

6 个答案:

答案 0 :(得分:8)

这里有两点可以简化:循环遍历列表元素并分别减去d中的每个值。

要替换for循环,您可以使用lapply(“l”,因为我们在列表上进行迭代)。

# Run function for every element i in list env
lapply(env, function(i))

为了简化减法,你可以:

  1. 转置数据框t(i)
  2. 执行减法t(i) - d
  3. 将其转置回t(t(i) - d)
  4. 所以最终的代码是:

    lapply(env, function(i) t(t(i) - d))
    

答案 1 :(得分:3)

1)扫描使用sweep生成数据框列表:

lapply(env, sweep, 2, d, "-")

,并提供:

[[1]]
       a     b     c
1 -11.28 15.52 -8.43
2 -11.27 15.50 -8.33
3 -11.27 15.50 -8.37

[[2]]
      a    b     c
1 -6.27 4.22 -8.85
2 -6.26 4.19 -8.81
3 -6.28 4.23 -8.86

另请参阅How to divide each row of a matrix by elements of a vector in R了解与sweep等效或几乎等效的多个表达式。

2)缩放或像这样使用scale;但是,它给出了一个数字矩阵列表而不是数据帧列表:

lapply(env, scale, d, FALSE)

,并提供:

[[1]]
          a     b     c
[1,] -11.28 15.52 -8.43
[2,] -11.27 15.50 -8.33
[3,] -11.27 15.50 -8.37
attr(,"scaled:center")
     a      b      c 
 10.20 -10.91  11.89 

[[2]]
         a    b     c
[1,] -6.27 4.22 -8.85
[2,] -6.26 4.19 -8.81
[3,] -6.28 4.23 -8.86
attr(,"scaled:center")
     a      b      c 
 10.20 -10.91  11.89 

答案 2 :(得分:1)

以下是使用lapply

的黑客解决方案
nrows <- 3
lapply(env, function(x) x - matrix(rep(d, nrows), nrow = nrows, byrow = TRUE))

答案 3 :(得分:0)

我的PoGibas' answer(+1)版本:

lapply(lapply(lapply(env, t), "-", d), t)

它完全相同:

  • 转置data.frame个对象
  • 使用回收规则减去d
  • 将它们转移回原来的位置
  • 返回matrix个对象,这不是OP想要的。

我认为它更彻底地使用矢量化它会更快一点。但情况并非如此。

microbenchmark(
  f1 = lapply(env, function(i) t(t(i) - d)),
  f2 = lapply(lapply(lapply(env, t), "-", d), t), times = 1E5L)
#Unit: microseconds
# expr     min      lq     mean  median      uq        max neval cld
#   f1  99.838 103.104 114.8280 104.970 108.702 106230.106 1e+05  a 
#   f2 103.570 107.303 118.9683 110.102 113.834   7765.414 1e+05   b

答案 4 :(得分:0)

非常感谢您快速而有趣的答案。

我在microbenchmark :: microbenchmark函数中运行了你发布的所有解决方案。

对于生成矩阵列表的解决方案,我(仅使用我当前的R知识)添加了一条额外的行,将它们转换为数据帧列表。

env1 <- env
env2 <- env
env3 <- env
env4 <- env
env5 <- env
env6 <- env
env7 <- env

## install.packages
library("microbenchmark")
microbenchmark(
## 1; the original.
for(i in 1:length(env1)) {
    env1[[i]][, 1] <- env1[[i]][, 1] - d[1]
    env1[[i]][, 2] <- env1[[i]][, 2] - d[2]
    env1[[i]][, 3] <- env1[[i]][, 3] - d[3]}
,

## 2
for(i in 1:length(env2)) {
    for (j in 1:length(env2[[i]])) {
        env2[[i]][, j] <- env2[[i]][, j] - d[j]
    }
}
,

## 3
{env3 <- lapply(env3, function(i) t(t(i) - d))
env3 <- lapply(env3, function(i) as.data.frame(i))}
,

## 4
{env4 <- lapply(env4, scale, center=d, scale=FALSE)
env4 <- lapply(env4, function(i) as.data.frame(i))}
,

## 5
{nrows <- 3
env5 <- lapply(env5, function(x) x - matrix(rep(d, nrows), nrow = 
nrows, byrow = TRUE))}
,

## 6
env6 <- lapply(env6, sweep, 2, d, "-")
,

## 7
{env7 <- lapply(lapply(lapply(env7, t), "-", d), t)
env7 <- lapply(env7, function(i) as.data.frame(i))}
)

## install.packages("compare")
library("compare")
identical(env1, env2)
identical(env1, env3)
identical(env1, env4)
identical(env1, env5)
identical(env1, env6)
identical(env1, env7)

正如您将看到的,所有线条都会生成相同的对象。

执行“microbenchmark”功能5次后,上述代码中的## 7解决方案速度更快,但解决方案## 3只是稍慢一点。

我将详细研究您提出的每个解决方案,并再次非常感谢您!

为表示感谢,请欣赏我喜欢的这首歌! https://www.youtube.com/watch?v=QnguI5OrfZ4

问候!

答案 5 :(得分:0)

您还可以使用map函数来完成相同的操作。具体来说,您可以使用map()循环列表env,然后map2()循环(并发)d以及各个数据框env[[1]]和{ {1}}。 env[[2]]是数据集中的地方。

j-k

屈服,

library('purrr')
map(env, function(i){
  map2(i, d, function(j,k){
    j-k
  })
})