R:数值向量的条件求和

时间:2016-08-23 13:49:12

标签: r loops

我有具有数值的向量。例如:

inVector <- c(2, -10, 5, 34, 7)

我需要对此进行转换,以便在遇到负面元素时,该负面元素与后续元素相加,直到将总和变为正数的元素:

outVector <- c(2, 0, 0, 29, 7)

将负元素设为零,以便保留总和。所以元素2和3将为零,第四个元素等于29 = -10 + 5 + 34.我尝试了这样的for循环解决方案:

outVector <- numeric(length = length(inVector))

for(i in 1:length(inVector)) {
   outVector <- inVector
   outVector[i] <- ifelse(outVector[i] < 0, 0, outVector[i])
   outVector[i + 1] <- ifelse(outVector[i] == 0, sum(inVector[i:(i+1)]), outVector[i + 1])
   outVector <- outVector[1:length(inVector)]
   }

但那并没有奏效。但是,我最感兴趣的是一个在dplyr管道中工作的解决方案。

2 个答案:

答案 0 :(得分:6)

如果我们想要优化,我们可以使用更高效的Reduce函数迭代向量:

#Help function
zeroElement <- function(vec) {
  r <- Reduce(function(x,y) if(x >= 0) y else sum(x,y), vec, acc=TRUE)
  r[r < 0] <- 0
  return(r)
}

#Use function
zeroElement(x)
#[1]  2  0  0 29  7

速度测试:快25%:

t3 <- MakeNonNeg(BigVec)
t4 <- zeroElement(BigVec)
all.equal(t3, t4)
#[1] TRUE
library(microbenchmark)
microbenchmark(
  makeNonNeg = MakeNonNeg(BigVec),
  zeroElement = zeroElement(BigVec),
  times=10)
# Unit: seconds
#        expr      min       lq     mean   median       uq      max neval cld
#  makeNonNeg 2.047484 2.099289 2.195988 2.111135 2.248381 2.531009    10   b
# zeroElement 1.529257 1.580789 1.666000 1.664855 1.725528 1.837825    10  a

添加会话信息以进行比较:

sessionInfo()
R version 3.3.0 (2016-05-03)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

答案 1 :(得分:5)

试试这个:

MakeNonNeg <- function(v) {
    size <- length(v)
    myOut <- as.numeric(v)
    if (size > 1L) {
        for (i in 1:(size-1L)) {
            if (myOut[i] >= 0) {next}
            myOut[i+1L] <- myOut[i]+myOut[i+1L]
            myOut[i] <- 0
        }
    }
    myOut
}

MakeNonNeg(inVector)
[1]  2  0  0 29  7

以下是一个更具异国情调的例子:

set.seed(4242)

BigVec <- sample(-40000:100000, 100000, replace = TRUE)
gmp::sum.bigz(BigVec)
Big Integer ('bigz') :
    [1] 2997861106

t3 <- MakeNonNeg(BigVec)
gmp::sum.bigz(t3)
Big Integer ('bigz') :
    [1] 2997861106

BigVec[1:20]
[1]  98056   8680  -7814  53620  58390  90832  74970 -16392  52648  83779 -17229  38484 -36589  75156  71200  95968 -11599  57705
[19]  19209 -21596

t3[1:20]
[1] 98056  8680     0 45806 58390 90832 74970     0 36256 83779     0 21255     0 38567 71200 95968     0 46106 19209     0

这是我的系统信息:

sessionInfo()
R version 3.3.0 (2016-05-03)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

以下是禁用JIT的两个功能的时间安排。

microbenchmark(
    makeNonNeg = MakeNonNeg(BigVec),
    zeroElement = zeroElement(BigVec),
    times=10)
Unit: milliseconds
       expr      min       lq     mean   median       uq      max neval
 makeNonNeg 254.1255 255.8430 267.9527 258.6369 277.0222 303.6516    10
zeroElement 152.0358 164.7988 175.3191 166.4948 198.3855 209.8739    10

启用JIT后,我们会为makeNonNeg获得更多不同的结果。但是,zeroElement的结果不会发生太大的变化(我认为,因为Reduce是函数的主要部分并且它已经是字节编码,所以没有太多空间改进)。

library(compiler)
enableJIT(3)
[1] 0

microbenchmark(
    makeNonNeg = MakeNonNeg(BigVec),
    zeroElement = zeroElement(BigVec),
    times=10)
Unit: milliseconds
       expr       min        lq      mean    median        uq       max neval
 makeNonNeg  11.20514  11.55366  12.76953  11.84655  12.20554  20.60036    10
zeroElement 144.15123 149.33591 163.66421 157.34711 176.20139 198.57268    10

因此,如果禁用JIT,则zeroElement的速度提高约50%,启用JIT后,MakeNonNeg的速度提高约13倍。