R: Replace "off-diagonal" elements of a random matrix - r

I'm using the following code to generate a random matrix with some elements = 1 near the diagonal, the rest = 0. (This is basically a random walk along the main diagonal.)
n <- 20
rw <- matrix(0, ncol = 2, nrow = n)
indx <- cbind(seq(n), sample(c(1, 2), n, TRUE))
rw[indx] <- 1
rw[,1] <- cumsum(rw[, 1])+1
rw[,2] <- cumsum(rw[, 2])+1
rw2 <- subset(rw, (rw[,1] <= 10 & rw[,2] <= 10))
field <- matrix(0, ncol = 10, nrow = 10)
field[rw2] <- 1
field
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 1 1 1 0 0 0 0 0 0
[2,] 0 0 0 1 0 0 0 0 0 0
[3,] 0 0 0 1 0 0 0 0 0 0
[4,] 0 0 0 1 1 1 1 0 0 0
[5,] 0 0 0 0 0 0 1 1 0 0
[6,] 0 0 0 0 0 0 0 1 0 0
[7,] 0 0 0 0 0 0 0 1 0 0
[8,] 0 0 0 0 0 0 0 1 1 1
[9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 0 0 0 0
Next thing, I would like to replace the 0 elements to the right-hand/upper side of the 1-elements by 1. For the above matrix the desired output would be:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 1 1 1 1 1 1 1 1 1
[2,] 0 0 0 1 1 1 1 1 1 1
[3,] 0 0 0 1 1 1 1 1 1 1
[4,] 0 0 0 1 1 1 1 1 1 1
[5,] 0 0 0 0 0 0 1 1 1 1
[6,] 0 0 0 0 0 0 0 1 1 1
[7,] 0 0 0 0 0 0 0 1 1 1
[8,] 0 0 0 0 0 0 0 1 1 1
[9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 0 0 0 0
I have tried
fill <- function(row) {first = match(1, row); if (is.na(first)) {row = rep(1, 10)} else {row[first:10] = 1}; return(row)}
field2 <- apply(field, 1, fill)
field2
But that gives me instead:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 0 0 0 1 1
[2,] 1 0 0 0 0 0 0 0 1 1
[3,] 1 0 0 0 0 0 0 0 1 1
[4,] 1 1 1 1 0 0 0 0 1 1
[5,] 1 1 1 1 0 0 0 0 1 1
[6,] 1 1 1 1 0 0 0 0 1 1
[7,] 1 1 1 1 1 0 0 0 1 1
[8,] 1 1 1 1 1 1 1 1 1 1
[9,] 1 1 1 1 1 1 1 1 1 1
[10,] 1 1 1 1 1 1 1 1 1 1
Can anyone help me fix this?
Cheers,
mce
PS: If the first row is all zeros (as it can happen with the above code) it should be changed to all ones.

Why not just:
t(apply(field,1,cummax))
One instance:
dput(field)
structure(c(0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0), .Dim = c(10L,
10L))
> field
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 0 0 0 0 0
[2,] 1 1 1 1 1 1 0 0 0 0
[3,] 0 0 0 0 0 1 0 0 0 0
[4,] 0 0 0 0 0 1 0 0 0 0
[5,] 0 0 0 0 0 1 1 1 1 1
[6,] 0 0 0 0 0 0 0 0 0 0
[7,] 0 0 0 0 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0 0 0 0
[9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 0 0 0 0
The output:
> t(apply(field,1,cummax))
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 0 0 0 0 0
[2,] 1 1 1 1 1 1 1 1 1 1
[3,] 0 0 0 0 0 1 1 1 1 1
[4,] 0 0 0 0 0 1 1 1 1 1
[5,] 0 0 0 0 0 1 1 1 1 1
[6,] 0 0 0 0 0 0 0 0 0 0
[7,] 0 0 0 0 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0 0 0 0
[9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 0 0 0 0

This should work:
MaxFull <- which.max((apply(field,1,sum) > 0) * (1:10))
rbind(t(apply(field[1:MaxFull,], 1, fill)),matrix(0,ncol=10,nrow=10-MaxFull))
notice that it uses fill as you defined it.

In the help for the value of apply, "If each call to FUN returns a vector of length n, then apply returns an array of dimension c(n, dim(X)[MARGIN])". So, you want the transpose of this. Print statements were added to the fill function to confirm the operation. You may want to check if your function is hiding another function, there is a function named fill, but it doesn't matter in this case.
n <- 20
rw <- matrix(0, ncol = 2, nrow = n)
indx <- cbind(seq(n), sample(c(1, 2), n, TRUE))
rw[indx] <- 1
rw[,1] <- cumsum(rw[, 1])+1
rw[,2] <- cumsum(rw[, 2])+1
rw2 <- subset(rw, (rw[,1] <= 10 & rw[,2] <= 10))
field <- matrix(0, ncol = 10, nrow = 10)
field[rw2] <- 1
field
myfill <- function(row) {
print("Function start")
print(row)
first = match(1, row)
print(paste("Match", first))
if (is.na(first)) {
row = rep(1, 10)
} else {
row[first:10] = 1
};
print(row)
flush.console()
return(row)
}
field2 = t(apply(field, 1, myfill))
field2

Related

How to populate a matrix quickly without nested for loops

I have a dataset with values 0, 1, and 2.
data <- matrix(c(1, 0, 0, 1, 2, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 1), nrow = 5, ncol = 4)
> data
[,1] [,2] [,3] [,4]
[1,] 1 1 0 2
[2,] 0 1 0 0
[3,] 0 0 0 1
[4,] 1 1 0 1
[5,] 2 0 0 1
I would like to a create matrix based on this data such that the value 0 is (0, 0), 1 is (1, 0), and 2 is (0, 1). Below is the code that I'm using:
data.exp <- matrix(NA, nrow = nrow(data)*2, ncol = ncol(data))
for(i in 1:nrow(data)){
for(j in 1:(ncol(data))){
if(data[i,j] == 1){
vec <- c(1, 0)
}else if(data[i, j] == 0){
vec <- c(0, 0)
}else{
vec <- c(0, 1)
}
data.exp[((i*2-1):(i*2)), j] <- vec
}
}
> data.exp
[,1] [,2] [,3] [,4]
[1,] 1 1 0 0
[2,] 0 0 0 1
[3,] 0 1 0 0
[4,] 0 0 0 0
[5,] 0 0 0 1
[6,] 0 0 0 0
[7,] 1 1 0 1
[8,] 0 0 0 0
[9,] 0 0 0 1
[10,] 1 0 0 0
Is there a faster way to generate the matrix, data.exp, without having to use a nested for loop in R? As the sample size increases, the nested for loop approach is not very efficient.
apply should be pretty fast for matrices. Create a list, v, with appropriate values and subset by considering 0, 1, or 2 in data as indices of v
v = list(c(0, 0), c(1, 0), c(0, 1))
apply(data, 2, function(i) do.call(cbind, v[i + 1]))
# [,1] [,2] [,3] [,4]
# [1,] 1 1 0 0
# [2,] 0 0 0 1
# [3,] 0 1 0 0
# [4,] 0 0 0 0
# [5,] 0 0 0 1
# [6,] 0 0 0 0
# [7,] 1 1 0 1
# [8,] 0 0 0 0
# [9,] 0 0 0 1
# [10,] 1 0 0 0
Here is an option without any loop
t(
matrix(
scan(text = toString(c("0, 0", "1, 0", "0, 1")[data + 1]), sep = ","),
byrow = TRUE,
nrow = ncol(data)
)
)
which gives
[,1] [,2] [,3] [,4]
[1,] 1 1 0 0
[2,] 0 0 0 1
[3,] 0 1 0 0
[4,] 0 0 0 0
[5,] 0 0 0 1
[6,] 0 0 0 0
[7,] 1 1 0 1
[8,] 0 0 0 0
[9,] 0 0 0 1
[10,] 1 0 0 0
A more concise option (thank #akrun's contribution)
> matrix(unlist(list(c(0, 0), c(1, 0), c(0, 1))[data + 1]), nrow = nrow(data) * 2)
[,1] [,2] [,3] [,4]
[1,] 1 1 0 0
[2,] 0 0 0 1
[3,] 0 1 0 0
[4,] 0 0 0 0
[5,] 0 0 0 1
[6,] 0 0 0 0
[7,] 1 1 0 1
[8,] 0 0 0 0
[9,] 0 0 0 1
[10,] 1 0 0 0
Start by making two matrices of the same dimensions as data, one which sets all of the 2s to 0 and the other which sets all of the 1s to 0 and all of the 2s to 1. Then interleave the two matrices row by row.
The first part if accomplished using ifelse; for the second part, flodel's answer to this question helps.
Putting it all together, you have
data <- matrix(c(1, 0, 0, 1, 2, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 1), nrow = 5, ncol = 4)
l<-list(ifelse(data < 2, data, 0),
ifelse(data > 1, 1, 0))
do.call(rbind, l)[order(sequence(sapply(l, nrow))), ]
# [,1] [,2] [,3] [,4]
# [1,] 1 1 0 0
# [2,] 0 0 0 1
# [3,] 0 1 0 0
# [4,] 0 0 0 0
# [5,] 0 0 0 1
# [6,] 0 0 0 0
# [7,] 1 1 0 1
# [8,] 0 0 0 0
# [9,] 0 0 0 1
# [10,] 1 0 0 0

Creating matrix with probabilities depending on index column

How do I create a matrix 10x10, with only 1's (heads), and 0's (tails), with the probability of a heads is 1 divided by the index of the column.
I tried several things but it won't work which is really frustrating. I tried to do it with a vector and a for loop.
mat <- matrix(sample(c(0,1), 100, replace=TRUE, prob=c(1/h, 1-(1/h)), 10))
But now the only question is how to define h.
Here is an option using sapply
n_col <- 10
n_row <- 10
mat <- matrix(nrow = n_row,
ncol = n_col)
set.seed(1)
sapply(1:n_col, function(x) {
mat[, x] <- sample(x = c(1, 0),
size = n_row,
replace = TRUE,
prob = c(1/x, 1 - 1/x))
})
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
# [1,] 1 0 1 0 1 0 1 0 0 0
# [2,] 1 0 0 0 0 1 0 0 0 0
# [3,] 1 1 0 0 0 0 0 0 0 0
# [4,] 1 0 0 0 0 0 0 0 0 0
# [5,] 1 1 0 1 0 0 0 0 0 0
# [6,] 1 0 0 0 0 0 0 1 0 0
# [7,] 1 1 0 1 0 0 0 0 0 0
# [8,] 1 1 0 0 0 0 0 0 0 0
# [9,] 1 0 1 0 0 0 0 0 0 0
#[10,] 1 1 0 0 0 0 1 1 0 0
Hope it helps.

Selecting the first non-zero value from each column in a data frame

I have a 10x100 data frame called CoeNIST. The rows are in order of significance (i.e. the value in row 1 is more important than the value in row 2) and each column represents a different sample. I would like to extract only the most significant non-zero value, i.e. the first non-zero value, for each sample.
Here is a sample from the first 9 columns of CoeNIST.
> CoeNIST[,1:9]
1 2 3 4 5 6 7 8 9
1 0 352232 0 0 0 0 0 28733 0
2 332829 0 0 380109 0 0 0 380343 0
3 0 0 0 380111 0 0 0 380409 0
4 0 0 0 380101 0 0 0 0 0
5 0 0 299211 380112 0 0 0 0 0
6 0 0 0 380103 0 0 0 0 0
7 0 0 0 380100 0 0 0 71899 0
8 0 0 0 24812 0 0 0 0 0
9 0 0 0 0 0 0 0 380410 0
10 0 332958 0 0 0 0 0 380440 0
And here is what I would like the outcome to look like
> NIST
[1] 332829 352232 299211 380109 NA NA NA 28733 NA
OR...as a list...
> NIST
[[1]]
[1] 332829
[[2]]
[1] 352232
[[3]]
[1] 299211
[[4]]
[1] 380109
[[5]] integer(0)
[[6]] integer(0)
[[7]] integer(0)
[[8]]
[1] 28733
[[9]] integer(0)
CoeNIST <- read.table(header=TRUE,text="
1 2 3 4 5 6 7 8 9
1 0 352232 0 0 0 0 0 28733 0
2 332829 0 0 380109 0 0 0 380343 0
3 0 0 0 380111 0 0 0 380409 0
4 0 0 0 380101 0 0 0 0 0
5 0 0 299211 380112 0 0 0 0 0
6 0 0 0 380103 0 0 0 0 0
7 0 0 0 380100 0 0 0 71899 0
8 0 0 0 24812 0 0 0 0 0
9 0 0 0 0 0 0 0 380410 0
10 0 332958 0 0 0 0 0 380440 0")
I would describe your problem as "selecting the first non-zero value in each column." My solution gives you NA values when there are only zeros in the column ...
apply(CoeNIST,2,function(x) (x[x>0])[1])
## X1 X2 X3 X4 X5 X6 X7 X8 X9
## 332829 352232 299211 380109 NA NA NA 28733 NA
CoeNIST = matrix(c(0, 352232, 0, 0, 0, 0, 0, 28733, 0, 332829, 0, 0, 380109, 0, 0, 0, 380343, 0, 0, 0, 0, 380111, 0, 0, 0, 380409, 0, 0, 0, 0, 380101, 0, 0, 0, 0, 0, 0, 0, 299211, 380112, 0, 0, 0, 0, 0, 0, 0, 0, 380103, 0, 0, 0, 0, 0, 0, 0, 0, 380100, 0, 0, 0, 71899, 0, 0, 0, 0, 24812, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 380410, 0, 0, 332958, 0, 0, 0, 0, 0, 380440, 0), nrow=10, ncol=10, byrow=T)
> CoeNIST
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 352232 0 0 0 0 0 28733 0 332829
[2,] 0 0 380109 0 0 0 380343 0 0 0
[3,] 0 380111 0 0 0 380409 0 0 0 0
[4,] 380101 0 0 0 0 0 0 0 299211 380112
[5,] 0 0 0 0 0 0 0 0 380103 0
[6,] 0 0 0 0 0 0 0 380100 0 0
[7,] 0 71899 0 0 0 0 24812 0 0 0
[8,] 0 0 0 0 0 0 0 0 0 380410
[9,] 0 0 332958 0 0 0 0 0 380440 0
[10,] 0 352232 0 0 0 0 0 28733 0 332829
This is for the max of each column:
apply(CoeNIST, 2, function(x){x_max = max(x); if(x_max == 0) NULL else x_max})})
This is for the first non-zero value in a column:
apply(CoeNIST, 2, function(x){x_top_non_zero = min(which(x>0)); if(x_top_non_zero == Inf) {NaN} else {x_top_non_zero}} )

How to combine R matrices of a different size by growing rows

Suppose I have 3 matrices
A
1 0 0
0 1 0
0 1 0
B
0 0 0 1
1 0 0 0
C
0 0 0 0 1
0 1 0 0 0
1 0 0 0 0
Notice that each matrix only has one "1" per row. I need to keep this property. I want to now make a matrix that's (3 + 4 + 5)=12 columns wide and (3 + 2 + 3) = 8 rows deep that looks like this:
1 2 3 4 5 6 7 8 9 10 11 12
----------------------------
1 1 0 0 0 0 0 0 0 0 0 0 0
2 0 1 0 0 0 0 0 0 0 0 0 0
3 0 1 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 1 0 0 0 0 0
5 0 0 0 1 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0 0 0 0 1
7 0 0 0 0 0 0 0 0 1 0 0 0
8 0 0 0 0 0 0 0 1 0 0 0 0
While I have 3 matrices here, what I actually have is a list mylist which has an arbitrary number of matrices of different sizes. The only rule is that for each matrix, each row only has one "1".
Before anyone asks, this is not hw, I'm trying to combine mutation matrices in my data that are all 0/1 (where the columns represent a missense substitution for that amino acid).
Is there also a way that I can bounce around? For instance, suppose I wanted A1, A3, B1, C1, C3, ... (where the first letter represents the matrix and the number represents the column)? Or would i have to make the matrix first and then just reorder?
You can use Reduce.
combine <- function(A, B) {
rbind(cbind(A, array(0, dim = c(nrow(A), ncol(B)))),
cbind(array(0, dim = c(nrow(B), ncol(A))), B))
}
D <- Reduce(combine, list(A,B,C))
Example
A <- structure(c(1, 0, 0, 0, 1, 1, 0, 0, 0), .Dim = c(3L, 3L))
B <- structure(c(0, 1, 0, 0, 0, 0, 1, 0), .Dim = c(2L, 4L))
C <- structure(c(0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0), .Dim = c(3L,5L))
D <- Reduce(combine, list(A,B,C))
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 1 0 0 0 0 0 0 0 0 0 0 0
[2,] 0 1 0 0 0 0 0 0 0 0 0 0
[3,] 0 1 0 0 0 0 0 0 0 0 0 0
[4,] 0 0 0 0 0 0 1 0 0 0 0 0
[5,] 0 0 0 1 0 0 0 0 0 0 0 0
[6,] 0 0 0 0 0 0 0 0 0 0 0 1
[7,] 0 0 0 0 0 0 0 0 1 0 0 0
[8,] 0 0 0 0 0 0 0 1 0 0 0 0
Robert's answer is more pleasantly succinct, but nevertheless i'll present an alternative presentation. First, the input
A<-matrix(c(1,0,0,0,1,1,0,0,0), ncol=3)
B<-matrix(c(0,1,0,0,0,0,1,0), ncol=4)
C<-matrix(c(0,0,1,0,1,0,0,0,0,0,0,0,1,0,0), ncol=5)
Now i'll create a helper function called dbind which works like rbind and cbind but concatenates the matrix along the "diagonal" filling the rest with 0. Here's the function which can take an arbitrary number of matrices
dbind<-function(...) {
dots<-list(...)
dm<-sapply(dots, dim)
dx<-rowSums(dm)
of<-apply(dm,1,function(x) cumsum(c(0,x[-length(x)])))
r<-array(0, dim=dx)
Map(function(d,i) {
r[sweep(which(!is.na(d), arr.ind=T), 2, i, `+`)] <<- d
}, dots, split(of, 1:nrow(of)))
r
}
and then you call it with
dbind(A,B,C)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
# [1,] 1 0 0 0 0 0 0 0 0 0 0 0
# [2,] 0 1 0 0 0 0 0 0 0 0 0 0
# [3,] 0 1 0 0 0 0 0 0 0 0 0 0
# [4,] 0 0 0 0 0 0 1 0 0 0 0 0
# [5,] 0 0 0 1 0 0 0 0 0 0 0 0
# [6,] 0 0 0 0 0 0 0 0 0 0 0 1
# [7,] 0 0 0 0 0 0 0 0 1 0 0 0
# [8,] 0 0 0 0 0 0 0 1 0 0 0 0
Here is a way to combine using bdiag function from the Matrix package
If your matrices are in a list
library(Matrix)
l <- list(A,B,C)
as.matrix(bdiag(l))
The magic package includes a bespoke function adiag() which does exactly what you are looking for. The original application was for high-dimensional magic hypercubes but it works nicely for regular matrices and preseves dimnames. Also, one can control the pad value. Use-case:
> library(magic)
> A <- structure(c(1, 0, 0, 0, 1, 1, 0, 0, 0), .Dim = c(3L, 3L))
> B <- structure(c(0, 1, 0, 0, 0, 0, 1, 0), .Dim = c(2L, 4L))
> C <- structure(c(0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0), .Dim = c(3L,5L))
> adiag(A,B,C)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 1 0 0 0 0 0 0 0 0 0 0 0
[2,] 0 1 0 0 0 0 0 0 0 0 0 0
[3,] 0 1 0 0 0 0 0 0 0 0 0 0
[4,] 0 0 0 0 0 0 1 0 0 0 0 0
[5,] 0 0 0 1 0 0 0 0 0 0 0 0
[6,] 0 0 0 0 0 0 0 0 0 0 0 1
[7,] 0 0 0 0 0 0 0 0 1 0 0 0
[8,] 0 0 0 0 0 0 0 1 0 0 0 0
>

R : Updating a matrix given a set of indices

I have a matrix(initialized to zeros) and a set of indices. If the i'th value in indices is j, then I want to set the (j,i)th entry of the matrix to 1.
For eg:
> m = matrix(0, 10, 7)
> indices
[1] 2 9 3 4 5 1 10
And the result should be
> result
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 0 0 0 0 0 1 0
[2,] 1 0 0 0 0 0 0
[3,] 0 0 1 0 0 0 0
[4,] 0 0 0 1 0 0 0
[5,] 0 0 0 0 1 0 0
[6,] 0 0 0 0 0 0 0
[7,] 0 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0
[9,] 0 1 0 0 0 0 0
[10,] 0 0 0 0 0 0 1
I asked a somewhat related question a little while back, which used a vector instead of a matrix. Is there a similar simple solution to this problem?
## OP's example data
m = matrix(0, 10, 7)
j <- c(2, 9, 3, 4, 5, 1, 10)
## Construct a two column matrix of indices (1st column w. rows & 2nd w. columns)
ij <- cbind(j, seq_along(j))
## Use it to subassign into the matrix
m[ij] <- 1
m
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] 0 0 0 0 0 1 0
# [2,] 1 0 0 0 0 0 0
# [3,] 0 0 1 0 0 0 0
# [4,] 0 0 0 1 0 0 0
# [5,] 0 0 0 0 1 0 0
# [6,] 0 0 0 0 0 0 0
# [7,] 0 0 0 0 0 0 0
# [8,] 0 0 0 0 0 0 0
# [9,] 0 1 0 0 0 0 0
# [10,] 0 0 0 0 0 0 1
For the record, the answer in your linked question can easily be adapted to suit this scenario too by using sapply:
indices <- c(2, 9, 3, 4, 5, 1, 10)
sapply(indices, tabulate, nbins = 10)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] 0 0 0 0 0 1 0
# [2,] 1 0 0 0 0 0 0
# [3,] 0 0 1 0 0 0 0
# [4,] 0 0 0 1 0 0 0
# [5,] 0 0 0 0 1 0 0
# [6,] 0 0 0 0 0 0 0
# [7,] 0 0 0 0 0 0 0
# [8,] 0 0 0 0 0 0 0
# [9,] 0 1 0 0 0 0 0
# [10,] 0 0 0 0 0 0 1
For small datasets you might not notice the performance difference, but Josh's answer, which uses matrix indexing, would definitely be much faster, even if you changed my answer here to use vapply instead of sapply.

Resources