Reputation: 1411
Given a vector:
vec <-1:5
What is an efficient way to create a matrix where the difference between the vector components are displayed in a matrix, a difference matrix, if you will. I could obviously do this with two for loops, but I need to do this with a much larger set of data. There is likely a term for this matrix I am trying to make, but I am not having any luck finding it. Here is what the result would look like.
m<-matrix(c(NA), ncol=5, nrow=5, byrow=TRUE)
rownames(m)<-1:5;colnames(m)<-1:5
for(i in 1:5){for(j in 1:5){m[i,j]<-(as.numeric(rownames(m)[i])-as.numeric(rownames(m)[j]))}}
m
Thanks for any help!
Upvotes: 10
Views: 6115
Reputation: 1004
The answers here may well do the job just fine, but I ended up just throwing together a function that should do this with some simple brute force base logic. Thought I'd share here in case anyone else might get use out of it.
diffmat = function(x){
D = matrix(as.numeric(NA), NROW(x), NROW(x))
for (i in 1:NROW(x)){
d = x[[i]] - x[-i]
D[i,-i] = d
}
if (!all(is.na(diag(D)))){
stop("Not all diagonal elements zero")
}
diag(D) = 0
if (!is.null(names(x))) colnames(D) = rownames(D) = names(x)
return(D)
}
Use example:
> x = runif(5)
> names(x) = LETTERS[1:length(x)]
> x
A B C D E
0.22095809 0.16006394 0.07402069 0.29795031 0.16199981
> diffmat(x)
A B C D E
A 0.00000000 0.060894142 0.14693739 -0.07699223 0.058958278
B -0.06089414 0.000000000 0.08604325 -0.13788637 -0.001935864
C -0.14693739 -0.086043252 0.00000000 -0.22392962 -0.087979116
D 0.07699223 0.137886368 0.22392962 0.00000000 0.135950504
E -0.05895828 0.001935864 0.08797912 -0.13595050 0.000000000
Upvotes: 1
Reputation: 8435
I would use a vapply
function - it is not much slower than the matrix
approach ... and i don't like how outer
returns the output. See below:
dd <- 1:5
vapply(seq_along(dd),
FUN = function(i, X) X[[i]] - X,
FUN.VALUE = numeric(length(dd)),
dd)
vf <- function() vapply(seq_along(dd),
FUN = function(i, X) X[[i]] - X,
FUN.VALUE = numeric(length(dd)),
dd)
mf <- function() matrix(dd, length(dd), length(dd), byrow=T) -
matrix(dd, length(dd), length(dd), byrow=FALSE)
microbenchmark(vf(), mf(), times = 1e4)
Unit: microseconds
expr min lq mean median uq max neval cld
vf() 20.527 22.037 26.678118 22.942 24.149 785.434 1000 b
mf() 4.227 4.831 6.343785 5.132 5.434 503.499 1000 a
Upvotes: 1
Reputation: 23768
This is typically done with outer
.
outer(1:5, 1:5, '-')
See ?outer
for details.
Upvotes: 22
Reputation: 4614
A possible solution:
matrix(vec, 5, 5, byrow=TRUE) - matrix(vec, 5, 5, byrow=FALSE)
or even
matrix(vec, 5, 5, byrow=TRUE) - vec
Please excuse hardcoded 5
, it's for simplicity.
Upvotes: 2