Error: The observed covariance matrix is not a symmetric matrix

I've got the following error message when fitting a one factor model:
Error: The observed covariance matrix is not a symmetric matrix
Below is the code. When looking at the cov matrix it looked quite summetrical to me...
Does somebody have suggestions?
Thanks a lot in advance.
obsnames = c("intrthought","recdreams","flashbk","emoreact","physreact","avoidthou","avoidremind","amnesia","lossinter","detach","restrAff","foreFut","SleepDis","Irritab","DifConcentr","Hypervigilance","ExaggStartle")
obslabels = list (obsnames,obsnames)
values = c(1.00,
.70, 1.00,
.60, .70, 1.00,
.67, .66, .64, 1.00,
.64, .66, .62, .66, 1.00,
.43, .47, .49, .50, .58, 1.00,
.53, .55, .51, .55, .63, .68, 1.00,
.34, .39, .40, .37, .45, .36, .46, 1.00,
.47, .45, .44, .48, .55, .52, .61, .52, 1.00,
.46, .49, .47, .47, .52, .46, .52, .38, .64, 1.00,
.42, .45, .41, .44, .51, .43, .48, .42, .57, .69, 1.00,
.45, .49, .45, .50, .54, .44, .49, .36, .50, .57, .53, 1.00,
.48, .48, .45, .47, .49, .45, .47, .34, .48, .54, .46, .54, 1.00,
.45, .45, .48, .47, .47, .47, .48, .37, .55, .55, .50, .53, .66, 1.00,
.51, .47, .44, .49, .53, .47, .45, .41, .55, .58, .55, .54, .62, .66, 1.00,
.46, .46, .39, .44, .45, .35, .03, .27, .40, .45, .39, .51, .46, .43, .55, 1.00,
.55, .54, .50, .53, .57, .41, .46, .36, .48, .50, .43, .54, .53, .53, .56, .72, 1.00)
SD = c(1.42,1.47,1.49,1.53,1.54,1.50,1.50,1.47,1.53,1.56,1.55,1.50,1.51,1.47,1.43,1.53,1.51)
krausecov = SEM_obsmatrix(lowtri=values,SD=SD,labels=obslabels)
krausecov
SEM_obsmatrix?
What's the SEM_obsmatrix() function? My bet is the problem is there. I think the matrix it constructs is "close" to symmetric, but not always within numerical precision. For example, it's not within
.Machine$double.eps
[1] 2.220446e-16
I would contact the author of the SEM_obsmatrix() function and help them re-write it with something like this
SEM_obsmatrix <- function(lowertri, SD, labels){
#TODO: Add error checking
# Make triangular matrix
ret <- matrix(NA, length(SD), length(SD))
ret[t(lower.tri(ret, diag=TRUE))] <- lowertri
# Make cor2cov
diag(ret) <- SD^2
for(i in 1:(length(SD)-1)){
for(j in (i+1):length(SD)){
ret[i,j] <- ret[i,j]*sqrt(SD[i]*SD[j])
}
}
# Make symmetric
ret[lower.tri(ret, diag=FALSE)] <- t(ret)[lower.tri(ret, diag=FALSE)]
# Add row and column names
rownames(ret) <- labels[[1]]
colnames(ret) <- labels[[2]]
# Return
return(ret)
}
# Example Usage
b <- SEM_obsmatrix(values, SD=SD, labels=obslabels)
identical(b, t(b)) #Should be TRUE if and only if b is symmetrix
Cheers!
Log in or register to post comments
In reply to SEM_obsmatrix? by mhunter
Hi, thanks for your comment.
Hi,
thanks for your comment. The SEM function is a function we have received and used in our Structural Equation modelling course at the university.
So yes I already thought that it would be in the function.
With some further googleing I finally arrived at the option that makes me guessing that the default measure machine precision in R might be too narrow??
I used the following function:
krausecov - t(krausecov),
and now I can see that some variables have the following values, which I assume to be the problematic ones...
0e+00; 1e-04
Do you have any further suggestions how to change the floating point precision ?
I hope that the problem than will dissapear.
Log in or register to post comments
In reply to Hi, thanks for your comment. by Daria Henning
lower.tri = upper.tri
you don't want to change the precision, just either round(theMatrix,8) or set the lower.tri to be the contents of the upper.tri
Best to get that original helper fixed: doing this like this is a recipe for error.
You might also get some value from the umx helper library. It has functions like umxLabel and umxStart that take the pain out of labelling and setting start values.
install.packages("devtools")
library("devtools")
install_github("umx", username = "tbates")
library("umx")
?umx
mat = matrix(c(1:9), nrow = 3, byrow = T, dimnames = list(r = paste("r", 1:3, sep = ""), c = paste("c", 1:3, sep = "")))
# r c1 c2 c3
# r1 1 2 3
# r2 4 5 6
# r3 7 8 9
mat[lower.tri(mat, diag = F)] <- mat[upper.tri(mat, diag = F)]; mat
# c
# r c1 c2 c3
# r1 1 2 3
# r2 2 5 6
# r3 3 6 9
mat[lower.tri(mat, diag = F)] <- mat[upper.tri(mat, diag = F)]; mat
Log in or register to post comments