在 R 中避免 for 循环的建议
Suggestions for avoiding for-loops in R
我试图避免使用 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 个。我想计算多个统计数据,包括 mean
、std
和 variance
,例如, 对于每个间隔。 for()
-loops 可以解决问题,但速度很慢。我正在查看 apply
函数,但我似乎无法弄明白。 mean(df[x1:x2])
也没有用,因为它只取 x1
和 x2
的第一个值。
有什么建议吗?
--tstev
将 Map
与 plyr
软件包中有用的 each
一起使用的好机会:
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))
这是您的问题的解决方案:
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()
即可得到标准差或方差。如果 df
.
中缺少数据,请不要忘记 na.rm=TRUE
参数
也许您可以使用 apply 两次来代替 for 循环?所需的计算可以包装到一个函数中(在我的示例中是 compute_mean
),然后可以在 x1
和 x2
的索引对上调用此函数。鉴于 x1
和 x2
的长度相同,使用 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)
}))
我倾向于反对在 data.frame 的行上使用 apply
(因为任何错误步骤都会将所有内容转换为字符 class)。我不得不做一些与您在其他代码中要求的非常相似的事情,我选择了 mapply
.
它对 2(或更多)vectors/lists 的第一个元素执行 "something",然后对相同 vectors/lists 的第二个元素执行相同的 "something",等等 "something" 当然是由第一个参数定义的——一个函数,类似于其他 *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 所建议的那样,Map
(以及 mapply
和 SIMPLIFY=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 (因此它的效率)但是 mapply
和 Map
是无法应对data.table。 (我将 mean
、sd
、var
烘焙到我的 mapply
调用中,以便将苹果与苹果进行比较。)
编辑 #2:
之前的基准测试看起来令人印象深刻且具有决定性,但并未描述调用开销与大数据引擎效率的关系。这是另一个 运行 有关更多数据的内容。
当各个子集相当大时——即来自源data.frame的"chunks"较少——性能趋于平衡。这里我用 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
较小的数据集与较大的数据集花费的时间大致相同。不错
谎言分为三种:谎言、该死的谎言和统计数据。(本杰明·迪斯雷利)这同样适用于基准测试。
我试图避免使用 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 个。我想计算多个统计数据,包括 mean
、std
和 variance
,例如, 对于每个间隔。 for()
-loops 可以解决问题,但速度很慢。我正在查看 apply
函数,但我似乎无法弄明白。 mean(df[x1:x2])
也没有用,因为它只取 x1
和 x2
的第一个值。
有什么建议吗?
--tstev
将 Map
与 plyr
软件包中有用的 each
一起使用的好机会:
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))
这是您的问题的解决方案:
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()
即可得到标准差或方差。如果 df
.
na.rm=TRUE
参数
也许您可以使用 apply 两次来代替 for 循环?所需的计算可以包装到一个函数中(在我的示例中是 compute_mean
),然后可以在 x1
和 x2
的索引对上调用此函数。鉴于 x1
和 x2
的长度相同,使用 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)
}))
我倾向于反对在 data.frame 的行上使用 apply
(因为任何错误步骤都会将所有内容转换为字符 class)。我不得不做一些与您在其他代码中要求的非常相似的事情,我选择了 mapply
.
它对 2(或更多)vectors/lists 的第一个元素执行 "something",然后对相同 vectors/lists 的第二个元素执行相同的 "something",等等 "something" 当然是由第一个参数定义的——一个函数,类似于其他 *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 所建议的那样,Map
(以及 mapply
和 SIMPLIFY=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 (因此它的效率)但是 mapply
和 Map
是无法应对data.table。 (我将 mean
、sd
、var
烘焙到我的 mapply
调用中,以便将苹果与苹果进行比较。)
编辑 #2:
之前的基准测试看起来令人印象深刻且具有决定性,但并未描述调用开销与大数据引擎效率的关系。这是另一个 运行 有关更多数据的内容。
当各个子集相当大时——即来自源data.frame的"chunks"较少——性能趋于平衡。这里我用 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
较小的数据集与较大的数据集花费的时间大致相同。不错
谎言分为三种:谎言、该死的谎言和统计数据。(本杰明·迪斯雷利)这同样适用于基准测试。