I’m working with some code that has crucial similarities to the following trivial script:
scores <- matrix(rnorm(4*20), ncol=4,nrow=20)
result <- matrix(NA, ncol=2, nrow=20)
index <- as.logical(rbinom(20,1,.2))
result[index, 1:3] <- cbind(1, scores[index,3:4])
where index is a logical vector and the sum(index) is typically greater than 1but can occasionally be 1 or 0.
The script fails in the case where sum(index) == 1:
> scores <- matrix(rnorm(4*20), ncol=4,nrow=20)
> result <- matrix(NA, ncol=3, nrow=20)
> index <- c(rep(FALSE, 19),TRUE)
> result[index, 1:3] <- cbind(1, scores[index,3:4])
Error in result[index, 1:3] <- cbind(1, scores[index, 3:4]) :
number of items to replace is not a multiple of replacement length
> cbind(1, scores[index,3:4])
[,1] [,2]
[1,] 1 -0.1780255
[2,] 1 -0.6840048
> #should be:
> c(1, scores[index,3:4])
[1] 1.0000000 -0.1780255 -0.6840048
and where sum(index) ==0:
> scores <- matrix(rnorm(4*20), ncol=4,nrow=20)
> result <- matrix(NA, ncol=3, nrow=20)
> index <- rep(FALSE, 20)
> result[index, 1:3] <- cbind(1, scores[index,3:4])
Warning message:
In cbind(1, scores[index, 3:4]) :
number of rows of result is not a multiple of vector length (arg 1)
> #cbinding to a zero-row matrix returns an error
The obvious solution to this problem is the following:
scores <- matrix(rnorm(4*20), ncol=4,nrow=20)
result <- matrix(NA, ncol=3, nrow=20)
index <- as.logical(rbinom(20,1,.1))
if(sum(index) > 1){
result[index, 1:3] <- cbind(1, scores[index,3:4])
}else{
if(sum(index) ==1){
result[index, 1:3] <- c(1, scores[index,3:4])
}
}
However, I’m interested in advice on how to code to avoid this error without having to write a bunch of if statements. Is there a trick to binding an atomic vector to an nx2 matrix OR 2-length vector (n=1) such that the result is always an nx3 matrix? Extra points if the script can do this without producing an error when n=0.
If it weren’t for like an hour of debugging, I wouldn’t have identified this issue–it was buried quite a few functions down in a batch processing script. Any general advice on coding in a way of avoiding such ‘gotchas’?
Generally adding
drop=FALSEtomtx[1,]calls will avoid the difficulties that arise with single row extractions and subsequent operations that assume a matrix structure:I haven’t quite figured out how you want us to do to avoid errors with assignment of zero row objects to zero row objects. You should instead check for
length(index)==0(The real problem is that you were assigning a three column matrix to a two column target. Oh, I see you tried to fix that, except you were still trying to assign to a third column that was not there in the dimensions.)