Reputation: 381
Given a 0/1 matrix, I am trying to generate a list of the index positions of the 1s in each row. I can do this using a for
loop:
B <- matrix(sample(c(0,1),100,replace=T),nrow=10,ncol=10)
ones <- list()
for (row in 1:nrow(B)) {ones[[row]] <- (which(B[row,]==1))}
The resulting object ones
is what I want. I assume using apply()
will be slightly faster, but I'm having trouble translating the loop into apply. This doesn't yield the same result:
alt <- apply(B, 1, function(x) which(B[x,]==1))
I'd appreciate any advice on how to correct apply
so it yields the same result as the loop approach. Thanks!
UPDATE -
I accepted @TarJae’s answer because it works, only requires one line, and is fast. @Ronak’s approach also works, but is slower than either of the other options. Here’s a comparison of the three methods:
library(rbenchmark)
B <- matrix(sample(c(0,1),1000000,replace=T),nrow=1000,ncol=1000)
benchmark(
"loop" = {
ones_loop <- list()
for (row in 1:nrow(B)) {ones_loop[[row]] <- (which(B[row,]==1))}
},
"apply" = {
ones_apply <- apply(B==1,1, which)
},
"split" = {
ones_split <- which(B == 1, arr.ind = TRUE)
ones_split <- split(ones_split[, 2], ones_split[, 1])
},
replications = 500
)
test replications elapsed relative user.self sys.self user.child sys.child
2 apply 500 17.211 1.000 15.068 2.111 0 0
1 loop 500 19.466 1.131 16.778 2.349 0 0
3 split 500 40.291 2.341 33.757 6.150 0 0
Upvotes: 2
Views: 235
Reputation: 887193
We could use asplit
lapply(asplit(B == 1, 1), which)
-output
[[1]]
[1] 1 10
[[2]]
[1] 3 8 9 10
[[3]]
[1] 1 3 6 7 8 9
[[4]]
[1] 2 5 6 7 9
[[5]]
[1] 1 2 4 5 7 10
[[6]]
[1] 2 8 10
[[7]]
[1] 1 2 6 7 8
[[8]]
[1] 3 7 9
[[9]]
[1] 1 2 4 5 6 7 9 10
[[10]]
[1] 1 4 6 7
Upvotes: 1
Reputation: 388992
which(matrix, arr.ind = TRUE)
as suggested by @Henrik provides you all the necessary information needed. If you need to have a list output as the for
loop you can use split
.
set.seed(123)
B <- matrix(sample(c(0,1),100,replace=T),nrow=10,ncol=10)
mat <- which(B == 1, arr.ind = TRUE)
split(mat[, 2], mat[, 1])
#`1`
#[1] 2 6 8 9 10
#$`2`
#[1] 2 3 4 5 7 9 10
#$`3`
#[1] 2 5 7
#$`4`
#[1] 1 4 9 10
#$`5`
#[1] 2 4 9 10
#$`6`
#[1] 1 6 7 8 9
#$`7`
#[1] 1 2 3 6 8
#$`8`
#[1] 1 3 5 10
#$`9`
#[1] 6 8 9
#$`10`
#[1] 3 4 8
Upvotes: 1
Reputation: 78937
You could use apply
this was way: Gives the same result as the loop:
apply(B==1,1, which)
> apply(B==1,1, which)
[[1]]
[1] 4 6 7 8 10
[[2]]
[1] 1 4 5 10
[[3]]
[1] 1 2 3 4 5 7 9
[[4]]
[1] 2 3 4 5 9
[[5]]
[1] 2 4 8 10
[[6]]
[1] 4 5 7 8
[[7]]
[1] 1 2 5 8 9 10
[[8]]
[1] 1 3 4 7 8 9 10
[[9]]
[1] 1 2 4 5 6 7 10
[[10]]
[1] 1 3 4 6 7 10
Upvotes: 3