关于避免R中的for循环的建议

时间:2015-02-06 15:22:03

标签: r apply

我正在尝试避免使用for()循环来解决我的问题。让我们说我有两个向量,为了简单起见:x1 <- c(1,10,30)x2 <- c(11,31,40)。这些向量包含指向我df中某些区间的参考点,每个区间都有变量,在这种情况下,每个变量有40个观察值。所以:
df(x1[1]:x2[1])将成为前十个观察结果。 df(x1[2]:x2[2])将是接下来的20个观察,最后一个(30,40)代表最后10个。我想计算多个统计数据,例如meanstdvariance ,对于每个间隔。 for() - 循环可以解决问题,但速度非常慢。我正在查看apply函数,但我似乎无法弄明白。 mean(df[x1:x2])也没有做到这一点,因为它只取x1x2的第一个值。

有什么建议吗?

- tstev

4 个答案:

答案 0 :(得分:2)

我倾向于在data.frame的行上使用apply(因为任何错误的步骤都会将所有内容转换为字符类)。我必须做一些与您在其他代码中提出的问题非常相似的内容,并且我选择了mapply

它有&#34;某事&#34;使用2个(或更多)向量/列表的第一个元素,然后执行相同的&#34;某些事情&#34;与相同的向量/列表的第二个元素等。&#34;东西&#34;当然是由第一个参数定义的 - 一个函数,类似于其他*apply函数。

set.seed(42)
x1 <- c(1,10,30)
x2 <- c(11,31,40)
df <- as.data.frame(sample(40))
ret <- mapply(function(a,b) df[a:b,], x1, x2)
ret
## [[1]]
##  [1] 37 40 11 31 24 19 26  5 22 32 14
## [[2]]
##  [1] 32 14 21 27  7 13 36 25  3 38 12 35 23 18 17  2  8  6 29 30 10 15
## [[3]]
##  [1] 10 15 39  4 33  1 28 34  9 16 20

从这里开始,应用您想要的任何其他统计摘要是微不足道的:

sapply(ret, function(x) c(mean=mean(x), sd=sd(x)))
##          [,1]     [,2]     [,3]
## mean 23.72727 19.13636 19.00000
## sd   10.95528 11.14107 12.87633

(或者您可以随时扩展mapply调用以直接调用这些其他函数。)

编辑#1

根据@docendo discimus的建议,MapmapplySIMPLIFY=FALSE)稍快一些。为了比较:

set.seed(3)
x1 <- c(1,11,31)
x2 <- c(10,30,40)
df1 <- data.frame(V1 = sample(40))
df2 <- df1[,,drop = FALSE]
df3 <- df1[,,drop = FALSE]
grp <- rep(seq_along(x1), (x2-x1) + 1L)
df2 <- cbind(df2, grp)

library(data.table)
library(dplyr)
library(microbenchmark)

microbenchmark(dt=setDT(df1)[, list(mean(V1), sd(V1), var(V1)), by = grp],
               dplyr=df2 %>% group_by(grp) %>% summarise_each(funs(mean, sd, var)),
               mapplyT=mapply(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2, SIMPLIFY=TRUE),
               mapplyF=mapply(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2, SIMPLIFY=FALSE),
               Map=Map(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2))
## Unit: microseconds
##     expr      min        lq      mean    median        uq      max neval
##       dt  925.964 1006.9570 1176.5629 1081.4810 1184.7870 2582.434   100
##    dplyr 1843.449 1967.0590 2154.9829 2042.2515 2185.2745 3839.960   100
##  mapplyT  208.398  237.8500  272.8850  260.8315  286.2685  511.846   100
##  mapplyF  187.424  208.6205  237.6805  225.1320  247.2215  445.801   100
##      Map  191.441  215.7610  240.9025  231.6025  258.6005  441.785   100

我明确提供了data.frame的深层副本,因为setDT修改了data.frame(其效率),但是mapplyMap无法应对data.table。 (我将meansdvar添加到我的mapply来电中,以便将苹果与苹果进行比较。)

编辑#2

之前的基准测试看起来令人印象深刻且具有决定性,但并未描述呼叫的开销与大数据引擎的效率。这是另一个有更多数据的事情。

当各个子集相当大时 - 即更少&#34;块&#34;来自源data.frame - 性能趋于平衡。在这里,我用k控制块大小:

n <- 4000
k <- 100
x1 <- c(1, sort(sample(n, size = n/k - 1)))
x2 <- c(x1[-1] - 1, n)
df1 <- data.frame(V1 = sample(n))
df2 <- df1[,,drop = FALSE]
df3 <- df1[,,drop = FALSE]
grp <- rep(seq_along(x1), (x2-x1) + 1L)
df2 <- cbind(df2, grp)

microbenchmark(dt=setDT(df1)[, list(mean(V1), sd(V1), var(V1)), by = grp],
               dplyr=df2 %>% group_by(grp) %>% summarise_each(funs(mean, sd, var)),
               mapplyT=mapply(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2, SIMPLIFY=TRUE),
               mapplyF=mapply(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2, SIMPLIFY=FALSE),
               Map=Map(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2))
## Unit: milliseconds
##     expr      min       lq     mean   median       uq      max neval
##       dt 2.133063 2.297282 2.549046 2.435618 2.655842 4.305396   100
##    dplyr 2.145558 2.401482 2.643981 2.552090 2.720102 4.374118   100
##  mapplyT 2.599392 2.775883 3.135473 2.926045 3.156978 5.430832   100
##  mapplyF 2.498540 2.738398 3.079050 2.882535 3.094057 7.041340   100
##      Map 2.624382 2.725680 3.158272 2.894808 3.184869 6.533956   100

但是,如果块大小减少,那么性能已经很好的dplyr会提前出现:

n <- 4000
k <- 10
x1 <- c(1, sort(sample(n, size = n/k - 1)))
x2 <- c(x1[-1] - 1, n)
df1 <- data.frame(V1 = sample(n))
df2 <- df1[,,drop = FALSE]
df3 <- df1[,,drop = FALSE]
grp <- rep(seq_along(x1), (x2-x1) + 1L)
df2 <- cbind(df2, grp)

microbenchmark(dt=setDT(df1)[, list(mean(V1), sd(V1), var(V1)), by = grp],
               dplyr=df2 %>% group_by(grp) %>% summarise_each(funs(mean, sd, var)),
               mapplyT=mapply(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2, SIMPLIFY=TRUE),
               mapplyF=mapply(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2, SIMPLIFY=FALSE),
               Map=Map(function(a,b) { x <- df3[a:b,]; c(mean(x), sd(x), var(x)); }, x1, x2))
## Unit: milliseconds
##     expr       min       lq      mean    median        uq       max neval
##       dt 11.494443 12.45187 14.163123 13.716532 14.655883 62.424668   100
##    dplyr  2.729696  3.05501  3.286876  3.148276  3.324098  4.832414   100
##  mapplyT 25.195579 27.67426 28.488846 28.319758 29.247729 32.897811   100
##  mapplyF 25.455742 27.42816 28.713237 28.038622 28.958785 76.587224   100
##      Map 25.184870 27.32730 28.737281 28.198155 28.768237 77.830470   100

如果您注意到,dplyr对于较小的数据集所花费的时间与较大的数据集大致相同。好的。

有三种谎言:谎言,该死的谎言和统计数据。(Benjamin Disraeli)这同样适用于基准测试。

答案 1 :(得分:1)

使用Map来自each包的有用plyr的好机会:

library(plyr)

Map(function(u,v) each(mean, sd, var)(df[u:v,1]), x1, x2)

#[[1]]
#    mean        sd       var
#17.90000  10.15929 103.21111  

#[[2]]
#    mean        sd       var
#19.14286  12.18313 148.42857

#[[3]]
#    mean        sd       var 
#24.81818  10.78720 116.36364

数据:

x1 <- c(1,10,30)
x2 <- c(10,30,40)
set.seed(3)
df <- as.data.frame(sample(40))

答案 2 :(得分:1)

以下是您问题的解决方案:

x1 <- c(1,10,30)
x2 <- c(10,30,40)

df <- as.data.frame(sample(40))
df2 <- data.frame(x1,x2)

apply(df2,1, function(x) mean(df[x[1]:x[2],]))

只需将mean()替换为sd()var()即可获得标准差或差异。如果na.rm=TRUE中缺少数据,请不要忘记df参数。

答案 3 :(得分:1)

也许代替for循环你可以使用两次申请?可以将所需的计算包装到函数中(在我的示例中为compute_mean),然后可以在x1x2的索引对上调用此函数。鉴于x1x2长度相同,使用lapply很容易

x1 <- c(1,10,30)
x2 <- c(10,30,40)
df <- as.data.frame(sample(40))

compute_mean <- function(df, ind1, ind2, i){
    result <- apply( df[c(ind1[i]:ind2[i]), , drop = F], 2, mean )
    return(result)
}

unlist(lapply(c(1:length(x1)), function(x){
    out <- compute_mean(df = df, ind1 = x1, ind2 = x2, i = x)
    return(out)
}))