【问题标题】:Conditional vectorization条件向量化
【发布时间】:2016-05-15 21:41:15
【问题描述】:

给定如下数据框:

set.seed(123)
df1 <- data.frame(V1=sample(c(0,1,2),100,replace=TRUE),
  V2=sample(c(2,3,4),100,replace=TRUE),
  V3=sample(c(4,5,6),100,replace=TRUE),
  V4=sample(c(6,7,8),100,replace=TRUE),
  V5=sample(c(6,7,8),100,replace=TRUE))

我想对每一行求和,从值 >=2 的第一列开始,以值 >6 的列结束,否则求和直到行尾。

如何以矢量化方式执行此操作?

更新:这不适用于任何家庭作业。我只是想要更多可以学习和学习的矢量化代码示例。我之前不得不做类似上面的事情,但无法弄清楚这个特定任务的 apply 语法并诉诸 for 循环。

【问题讨论】:

  • 我不明白这两个接近的投票,但也许它与你要求外部资源的最后一句话有关(我删除了)。我还怀疑这个问题根本上不是矢量化提供了很大希望的任务。你真的应该描述底层任务(至少如果它不仅仅是一个 CS HW 问题)。

标签: r vectorization


【解决方案1】:

我确信有一种更优雅的方法,但对于蛮力方法,您可以编写一个函数并将其传递给应用。

首先,定义您的示例数据

df <- data.frame(V1=sample(c(0,1,2),100,replace=TRUE),
                 V2=sample(c(2,3,4),100,replace=TRUE),
                 V3=sample(c(4,5,6),100,replace=TRUE),
                 V4=sample(c(6,7,8),100,replace=TRUE),
                 V5=sample(c(6,7,8),100,replace=TRUE))

编写一个定义条件语句的函数。使用 which 返回条件在向量中的位置。其中“start”的第一次使用拉动条件第一次出现的位置,因此括号使用[1]。由于结束位置有多种潜在结果,我使用 if 语句来实现它。如果没有一个值满足条件 > 6 对于“end”,则变量被分配到向量的最后一个位置,否则该位置满足条件。然后,只需根据要使用 sum 评估的开始值和结束值对向量进行子集化。

sum.col <- function(x) {
  start <- which(x >= 2)[1]
  end <- which(x > 6)
    if( length(end) == 0 ) {
      end <- length(x)
    } else {
      end <- end[length(end)]
  }
  return( sum( x[start:end] ) )  
}

现在我们可以将函数传递给apply,它为我们处理每一行的向量化。

apply(df, FUN=sum.col, MARGIN = 1)  

【讨论】:

  • apply 函数不是矢量化。这只是一个伪装的for-loop。
【解决方案2】:

这是看起来最像 R 的方法,但我不认为它在 R 的含义中是“矢量化的”:

apply( df1, 1, function(x) sum( x[which(x>=2)[1]: min(which(x>6)[1], 5, na.rm=TRUE)] ) )
#---------
  [1] 15 22 16 19 17 17 23 21 14 13 18 13 16 23 15 18 16 21 16 19 17 23 21 18
 [25] 21 24 15 20 15 18 17 24 19 18 19 15 18 17 15 17 14 21 13 19 15 15 15 15
 [49] 21 19 21 15 17 18 14 17 15 16 22 16 23 22 17 21 17 16 23 23 16 14 18 13
 [73] 18 15 17 17 17 20 20 16 17 16 16 16 14 16 20 23 23 24 14 18 16 17 22 23
 [97] 23 19 20 17

【讨论】:

  • 对不起。我在测试中使用了一个较小的示例,因此我将在提供的示例中添加测试的输出。
  • 我不能保证第二列总是&gt;= 2,但我猜这个例子是正确的。也许这只是一个 CS HW 问题,我们应该注意到这一点和终止条件。
  • 是的,这是一个比我的更好的一般答案 - 如果条件或采样发生变化,您的很容易修复,我的没那么多。
【解决方案3】:

由于您的采样结构,我们可以很容易地进行矢量化。

我们知道只有第一列可以小于 2,因此被排除在外,并且必须包含列 V2、V3 和 V4,因为它们要么低于 6,要么第一个非 6。仅当 V4 列高于 6 时才排除 V5 列。

所以:

(df1$V1 == 2) * df1$V1 + df1$V2 + df1$V3 + df1$V4 + df1$V5 * !(df1$V4 > 6)

  [1] 15 22 16 19 17 17 23 21 14 13 18 13 16 23 15 18 16 21 16 19 17 23 21 18 21 24 15 20 15 18 17 24 19 18
 [35] 19 15 18 17 15 17 14 21 13 19 15 15 15 15 21 19 21 15 17 18 14 17 15 16 22 16 23 22 17 21 17 16 23 23
 [69] 16 14 18 13 18 15 17 17 17 20 20 16 17 16 16 16 14 16 20 23 23 24 14 18 16 17 22 23 23 19 20 17

是您的矢量化计算。这显然比这里的其他答案要少得多,但符合您的问题。

【讨论】:

    【解决方案4】:

    使用apply 将是最明智的解决方案。然而,由于我们似乎在竞争谁能在不使用基于 R 的循环的情况下回答这个问题,所以我谦虚地提供这个

    m<-as.matrix(df1)
    start<-max.col(m>=2,ties="first")
    end<-max.col(`[<-`(m>6,,ncol(m),TRUE),ties="first")
    i<-t(matrix(1:ncol(m),nrow=ncol(m),ncol=nrow(m)))
    rowSums(m*(i>=start & i<=end))
    

    输出与theseanswres相同。

    【讨论】:

      猜你喜欢
      • 2021-12-10
      • 2016-07-03
      • 2012-07-07
      • 2018-12-16
      • 2017-08-01
      • 2021-07-29
      • 2020-05-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多