-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathREADME.Rmd
301 lines (241 loc) · 10.7 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
```
# lavaanExtra: Convenience functions for `lavaan`
```{r version, echo = FALSE, message = FALSE, warning = FALSE}
flextable::set_flextable_defaults(background.color = "white")
library(lavaan)
library(lavaanExtra)
```
<!-- badges: start -->
[](https://github.com/rempsyc/lavaanExtra/actions/workflows/R-CMD-check.yaml)
[](https://cran.r-project.org/package=lavaanExtra)
[](https://rempsyc.r-universe.dev/lavaanExtra)
[](https://github.com/rempsyc/lavaanExtra/commits/main)
[](https://app.codecov.io/gh/rempsyc/lavaanExtra?branch=main)
[](https://lifecycle.r-lib.org/articles/stages.html)
[](https://shinyus.ipub.com/cranview/)
[](https://shinyus.ipub.com/cranview/)
[](https://doi.org/10.21105/joss.05701)
[](https://github.com/sponsors/rempsyc)
[](https://github.com/rempsyc?tab=followers)
[](https://github.com/rempsyc/lavaanExtra/stargazers)
<!-- badges: end -->
Affords an alternative, vector-based syntax to `lavaan`, as well as other convenience functions such as naming paths and defining indirect links automatically. Also offers convenience formatting optimized for a publication and script sharing workflow.
## Installation
You can install the `lavaanExtra` package directly from CRAN:
``` r
install.packages("lavaanExtra")
```
Or the development version from the r-universe (note that there is a 24-hour delay with GitHub):
``` r
install.packages("lavaanExtra", repos = c(
rempsyc = "https://rempsyc.r-universe.dev",
CRAN = "https://cloud.r-project.org"))
```
Or from GitHub, for the very latest version:
``` r
# If not already installed, install package `remotes` with `install.packages("remotes")`
remotes::install_github("rempsyc/lavaanExtra")
```
To see all the available functions, use:
``` r
help(package = "lavaanExtra")
```
**Dependencies:** Because `lavaanExtra` is a package of convenience functions relying on several external packages, it uses (inspired by the [`easystats`](https://easystats.github.io/easystats/) packages) a minimalist philosophy of only installing packages that you need when you need them through `rlang::check_installed()`. Should you wish to specifically install all suggested dependencies at once (you can view the full list by clicking on the CRAN badge on this page), you can run the following (be warned that this may take a long time, as some of the suggested packages are only used in the vignettes or examples):
``` r
install.packages("lavaanExtra", dependencies = TRUE)
```
## Why use `lavaanExtra`?
1. **Reusable code**. Don't repeat yourself anymore when you only want to change a few things when comparing and fitting models.
2. **Shorter code**. Because of point 1, you can have shorter code, since you write it once and simply reuse it. For items with similar patterns, you can also use `paste0()` with appropriate item numbers instead of typing each one every time.
3. **Less error-prone code**. Because of point 1, you can have less risk of human errors since you don't have possibly multiple different version of the same thing (which makes it easier to correct too).
4. **Better control over your code**. Because of point 1, you are in control of the whole flow. You change it once, and it will change it everywhere else in the script, without having to change it manually for each model.
5. **More readable code**. Because of point 1, other people (but also yourself) only have to process the information the first time to make sure it's been specified correctly, and not every time you check the new models.
6. **Prettier code**. Because it will format the model for you in a pretty way, every time. You don't have to worry about manually making your model good-looking and readable anymore.
7. **More accessible code**. You don't have to remember the exact syntax (although it is recommended) for it to work. It uses intuitive variable names that most people can understand. This benefit is most apparent for beginners, but it also saves precious typing time for veterans.
## Overview
```{r headers, results = "asis", echo = FALSE}
section.1 <- "CFA example"
section.2 <- "SEM example"
section.3 <- "Final note"
cute_cat <- function(x, header.level = 1) {
cat(rep("#", header.level), " ", x, sep = "")
}
cute_TOC <- function(section) {
cat("[", section, "]",
"(#", tolower(gsub(" ", "-", gsub(",", "", section))), ")",
"<a name = '", section, "'/>",
"\n \n",
sep = ""
)
}
invisible(lapply(
list(
section.1, section.2, section.3
),
cute_TOC
))
```
## CFA example
```{r cfaplot}
# Load libraries
library(lavaan)
library(lavaanExtra)
# Define latent variables
latent <- list(
visual = c("x1", "x2", "x3"),
textual = c("x4", "x5", "x6"),
speed = c("x7", "x8", "x9")
)
# If you have many items, you can also use the `paste0` function:
x <- paste0("x", 1:9)
latent <- list(
visual = x[1:3],
textual = x[4:6],
speed = x[7:9]
)
# Write the model, and check it
cfa.model <- write_lavaan(latent = latent)
cat(cfa.model)
# Fit the model fit and plot with `lavaanExtra::cfa_fit_plot`
# to get the factor loadings visually (optionally as PDF)
fit.cfa <- cfa_fit_plot(cfa.model, HolzingerSwineford1939)
```
<img src="man/figures/cfaplot.png" width="30%" />
```{r cfa2, results='hide'}
# Get nice fit indices with the `rempsyc::nice_table` integration
nice_fit(fit.cfa, nice_table = TRUE)
```
```{r cfa2.save, include=FALSE}
table_temp <- nice_fit(fit.cfa, nice_table = TRUE) |>
flextable::autofit()
flextable::save_as_image(table_temp,
path = "man/figures/README-cfa2-1.png",
expand = 0, res = 300
)
```
<img src="man/figures/README-cfa2-1.png" width="100%" />
## SEM example
Note that latent variables have been defined above, so we can reuse them as is, without having to redefine them.
```{r saturated}
# Define our other variables
M <- "visual"
IV <- c("ageyr", "grade")
DV <- c("speed", "textual")
# Define our lavaan lists
mediation <- list(speed = M, textual = M, visual = IV)
regression <- list(speed = IV, textual = IV)
covariance <- list(speed = "textual", ageyr = "grade")
# Define indirect effects object
indirect <- list(IV = IV, M = M, DV = DV)
# Write the model, and check it
model <- write_lavaan(
mediation = mediation,
regression = regression,
covariance = covariance,
indirect = indirect,
latent = latent,
label = TRUE
)
cat(model)
fit.sem <- sem(model, data = HolzingerSwineford1939)
```
```{r saturated2, results='hide'}
# Get regression parameters and make pretty with `rempsyc::nice_table`
lavaan_reg(fit.sem, nice_table = TRUE, highlight = TRUE)
```
```{r saturated2.save, include=FALSE}
table_temp <- lavaan_reg(fit.sem, nice_table = TRUE, highlight = TRUE) |>
flextable::autofit()
flextable::save_as_image(table_temp,
path = "man/figures/README-saturated-1.png",
expand = 0, res = 300
)
```
<img src="man/figures/README-saturated-1.png" width="100%" />
```{r correlation, results='hide'}
# Get covariances/correlations and make them pretty with
# the `rempsyc::nice_table` integration
lavaan_cor(fit.sem, nice_table = TRUE)
```
```{r covariance.save, include=FALSE}
table_temp <- lavaan_cor(fit.sem, nice_table = TRUE) |>
flextable::autofit()
flextable::save_as_image(table_temp,
path = "man/figures/README-covariance-1.png",
expand = 0, res = 300
)
```
<img src="man/figures/README-covariance-1.png" width="100%" />
```{r path2, results='hide'}
# Get nice fit indices with the `rempsyc::nice_table` integration
fit_table <- nice_fit(list(fit.cfa, fit.sem), nice_table = TRUE)
fit_table
```
```{r path2.save, include=FALSE}
table_temp <- nice_fit(list(fit.cfa, fit.sem), nice_table = TRUE) |>
flextable::autofit()
flextable::save_as_image(table_temp,
path = "man/figures/README-path2-1.png",
expand = 0, res = 300
)
```
<img src="man/figures/README-path2-1.png" width="100%" />
```{r indirect2, results='hide'}
# Save fit table to Word!
flextable::save_as_docx(fit_table, path = "fit_table.docx")
# Note that it will also render to PDF in an `rmarkdown` document
# with `output: pdf_document`, but using `latex_engine: xelatex`
# is necessary when including Unicode symbols in tables like with
# the `nice_fit()` function.
# Let's get the user-defined (e.g., indirect) effects only and make it pretty
# with the `rempsyc::nice_table` integration
lavaan_defined(fit.sem, nice_table = TRUE)
```
```{r indirect2.save, include=FALSE}
table_temp <- lavaan_defined(fit.sem, nice_table = TRUE) |>
flextable::autofit()
flextable::save_as_image(table_temp,
path = "man/figures/README-indirect2-1.png",
expand = 0, res = 300
)
```
<img src="man/figures/README-indirect2-1.png" width="100%" />
```{r, echo=FALSE}
unlink("fit_table.docx")
```
```{r plot, eval = FALSE}
# Plot our model
nice_lavaanPlot(fit.sem)
```
<img src="man/figures/semplot.png" width="70%" />
```{r plot2}
# Alternative way to plot
mylayout <- data.frame(
IV = c("", "x1", "grade", "", "ageyr", "", ""),
M = c("", "x2", "", "visual", "", "", ""),
DV = c("", "x3", "textual", "", "speed", "", ""),
DV.items = c(paste0("x", 4:6), "", paste0("x", 7:9))
) |>
as.matrix()
mylayout
```
```{r plot3, eval = FALSE}
nice_tidySEM(fit.sem, layout = mylayout, label_location = 0.7)
ggplot2::ggsave("my_semPlot.pdf", width = 6, height = 6, limitsize = FALSE)
```
<img src="man/figures/semplot2.png" width="70%" />
## Final note
This is an experimental package in a *very* early stage. Any feedback or feature request is appreciated, and the package will likely change and evolve over time based on community feedback. Feel free to open an issue or discussion to share your questions or concerns. And of course, please have a look at the other tutorials to discover even more cool features: https://lavaanExtra.remi-theriault.com/articles/
## Support me and this package
Thank you for your support. You can support me and this package here: https://github.com/sponsors/rempsyc