Error: The observed covariance matrix is not a symmetric matrix

Posted on
Picture of user. Daria Henning Joined: 12/18/2013

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

Replied on Sun, 12/22/2013 - 11:30
Picture of user. mhunter Joined: 07/31/2009

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!

Replied on Sun, 12/22/2013 - 11:49
Picture of user. Daria Henning Joined: 12/18/2013

In reply to by mhunter

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.

Replied on Sun, 12/22/2013 - 15:27
Picture of user. tbates Joined: 07/31/2009

In reply to by Daria Henning

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