Reputation:
How can I efficiently generate a symmetric matrix with 1s in the diagonal from a data frame like this (original df has ~12000 combinations of variables):
mydata <- data.frame(Var1 = c("A", "A", "B"), Var2 = c("B", "C", "C"), values = c(2, 3, 6))
Var1 Var2 values
1 A B 2
2 A C 3
3 B C 6
I'd like to have the following output:
mymatrix <- matrix(c(1,2,3,2,1,6,3,6,1), ncol = 3, dimnames = list(c("A", "B", "C"), c("A", "B", "C")))
A B C
A 1 2 3
B 2 1 6
C 3 6 1
dcast in itself won't give a proper symmetric matrix and maybe there is an efficient way to do it.
Upvotes: 1
Views: 1261
Reputation: 206207
The easiest way it just to copy the upper.tri to the lower.tri. For example
#create new matrix
vals<-sort(unique(c(as.character(mydata$Var1), as.character(mydata$Var2))))
nm<-matrix(NA, nrow=length(vals), ncol=length(vals), dimnames=list(vals, vals))
diag(nm)<-1
#fill
nm[as.matrix(mydata[, 1:2])] <- mydata[,3]
#symmetric
nm[lower.tri(nm)] <- nm[upper.tri(nm)]
nm
# A B C
# A 1 2 3
# B 2 1 6
# C 3 6 1
Of course this assumes all the values you are filling in are in the upper triangle of the matrix to begin with
Alternatively, you could double fill
nm <- matrix(NA, nrow=length(vals), ncol=length(vals), dimnames=list(vals, vals))
diag(nm) <- 1
#fill
nm[as.matrix(mydata[, 1:2])] <- mydata[,3]
#fill reversed
nm[as.matrix(mydata[, 2:1])] <- mydata[,3]
nm
# A B C
# A 1 2 3
# B 2 1 6
# C 3 6 1
which doesn't assume anything about the order in which the matrix is filled.
Finally, you could coalesce with the transpose (if you don't mind using a non-base helper function)
nm<-matrix(NA, nrow=length(vals), ncol=length(vals), dimnames=list(vals, vals))
diag(nm)<-1
#fill
nm[as.matrix(mydata[, 1:2])] <- mydata[,3]
#coalesce
nm<-coalesce(nm, t(nm))
nm
# A B C
# A 1 2 3
# B 2 1 6
# C 3 6 1
Upvotes: 1