具有多个条件的 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]
然后将[<-
与减法相结合
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 循环已经足够快?