' > ./docs/index.html
58 | - name: Deploy to Netlify
59 | if: contains(env.isPush, 'false')
60 | id: netlify-deploy
61 | uses: nwtgck/actions-netlify@v1.1
62 | with:
63 | publish-dir: './docs'
64 | production-branch: master
65 | github-token: ${{ secrets.GITHUB_TOKEN }}
66 | deploy-message:
67 | 'Deploy from GHA: ${{ github.event.pull_request.title || github.event.head_commit.message }} (${{ github.sha }})'
68 | # these all default to 'true'
69 | enable-pull-request-comment: false
70 | enable-commit-comment: false
71 | # enable-commit-status: true
72 | #o verwrites-pull-request-comment: true
73 | timeout-minutes: 1
74 |
--------------------------------------------------------------------------------
/.github/workflows/recheck.yml:
--------------------------------------------------------------------------------
1 | on:
2 | workflow_dispatch:
3 | inputs:
4 | which:
5 | type: choice
6 | description: Which dependents to check
7 | options:
8 | - strong
9 | - most
10 |
11 | name: Reverse dependency check
12 |
13 | jobs:
14 | revdep_check:
15 | name: Reverse check ${{ inputs.which }} dependents
16 | uses: r-devel/recheck/.github/workflows/recheck.yml@v1
17 | with:
18 | which: ${{ inputs.which }}
19 |
--------------------------------------------------------------------------------
/.github/workflows/test-coverage.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 |
9 | name: test-coverage
10 |
11 | permissions: read-all
12 |
13 | jobs:
14 | test-coverage:
15 | runs-on: ubuntu-latest
16 | env:
17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - uses: r-lib/actions/setup-r@v2
23 | with:
24 | use-public-rspm: true
25 |
26 | - uses: r-lib/actions/setup-r-dependencies@v2
27 | with:
28 | extra-packages: any::covr, any::xml2
29 | needs: coverage
30 |
31 | - name: Test coverage
32 | run: |
33 | cov <- covr::package_coverage(
34 | quiet = FALSE,
35 | clean = FALSE,
36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
37 | )
38 | covr::to_cobertura(cov)
39 | shell: Rscript {0}
40 |
41 | - uses: codecov/codecov-action@v4
42 | with:
43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }}
44 | file: ./cobertura.xml
45 | plugin: noop
46 | disable_search: true
47 | token: ${{ secrets.CODECOV_TOKEN }}
48 |
49 | - name: Show testthat output
50 | if: always()
51 | run: |
52 | ## --------------------------------------------------------------------
53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
54 | shell: bash
55 |
56 | - name: Upload test results
57 | if: failure()
58 | uses: actions/upload-artifact@v4
59 | with:
60 | name: coverage-test-failures
61 | path: ${{ runner.temp }}/package
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # History files
2 | .Rhistory
3 | .Rapp.history
4 | # Session Data files
5 | .RData
6 | # Example code in package build process
7 | *-Ex.R
8 | # Output files from R CMD build
9 | /*.tar.gz
10 | # Output files from R CMD check
11 | /*.Rcheck/
12 | # RStudio files
13 | *.Rproj.user/
14 | *.Rproj
15 | # produced vignettes
16 | vignettes/*.html
17 | vignettes/*.pdf
18 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
19 | .httr-oauth
20 | # knitr and R markdown default cache directories
21 | /*_cache/
22 | /cache/
23 | # Temporary files created by R markdown
24 | *.utf8.md
25 | *.knit.md
26 | .Rproj.user
27 | inst/doc
28 | Simple slopes macro replication.Rmd
29 | Simple slopes macro replication.nb.html
30 |
31 | *.Rhistory
32 |
33 | jtools\.sublime-project
34 |
35 | jtools\.sublime-workspace
36 |
37 | docs/
38 |
39 | tests/testthat/Rplots\.pdf
40 |
41 | CRAN-RELEASE
42 | doc
43 | Meta
44 |
45 | .DS_Store
46 | .vscode/launch.json
47 | /doc/
48 | /Meta/
49 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "r.lintr.enabled": false
3 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "R CMD check",
8 | "type": "shell",
9 | "command": "R --no-restore-data -e 'devtools::check()'",
10 | "group": "test",
11 | "presentation": {
12 | "echo": true,
13 | "reveal": "always",
14 | "focus": false,
15 | "panel": "shared"
16 | }
17 | },
18 | {
19 | "label": "Test package",
20 | "type": "shell",
21 | "command": "R --no-restore-data -e devtools::test() -e q()",
22 | "group": "test",
23 | "presentation": {
24 | "echo": true,
25 | "reveal": "always",
26 | "focus": false,
27 | "panel": "shared"
28 | },
29 | "problemMatcher": []
30 | }
31 | ]
32 | }
--------------------------------------------------------------------------------
/CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at jacob.long@sc.edu. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: interactions
2 | Type: Package
3 | Title: Comprehensive, User-Friendly Toolkit for Probing Interactions
4 | Version: 1.2.0
5 | Authors@R: person(c("Jacob","A."), "Long", email = "jacob.long@sc.edu",
6 | role = c("aut", "cre"), comment = c(ORCID = "0000-0002-1582-6214"))
7 | Description: A suite of functions for conducting and interpreting analysis
8 | of statistical interaction in regression models that was formerly part of the
9 | 'jtools' package. Functionality includes visualization of two- and three-way
10 | interactions among continuous and/or categorical variables as well as
11 | calculation of "simple slopes" and Johnson-Neyman intervals (see e.g.,
12 | Bauer & Curran, 2005 ). These
13 | capabilities are implemented for generalized linear models in addition to the
14 | standard linear regression context.
15 | URL: https://interactions.jacob-long.com
16 | BugReports: https://github.com/jacob-long/interactions/issues
17 | License: MIT + file LICENSE
18 | Encoding: UTF-8
19 | Imports:
20 | ggplot2 (>= 3.4.0),
21 | cli,
22 | generics,
23 | broom,
24 | jtools (>= 2.0.3),
25 | rlang (>= 0.3.0),
26 | tibble
27 | Suggests:
28 | cowplot,
29 | broom.mixed,
30 | glue,
31 | huxtable (>= 3.0.0),
32 | lme4,
33 | margins,
34 | sandwich,
35 | survey,
36 | knitr,
37 | rmarkdown,
38 | testthat,
39 | vdiffr
40 | Enhances:
41 | brms,
42 | rstanarm
43 | VignetteBuilder: knitr
44 | Roxygen: list(markdown = TRUE)
45 | RoxygenNote: 7.3.2.9000
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2024
2 | COPYRIGHT HOLDER: Jacob A. Long
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2024 Jacob A. Long
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 |
4 | if (getRversion() >= "3.6.0") {
5 | S3method(huxtable::as_huxtable, sim_margins)
6 | } else {
7 | export(as_huxtable.sim_margins)
8 | }
9 |
10 | if (getRversion() >= "3.6.0") {
11 | S3method(huxtable::as_huxtable, sim_slopes)
12 | } else {
13 | export(as_huxtable.sim_slopes)
14 | }
15 | S3method(glance,sim_margins)
16 | S3method(glance,sim_slopes)
17 | S3method(nobs,sim_margins)
18 | S3method(nobs,sim_slopes)
19 | S3method(plot,sim_margins)
20 | S3method(plot,sim_slopes)
21 | S3method(print,johnson_neyman)
22 | S3method(print,probe_interaction)
23 | S3method(print,sim_margins)
24 | S3method(print,sim_slopes)
25 | S3method(tidy,sim_margins)
26 | S3method(tidy,sim_slopes)
27 | export(cat_plot)
28 | export(glance)
29 | export(interact_plot)
30 | export(johnson_neyman)
31 | export(probe_interaction)
32 | export(sim_margins)
33 | export(sim_slopes)
34 | export(tidy)
35 | import(ggplot2)
36 | import(jtools)
37 | import(rlang)
38 | importFrom(cli,cat_rule)
39 | importFrom(cli,rule)
40 | importFrom(generics,glance)
41 | importFrom(generics,tidy)
42 | importFrom(stats,aggregate)
43 | importFrom(stats,approx)
44 | importFrom(stats,as.formula)
45 | importFrom(stats,coef)
46 | importFrom(stats,coefficients)
47 | importFrom(stats,complete.cases)
48 | importFrom(stats,contrasts)
49 | importFrom(stats,df.residual)
50 | importFrom(stats,ecdf)
51 | importFrom(stats,family)
52 | importFrom(stats,formula)
53 | importFrom(stats,getCall)
54 | importFrom(stats,lm)
55 | importFrom(stats,median)
56 | importFrom(stats,model.frame)
57 | importFrom(stats,model.offset)
58 | importFrom(stats,nobs)
59 | importFrom(stats,predict)
60 | importFrom(stats,pt)
61 | importFrom(stats,qnorm)
62 | importFrom(stats,qt)
63 | importFrom(stats,quantile)
64 | importFrom(stats,relevel)
65 | importFrom(stats,residuals)
66 | importFrom(stats,sd)
67 | importFrom(stats,terms)
68 | importFrom(stats,update)
69 | importFrom(stats,update.formula)
70 | importFrom(stats,vcov)
71 | importFrom(stats,weighted.mean)
72 | importFrom(utils,methods)
73 |
--------------------------------------------------------------------------------
/R/utils.R:
--------------------------------------------------------------------------------
1 | ## Quicker way to get last item of vector
2 | last <- function(x) {return(x[length(x)])}
3 | ## Just so code reads more clearly when using last(x)
4 | first <- function(x) {return(x[1])}
5 |
6 | # Get levels if they exist, otherwise unique
7 | ulevels <- function(x) {
8 | if (!is.null(levels(x))) {
9 | return(levels(x))
10 | } else {
11 | if (!is.numeric(x)) {
12 | return(unique(x))
13 | } else {
14 | return(sort(unique(x)))
15 | }
16 | }
17 | }
18 |
19 | make_ci_labs <- function(ci.width) {
20 |
21 | alpha <- (1 - ci.width) / 2
22 |
23 | lci_lab <- 0 + alpha
24 | lci_lab <- paste(round(lci_lab * 100, 1), "%", sep = "")
25 |
26 | uci_lab <- 1 - alpha
27 | uci_lab <- paste(round(uci_lab * 100, 1), "%", sep = "")
28 |
29 | list(lci = lci_lab, uci = uci_lab)
30 |
31 | }
32 |
33 |
34 | mean_or_base <- function(x, weights = NA) {
35 | if (is.numeric(x)) {
36 | if (all(is.na(weights))) {
37 | mean(x, na.rm = TRUE)
38 | } else {
39 | weighted.mean(x, weights, na.rm = TRUE)
40 | }
41 | } else if (!is.logical(x)) {
42 | levels(factor(x))[1]
43 | } else {
44 | FALSE
45 | }
46 | }
47 |
48 | ## Taken from panelr for handling non-synactic variable names
49 | bt <- function(x) {
50 | if (!is.null(x)) {
51 | btv <- paste0("`", x, "`")
52 | btv <- gsub("``", "`", btv, fixed = TRUE)
53 | btv <- btv %not% c("", "`")
54 | } else btv <- NULL
55 | return(btv)
56 | }
57 |
58 | un_bt <- function(x) {
59 | gsub("`", "", x)
60 | }
61 |
62 | # bt_if_needed <- function(string) {
63 | # if (make.names(string) != string) {
64 | # return(bt(string))
65 | # } else {
66 | # return(string)
67 | # }
68 | # }
69 |
70 | ## Taken from Hmisc package to avoid importing for a minor feature
71 | ## Added "levels.median"
72 | #' @importFrom stats approx
73 | #'
74 | cut2 <- function(x, cuts, m = 150, g, levels.mean = FALSE,
75 | levels.median = FALSE, digits,
76 | minmax = TRUE, oneval = TRUE, onlycuts = FALSE) {
77 | method <- 1
78 | x.unique <- sort(unique(c(x[!is.na(x)], if (!missing(cuts)) cuts)))
79 | min.dif <- min(diff(x.unique))/2
80 | min.dif.factor <- 1
81 | if (missing(digits))
82 | digits <- if (levels.mean)
83 | 5
84 | else 3
85 | oldopt <- options("digits")
86 | options(digits = digits)
87 | on.exit(options(oldopt))
88 | xlab <- attr(x, "label")
89 | if (missing(cuts)) {
90 | nnm <- sum(!is.na(x))
91 | if (missing(g))
92 | g <- max(1, floor(nnm/m))
93 | if (g < 1)
94 | stop("g must be >=1, m must be positive")
95 | options(digits = 15)
96 | n <- table(x)
97 | xx <- as.double(names(n))
98 | options(digits = digits)
99 | cum <- cumsum(n)
100 | m <- length(xx)
101 | y <- as.integer(ifelse(is.na(x), NA, 1))
102 | labs <- character(g)
103 | cuts <- approx(cum, xx, xout = (1:g) * nnm/g, method = "constant",
104 | rule = 2, f = 1)$y
105 | cuts[length(cuts)] <- max(xx)
106 | lower <- xx[1]
107 | upper <- 1e+45
108 | up <- low <- double(g)
109 | i <- 0
110 | for (j in 1:g) {
111 | cj <- if (method == 1 || j == 1)
112 | cuts[j]
113 | else {
114 | if (i == 0)
115 | stop("program logic error")
116 | s <- if (is.na(lower))
117 | FALSE
118 | else xx >= lower
119 | cum.used <- if (all(s))
120 | 0
121 | else max(cum[!s])
122 | if (j == m)
123 | max(xx)
124 | else if (sum(s) < 2)
125 | max(xx)
126 | else approx(cum[s] - cum.used, xx[s], xout = (nnm -
127 | cum.used)/(g - j + 1),
128 | method = "constant",
129 | rule = 2, f = 1)$y
130 | }
131 | if (cj == upper)
132 | next
133 | i <- i + 1
134 | upper <- cj
135 | y[x >= (lower - min.dif.factor * min.dif)] <- i
136 | low[i] <- lower
137 | lower <- if (j == g)
138 | upper
139 | else min(xx[xx > upper])
140 | if (is.na(lower))
141 | lower <- upper
142 | up[i] <- lower
143 | }
144 | low <- low[1:i]
145 | up <- up[1:i]
146 | variation <- logical(i)
147 | for (ii in 1:i) {
148 | r <- range(x[y == ii], na.rm = TRUE)
149 | variation[ii] <- diff(r) > 0
150 | }
151 | if (onlycuts)
152 | return(unique(c(low, max(xx))))
153 | flow <- format(low)
154 | fup <- format(up)
155 | bb <- c(rep(")", i - 1), "]")
156 | labs <- ifelse(low == up | (oneval & !variation), flow,
157 | paste("[", flow, ",", fup, bb, sep = ""))
158 | ss <- y == 0 & !is.na(y)
159 | if (any(ss))
160 | stop_wrap("categorization error in cut2. Values of x not appearing in
161 | any interval:", paste(format(x[ss], digits = 12),
162 | collapse = " "),
163 | "Lower endpoints:", paste(format(low, digits = 12),
164 | collapse = " "),
165 | "\nUpper endpoints:", paste(format(up, digits = 12),
166 | collapse = " "))
167 | y <- structure(y, class = "factor", levels = labs)
168 | } else {
169 | if (minmax) {
170 | r <- range(x, na.rm = TRUE)
171 | if (r[1] < cuts[1])
172 | cuts <- c(r[1], cuts)
173 | if (r[2] > max(cuts))
174 | cuts <- c(cuts, r[2])
175 | }
176 | l <- length(cuts)
177 | k2 <- cuts - min.dif
178 | k2[l] <- cuts[l]
179 | y <- cut(x, k2)
180 | if (!levels.mean) {
181 | brack <- rep(")", l - 1)
182 | brack[l - 1] <- "]"
183 | fmt <- format(cuts)
184 | labs <- paste("[", fmt[1:(l - 1)], ",", fmt[2:l],
185 | brack, sep = "")
186 | if (oneval) {
187 | nu <- table(cut(x.unique, k2))
188 | if (length(nu) != length(levels(y)))
189 | stop("program logic error")
190 | levels(y) <- ifelse(nu == 1, c(fmt[1:(l - 2)],
191 | fmt[l]), labs)
192 | }
193 | else levels(y) <- labs
194 | }
195 | }
196 | if (levels.mean) {
197 | means <- tapply(x, y, function(w) mean(w, na.rm = TRUE))
198 | levels(y) <- format(means)
199 | } else if (levels.median) {
200 | medians <- tapply(x, y, function(w) median(w, na.rm = TRUE))
201 | levels(y) <- format(medians)
202 | }
203 | attr(y, "class") <- "factor"
204 | # if (length(xlab))
205 | # label(y) <- xlab
206 | y
207 | }
208 |
209 | # Some shorthand functions to automatically exclude NA
210 | quant <- function(x, ...) {
211 | quantile(x, ..., na.rm = TRUE)
212 | }
213 | min2 <- function(...) {
214 | min(..., na.rm = TRUE)
215 | }
216 | max2 <- function(...) {
217 | max(..., na.rm = TRUE)
218 | }
219 |
220 | # Avoiding unnecessary import of scales --- this is scales::squish
221 | squish <- function(x, range = c(0, 1), only.finite = TRUE) {
222 | force(range)
223 | finite <- if (only.finite)
224 | is.finite(x)
225 | else TRUE
226 | x[finite & x < range[1]] <- range[1]
227 | x[finite & x > range[2]] <- range[2]
228 | x
229 | }
230 |
231 | #'@export
232 | #'@importFrom generics tidy
233 | generics::tidy
234 |
235 | #'@export
236 | #'@importFrom generics glance
237 | generics::glance
238 |
239 | ### Hadley update #############################################################
240 | # modified from https://stackoverflow.com/questions/13690184/update-inside-a-function-
241 | # only-searches-the-global-environment
242 | #' @importFrom stats update.formula
243 |
244 | j_update <- function(mod, formula = NULL, data = NULL, offset = NULL,
245 | weights = NULL, call.env = parent.frame(), ...) {
246 | call <- getCall(mod)
247 | if (is.null(call)) {
248 | stop("Model object does not support updating (no call)", call. = FALSE)
249 | }
250 | term <- terms(mod)
251 | if (is.null(term)) {
252 | stop("Model object does not support updating (no terms)", call. = FALSE)
253 | }
254 |
255 | if (!is.null(data)) call$data <- data
256 | if (!is.null(formula)) call$formula <- update.formula(call$formula, formula)
257 | env <- attr(term, ".Environment")
258 | # Jacob add
259 | # if (!is.null(offset))
260 | call$offset <- offset
261 | # if (!is.null(weights))
262 | call$weights <- weights
263 |
264 |
265 | extras <- as.list(match.call())[-1]
266 | extras <- extras[which(names(extras) %nin% c("mod", "formula", "data",
267 | "offset", "weights",
268 | "call.env"))]
269 | for (i in seq_along(extras)) {
270 | if (is.name(extras[[i]])) {
271 | extras[[i]] <- eval(extras[[i]], envir = call.env)
272 | }
273 | }
274 |
275 | existing <- !is.na(match(names(extras), names(call)))
276 | for (a in names(extras)[existing]) call[[a]] <- extras[[a]]
277 | if (any(!existing)) {
278 | call <- c(as.list(call), extras[!existing])
279 | call <- as.call(call)
280 | }
281 |
282 | if (is.null(call.env)) {call.env <- parent.frame()}
283 |
284 | eval(call, env, call.env)
285 | }
286 |
287 | # adapted from https://stackoverflow.com/a/42742370
288 | # Looking for whether a method is defined for a given object (...)
289 | # getS3method() doesn't work for something like merMod because the string
290 | # "merMod" is not in the vector returned by class()
291 | #' @importFrom utils methods
292 | check_method <- function(generic, ...) {
293 | ch <- deparse(substitute(generic))
294 | f <- X <- function(x, ...) UseMethod("X")
295 | for(m in methods(ch)) assign(sub(ch, "X", m, fixed = TRUE), "body<-"(f, value = m))
296 | tryCatch({
297 | X(...)
298 | TRUE
299 | }, error = function(e) {
300 | FALSE
301 | })
302 | }
303 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | output: github_document
3 | ---
4 |
5 |
6 |
7 | ```{r, echo = FALSE}
8 | knitr::opts_chunk$set(
9 | collapse = FALSE,
10 | comment = "#>",
11 | fig.path = "man/figures/",
12 | fig.width = 6.5,
13 | fig.height = 4,
14 | #render = knitr::normal_print,
15 | #dev.args=list(type="cairo"),
16 | # dev = "png",
17 | dpi = 300,
18 | retina = 1
19 | )
20 | library(jtools)
21 | ```
22 |
23 | # interactions
24 |
25 |
26 | [](https://cran.r-project.org/package=interactions)
27 | [](https://github.com/jacob-long/interactions)
28 | [](https://cran.r-project.org/package=interactions)
29 | [](https://github.com/jacob-long/interactions/actions/workflows/R-CMD-check.yaml)
30 | [](https://ci.appveyor.com/project/jacob-long/interactions)
31 | [](https://app.codecov.io/gh/jacob-long/interactions)
32 | [](https://www.repostatus.org/#active) [](https://opensource.org/licenses/MIT)
33 |
34 |
35 |
36 | This package consists of a number of tools for the analysis and
37 | interpretation of statistical interactions in regression models. Some of these
38 | features, especially those that pertain to visualization, are not highly
39 | labor-intensive to do oneself but are tedious and error-prone when done
40 | "by hand."
41 |
42 | Quick rundown of features:
43 |
44 | * simple slopes analysis
45 | * calculation of Johnson-Neyman intervals
46 | * visualization of predicted and observed values using `ggplot2`
47 |
48 | All of these are implemented in a consistent interface designed to be as
49 | simple as possible with tweaks and guts available to advanced users.
50 | GLMs, models from the `survey` package, and multilevel models from `lme4`
51 | are fully supported as is visualization for Bayesian models from `rstanaram`
52 | and `brms`. Several other model types work "out of the box" even though they
53 | are not officially supported.
54 |
55 |
56 | ## Installation
57 |
58 | The package is available via CRAN.
59 |
60 | ```r
61 | install.packages("interactions")
62 | ```
63 |
64 | ## Usage
65 |
66 | Unless you have a keen eye and good familiarity with both the
67 | underlying mathematics and the scale of your variables, it can be very
68 | difficult to look at the output of regression model that includes an
69 | interaction and completely understand what the model is telling you.
70 |
71 | This package contains several means of aiding the understanding of and
72 | doing statistical inference with interactions.
73 |
74 | ### Johnson-Neyman intervals and simple slopes analysis
75 |
76 | The "classic" way of probing an interaction effect is to calculate the
77 | slope of the focal predictor at different values of the moderator. When
78 | the moderator is categorical, this is especially informative---e.g., what is
79 | the slope for cats vs. dogs? But you can also arbitrarily choose points
80 | for continuous moderators.
81 |
82 | With that said, the more statistically rigorous way to explore these effects
83 | is to find the Johnson-Neyman interval, which tells you the range of values
84 | of the moderator in which the slope of the predictor is significant vs.
85 | nonsignificant at a specified alpha level.
86 |
87 | The `sim_slopes` function will by default find the Johnson-Neyman interval
88 | and tell you the predictor's slope at specified values of the moderator;
89 | by default either both values of binary predictors or the mean and the
90 | mean +/- one standard deviation for continuous moderators.
91 |
92 | ```{r j-n-plot}
93 | library(interactions)
94 | fiti <- lm(mpg ~ hp * wt, data = mtcars)
95 | sim_slopes(fiti, pred = hp, modx = wt, jnplot = TRUE)
96 | ```
97 |
98 | The Johnson-Neyman plot can help you get a handle on what the interval
99 | is telling you, too. Note that you can look at the Johnson-Neyman interval
100 | directly with the `johnson_neyman()` function.
101 |
102 | The above all generalize to three-way interactions, too, although
103 | Johnson-Neyman intervals do not handle the second moderator in the way that
104 | they do the first.
105 |
106 | ### Visualizing interaction effects
107 |
108 | This function plots two- and three-way interactions using `ggplot2` with a
109 | similar interface to the aforementioned `sim_slopes` function. Users can
110 | customize the appearance with familiar `ggplot2` commands. It supports several
111 | customizations, like confidence intervals.
112 |
113 | ```{r interact_plot_continuous}
114 | interact_plot(fiti, pred = hp, modx = wt, interval = TRUE)
115 | ```
116 |
117 | You can also plot the observed data for comparison:
118 |
119 | ```{r interact_plot_continuous_points}
120 | interact_plot(fiti, pred = hp, modx = wt, plot.points = TRUE)
121 | ```
122 |
123 | The function also supports categorical moderators---plotting observed data
124 | in these cases can reveal striking patterns.
125 |
126 | ```{r interact_plot_factor}
127 | fitiris <- lm(Petal.Length ~ Petal.Width * Species, data = iris)
128 | interact_plot(fitiris, pred = Petal.Width, modx = Species, plot.points = TRUE)
129 | ```
130 |
131 | You may also combine the plotting and simple slopes functions by using
132 | `probe_interaction`, which calls both functions simultaneously. Categorical by
133 | categorical interactions can be investigated using the `cat_plot()` function.
134 |
135 | ## Contributing
136 |
137 | I'm happy to receive bug reports, suggestions, questions, and (most of all)
138 | contributions to fix problems and add features. I prefer you use the Github
139 | issues system over trying to reach out to me in other ways. Pull requests for
140 | contributions are encouraged.
141 |
142 | Please note that this project is released with a
143 | [Contributor Code of Conduct](https://github.com/jacob-long/interactions/blob/master/CONDUCT.md).
144 | By participating in this project you agree to abide by its terms.
145 |
146 | ## License
147 |
148 | The source code of this package is licensed under the
149 | [MIT License](https://opensource.org/licenses/mit-license.php).
150 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # interactions
5 |
6 |
7 |
8 | [](https://cran.r-project.org/package=interactions)
9 | [](https://github.com/jacob-long/interactions)
11 | [](https://cran.r-project.org/package=interactions)
13 | [](https://github.com/jacob-long/interactions/actions/workflows/R-CMD-check.yaml)
14 | [](https://ci.appveyor.com/project/jacob-long/interactions)
16 | [](https://app.codecov.io/gh/jacob-long/interactions)
17 | [](https://www.repostatus.org/#active)
20 | [](https://opensource.org/licenses/MIT)
22 |
23 |
24 | This package consists of a number of tools for the analysis and
25 | interpretation of statistical interactions in regression models. Some of
26 | these features, especially those that pertain to visualization, are not
27 | highly labor-intensive to do oneself but are tedious and error-prone
28 | when done “by hand.”
29 |
30 | Quick rundown of features:
31 |
32 | - simple slopes analysis
33 | - calculation of Johnson-Neyman intervals
34 | - visualization of predicted and observed values using `ggplot2`
35 |
36 | All of these are implemented in a consistent interface designed to be as
37 | simple as possible with tweaks and guts available to advanced users.
38 | GLMs, models from the `survey` package, and multilevel models from
39 | `lme4` are fully supported as is visualization for Bayesian models from
40 | `rstanaram` and `brms`. Several other model types work “out of the box”
41 | even though they are not officially supported.
42 |
43 | ## Installation
44 |
45 | The package is available via CRAN.
46 |
47 | ``` r
48 | install.packages("interactions")
49 | ```
50 |
51 | ## Usage
52 |
53 | Unless you have a keen eye and good familiarity with both the underlying
54 | mathematics and the scale of your variables, it can be very difficult to
55 | look at the output of regression model that includes an interaction and
56 | completely understand what the model is telling you.
57 |
58 | This package contains several means of aiding the understanding of and
59 | doing statistical inference with interactions.
60 |
61 | ### Johnson-Neyman intervals and simple slopes analysis
62 |
63 | The “classic” way of probing an interaction effect is to calculate the
64 | slope of the focal predictor at different values of the moderator. When
65 | the moderator is categorical, this is especially informative—e.g., what
66 | is the slope for cats vs. dogs? But you can also arbitrarily choose
67 | points for continuous moderators.
68 |
69 | With that said, the more statistically rigorous way to explore these
70 | effects is to find the Johnson-Neyman interval, which tells you the
71 | range of values of the moderator in which the slope of the predictor is
72 | significant vs. nonsignificant at a specified alpha level.
73 |
74 | The `sim_slopes` function will by default find the Johnson-Neyman
75 | interval and tell you the predictor’s slope at specified values of the
76 | moderator; by default either both values of binary predictors or the
77 | mean and the mean +/- one standard deviation for continuous moderators.
78 |
79 | ``` r
80 | library(interactions)
81 | fiti <- lm(mpg ~ hp * wt, data = mtcars)
82 | sim_slopes(fiti, pred = hp, modx = wt, jnplot = TRUE)
83 | ```
84 |
85 | #> JOHNSON-NEYMAN INTERVAL
86 | #>
87 | #> When wt is OUTSIDE the interval [3.69, 5.90], the slope of hp is p < .05.
88 | #>
89 | #> Note: The range of observed values of wt is [1.51, 5.42]
90 |
91 | 
92 |
93 | #> SIMPLE SLOPES ANALYSIS
94 | #>
95 | #> Slope of hp when wt = 2.238793 (- 1 SD):
96 | #>
97 | #> Est. S.E. t val. p
98 | #> ------- ------ -------- ------
99 | #> -0.06 0.01 -5.66 0.00
100 | #>
101 | #> Slope of hp when wt = 3.217250 (Mean):
102 | #>
103 | #> Est. S.E. t val. p
104 | #> ------- ------ -------- ------
105 | #> -0.03 0.01 -4.07 0.00
106 | #>
107 | #> Slope of hp when wt = 4.195707 (+ 1 SD):
108 | #>
109 | #> Est. S.E. t val. p
110 | #> ------- ------ -------- ------
111 | #> -0.00 0.01 -0.31 0.76
112 |
113 | The Johnson-Neyman plot can help you get a handle on what the interval
114 | is telling you, too. Note that you can look at the Johnson-Neyman
115 | interval directly with the `johnson_neyman()` function.
116 |
117 | The above all generalize to three-way interactions, too, although
118 | Johnson-Neyman intervals do not handle the second moderator in the way
119 | that they do the first.
120 |
121 | ### Visualizing interaction effects
122 |
123 | This function plots two- and three-way interactions using `ggplot2` with
124 | a similar interface to the aforementioned `sim_slopes` function. Users
125 | can customize the appearance with familiar `ggplot2` commands. It
126 | supports several customizations, like confidence intervals.
127 |
128 | ``` r
129 | interact_plot(fiti, pred = hp, modx = wt, interval = TRUE)
130 | ```
131 |
132 | 
133 |
134 | You can also plot the observed data for comparison:
135 |
136 | ``` r
137 | interact_plot(fiti, pred = hp, modx = wt, plot.points = TRUE)
138 | ```
139 |
140 | 
141 |
142 | The function also supports categorical moderators—plotting observed data
143 | in these cases can reveal striking patterns.
144 |
145 | ``` r
146 | fitiris <- lm(Petal.Length ~ Petal.Width * Species, data = iris)
147 | interact_plot(fitiris, pred = Petal.Width, modx = Species, plot.points = TRUE)
148 | ```
149 |
150 | 
151 |
152 | You may also combine the plotting and simple slopes functions by using
153 | `probe_interaction`, which calls both functions simultaneously.
154 | Categorical by categorical interactions can be investigated using the
155 | `cat_plot()` function.
156 |
157 | ## Contributing
158 |
159 | I’m happy to receive bug reports, suggestions, questions, and (most of
160 | all) contributions to fix problems and add features. I prefer you use
161 | the Github issues system over trying to reach out to me in other ways.
162 | Pull requests for contributions are encouraged.
163 |
164 | Please note that this project is released with a [Contributor Code of
165 | Conduct](https://github.com/jacob-long/interactions/blob/master/CONDUCT.md).
166 | By participating in this project you agree to abide by its terms.
167 |
168 | ## License
169 |
170 | The source code of this package is licensed under the [MIT
171 | License](https://opensource.org/licenses/mit-license.php).
172 |
--------------------------------------------------------------------------------
/_pkgdown.yml:
--------------------------------------------------------------------------------
1 | url: https://interactions.jacob-long.com
2 |
3 | template:
4 | bootstrap: 5
5 | params:
6 | bootswatch: litera
7 | bslib:
8 | primary: "#570008"
9 | link-color: "#73000a"
10 | base_font: {google: "Lato"}
11 | font-family-sans-serif: font_google("Lato")
12 | font-family-serif: font_google("Lato")
13 | heading_font: {google: "Raleway"}
14 | code_font: {google: "IBM Plex Mono"}
15 |
16 | navbar:
17 | type: inverse
18 | left:
19 | - icon: fa-home
20 | href: index.html
21 | - icon: fa-newspaper-o
22 | text: "News"
23 | href: news/index.html
24 | - icon: fa-file-code-o
25 | text: "Documentation"
26 | href: reference/index.html
27 | - icon: fa-book
28 | text: "Vignettes"
29 | menu:
30 | - text: Exploring interactions with continuous predictors in regression models
31 | href: articles/interactions.html
32 | - text: Plotting interactions among categorical variables in regression models
33 | href: articles/categorical.html
34 |
35 | right:
36 | - icon: fa-github fa-lg
37 | text: "Github"
38 | href: https://github.com/jacob-long/interactions
39 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # DO NOT CHANGE the "init" and "install" sections below
2 |
3 | # Download script file from GitHub
4 | init:
5 | ps: |
6 | $ErrorActionPreference = "Stop"
7 | Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1"
8 | Import-Module '..\appveyor-tool.ps1'
9 |
10 | install:
11 | ps: Bootstrap
12 |
13 | cache:
14 | - C:\RLibrary
15 |
16 | # Adapt as necessary starting from here
17 |
18 | build_script:
19 | - travis-tool.sh install_deps
20 |
21 | test_script:
22 | - travis-tool.sh run_tests
23 |
24 | on_failure:
25 | - 7z a failure.zip *.Rcheck\*
26 | - appveyor PushArtifact failure.zip
27 |
28 | artifacts:
29 | - path: '*.Rcheck\**\*.log'
30 | name: Logs
31 |
32 | - path: '*.Rcheck\**\*.out'
33 | name: Logs
34 |
35 | - path: '*.Rcheck\**\*.fail'
36 | name: Logs
37 |
38 | - path: '*.Rcheck\**\*.Rout'
39 | name: Logs
40 |
41 | - path: '\*_*.tar.gz'
42 | name: Bits
43 |
44 | - path: '\*_*.zip'
45 | name: Bits
46 |
--------------------------------------------------------------------------------
/cran-comments.md:
--------------------------------------------------------------------------------
1 | ## Test environments
2 | * Local macOS install, R 4.3.2
3 | * ubuntu-latest (via Github Actions), R-release, R-devel, oldrel-1
4 | * macOS-latest (via Github Actions), R-release
5 | * windows-latest (via Github Actions), R-release
6 | * Windows 2012 Server (on Appveyor) R-release, R-devel
7 |
8 | ## R CMD check results
9 | There were no NOTEs, WARNINGs, or ERRORs.
10 |
--------------------------------------------------------------------------------
/hex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/hex.png
--------------------------------------------------------------------------------
/inst/CITATION:
--------------------------------------------------------------------------------
1 | bibentry(
2 | bibtype = "Manual",
3 | title = "interactions: Comprehensive, User-Friendly Toolkit for Probing Interactions",
4 | author = person(c("Jacob","A."), "Long", email = "jacob.long@sc.edu",
5 | role = c("aut", "cre")),
6 | year = "2024",
7 | note = "R package version 1.2.0",
8 | url = "https://cran.r-project.org/package=interactions",
9 | key = "interactions",
10 | doi = "10.32614/CRAN.package.interactions"
11 | )
12 |
--------------------------------------------------------------------------------
/make_hex.R:
--------------------------------------------------------------------------------
1 | library(showtext)
2 | library(jtools)
3 | library(interactions)
4 | library(ggplot2)
5 | library(hexSticker)
6 |
7 | font_add("Fantasque Sans Mono", "FantasqueSansMono-Regular.ttf")
8 | font_add("monofur", "monof55.ttf")
9 |
10 | # states <- as.data.frame(state.x77)
11 | # states$HSGrad <- states$`HS Grad`
12 | # fit <- lm(Income ~ HSGrad + Murder,
13 | # data = states)
14 | # p <- effect_plot(model = fit, pred = Murder, x.label = "x", y.label = "y")
15 |
16 | set.seed(1000)
17 | x <- rnorm(1000)
18 | z <- rnorm(1000)
19 | y <- x + z + x*z + rnorm(1000, sd=.1)
20 | d <- data.frame(cbind(x, y, z))
21 | p <- interact_plot(fit <- lm(y ~ x * z, data = d), x, z, colors = "CUD Bright")
22 | p <- p + theme(axis.text.y = element_blank(), axis.text.x = element_blank(),
23 | axis.title.x = element_text(color = "white", size = 5), axis.title.y = element_text(color = "white", size = 5),
24 | panel.grid.major = element_line(linetype='longdash', size = 0.25, color = "#e8e8e8b3"), legend.position = "none") + xlim(-1, 3) +
25 | ylim(-1.75, 5.75)
26 |
27 | plot(sticker(p + theme_transparent(), package="interactions", p_family = "Fantasque Sans Mono",
28 | s_width = 1.25, s_height = 1.2, s_x = 0.93, s_y = 0.83, p_size = 4.5, p_y = 1.53, h_color = "#570008",
29 | h_fill = "#844247", url = "interactions.jacob-long.com", u_size = 1.6,
30 | u_family = "Fantasque Sans Mono", filename = "hex.png", dpi = 500))
31 |
32 |
33 | # atlantic: #466A9F
34 | # garnet: #73000a
35 | # sandstorm: #FFF2E3
36 | # azalea: #844247
37 | # dark garnet: #570008
38 |
--------------------------------------------------------------------------------
/man/as_huxtable.sim_margins.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simple_margins.R
3 | \name{as_huxtable.sim_margins}
4 | \alias{as_huxtable.sim_margins}
5 | \title{Create tabular output for simple margins analysis}
6 | \usage{
7 | as_huxtable.sim_margins(
8 | x,
9 | format = "{estimate} ({std.error})",
10 | sig.levels = c(`***` = 0.001, `**` = 0.01, `*` = 0.05, `#` = 0.1),
11 | digits = getOption("jtools-digits", 2),
12 | conf.level = 0.95,
13 | intercept = attr(x, "cond.int"),
14 | int.format = format,
15 | ...
16 | )
17 | }
18 | \arguments{
19 | \item{x}{A \code{sim_margins} object.}
20 |
21 | \item{format}{The method for sharing the slope and associated uncertainty.
22 | Default is \code{"{estimate} ({std.error})"}. See the instructions for the
23 | \code{error_format} argument of \code{\link[jtools:export_summs]{jtools::export_summs()}} for more on your
24 | options.}
25 |
26 | \item{sig.levels}{A named vector in which the values are potential p value
27 | thresholds and the names are significance markers (e.g., "*") for when
28 | p values are below the threshold. Default is
29 | \code{c(`***` = .001, `**` = .01, `*` = .05, `#` = .1)}.}
30 |
31 | \item{digits}{How many digits should the outputted table round to? Default
32 | is 2.}
33 |
34 | \item{conf.level}{How wide the confidence interval should be, if it
35 | is used. .95 (95\% interval) is the default.}
36 |
37 | \item{intercept}{Should conditional intercepts be included? Default is
38 | whatever the \code{cond.int} argument to \code{x} was.}
39 |
40 | \item{int.format}{If conditional intercepts were requested, how should
41 | they be formatted? Default is the same as \code{format}.}
42 |
43 | \item{...}{Ignored.}
44 | }
45 | \description{
46 | This function converts a \code{sim_margins} object into a
47 | \code{huxtable} object, making it suitable for use in external documents.
48 | }
49 | \details{
50 | For more on what you can do with a \code{huxtable}, see \pkg{huxtable}.
51 | }
52 |
--------------------------------------------------------------------------------
/man/as_huxtable.sim_slopes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simple_slopes.R
3 | \name{as_huxtable.sim_slopes}
4 | \alias{as_huxtable.sim_slopes}
5 | \title{Create tabular output for simple slopes analysis}
6 | \usage{
7 | as_huxtable.sim_slopes(
8 | x,
9 | format = "{estimate} ({std.error})",
10 | sig.levels = c(`***` = 0.001, `**` = 0.01, `*` = 0.05, `#` = 0.1),
11 | digits = getOption("jtools-digits", 2),
12 | conf.level = 0.95,
13 | intercept = attr(x, "cond.int"),
14 | int.format = format,
15 | ...
16 | )
17 | }
18 | \arguments{
19 | \item{x}{The \code{\link[=sim_slopes]{sim_slopes()}} object.}
20 |
21 | \item{format}{The method for sharing the slope and associated uncertainty.
22 | Default is \code{"{estimate} ({std.error})"}. See the instructions for the
23 | \code{error_format} argument of \code{\link[jtools:export_summs]{jtools::export_summs()}} for more on your
24 | options.}
25 |
26 | \item{sig.levels}{A named vector in which the values are potential p value
27 | thresholds and the names are significance markers (e.g., "*") for when
28 | p values are below the threshold. Default is
29 | \code{c(`***` = .001, `**` = .01, `*` = .05, `#` = .1)}.}
30 |
31 | \item{digits}{How many digits should the outputted table round to? Default
32 | is 2.}
33 |
34 | \item{conf.level}{How wide the confidence interval should be, if it
35 | is used. .95 (95\% interval) is the default.}
36 |
37 | \item{intercept}{Should conditional intercepts be included? Default is
38 | whatever the \code{cond.int} argument to \code{x} was.}
39 |
40 | \item{int.format}{If conditional intercepts were requested, how should
41 | they be formatted? Default is the same as \code{format}.}
42 |
43 | \item{...}{Ignored.}
44 | }
45 | \description{
46 | This function converts a \code{sim_slopes} object into a
47 | \code{huxtable} object, making it suitable for use in external documents.
48 | }
49 | \details{
50 | For more on what you can do with a \code{huxtable}, see \pkg{huxtable}.
51 | }
52 |
--------------------------------------------------------------------------------
/man/figures/interact_plot_continuous-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/man/figures/interact_plot_continuous-1.png
--------------------------------------------------------------------------------
/man/figures/interact_plot_continuous_points-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/man/figures/interact_plot_continuous_points-1.png
--------------------------------------------------------------------------------
/man/figures/interact_plot_factor-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/man/figures/interact_plot_factor-1.png
--------------------------------------------------------------------------------
/man/figures/j-n-plot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/man/figures/j-n-plot-1.png
--------------------------------------------------------------------------------
/man/figures/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/man/figures/logo.png
--------------------------------------------------------------------------------
/man/johnson_neyman.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/johnson_neyman.R
3 | \name{johnson_neyman}
4 | \alias{johnson_neyman}
5 | \title{Calculate Johnson-Neyman intervals for 2-way interactions}
6 | \usage{
7 | johnson_neyman(
8 | model,
9 | pred,
10 | modx,
11 | vmat = NULL,
12 | alpha = 0.05,
13 | plot = TRUE,
14 | control.fdr = FALSE,
15 | line.thickness = 0.5,
16 | df = "residual",
17 | digits = getOption("jtools-digits", 2),
18 | critical.t = NULL,
19 | sig.color = "#00BFC4",
20 | insig.color = "#F8766D",
21 | mod.range = NULL,
22 | title = "Johnson-Neyman plot",
23 | y.label = NULL,
24 | modx.label = NULL
25 | )
26 | }
27 | \arguments{
28 | \item{model}{A regression model. It is tested with \code{lm}, \code{glm}, and
29 | \code{svyglm} objects, but others may work as well. It should contain the
30 | interaction of interest. Be aware that just because the computations
31 | work, this does not necessarily mean the procedure is appropriate for
32 | the type of model you have.}
33 |
34 | \item{pred}{The predictor variable involved in the interaction.}
35 |
36 | \item{modx}{The moderator variable involved in the interaction.}
37 |
38 | \item{vmat}{Optional. You may supply the variance-covariance matrix of the
39 | coefficients yourself. This is useful if you are using robust standard
40 | errors, as you could if using the \pkg{sandwich} package.}
41 |
42 | \item{alpha}{The alpha level. By default, the standard 0.05.}
43 |
44 | \item{plot}{Should a plot of the results be printed? Default is \code{TRUE}.
45 | The \code{ggplot2} object is returned either way.}
46 |
47 | \item{control.fdr}{Logical. Use the procedure described in Esarey & Sumner
48 | (2017) to limit the false discovery rate? Default is FALSE. See details
49 | for more on this method.}
50 |
51 | \item{line.thickness}{How thick should the predicted line be? This is
52 | passed to \code{geom_path} as the \code{size} argument, but because of the way the
53 | line is created, you cannot use \code{geom_path} to modify the output plot
54 | yourself.}
55 |
56 | \item{df}{How should the degrees of freedom be calculated for the critical
57 | test statistic? Previous versions used the large sample approximation; if
58 | alpha was .05, the critical test statistic was 1.96 regardless of sample
59 | size and model complexity. The default is now "residual", meaning the same
60 | degrees of freedom used to calculate p values for regression coefficients.
61 | You may instead choose any number or "normal", which reverts to the
62 | previous behavior. The argument is not used if \code{control.fdr = TRUE}.}
63 |
64 | \item{digits}{An integer specifying the number of digits past the decimal to
65 | report in the output. Default is 2. You can change the default number of
66 | digits for all jtools functions with
67 | \code{options("jtools-digits" = digits)} where digits is the desired
68 | number.}
69 |
70 | \item{critical.t}{If you want to provide the critical test statistic instead
71 | relying on a normal or \emph{t} approximation, or the \code{control.fdr} calculation,
72 | you can give that value here. This allows you to use other methods for
73 | calculating it.}
74 |
75 | \item{sig.color}{Sets the color for areas of the Johnson-Neyman plot where
76 | the slope of the moderator is significant at the specified level. \code{"black"}
77 | can be a good choice for greyscale publishing.}
78 |
79 | \item{insig.color}{Sets the color for areas of the Johnson-Neyman plot where
80 | the slope of the moderator is insignificant at the specified level. \code{"grey"}
81 | can be a good choice for greyscale publishing.}
82 |
83 | \item{mod.range}{The range of values of the moderator (the x-axis) to plot.
84 | By default, this goes from one standard deviation below the observed range
85 | to one standard deviation above the observed range and the observed range
86 | is highlighted on the plot. You could instead choose to provide the
87 | actual observed minimum and maximum, in which case the range of the
88 | observed data is not highlighted in the plot. Provide the range as a vector,
89 | e.g., \code{c(0, 10)}.}
90 |
91 | \item{title}{The plot title. \code{"Johnson-Neyman plot"} by default.}
92 |
93 | \item{y.label}{If you prefer to override the automatic labelling of the
94 | y axis, you can specify your own label here. The y axis represents a
95 | \emph{slope} so it is recommended that you do not simply give the name of the
96 | predictor variable but instead make clear that it is a slope. By default,
97 | "Slope of [pred]" is used (with whatever \code{pred} is).}
98 |
99 | \item{modx.label}{If you prefer to override the automatic labelling of
100 | the x axis, you can specify your own label here. By default, the name
101 | \code{modx} is used.}
102 | }
103 | \value{
104 | \item{bounds}{The two numbers that make up the interval.}
105 | \item{cbands}{A dataframe with predicted values of the predictor's slope
106 | and lower/upper bounds of confidence bands if you would like to make your
107 | own plots}
108 | \item{plot}{The \code{ggplot} object used for plotting. You can tweak the
109 | plot like you could any other from \code{ggplot}.}
110 | }
111 | \description{
112 | \code{johnson_neyman} finds so-called "Johnson-Neyman" intervals for
113 | understanding where simple slopes are significant in the context of
114 | interactions in multiple linear regression.
115 | }
116 | \details{
117 | The interpretation of the values given by this function is important and not
118 | always immediately intuitive. For an interaction between a predictor variable
119 | and moderator variable, it is often the case that the slope of the predictor
120 | is statistically significant at only some values of the moderator. For
121 | example, perhaps the effect of your predictor is only significant when the
122 | moderator is set at some high value.
123 |
124 | The Johnson-Neyman interval provides the two values of the moderator at
125 | which the slope of the predictor goes from non-significant to significant.
126 | Usually, the predictor's slope is only significant \emph{outside} of the
127 | range given by the function. The output of this function will make it clear
128 | either way.
129 |
130 | One weakness of this method of probing interactions is that it is analogous
131 | to making multiple comparisons without any adjustment to the alpha level.
132 | Esarey & Sumner (2017) proposed a method for addressing this, which is
133 | implemented in the \code{interactionTest} package. This function implements that
134 | procedure with modifications to the \code{interactionTest} code (that package is
135 | not required to use this function). If you set \code{control.fdr = TRUE}, an
136 | alternative \emph{t} statistic will be calculated based on your specified alpha
137 | level and the data. This will always be a more conservative test than when
138 | \code{control.fdr = FALSE}. The printed output will report the calculated
139 | critical \emph{t} statistic.
140 |
141 | This technique is not easily ported to 3-way interaction contexts. You could,
142 | however, look at the J-N interval at two different levels of a second
143 | moderator. This does forgo a benefit of the J-N technique, which is not
144 | having to pick arbitrary points. If you want to do this, just use the
145 | \code{\link{sim_slopes}} function's ability to handle 3-way interactions
146 | and request Johnson-Neyman intervals for each.
147 | }
148 | \examples{
149 | # Using a fitted lm model
150 | states <- as.data.frame(state.x77)
151 | states$HSGrad <- states$`HS Grad`
152 | fit <- lm(Income ~ HSGrad + Murder*Illiteracy,
153 | data = states)
154 | johnson_neyman(model = fit, pred = Murder,
155 | modx = Illiteracy)
156 |
157 | }
158 | \references{
159 | Bauer, D. J., & Curran, P. J. (2005). Probing interactions in fixed and
160 | multilevel regression: Inferential and graphical techniques.
161 | \emph{Multivariate Behavioral Research}, \emph{40}(3), 373-400.
162 | \doi{10.1207/s15327906mbr4003_5}
163 |
164 | Esarey, J., & Sumner, J. L. (2017). Marginal effects in interaction models:
165 | Determining and controlling the false positive rate. Comparative Political
166 | Studies, 1–33. Advance online publication.
167 | \doi{10.1177/0010414017730080}
168 |
169 | Johnson, P.O. & Fay, L.C. (1950). The Johnson-Neyman technique, its theory
170 | and application. \emph{Psychometrika}, \emph{15}, 349-367.
171 | \doi{10.1007/BF02288864}
172 | }
173 | \seealso{
174 | Other interaction tools:
175 | \code{\link{probe_interaction}()},
176 | \code{\link{sim_margins}()},
177 | \code{\link{sim_slopes}()}
178 | }
179 | \author{
180 | Jacob Long \email{jacob.long@sc.edu}
181 | }
182 | \concept{interaction tools}
183 |
--------------------------------------------------------------------------------
/man/plot.sim_margins.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simple_margins.R
3 | \name{plot.sim_margins}
4 | \alias{plot.sim_margins}
5 | \title{Plot coefficients from simple slopes analysis}
6 | \usage{
7 | \method{plot}{sim_margins}(x, ...)
8 | }
9 | \arguments{
10 | \item{x}{A \code{\link[=sim_margins]{sim_margins()}} object.}
11 |
12 | \item{...}{arguments passed to \code{\link[jtools:plot_summs]{jtools::plot_coefs()}}}
13 | }
14 | \description{
15 | This creates a coefficient plot to visually summarize the
16 | results of simple slopes analysis.
17 | }
18 |
--------------------------------------------------------------------------------
/man/plot.sim_slopes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simple_slopes.R
3 | \name{plot.sim_slopes}
4 | \alias{plot.sim_slopes}
5 | \title{Plot coefficients from simple slopes analysis}
6 | \usage{
7 | \method{plot}{sim_slopes}(x, ...)
8 | }
9 | \arguments{
10 | \item{x}{A \code{\link[=sim_slopes]{sim_slopes()}} object.}
11 |
12 | \item{...}{arguments passed to \code{\link[jtools:plot_summs]{jtools::plot_coefs()}}}
13 | }
14 | \description{
15 | This creates a coefficient plot to visually summarize the
16 | results of simple slopes analysis.
17 | }
18 |
--------------------------------------------------------------------------------
/man/probe_interaction.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/int_utils.R
3 | \name{probe_interaction}
4 | \alias{probe_interaction}
5 | \title{Probe interaction effects via simple slopes and plotting}
6 | \usage{
7 | probe_interaction(model, pred, modx, mod2 = NULL, ...)
8 | }
9 | \arguments{
10 | \item{model}{A regression model. The function is tested with \code{lm},
11 | \code{glm}, \code{\link[survey]{svyglm}}, \code{\link[lme4]{merMod}},
12 | \code{\link[quantreg]{rq}}, \code{\link[brms]{brmsfit}},
13 | \code{stanreg} models.
14 | Models from other classes may work as well but are not officially
15 | supported. The model should include the interaction of interest.}
16 |
17 | \item{pred}{The name of the predictor variable involved
18 | in the interaction. This can be a bare name or string. Note that it
19 | is evaluated using \code{rlang}, so programmers can use the \verb{!!} syntax
20 | to pass variables instead of the verbatim names.}
21 |
22 | \item{modx}{The name of the moderator variable involved
23 | in the interaction. This can be a bare name or string. The same
24 | \code{rlang} proviso applies as with \code{pred}.}
25 |
26 | \item{mod2}{Optional. The name of the second moderator
27 | variable involved in the interaction. This can be a bare name or string.
28 | The same \code{rlang} proviso applies as with \code{pred}.}
29 |
30 | \item{...}{Other arguments accepted by \code{\link{sim_slopes}} and
31 | \code{\link{interact_plot}}}
32 | }
33 | \value{
34 | \item{simslopes}{The \code{sim_slopes} object created.}
35 | \item{interactplot}{The \code{ggplot} object created by
36 | \code{interact_plot}.}
37 | }
38 | \description{
39 | \code{probe_interaction} is a convenience function that allows users to call
40 | both \code{\link{sim_slopes}} and \code{\link{interact_plot}} with a single
41 | call.
42 | }
43 | \details{
44 | This function simply merges the nearly-equivalent arguments needed to call
45 | both \code{\link{sim_slopes}} and \code{\link{interact_plot}} without the
46 | need for re-typing their common arguments. Note that each function is called
47 | separately and they re-fit a separate model for each level of each
48 | moderator; therefore, the runtime may be considerably longer than the
49 | original model fit. For larger models, this is worth keeping in mind.
50 |
51 | Sometimes, you may want different parameters when doing simple slopes
52 | analysis compared to when plotting interaction effects. For instance, it is
53 | often easier to interpret the regression output when variables are
54 | standardized; but plots are often easier to understand when the variables
55 | are in their original units of measure.
56 |
57 | \code{probe_interaction} does not
58 | support providing different arguments to each function. If that is needed,
59 | use \code{sim_slopes} and \code{interact_plot} directly.
60 | }
61 | \examples{
62 |
63 | # Using a fitted model as formula input
64 | fiti <- lm(Income ~ Frost + Murder * Illiteracy,
65 | data=as.data.frame(state.x77))
66 | probe_interaction(model = fiti, pred = Murder, modx = Illiteracy,
67 | modx.values = "plus-minus")
68 | # 3-way interaction
69 | fiti3 <- lm(Income ~ Frost * Murder * Illiteracy,
70 | data=as.data.frame(state.x77))
71 | probe_interaction(model = fiti3, pred = Murder, modx = Illiteracy,
72 | mod2 = Frost, mod2.values = "plus-minus")
73 |
74 | # With svyglm
75 | if (requireNamespace("survey")) {
76 | library(survey)
77 | data(api)
78 | dstrat <- svydesign(id = ~1, strata = ~stype, weights = ~pw,
79 | data = apistrat, fpc = ~fpc)
80 | regmodel <- svyglm(api00 ~ ell * meals + sch.wide, design = dstrat)
81 | probe_interaction(model = regmodel, pred = ell, modx = meals,
82 | modx.values = "plus-minus", cond.int = TRUE)
83 |
84 | # 3-way with survey and factor input
85 | regmodel3 <- svyglm(api00 ~ ell * meals * sch.wide, design = dstrat)
86 | probe_interaction(model = regmodel3, pred = ell, modx = meals,
87 | mod2 = sch.wide)
88 | # Can try different configurations of 1st vs 2nd mod
89 | probe_interaction(model = regmodel3, pred = ell, modx = sch.wide,
90 | mod2 = meals)
91 | }
92 |
93 | }
94 | \seealso{
95 | Other interaction tools:
96 | \code{\link{johnson_neyman}()},
97 | \code{\link{sim_margins}()},
98 | \code{\link{sim_slopes}()}
99 | }
100 | \author{
101 | Jacob Long \email{jacob.long@sc.edu}
102 | }
103 | \concept{interaction tools}
104 |
--------------------------------------------------------------------------------
/man/reexports.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \docType{import}
4 | \name{reexports}
5 | \alias{reexports}
6 | \alias{tidy}
7 | \alias{glance}
8 | \title{Objects exported from other packages}
9 | \keyword{internal}
10 | \description{
11 | These objects are imported from other packages. Follow the links
12 | below to see their documentation.
13 |
14 | \describe{
15 | \item{generics}{\code{\link[generics]{glance}}, \code{\link[generics]{tidy}}}
16 | }}
17 |
18 |
--------------------------------------------------------------------------------
/man/sim_margins.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simple_margins.R
3 | \name{sim_margins}
4 | \alias{sim_margins}
5 | \title{Perform a simple margins analysis.}
6 | \usage{
7 | sim_margins(
8 | model,
9 | pred,
10 | modx,
11 | mod2 = NULL,
12 | modx.values = NULL,
13 | mod2.values = NULL,
14 | data = NULL,
15 | cond.int = FALSE,
16 | vce = c("delta", "simulation", "bootstrap", "none"),
17 | iterations = 1000,
18 | digits = getOption("jtools-digits", default = 2),
19 | pvals = TRUE,
20 | confint = FALSE,
21 | ci.width = 0.95,
22 | cluster = NULL,
23 | modx.labels = NULL,
24 | mod2.labels = NULL,
25 | ...
26 | )
27 | }
28 | \arguments{
29 | \item{model}{A regression model. The function is tested with \code{lm},
30 | \code{glm}, \code{\link[survey]{svyglm}}, \code{\link[lme4]{merMod}},
31 | \code{\link[quantreg]{rq}}, \code{\link[brms]{brmsfit}},
32 | \code{stanreg} models.
33 | Models from other classes may work as well but are not officially
34 | supported. The model should include the interaction of interest.}
35 |
36 | \item{pred}{The name of the predictor variable involved
37 | in the interaction. This can be a bare name or string. Note that it
38 | is evaluated using \code{rlang}, so programmers can use the \verb{!!} syntax
39 | to pass variables instead of the verbatim names.}
40 |
41 | \item{modx}{The name of the moderator variable involved
42 | in the interaction. This can be a bare name or string. The same
43 | \code{rlang} proviso applies as with \code{pred}.}
44 |
45 | \item{mod2}{Optional. The name of the second moderator
46 | variable involved in the interaction. This can be a bare name or string.
47 | The same \code{rlang} proviso applies as with \code{pred}.}
48 |
49 | \item{modx.values}{For which values of the moderator should lines be
50 | plotted? There are two basic options:
51 | \itemize{
52 | \item A vector of values (e.g., \code{c(1, 2, 3)})
53 | \item A single argument asking to calculate a set of values. See details
54 | below.
55 | }
56 |
57 | Default is \code{NULL}. If \code{NULL} (or \code{mean-plus-minus}),
58 | then the customary +/- 1 standard
59 | deviation from the mean as well as the mean itself are used for continuous
60 | moderators. If \code{"plus-minus"}, plots lines when the moderator is at
61 | +/- 1 standard deviation without the mean. You may also choose \code{"terciles"}
62 | to split the data into equally-sized groups and choose the point at the
63 | mean of each of those groups.
64 |
65 | If the moderator is a factor variable and \code{modx.values} is
66 | \code{NULL}, each level of the factor is included. You may specify
67 | any subset of the factor levels (e.g., \code{c("Level 1", "Level 3")}) as long
68 | as there is more than 1. The levels will be plotted in the order you
69 | provide them, so this can be used to reorder levels as well.}
70 |
71 | \item{mod2.values}{For which values of the second moderator should the plot
72 | be
73 | facetted by? That is, there will be a separate plot for each level of this
74 | moderator. Defaults are the same as \code{modx.values}.}
75 |
76 | \item{data}{Optional, default is NULL. You may provide the data used to
77 | fit the model. This can be a better way to get mean values for centering
78 | and can be crucial for models with variable transformations in the formula
79 | (e.g., \code{log(x)}) or polynomial terms (e.g., \code{poly(x, 2)}). You will
80 | see a warning if the function detects problems that would likely be
81 | solved by providing the data with this argument and the function will
82 | attempt to retrieve the original data from the global environment.}
83 |
84 | \item{cond.int}{Should conditional intercepts be printed in addition to the
85 | slopes? Default is \code{FALSE}.}
86 |
87 | \item{vce}{A character string indicating the type of estimation procedure to use for estimating variances. The default (\dQuote{delta}) uses the delta method. Alternatives are \dQuote{bootstrap}, which uses bootstrap estimation, or \dQuote{simulation}, which averages across simulations drawn from the joint sampling distribution of model coefficients. The latter two are extremely time intensive.}
88 |
89 | \item{iterations}{If \code{vce = "bootstrap"}, the number of bootstrap iterations. If \code{vce = "simulation"}, the number of simulated effects to draw. Ignored otherwise.}
90 |
91 | \item{digits}{An integer specifying the number of digits past the decimal to
92 | report in the output. Default is 2. You can change the default number of
93 | digits for all jtools functions with
94 | \code{options("jtools-digits" = digits)} where digits is the desired
95 | number.}
96 |
97 | \item{pvals}{Show p values? If \code{FALSE}, these
98 | are not printed. Default is \code{TRUE}.}
99 |
100 | \item{confint}{Show confidence intervals instead of standard errors? Default
101 | is \code{FALSE}.}
102 |
103 | \item{ci.width}{A number between 0 and 1 that signifies the width of the
104 | desired confidence interval. Default is \code{.95}, which corresponds
105 | to a 95\% confidence interval. Ignored if \code{confint = FALSE}.}
106 |
107 | \item{cluster}{For clustered standard errors, provide the column name of
108 | the cluster variable in the input data frame (as a string). Alternately,
109 | provide a vector of clusters.}
110 |
111 | \item{modx.labels}{A character vector of labels for each level of the
112 | moderator values, provided in the same order as the \code{modx.values}
113 | argument. If \code{NULL}, the values themselves are used as labels unless
114 | \code{modx,values} is also \code{NULL}. In that case, "+1 SD" and "-1 SD"
115 | are used.}
116 |
117 | \item{mod2.labels}{A character vector of labels for each level of the 2nd
118 | moderator values, provided in the same order as the \code{mod2.values}
119 | argument. If \code{NULL}, the values themselves are used as labels unless
120 | \code{mod2.values} is also \code{NULL}. In that case, "+1 SD" and "-1 SD"
121 | are used.}
122 |
123 | \item{...}{ignored.}
124 | }
125 | \value{
126 | A list object with the following components:
127 |
128 | \item{slopes}{A table of coefficients for the focal predictor at each
129 | value of the moderator}
130 | \item{ints}{A table of coefficients for the intercept at each value of the
131 | moderator}
132 | \item{modx.values}{The values of the moderator used in the analysis}
133 | }
134 | \description{
135 | \code{sim_margins} conducts a simple margins analysis for the purposes of
136 | understanding two- and three-way interaction effects in linear regression.
137 | }
138 | \details{
139 | This allows the user to perform a simple margins analysis for the
140 | purpose of probing interaction effects in a linear regression. Two- and
141 | three-way interactions are supported, though one should be warned that
142 | three-way interactions are not easy to interpret in this way.
143 |
144 | The function is tested with \code{lm}, \code{glm}, \code{svyglm}, and \code{merMod} inputs.
145 | Others may work as well, but are not tested. In all but the linear model
146 | case, be aware that not all the assumptions applied to simple slopes
147 | analysis apply.
148 | }
149 | \references{
150 | Bauer, D. J., & Curran, P. J. (2005). Probing interactions in fixed and
151 | multilevel regression: Inferential and graphical techniques.
152 | \emph{Multivariate Behavioral Research}, \emph{40}(3), 373-400.
153 | \doi{10.1207/s15327906mbr4003_5}
154 |
155 | Cohen, J., Cohen, P., West, S. G., & Aiken, L. S. (2003). \emph{Applied
156 | multiple regression/correlation analyses for the behavioral sciences} (3rd
157 | ed.). Mahwah, NJ: Lawrence Erlbaum Associates, Inc.
158 |
159 | Hanmer, M. J., & Kalkan, K. O. (2013). Behind the curve: Clarifying the best
160 | approach to calculating predicted probabilities and marginal effects from
161 | limited dependent variable models. \emph{American Journal of Political Science},
162 | \emph{57}, 263–277. \doi{10.1111/j.1540-5907.2012.00602.x}
163 | }
164 | \seealso{
165 | \code{\link[margins:margins]{margins::margins()}}
166 |
167 | Other interaction tools:
168 | \code{\link{johnson_neyman}()},
169 | \code{\link{probe_interaction}()},
170 | \code{\link{sim_slopes}()}
171 | }
172 | \author{
173 | Jacob Long \email{jacob.long@sc.edu}
174 | }
175 | \concept{interaction tools}
176 |
--------------------------------------------------------------------------------
/man/sim_margins_tidiers.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simple_margins.R
3 | \name{tidy.sim_margins}
4 | \alias{tidy.sim_margins}
5 | \alias{glance.sim_margins}
6 | \title{Tidiers for \code{\link[=sim_margins]{sim_margins()}} objects.}
7 | \usage{
8 | \method{tidy}{sim_margins}(x, conf.level = 0.95, ...)
9 |
10 | \method{glance}{sim_margins}(x, ...)
11 | }
12 | \arguments{
13 | \item{x}{The \code{sim_margins} object}
14 |
15 | \item{conf.level}{The width of confidence intervals. Default is .95 (95\%).}
16 |
17 | \item{...}{Ignored.}
18 | }
19 | \description{
20 | You can use \code{\link[broom:reexports]{broom::tidy()}} and \code{\link[broom:reexports]{broom::glance()}} for "tidy"
21 | methods of storing \code{sim_margins} output.
22 | }
23 |
--------------------------------------------------------------------------------
/man/sim_slopes_tidiers.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simple_slopes.R
3 | \name{tidy.sim_slopes}
4 | \alias{tidy.sim_slopes}
5 | \alias{glance.sim_slopes}
6 | \title{Tidiers for \code{\link[=sim_slopes]{sim_slopes()}} objects.}
7 | \usage{
8 | \method{tidy}{sim_slopes}(x, conf.level = 0.95, ...)
9 |
10 | \method{glance}{sim_slopes}(x, ...)
11 | }
12 | \arguments{
13 | \item{x}{The \code{sim_slopes} object}
14 |
15 | \item{conf.level}{The width of confidence intervals. Default is .95 (95\%).}
16 |
17 | \item{...}{Ignored.}
18 | }
19 | \description{
20 | You can use \code{\link[broom:reexports]{broom::tidy()}} and \code{\link[broom:reexports]{broom::glance()}} for "tidy"
21 | methods of storing \code{sim_slopes} output.
22 | }
23 |
--------------------------------------------------------------------------------
/pkgdown/extra.css:
--------------------------------------------------------------------------------
1 | .page-header {
2 | min-height:25px !important;
3 | }
4 |
5 | .template-home img.logo {
6 | width: 200px;
7 | }
8 |
9 | .text-muted {
10 | color: #ced4da !important;
11 | }
12 |
13 | .bg-primary .navbar-nav .nav-link:hover, .bg-primary .navbar-nav .nav-link:hover {
14 | color: #ced4da !important;
15 | }
16 |
17 | .huxtable tr, .huxtable th, .huxtable td {
18 | border-style: none;
19 | }
20 |
21 | aside h2 {
22 | margin-top: 2rem;
23 | }
24 |
25 | .page-header {
26 | margin-top: 2rem;
27 | border-bottom: none;
28 | }
29 |
30 | h1, h2, h3, h4 {
31 | font-weight: 400;
32 | }
33 |
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/pkgdown/favicon/favicon.ico
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | library(testthat)
2 | library(vdiffr)
3 | library(interactions)
4 |
5 | test_check("interactions")
6 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/p0bar.svg:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/p0bari.svg:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/p0pt.svg:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/p0pti.svg:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/pcatbfit.svg:
--------------------------------------------------------------------------------
1 |
2 |
57 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/pglmcatoff.svg:
--------------------------------------------------------------------------------
1 |
2 |
57 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/plme4cat.svg:
--------------------------------------------------------------------------------
1 |
2 |
62 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/cat_plot/psvycat.svg:
--------------------------------------------------------------------------------
1 |
2 |
57 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/pbfcat.svg:
--------------------------------------------------------------------------------
1 |
2 |
57 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/pglmoff.svg:
--------------------------------------------------------------------------------
1 |
2 |
66 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/pglmrob.svg:
--------------------------------------------------------------------------------
1 |
2 |
66 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/plme4-interact-plot-to-cat-plot.svg:
--------------------------------------------------------------------------------
1 |
2 |
66 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/plme4.svg:
--------------------------------------------------------------------------------
1 |
2 |
60 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/plmlabelscpred-interact-plot-to-cat-plot.svg:
--------------------------------------------------------------------------------
1 |
2 |
69 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/plmlabelscpred.svg:
--------------------------------------------------------------------------------
1 |
2 |
60 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/plmnfchar.svg:
--------------------------------------------------------------------------------
1 |
2 |
66 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/interact_plot/plmtf.svg:
--------------------------------------------------------------------------------
1 |
2 |
62 |
--------------------------------------------------------------------------------
/tests/testthat/brmfit.rds:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/tests/testthat/brmfit.rds
--------------------------------------------------------------------------------
/tests/testthat/rsafit.rds:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacob-long/interactions/df22b3ddcf5f88c7608f0cd08ef25e60bcdf4b99/tests/testthat/rsafit.rds
--------------------------------------------------------------------------------
/tests/testthat/test_sim_margins.R:
--------------------------------------------------------------------------------
1 | skip_if_not_installed("margins")
2 | context("sim_margins lm")
3 |
4 | states <- as.data.frame(state.x77)
5 | states$HSGrad <- states$`HS Grad`
6 | states$o70 <- 0
7 | states$o70[states$`Life Exp` > 70] <- 1
8 | states$o70n <- states$o70
9 | states$o70 <- factor(states$o70)
10 | states$o70l <- states$`Life Exp` > 70
11 | states$o70c <- ifelse(states$o70l, yes = "yes", no = "no")
12 | set.seed(3)
13 | states$wts <- runif(50, 0, 3)
14 | fit <- lm(Income ~ HSGrad*Murder*Illiteracy + o70 + Area, data = states)
15 | fit2 <- lm(Income ~ HSGrad*o70, data = states)
16 | fit2n <- lm(Income ~ HSGrad*o70n, data = states)
17 | fitw <- lm(Income ~ HSGrad*Murder*Illiteracy + o70 + Area, data = states,
18 | weights = wts)
19 | fitl <- lm(Income ~ HSGrad*o70l, data = states)
20 | fitc <- lm(Income ~ HSGrad*Murder + o70c, data = states)
21 |
22 | if (requireNamespace("survey")) {
23 | suppressMessages(library(survey, quietly = TRUE))
24 | data(api)
25 | dstrat <- svydesign(id = ~1, strata = ~stype, weights = ~pw, data = apistrat,
26 | fpc = ~fpc)
27 | regmodel <- svyglm(api00 ~ ell * meals * both + sch.wide, design = dstrat)
28 | }
29 |
30 | test_that("sim_margins works for lm", {
31 | expect_s3_class(sim_margins(model = fit,
32 | pred = Murder,
33 | modx = Illiteracy,
34 | mod2 = HSGrad), "sim_margins")
35 | expect_s3_class(sim_margins(model = fit,
36 | pred = Murder,
37 | modx = Illiteracy,
38 | mod2 = HSGrad,
39 | vce = "bootstrap",
40 | iterations = 50), "sim_margins")
41 | expect_s3_class(sim_margins(model = fit,
42 | pred = Murder,
43 | modx = Illiteracy,
44 | mod2 = HSGrad,
45 | vce = "simulation",
46 | iterations = 50), "sim_margins")
47 | })
48 |
49 | test_that("sim_margins works for weighted lm", {
50 | expect_s3_class(sim_margins(model = fitw,
51 | pred = Murder,
52 | modx = Illiteracy,
53 | mod2 = HSGrad,
54 | modx.values = c(1.0, 1.5, 2.0)), class = "sim_margins")
55 | expect_s3_class(sim_margins(model = fitw,
56 | pred = Murder,
57 | modx = Illiteracy,
58 | mod2 = HSGrad,
59 | vce = "bootstrap",
60 | modx.values = c(1.0, 1.5, 2.0),
61 | iterations = 50), class = "sim_margins")
62 | expect_s3_class(sim_margins(model = fitw,
63 | pred = Murder,
64 | modx = Illiteracy,
65 | mod2 = HSGrad,
66 | vce = "simulation",
67 | modx.values = c(1.0, 1.5, 2.0),
68 | iterations = 50), class = "sim_margins")
69 | })
70 |
71 | test_that("sim_margins works for lm w/ logical", {
72 | expect_s3_class(sim_margins(model = fitl,
73 | pred = HSGrad,
74 | modx = o70l), "sim_margins")
75 | expect_s3_class(sim_margins(model = fitl,
76 | pred = HSGrad,
77 | modx = o70l,
78 | vce = "bootstrap",
79 | iterations = 50), "sim_margins")
80 | expect_s3_class(sim_margins(model = fitl,
81 | pred = HSGrad,
82 | modx = o70l,
83 | vce = "simulation",
84 | iterations = 50), "sim_margins")
85 | })
86 |
87 | test_that("sim_margins works for lm w/ non-focal character", {
88 | expect_s3_class(sim_margins(model = fitc,
89 | pred = HSGrad,
90 | modx = Murder), "sim_margins")
91 | expect_s3_class(sim_margins(model = fitc,
92 | pred = HSGrad,
93 | modx = Murder,
94 | vce = "bootstrap",
95 | iterations = 50), "sim_margins")
96 | expect_s3_class(sim_margins(model = fitc,
97 | pred = HSGrad,
98 | modx = Murder,
99 | vce = "simulation",
100 | iterations = 50), "sim_margins")
101 | })
102 |
103 | context("sim_margins methods")
104 |
105 | test_that("as_huxtable.sim_margins works", {
106 | skip_if_not_installed("huxtable")
107 | skip_if_not_installed("broom")
108 | ss3 <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
109 | mod2 = HSGrad)
110 | ss <- sim_margins(model = fit, pred = Murder, modx = Illiteracy)
111 | expect_is(as_huxtable.sim_margins(ss3), "huxtable")
112 | expect_is(as_huxtable.sim_margins(ss), "huxtable")
113 | ss3 <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
114 | mod2 = HSGrad, vce = "bootstrap", iterations = 50)
115 | ss <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
116 | vce = "bootstrap", iterations = 50)
117 | expect_is(as_huxtable.sim_margins(ss3), "huxtable")
118 | expect_is(as_huxtable.sim_margins(ss), "huxtable")
119 | ss3 <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
120 | mod2 = HSGrad, vce = "simulation", iterations = 50)
121 | ss <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
122 | vce = "simulation", iterations = 50)
123 | expect_is(as_huxtable.sim_margins(ss3), "huxtable")
124 | expect_is(as_huxtable.sim_margins(ss), "huxtable")
125 | })
126 |
127 | test_that("plot.sim_margins works", {
128 | skip_if_not_installed("broom.mixed")
129 | skip_if_not_installed("broom")
130 | ss3 <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
131 | mod2 = HSGrad)
132 | ss <- sim_margins(model = fit, pred = Murder, modx = Illiteracy)
133 | expect_is(plot(ss3), "ggplot")
134 | expect_is(plot(ss), "ggplot")
135 | ss3 <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
136 | mod2 = HSGrad, vce = "bootstrap", iterations = 50)
137 | ss <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
138 | vce = "bootstrap", iterations = 50)
139 | expect_is(plot(ss3), "ggplot")
140 | expect_is(plot(ss), "ggplot")
141 | ss3 <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
142 | mod2 = HSGrad, vce = "simulation", iterations = 50)
143 | ss <- sim_margins(model = fit, pred = Murder, modx = Illiteracy,
144 | vce = "simulation", iterations = 50)
145 | expect_is(plot(ss3), "ggplot")
146 | expect_is(plot(ss), "ggplot")
147 | })
148 |
149 | context("sim_margins svyglm")
150 |
151 | test_that("sim_margins works for svyglm", {
152 | skip_if_not_installed("survey")
153 | expect_is(sim_margins(regmodel, pred = ell, modx = meals, mod2 = both),
154 | "sim_margins")
155 | # margins bug
156 | # expect_is(sim_margins(regmodel, pred = ell, modx = meals, mod2 = both,
157 | # vce = "bootstrap", iterations = 50),
158 | # "sim_margins")
159 | # expect_is(sim_margins(regmodel, pred = ell, modx = meals, mod2 = both,
160 | # vce = "simulation", iterations = 50),
161 | # "sim_margins")
162 | })
163 |
164 | context("sim_margins merMod")
165 |
166 | test_that("sim_margins works for lme4", {
167 | skip_if_not_installed("lme4")
168 | library(lme4, quietly = TRUE)
169 | data(VerbAgg)
170 | fmVA0 <- glmer(r2 ~ Anger * Gender + btype + situ + (1|id) + (1|item),
171 | family = binomial, data = VerbAgg, nAGQ=0L)
172 | lmVA0 <- lmer(as.numeric(r2 == "Y") ~ Anger * Gender + btype + situ +
173 | (1|id) + (1|item), data = VerbAgg)
174 |
175 | expect_is(sim_margins(lmVA0, pred = Anger, modx = Gender), "sim_margins")
176 | expect_is(sim_margins(fmVA0, pred = Anger, modx = Gender), "sim_margins")
177 | })
178 |
--------------------------------------------------------------------------------
/tests/testthat/test_sim_slopes.R:
--------------------------------------------------------------------------------
1 | context("sim_slopes lm")
2 |
3 | states <- as.data.frame(state.x77)
4 | states$HSGrad <- states$`HS Grad`
5 | states$o70 <- 0
6 | states$o70[states$`Life Exp` > 70] <- 1
7 | states$o70n <- states$o70
8 | states$o70 <- factor(states$o70)
9 | states$o70l <- states$`Life Exp` > 70
10 | states$o70c <- ifelse(states$o70l, yes = "yes", no = "no")
11 | set.seed(3)
12 | states$wts <- runif(50, 0, 3)
13 | fit <- lm(Income ~ HSGrad*Murder*Illiteracy + o70 + Area, data = states)
14 | fit2 <- lm(Income ~ HSGrad*o70, data = states)
15 | fit2n <- lm(Income ~ HSGrad*o70n, data = states)
16 | fitw <- lm(Income ~ HSGrad*Murder*Illiteracy + o70 + Area, data = states,
17 | weights = wts)
18 | fitl <- lm(Income ~ HSGrad*o70l, data = states)
19 | fitc <- lm(Income ~ HSGrad*Murder + o70c, data = states)
20 |
21 | library(ggplot2)
22 | diamond <- diamonds
23 | diamond <- diamond[diamond$color != "D",]
24 | set.seed(10)
25 | samps <- sample(1:nrow(diamond), 2000)
26 | diamond <- diamond[samps,]
27 | fitd <- lm(price ~ cut * color * clarity, data = diamond)
28 |
29 | if (requireNamespace("survey")) {
30 | suppressMessages(library(survey, quietly = TRUE))
31 | data(api)
32 | dstrat <- svydesign(id = ~1, strata = ~stype, weights = ~pw, data = apistrat,
33 | fpc = ~fpc)
34 | regmodel <- svyglm(api00 ~ ell * meals * both + sch.wide, design = dstrat)
35 | }
36 |
37 | test_that("sim_slopes works for lm", {
38 | expect_silent(sim_slopes(model = fit,
39 | pred = Murder,
40 | modx = Illiteracy,
41 | mod2 = HSGrad,
42 | centered = "all"))
43 | expect_warning(sim_slopes(model = fit,
44 | pred = Murder,
45 | modx = Illiteracy,
46 | mod2 = HSGrad,
47 | centered = "HSGrad"))
48 | })
49 |
50 | test_that("sim_slopes works for weighted lm", {
51 | # Out of range warning
52 | expect_warning(sim_slopes(model = fitw,
53 | pred = Murder,
54 | modx = Illiteracy,
55 | mod2 = HSGrad,
56 | centered = "all"))
57 | expect_s3_class(suppressWarnings(sim_slopes(model = fitw,
58 | pred = Murder,
59 | modx = Illiteracy,
60 | mod2 = HSGrad,
61 | centered = "all")), class = "sim_slopes")
62 | })
63 |
64 | test_that("sim_slopes works for lm w/ logical", {
65 | expect_silent(sim_slopes(model = fitl,
66 | pred = HSGrad,
67 | modx = o70l,
68 | johnson_neyman = FALSE))
69 | })
70 |
71 | test_that("sim_slopes works for lm w/ non-focal character", {
72 | expect_silent(sim_slopes(model = fitc,
73 | pred = HSGrad,
74 | modx = Murder,
75 | johnson_neyman = FALSE))
76 | })
77 |
78 | test_that("sim_slopes accepts categorical predictor", {
79 | expect_warning(ss <- sim_slopes(fitd, pred = cut, modx = color))
80 | expect_s3_class(ss, "sim_slopes")
81 | expect_warning(ss <- sim_slopes(fitd, pred = cut, modx = color, mod2 = clarity))
82 | expect_s3_class(ss, "sim_slopes")
83 | })
84 |
85 | context("sim_slopes methods")
86 |
87 | test_that("as_huxtable.sim_slopes works", {
88 | skip_if_not_installed("huxtable")
89 | skip_if_not_installed("broom")
90 | ss3 <- sim_slopes(model = fit, pred = Murder, modx = Illiteracy,
91 | mod2 = HSGrad)
92 | ss <- sim_slopes(model = fit, pred = Murder, modx = Illiteracy)
93 | expect_is(as_huxtable.sim_slopes(ss3), "huxtable")
94 | expect_is(as_huxtable.sim_slopes(ss), "huxtable")
95 | })
96 | test_that("plot.sim_slopes works", {
97 | skip_if_not_installed("broom.mixed")
98 | skip_if_not_installed("broom")
99 | ss3 <- sim_slopes(model = fit, pred = Murder, modx = Illiteracy,
100 | mod2 = HSGrad)
101 | ss <- sim_slopes(model = fit, pred = Murder, modx = Illiteracy)
102 | expect_is(plot(ss3), "ggplot")
103 | expect_is(plot(ss), "ggplot")
104 | })
105 |
106 | context("sim_slopes svyglm")
107 |
108 | test_that("sim_slopes works for svyglm", {
109 | skip_if_not_installed("survey")
110 | expect_is(sim_slopes(regmodel, pred = ell, modx = meals, mod2 = both,
111 | centered = "all"), "sim_slopes")
112 | })
113 |
114 | context("sim_slopes merMod")
115 |
116 | test_that("sim_slopes works for lme4", {
117 | skip_if_not_installed("lme4")
118 | library(lme4, quietly = TRUE)
119 | data(VerbAgg)
120 | fmVA0 <- glmer(r2 ~ Anger * Gender + btype + situ + (1|id) + (1|item),
121 | family = binomial, data = VerbAgg, nAGQ=0L)
122 | lmVA0 <- lmer(as.numeric(r2 == "Y") ~ Anger * Gender + btype + situ +
123 | (1|id) + (1|item), data = VerbAgg)
124 |
125 | expect_is(sim_slopes(lmVA0, pred = Anger, modx = Gender,
126 | johnson_neyman = FALSE, t.df = "residual"),
127 | "sim_slopes")
128 | expect_is(sim_slopes(fmVA0, pred = Anger, modx = Gender,
129 | johnson_neyman = FALSE), "sim_slopes")
130 | })
131 |
132 |
133 | ### johnson_neyman ###########################################################
134 |
135 | context("j_n specific")
136 |
137 | test_that("johnson_neyman control.fdr argument works", {
138 | expect_s3_class(johnson_neyman(fit, pred = Murder, modx = Illiteracy,
139 | control.fdr = TRUE), "johnson_neyman")
140 | })
141 |
142 | test_that("johnson_neyman critical.t argument works", {
143 | expect_s3_class(johnson_neyman(fit, pred = Murder, modx = Illiteracy,
144 | critical.t = 2.1), "johnson_neyman")
145 | })
146 |
147 | test_that("johnson_neyman color arguments work", {
148 | expect_silent(johnson_neyman(fit, pred = Murder, modx = Illiteracy,
149 | sig.color = "black", insig.color = "grey")$plot)
150 | })
151 |
152 | test_that("johnson_neyman mod.range argument works", {
153 | expect_silent(johnson_neyman(fit, pred = Murder, modx = Illiteracy,
154 | mod.range = c(1, 2))$plot)
155 | })
156 |
--------------------------------------------------------------------------------
/vignettes/categorical.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Plotting interactions among categorical variables in regression models"
3 | author: "Jacob Long"
4 | date: "`r Sys.Date()`"
5 | output: html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Plotting interactions among categorical variables in regression models}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r echo=FALSE}
13 | knitr::opts_chunk$set(message = F, warning = F, fig.width = 6, fig.height = 5)
14 | library(jtools)
15 | library(interactions)
16 | ```
17 |
18 | When trying to understand interactions between categorical predictors,
19 | the types of visualizations called for tend to differ from those for continuous
20 | predictors. For that (and some other) reasons, `interactions` offers support for
21 | these in `cat_plot` while continuous predictors (perhaps in interactions with
22 | categorical predictors) are dealt with in `interact_plot`, which has a separate
23 | vignette.
24 |
25 | To be clear...
26 |
27 | If all the predictors involved in the interaction are categorical, use
28 | `cat_plot`. You can also use `cat_plot` to explore the effect of a single
29 | categorical predictor.
30 |
31 | If one or more are continuous, use `interact_plot`.
32 |
33 | ## Simple two-way interaction
34 |
35 | First, let's prep some data. I'm going to make some slight changes to the
36 | `mpg` dataset from `ggplot2` for didactic purposes to drop a few factor
37 | levels that have almost no values (e.g., there are 5 cylinder engines?).
38 |
39 | ```{r}
40 | library(ggplot2)
41 | mpg2 <- mpg
42 | mpg2$cyl <- factor(mpg2$cyl)
43 | mpg2["auto"] <- "auto"
44 | mpg2$auto[mpg2$trans %in% c("manual(m5)", "manual(m6)")] <- "manual"
45 | mpg2$auto <- factor(mpg2$auto)
46 | mpg2["fwd"] <- "2wd"
47 | mpg2$fwd[mpg2$drv == "4"] <- "4wd"
48 | mpg2$fwd <- factor(mpg2$fwd)
49 | ## Drop the two cars with 5 cylinders (rest are 4, 6, or 8)
50 | mpg2 <- mpg2[mpg2$cyl != "5",]
51 | ## Fit the model
52 | fit3 <- lm(cty ~ cyl * fwd * auto, data = mpg2)
53 | ```
54 |
55 | So basically what we're looking at here is an interaction between number of
56 | cylinders in the engine of some cars and whether the car has all-wheel drive or
57 | two-wheel drive. The DV is fuel mileage in the city.
58 |
59 | Here's summary output for our model:
60 |
61 | ```{r}
62 | library(jtools) # for summ()
63 | summ(fit3)
64 | ```
65 |
66 | Let's see what happens using all the default arguments:
67 |
68 | ```{r}
69 | cat_plot(fit3, pred = cyl, modx = fwd)
70 | ```
71 |
72 | This is with `geom = "point"`. We can see a main effect of `cyl` and maybe
73 | something is going on with the interaction as well, since the different
74 | between `2wd` and `4wd` seems to decrease as `cyl` gets higher.
75 |
76 | You can also plot the observed data on the plot:
77 |
78 | ```{r}
79 | cat_plot(fit3, pred = cyl, modx = fwd, plot.points = TRUE)
80 | ```
81 |
82 | ## Line plots
83 |
84 | And since `cyl` does have a clear order, it might make more sense to connect
85 | those dots. Let's try `geom = "line"`:
86 |
87 | ```{r}
88 | cat_plot(fit3, pred = cyl, modx = fwd, geom = "line")
89 | ```
90 |
91 | Okay, that makes the trend quite a bit clearer.
92 |
93 | You have some other options, too. Suppose you will need this plot to look
94 | good in black and white. Let's change the shape of those points for different
95 | values of the moderator.
96 |
97 | ```{r}
98 | cat_plot(fit3, pred = cyl, modx = fwd, geom = "line", point.shape = TRUE)
99 | ```
100 |
101 | You can change the line patterns as well for more clarity.
102 |
103 | ```{r}
104 | cat_plot(fit3, pred = cyl, modx = fwd, geom = "line", point.shape = TRUE,
105 | vary.lty = TRUE)
106 | ```
107 |
108 | You may also choose any color palette from `RColorBrewer` as well as several
109 | preset palettes available in `jtools`:
110 |
111 | ```{r}
112 | cat_plot(fit3, pred = cyl, modx = fwd, geom = "line", point.shape = TRUE,
113 | colors = "Set2")
114 | ```
115 |
116 | Use `?jtools_colors` for more on your color options.
117 |
118 | ## Bar/dynamite plots
119 |
120 | Last but not least, you can also make bar charts, AKA dynamite plots. For
121 | many situations, these are not the best way to show your data, but I know it's
122 | what a lot of people are looking for.
123 |
124 | ```{r}
125 | cat_plot(fit3, pred = cyl, modx = fwd, geom = "bar")
126 | ```
127 |
128 | The transparency of the fill color depends on the presence of the error bars
129 | and observed data points.
130 |
131 | ```{r}
132 | cat_plot(fit3, pred = cyl, modx = fwd, geom = "bar", interval = FALSE)
133 | ```
134 |
135 | Now let's look with observed data:
136 |
137 | ```{r}
138 | cat_plot(fit3, pred = cyl, modx = fwd, geom = "bar", interval = FALSE,
139 | plot.points = TRUE)
140 | ```
141 |
--------------------------------------------------------------------------------