4

I would like to make changes to my path diagram that I made with the lavaan and semPlot packages.

require(lavaan); require(semPlot)
head(mtcars)
model <-'
mpg ~ hp + gear + cyl
hp ~ cyl + disp
'
fit <- sem(model, "std", data = mtcars)
semPaths(fit, "std", fade = F, residuals = F)

enter image description here

Because mpg <- gear and mpg <- cyl are not significant, I would like to have it displayed in a transparent way (e.g., adding * to the significant pathlines or preventing from non-significant pathlines from showing up on a path diagram). Is there any way to do that?

Thank you for your support!

user8460166
  • 73
  • 1
  • 6
  • 24
  • 1
    When asking for help, you should include a simple [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input and desired output that can be used to test and verify possible solutions. – MrFlick Jul 10 '18 at 16:19
  • Thank you for your suggestion, @MrFlick. I made it reproducible. :) – user8460166 Jul 10 '18 at 16:40

2 Answers2

4

I know it's an old thread but I found it while looking for this, and figured I should provide my solution for others.

require(lavaan); require(semPlot) ; require(tidyverse)
#> Loading required package: lavaan
#> This is lavaan 0.6-3
#> lavaan is BETA software! Please report any bugs.
#> Loading required package: semPlot
#> Registered S3 methods overwritten by 'huge':
#>   method    from   
#>   plot.sim  BDgraph
#>   print.sim BDgraph
#> Loading required package: tidyverse
model <-'
mpg ~ hp + gear + cyl
hp ~ cyl + disp
'
fit <- sem(model, "std", data = mtcars)

# got this warning, but simply ignored it.
#> Warning in lav_partable_check(lavpartable, categorical =
#> lavoptions$categorical, : lavaan WARNING: parameter table does not contain
#> thresholds

lavaan::standardizedSolution(fit) %>% dplyr::filter(!is.na(pvalue)) %>% arrange(desc(pvalue)) %>% mutate_if("is.numeric","round",3) %>% select(-ci.lower,-ci.upper,-z)
#>   lhs op  rhs est.std    se pvalue
#> 1 mpg  ~ gear   0.022 0.087  0.801
#> 2 mpg  ~  cyl  -0.166 0.260  0.524
#> 3 mpg  ~   hp  -0.694 0.242  0.004
#> 4  hp ~~   hp   0.101 0.034  0.003
#> 5  hp ~1       -2.674 0.600  0.000
#> 6  hp  ~ disp   0.444 0.094  0.000
#> 7  hp  ~  cyl   0.529 0.098  0.000
#> 8 mpg ~1        4.514 0.751  0.000
#> 9 mpg ~~  mpg   0.258 0.039  0.000

pvalue_cutoff <- 0.05

obj <- semPlot:::semPlotModel(fit)

# save a copy of the original, so we can compare it later and be sure we removed only what we intended to remove
original_Pars <- obj@Pars

check_Pars <- obj@Pars %>% dplyr::filter(!(edge %in% c("int","<->") | lhs == rhs)) # this is the list of paramater to sift thru
keep_Pars <- obj@Pars %>% dplyr::filter(edge %in% c("int","<->") | lhs == rhs) # this is the list of paramater to keep asis
test_against <- lavaan::standardizedSolution(fit) %>% dplyr::filter(pvalue < pvalue_cutoff, rhs != lhs)
test_against_rev <- test_against %>% rename(rhs2 = lhs,   # for some reason, the rhs and lhs are reversed in the standardizedSolution() output, for some of the values
                                            lhs = rhs) %>% # I'll have to reverse it myself, and test against both orders
    rename(rhs = rhs2)
checked_Pars <-
    check_Pars %>% semi_join(test_against, by = c("lhs", "rhs")) %>% bind_rows(
        check_Pars %>% semi_join(test_against_rev, by = c("lhs", "rhs"))
    )

obj@Pars <- keep_Pars %>% bind_rows(checked_Pars)

#let's verify by looking at the list of the edges we removed from the object
anti_join(original_Pars,obj@Pars)
#> Joining, by = c("label", "lhs", "edge", "rhs", "est", "std", "group", "fixed", "par")
#>   label  lhs edge rhs        est        std group fixed par
#> 1       gear   ~> mpg  0.1582792  0.0218978       FALSE   2
#> 2        cyl   ~> mpg -0.4956938 -0.1660012       FALSE   3

# great, let's plot
semPlot::semPaths(obj, "std",fade = F, residuals = F)

Note this is highly tinkered, and the criterion for exclusion should be modified to your needs (especially the (edge %in% c("int","<->") parts)

Created on 2019-07-09 by the reprex package (v0.3.0)

redacted session_info()

#>  lavaan       * 0.6-3     2018-09-22 [1] CRAN (R 3.6.0)
#>  semPlot      * 1.1.1     2019-04-05 [1] CRAN (R 3.6.0)
#>  tidyverse    * 1.2.1     2017-11-14 [1] CRAN (R 3.6.0)
MichaelPi
  • 41
  • 3
  • Thank you for answering my question, @MichaelPi! However, your code did not run in my R environment. For example, this part: `lavaan::standardizedSolution(fit) %>% filter(!is.na(pvalue)) %>% arrange(desc(pvalue)) %>% mutate_if("is.numeric","round",3) %>% select(-ci.lower,-ci.upper,-z) ` : Would you double check if it works for you? – user8460166 Jul 09 '19 at 19:17
  • 1
    I edited the code and added `dplyr::` to all the `filter()` calls I spotted. I think this will do the trick.. – MichaelPi Jul 10 '19 at 20:29
  • Thank you for helping with such a beginners' struggle. Now the code works perfectly. I appreciate your help. :) – user8460166 Jul 11 '19 at 03:21
3

I have recently discovered the lavaanPlot package, which allows to show the coefficients for a specified significance criteria. The code is:

require(lavaan); require(lavaanPlot)
head(mtcars)
model <-'
mpg ~ hp + gear + cyl
hp ~ cyl + disp
'
fit <- sem(model, "std", data = mtcars)
sem.model <- lavaanPlot(model = fit, node_options = list(shape = "box", fontname = "Helvetica"), edge_options = list(color = "grey"), coefs = TRUE, sig = 0.05)

The resulting image looks like this: enter image description here

I believe it can be further customized.

flxflks
  • 498
  • 2
  • 13