How to connect the residual (error) of latent variable to residual (error) of the observed variable

1,531 views
Skip to first unread message

Alaa Al Dahdouh

unread,
Nov 29, 2017, 6:36:57 AM11/29/17
to lavaan

Hi,

I am new to Lavaan but I have previous experience with SPSS AMOS. I have a model which works well in AMOS and I tried to re-apply it in Lavaan but obviously I failed. Here, I am going to include a short version of the model as an example of the issue.

In the model, we have 3 latent variables (Culture, Reward, Team), each latent variable was measured by 3 observed variables. The regression among the latent variables are as follow:
Culture -> Reward -> Team.

Since the latent variable Team was predicted by Reward, then it should be a residual (error) which are remained unexplained by the predictor variable. In our model, the modification indices suggested that this error (attached to Team variable) correlate to one of the errors of Reward's observed variables. Such correlation has some support in our theoretical hypotheses and therefore we allowed it to happen. See the figure:


Now, the question is: how can we write this correlation in the model syntax?
Please note, I tried to use "sem" function which automatically creates error variables of the observed variables. When you write the covariance between latent variable's error and the observed variable's error as simply as you write the covariance between two observed variables the program generates error.

I also didn't find useful example on how to use "lavaan" function to define the errors explicitly. I know that "lavaan" function allows a full control and does not automatically create residual variables (unless you explicitly tell it so) but how to write a model with error latent variables is not clear to me.

Mikko Rönkkö

unread,
Nov 29, 2017, 6:52:14 AM11/29/17
to lav...@googlegroups.com
Hi,

When you say that you get an error message, it is always a good idea to tell the list what the message was. If I understood it correctly, this problem relates to how the model is represented as matrices. The following code example shows how it does not work and then shows a workaround.

Mikko


library(lavaan)

# This does not work

model <- '
  # measurement model
ind60 =~ x1 + x2 + x3
dem60 =~ y1 + y2 + y3 + y4
dem65 =~ y5 + y6 + y7 + y8
# regressions
dem60 ~ ind60
dem65 ~ ind60 + dem60
# error correlation
y1 ~~ dem65'


fit <- sem(model, data=PoliticalDemocracy)
summary(fit, standardized=TRUE)

# This does work

model <- '
  # measurement model
ind60 =~ x1 + x2 + x3
LATENTy1 =~ y1
dem60 =~ LATENTy1 + y2 + y3 + y4
dem65 =~ y5 + y6 + y7 + y8
# regressions
dem60 ~ ind60
dem65 ~ ind60 + dem60
# error correlation
LATENTy1 ~~ dem65'

fit <- sem(model, data=PoliticalDemocracy)
summary(fit, standardized=TRUE)




--
You received this message because you are subscribed to the Google Groups "lavaan" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lavaan+un...@googlegroups.com.
To post to this group, send email to lav...@googlegroups.com.
Visit this group at https://groups.google.com/group/lavaan.
For more options, visit https://groups.google.com/d/optout.

Edward Rigdon

unread,
Nov 29, 2017, 6:55:49 AM11/29/17
to lav...@googlegroups.com
Make the covariance connection between the two variables
Team ~~ y6
Since both are dependent, this is covariance between their residuals.

--
You received this message because you are subscribed to the Google Groups "lavaan" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lavaan+unsubscribe@googlegroups.com.

Alaa Al Dahdouh

unread,
Nov 29, 2017, 9:13:55 AM11/29/17
to lavaan
Thank you Edward, but a simple covariance between the two variables does not work. That was my first attempt and it generated the following error:
Error in lav_model(lavpartable = lavpartable, lavoptions = lavoptions,  : 
  lavaan ERROR: parameter is not defined: Team ~~ y6

It seems that Lavaan does not support covariance between observed and latent variables. The workaround solution provided Mikko Rönkkö has just confirmed this assumption. He suggested to create a "dummy" latent variable right in the middle between the observed variable "y6" and its latent construct "Team". Since the "dummy" variable is latent and the "Team" is also latent, Lavaan will allow them to connect.

Thank you anyway.
To unsubscribe from this group and stop receiving emails from it, send an email to lavaan+un...@googlegroups.com.

Yves Rosseel

unread,
Nov 29, 2017, 9:35:51 AM11/29/17
to lav...@googlegroups.com
This is due to the LISREL representation that is used by lavaan. In
LISREL, there is no such a thing as a covariance between an observed and
a latent variable. The only way out (for now) is to create a
single-indicator latent variable, as Mikko pointed out. One day, lavaan
may do this automatically (as it is already doing if you are using a
regression instead).

Amos is using the RAM representation, where these covariances are just
part of the 'symmetric' matrix.

Yves.

On 11/29/2017 03:13 PM, Alaa Al Dahdouh wrote:
> Thank you Edward, but a simple covariance between the two variables does
> not work. That was my first attempt and it generated the following error:
>
> Error in lav_model(lavpartable = lavpartable, lavoptions = lavoptions, :
> lavaan ERROR: parameter is not defined: Team ~~ y6
>
>
> It seems that Lavaan does not support covariance between observed and
> latent variables. The workaround solution provided Mikko Rönkkö has just
> confirmed this assumption. He suggested to create a "dummy" latent
> variable right in the middle between the observed variable "y6" and its
> latent construct "Team". Since the "dummy" variable is latent and the
> "Team" is also latent, Lavaan will allow them to connect.
>
> Thank you anyway.
>
>
> On Wednesday, November 29, 2017 at 1:55:49 PM UTC+2, Edward Rigdon wrote:
>
> Make the covariance connection between the two variables
> Team ~~ y6
> Since both are dependent, this is covariance between their residuals.
>
> On Nov 29, 2017 6:36 AM, "Alaa Al Dahdouh" <alaaal...@gmail.com
> <javascript:>> wrote:
>
> <https://lh3.googleusercontent.com/-BxzZzdPd9og/Wh6bqDXGOwI/AAAAAAAAKck/F2SAQSKpCv0BX8uiJ5tBDATFXD8Jb7nQgCLcBGAs/s1600/Capture.PNG>
>
> Hi,
>
> I am new to Lavaan but I have previous experience with SPSS
> AMOS. I have a model which works well in AMOS and I tried to
> re-apply it in Lavaan but obviously I failed. Here, I am going
> to include a short version of the model as an example of the issue.
>
> In the model, we have 3 latent variables (Culture, Reward,
> Team), each latent variable was measured by 3 observed
> variables. The regression among the latent variables are as follow:
> *Culture -> Reward -> Team.*
>
> Since the latent variable Team was predicted by Reward, then it
> should be a residual (error) which are remained unexplained by
> the predictor variable. In our model, the modification indices
> suggested that this error (attached to Team variable) correlate
> to one of the errors of Reward's observed variables. Such
> correlation has some support in our theoretical hypotheses and
> therefore we allowed it to happen. See the figure:
>
>
> Now, the question is: how can we write this correlation in the
> model syntax?
> Please note, I tried to use "sem" function which automatically
> creates error variables of the observed variables. When you
> write the covariance between latent variable's error and the
> observed variable's error as simply as you write the covariance
> between two observed variables the program generates error.
>
> I also didn't find useful example on how to use "lavaan"
> function to define the errors explicitly. I know that "lavaan"
> function allows a full control and does not automatically create
> residual variables (unless you explicitly tell it so) but how to
> write a model with error latent variables is not clear to me.
>
> --
> You received this message because you are subscribed to the
> Google Groups "lavaan" group.
> To unsubscribe from this group and stop receiving emails from
> it, send an email to lavaan+un...@googlegroups.com <javascript:>.
> To post to this group, send email to lav...@googlegroups.com
> <javascript:>.
> <https://groups.google.com/group/lavaan>.
> For more options, visit https://groups.google.com/d/optout
> <https://groups.google.com/d/optout>.
>
> --
> You received this message because you are subscribed to the Google
> Groups "lavaan" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to lavaan+un...@googlegroups.com
> <mailto:lavaan+un...@googlegroups.com>.
> To post to this group, send email to lav...@googlegroups.com
> <mailto:lav...@googlegroups.com>.

Alaa Al Dahdouh

unread,
Nov 29, 2017, 9:40:42 AM11/29/17
to lavaan
Thank you very much Mikko for your clarification. I have just followed your suggestion and it seems the model fit, but with following warning:
Warning message:
In lav_object_post_check(object) :
  lavaan WARNING: the covariance matrix of the residuals of the observed
                variables (theta) is not positive definite;
                use inspect(fit,"theta") to investigate.

The output of the Inspect function is quite puzzling because my original model is really big. For clarity and as you mentioned previously, it is better to show the results. Therefore, I listed the output below (near the end of this post), if you wish to help. Anyway, I knew that this warning may not need to be fixed providing that we understand the model is written correctly as we really want it to be. Thus, I moved forward and checked the model fit indices (CFI, RMSEA, TLI) and they showed comparable values to our results in AMOS, so far so good.

The real issue now is that when I ran the bootstrapping of 100 samples I got an error as shown below:
fit <- sem(model, data = dataset, estimator="WLS" , orthogonal = TRUE, se = "bootstrap", bootstrap = 100);
Error in se[def.idx] <- sqrt(diag.def.cov) : replacement has length zero

Running it again with verbose = TRUE, I got a repeated error as shown below:
 ... bootstrap draw number:    2     FAILED: creating sample statistics
 ... bootstrap draw number:    3     FAILED: creating sample statistics
... ... .
... .. .
... . .
... bootstrap draw number: 99 FAILED: creating sample statistics ... bootstrap draw number: 100 FAILED: creating sample statistics done. Error in se[def.idx] <- sqrt(diag.def.cov) : replacement has length zero

Can you please help in that?

---------------
> inspect(fit,"theta")
                  m___16 m___12 m___15 g_M_02 g_M_05 g_M_03 I_E_02 I_E_09 I_L_13 o_HIE_24
mind_a_g_16_Invrt  0.158                                                                 
mind_a_g_12_Invrt  0.000  0.477                                                          
mind_a_f_15        0.000  0.000  0.805                                                   
goal_M_02          0.000  0.000  0.000  0.242                                            
goal_M_05          0.000  0.000  0.000  0.000  0.251                                     
goal_M_03          0.000  0.000  0.000  0.000  0.000  0.232                              
Innov_E_02         0.000  0.000  0.000  0.000  0.000  0.000  0.273                       
Innov_E_09         0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.522                
Innov_L_13_Invrt   0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.404         
oca_HIE_24         0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.630  
oca_HIE_20         0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
oca_HIE_16         0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
growth_REW_06      0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
growth_ENC_01      0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
growth_ENC_02      0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
growth_COS_16      0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
growth_TES_17      0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
growth_TES_18      0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
goal_A_13          0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
goal_A_12          0.000 -0.106  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
goal_A_11          0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  
                  o_HIE_20 o_HIE_1 g_REW_ g_ENC_01 g_ENC_02 g_COS_ g_TES_17 g_TES_18 g_A_13
mind_a_g_16_Invrt                                                                          
mind_a_g_12_Invrt                                                                          
mind_a_f_15                                                                                
goal_M_02                                                                                  
goal_M_05                                                                                  
goal_M_03                                                                                  
Innov_E_02                                                                                 
Innov_E_09                                                                                 
Innov_L_13_Invrt                                                                           
oca_HIE_24                                                                                 
oca_HIE_20         0.156                                                                   
oca_HIE_16         0.000    0.555                                                          
growth_REW_06      0.000   -0.073   0.000                                                  
growth_ENC_01      0.000    0.000   0.066  0.191                                           
growth_ENC_02      0.000    0.000   0.000  0.000    0.042                                  
growth_COS_16      0.000    0.000   0.000  0.000    0.000    0.214                         
growth_TES_17      0.000    0.000   0.000  0.000    0.000    0.000  0.079                  
growth_TES_18      0.000    0.000   0.000  0.000    0.000    0.000  0.000    0.368         
goal_A_13          0.000    0.000   0.000  0.000    0.000    0.000  0.000    0.000    0.277
goal_A_12          0.000    0.000   0.000  0.000    0.000    0.000  0.000    0.000    0.000
goal_A_11          0.000    0.000   0.000  0.000    0.000    0.000  0.000    0.000    0.000
                  g_A_12 g_A_11
mind_a_g_16_Invrt              
mind_a_g_12_Invrt              
mind_a_f_15                    
goal_M_02                      
goal_M_05                      
goal_M_03                      
Innov_E_02                     
Innov_E_09                     
Innov_L_13_Invrt               
oca_HIE_24                     
oca_HIE_20                     
oca_HIE_16                     
growth_REW_06                  
growth_ENC_01                  
growth_ENC_02                  
growth_COS_16                  
growth_TES_17                  
growth_TES_18                  
goal_A_13                      
goal_A_12          0.320       
goal_A_11          0.000  0.590

>
 Many thanks in advance.
Alaa

Mikko Rönkkö

unread,
Nov 29, 2017, 9:45:45 AM11/29/17
to lav...@googlegroups.com
Hi

The problem is here (or at least a problem). I assume that you have constrained one error term variance to zero and allowed that error term to covary with something else in the model. A variable that does not vary cannot covary, and this is the reason for the error.


On 29 Nov 2017, at 16.40, Alaa Al Dahdouh <alaaal...@gmail.com> wrote:

oca_HIE_16         0.000    0.555                                                          
growth_REW_06      0.000   -0.073   0.000     

Other than that, I would recommend against boostrapping before you have solved all the issues with model fit. You are just wasting your time waiting for the bootstrap to complete to see that you have non-bootstrap related issues.

Mikko

Alaa Al Dahdouh

unread,
Nov 29, 2017, 10:18:58 AM11/29/17
to lavaan
Hi again,

Thank you for your prompt answer.
I know that bootstrapping has nothing to do with the warning message, but actually, the bootstrapping is the aim as to why we intend to re-apply the model in R Lavaan. When I ran the bootstrapping of the model using only 100 samples in AMOS, the program hanged and kept working for a long time (54 hours) before I ended it. So, the bootstrapping is what we concern about right now. I need to report the CIs of some direct, indirect, and total effects in the model. Thus, I really appreciate your help in this.

Regarding of whether I fixed the variance of any error to zero, I really didn't do so explicitly using the model syntax "0*variable", but I am not aware if the "sem" function did it automatically or not. Below you can find my complete syntax model and the sem function. The parts in bold font are those I updated upon your suggestion.

# create a model
model <- '# latent variable definitions
            Entity_Ability           =~ mind_a_g_16_Invrt + mind_a_g_12_Invrt + mind_a_f_15
            Mastery_Goal          =~ goal_M_02 + goal_M_05 + goal_M_03
            Innov                       =~ Innov_E_02 + Innov_E_09 + Innov_L_13_Invrt
            Hierarchy                 =~ oca_HIE_24 + oca_HIE_20 + oca_HIE_16
            e52                         =~ growth_REW_06
            Sprt_Rwd_Mang     =~ e52 + growth_ENC_01 + growth_ENC_02
            Oper_Capa_team      =~ growth_COS_16 + growth_TES_17 + growth_TES_18
            Avoidance                =~ goal_A_13 + goal_A_12 + goal_A_11

       
            # regressions
            Sprt_Rwd_Mang      ~ HR_SRM*Hierarchy
            Mastery_Goal         ~ ENTT_MG*Entity_Ability + SRM_MG*Sprt_Rwd_Mang
            Oper_Capa_team    ~ SRM_OCT*Sprt_Rwd_Mang
            Avoidance              ~ HR_AVOD*Hierarchy
            Innov                      ~ MG_INNOV*Mastery_Goal + SRM_INNOV*Sprt_Rwd_Mang + OCT_INNOV*Oper_Capa_team + AVOD_INNOV*Avoidance


            # residual (co)variances
            e52                         ~~ Oper_Capa_team 
            growth_REW_06     ~~ growth_ENC_01 + oca_HIE_16
            goal_A_12               ~~ mind_a_g_12_Invrt


            # indirect effects
            indEff_ENTT                := ENTT_MG*MG_INNOV
            indEff_HR_AVD           := HR_AVOD*AVOD_INNOV
            indEff_HR_SRM_dir     := HR_SRM*SRM_INNOV
            indEff_HR_SRM_MG   := HR_SRM*SRM_MG*MG_INNOV
            indEff_HR_SRM_OCT  := HR_SRM*SRM_OCT*OCT_INNOV
            indEff_SRM_MG          := SRM_MG*MG_INNOV
            indEff_SRM_OCT         := SRM_OCT*OCT_INNOV

            # total effect
            total_SRM                  := SRM_INNOV + indEff_SRM_MG + indEff_SRM_OCT
            total_HR                     := total_SRM*HR_SRM + indEff_HR_AVD
'

fit <- sem(model, data = dataset, estimator="WLS" , orthogonal = TRUE, se = "bootstrap", bootstrap = 100, verbose = TRUE);

Mikko Rönkkö

unread,
Nov 29, 2017, 10:22:43 AM11/29/17
to lav...@googlegroups.com
Hi,

This single indicator latent’s error is constrained to have zero variance by default because otherwise the model would not be identified.

 e52                         =~ growth_REW_06

Now to fix the problem, we need to know what is the purpose of this? 

            e52                         ~~ Oper_Capa_team  
            growth_REW_06     ~~ growth_ENC_01 + oca_HIE_16


Mikko

Alaa Al Dahdouh

unread,
Nov 29, 2017, 10:33:34 AM11/29/17
to lavaan
Those are the covariances between the error terms. Those variables have something in common. That is why we allow them to connect.

The parts in bold are those I updated based on your suggestion. Namely, e52 was not included before, but I added it instead of your "LATENTy1" in the your suggestion.

Thank again,

Mikko Rönkkö

unread,
Nov 29, 2017, 10:35:44 AM11/29/17
to lav...@googlegroups.com
Hi,

That does not make sense because a single indicator latent does not have an error term.

Mikko

Alaa Al Dahdouh

unread,
Nov 29, 2017, 10:51:29 AM11/29/17
to lavaan
Hi,

Ok, how do you interpret that your model works fine?
I can see that LATENTy1 is a single indicator latent variable as shown below:


LATENTy1 =~ y1
dem60 =~ LATENTy1 + y2 + y3 + y4

Yet, you can connect it to another latent variable "dem65".
LATENTy1 ~~ dem65

I also tested your model using the "PoliticalDemocracy" data set it works well.
I really appreciate your effort with me.

Alaa

Mikko Rönkkö

unread,
Nov 29, 2017, 10:54:10 AM11/29/17
to lav...@googlegroups.com
Hi,

On 29 Nov 2017, at 17.51, Alaa Al Dahdouh <alaaal...@gmail.com> wrote:

Hi,

Ok, how do you interpret that your model works fine?

I did not. I just showed how it can be estimated ;) 

I can see that LATENTy1 is a single indicator latent variable as shown below:

LATENTy1 =~ y1
dem60 =~ LATENTy1 + y2 + y3 + y4

Yet, you can connect it to another latent variable "dem65".
LATENTy1 ~~ dem65

I also tested your model using the "PoliticalDemocracy" data set it works well.
I really appreciate your effort with me.

The difference here is that I am not freeing the covariances between the errors of observed variables, but between the errors of two latent variables. Both these errors have non-zero variances.

Mikko

kma...@aol.com

unread,
Nov 30, 2017, 9:53:30 AM11/30/17
to lavaan

Alaa,
There are two ways to go about this. Mikko showed you one but your code suggests that you find the other more natural to think about.  The first way is to use the same approach that LISREL uses for path analysis and shadow an observed variable with a latent equivalent.  Mikko adapted that approach to your error covariance.

The second way is to delete the unique variance from the model and replace it with a latent variable.  In some ways, you may find this approach more intuitive because it avoids duplicate representations of the same variable.  In the following script y1UNIQE stands in for the unique variance of y1.  The variance of y1UNIQUE thus estimates the unique variance.  y1UNIQUE is represented the same way as other latent variables.  So, covariances are allowed in the phi matrix.


model2 <- '

  # measurement model
    ind60 =~ x1 + x2 + x3
    y1UNIQUE =~ 0 # latent variable to take place of y1 uniqueness

    dem60 =~ y1 + y2 + y3 + y4
    dem65 =~ y5 + y6 + y7 + y8
    y1 ~~ 0*y1 # remove y1 unique variance
    y1UNIQUE ~~ y1UNIQUE # replacement y1 unique variance
  # regressions
    y1 ~ 1*y1UNIQUE # Fix effect coefficient for unique variance to 1

    dem60 ~ ind60
    dem65 ~ ind60 + dem60
  # error correlation
    y1UNIQUE ~~ dem65'

fit2 <- sem(model2, data=PoliticalDemocracy)
summary(fit2, standardized=TRUE)

Just to clarify what Mikko said:  A single indicator can have a unique variance, but it is not usually possible to freely estimate that variance.  One typically has to fix it at a specified value prior to estimation.  That value need not be zero.  The approach given above avoids single indicators in your model syntax.  However, lavaan will include a fixed zero loading of y1UNIQUE on itself in its representation of the model.

Keith
------------------------
Keith A. Markus
John Jay College of Criminal Justice, CUNY
http://jjcweb.jjay.cuny.edu/kmarkus
Frontiers of Test Validity Theory: Measurement, Causation and Meaning.
http://www.routledge.com/books/details/9781841692203/

Reply all
Reply to author
Forward
0 new messages