具有多个条件的 for 循环向量化

Posted

技术标签:

【中文标题】具有多个条件的 for 循环向量化【英文标题】:Vectorisation of for loop with multiple conditions 【发布时间】:2017-05-26 23:39:53 【问题描述】:
dummies  = matrix(c(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0), nrow=6, ncol=6) 
colnames(dummies)  <- c("a","b", "c", "d", "e", "f")

我有一个带假人的矩阵

> dummies
     a b c d e f
[1,] 0 0 0 0 1 0
[2,] 0 0 1 0 0 0
[3,] 1 0 0 0 0 0
[4,] 0 0 0 0 0 1
[5,] 0 1 0 0 0 0
[6,] 0 0 0 1 0 0

我知道我的假人是相关的,因为第 1 行与 2、3 和 4 以及 5 和 6 分组。我想将每个虚拟代码 (1) 在同一行的同一组中的那些之间拆分:

> dummies
        a    b    c    d    e    f
[1,]  0.0  0.0 -0.5  0.0  0.5  0.0
[2,]  0.0  0.0  0.5  0.0 -0.5  0.0
[3,]  0.5  0.0  0.0  0.0  0.0 -0.5
[4,] -0.5  0.0  0.0  0.0  0.0  0.5
[5,]  0.0  0.5  0.0 -0.5  0.0  0.0
[6,]  0.0 -0.5  0.0  0.5  0.0  0.0 

为此,我执行以下操作:

dummies <- ifelse(dummies==1, 0.5, 0)
for (i in 1:nrow(dummies))
    column = which(dummies[i,] %in% 0.5)
    if (i %% 2 != 0)       
      dummies[i+1, column] <- -0.5
     else             
      dummies[i-1, column] <- -0.5
   
 

我的问题是我是否可以使用矢量化代码来实现这一点。在这种情况下,我无法弄清楚如何使用ifelse,因为我无法将它与行索引结合起来以在每一行上找到0.5

【问题讨论】:

【参考方案1】:

这是对基础 R 的一次尝试

# get locations of ones
ones <- which(dummies == 1)
# get adjacent locations
news <- ones + c(1L, -1L)[(ones %% 2 == 0L) + 1L]

# fill out matrix
dummiesDone <- dummies * 0.5
dummiesDone[news] <- -0.5

dummiesDone
        a    b    c    d    e    f
[1,]  0.0  0.0 -0.5  0.0  0.5  0.0
[2,]  0.0  0.0  0.5  0.0 -0.5  0.0
[3,]  0.5  0.0  0.0  0.0  0.0 -0.5
[4,] -0.5  0.0  0.0  0.0  0.0  0.5
[5,]  0.0  0.5  0.0 -0.5  0.0  0.0
[6,]  0.0 -0.5  0.0  0.5  0.0  0.0

此解决方案使用矩阵只是具有维度属性的向量这一事实。 which 在底层向量中找到 1 的位置。

第二行中的第二项c(1, -1)[(ones %% 2 == 0L) + 1L] 允许根据原始位置是偶数还是奇数来选择将用于拆分个值的向量的“对”元素。这在这里行得通,因为有偶数行,这在这个配对元素的问题中是必要的。

接下来的行根据元素是否最初是一个 (0.5) 或者它是否是相邻的对元素 (-0.5) 来填充矩阵。请注意,第二个命令利用了底层矢量位置概念。


第二种方法借鉴了 hubertl、thelatemail 和 martin-morgan 的帖子和 cmets 概念,首先从正确位置的原始矩阵中减去 0.5 以获得与上述相同的索引

# get locations of ones
ones <- which(dummies == 1)
# get adjacent locations
news <- ones + c(1L, -1L)[(ones %% 2 == 0L) + 1L]

然后将[&lt;-与减法相结合

dummies[c(ones, news)] <- dummies[c(ones, news)] - .5
dummies
        a    b    c    d    e    f
[1,]  0.0  0.0 -0.5  0.0  0.5  0.0
[2,]  0.0  0.0  0.5  0.0 -0.5  0.0
[3,]  0.5  0.0  0.0  0.0  0.0 -0.5
[4,] -0.5  0.0  0.0  0.0  0.0  0.5
[5,]  0.0  0.5  0.0 -0.5  0.0  0.0
[6,]  0.0 -0.5  0.0  0.5  0.0  0.0

【讨论】:

“接受”的答案是在清晰详细的解释的基础上选择的。谢谢。【参考方案2】:

创建一个表示行组的向量grp,并从组的每个成员中减去组均值rowsum(dummies, grp) / 2,为

grp = rep(seq_len(nrow(dummies) / 2), each=2)
dummies - rowsum(dummies, grp)[grp,] / 2

更普遍一点,允许不同大小和无序的组

dummies - (rowsum(dummies, grp) / tabulate(grp))[grp,]

【讨论】:

【参考方案3】:

这是另一种方法:

dummies[] <- sapply(split(dummies, gl(length(dummies)/2,2)), function(v) if(any(!!v))v-.5 else v)
        a    b    c    d    e    f
[1,]  0.0  0.0 -0.5  0.0  0.5  0.0
[2,]  0.0  0.0  0.5  0.0 -0.5  0.0
[3,]  0.5  0.0  0.0  0.0  0.0 -0.5
[4,] -0.5  0.0  0.0  0.0  0.0  0.5
[5,]  0.0  0.5  0.0 -0.5  0.0  0.0
[6,]  0.0 -0.5  0.0  0.5  0.0  0.0

【讨论】:

【参考方案4】:

另一种方法:

dummies - ((dummies[c(1,3,5),]+dummies[c(2,4,6),])/2)[c(1,1,2,2,3,3),]

        a    b    c    d    e    f
[1,]  0.0  0.0 -0.5  0.0  0.5  0.0
[2,]  0.0  0.0  0.5  0.0 -0.5  0.0
[3,]  0.5  0.0  0.0  0.0  0.0 -0.5
[4,] -0.5  0.0  0.0  0.0  0.0  0.5
[5,]  0.0  0.5  0.0 -0.5  0.0  0.0
[6,]  0.0 -0.5  0.0  0.5  0.0  0.0

【讨论】:

变体 - (dummies - dummies[c(2:1,4:3,6:5),])/2

以上是关于具有多个条件的 for 循环向量化的主要内容,如果未能解决你的问题,请参考以下文章

我们是不是需要 C++ 中的向量化或 for 循环已经足够快?

在python中为依赖于索引的函数向量化嵌套的for循环

GCC:两个相似循环之间的向量化差异

解释numpy向量化函数应用VS python for循环的速度差异

向量化

如何向量化嵌套循环