Error: The observed covariance matrix is not a symmetric matrix

3 replies [Last post]
Daria Henning's picture
Offline
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

mhunter's picture
Offline
Joined: 07/31/2009
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!

Daria Henning's picture
Offline
Joined: 12/18/2013
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.

tbates's picture
Offline
Joined: 07/31/2009
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