Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

metafor::rma tidiers - work in progress #674

Merged
merged 26 commits into from Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ea0234f
ported @malcolmbarrett's testing script
softloud Apr 4, 2019
77cd17b
added modeltests :notes:
softloud Apr 5, 2019
b85e01d
Merge pull request #1 from softloud/add-tests
softloud Apr 5, 2019
725c33a
basic tests added :notes:
softloud Apr 5, 2019
219d932
fix test errors, temporarily set strict = FALSE
malcolmbarrett Apr 6, 2019
7078e2b
Merge pull request #2 from softloud/tweak-tests
softloud Apr 6, 2019
0b70c4a
added @malcolmbarrett and I as ctb :notes:
softloud Apr 7, 2019
55ed153
fix cmd check errors, broom inconsistencies, set strict = TRUE
malcolmbarrett Apr 7, 2019
a4ffcf5
add rma to news, mb orcid
malcolmbarrett Apr 7, 2019
1b69212
rebuild docs
malcolmbarrett Apr 7, 2019
0a7c4f6
Merge pull request #4 from softloud/add-mb-contr
softloud Apr 7, 2019
26d8d63
Merge branch 'add-tests' into change-examples
softloud Apr 7, 2019
273dfc1
Merge pull request #3 from softloud/change-examples
softloud Apr 7, 2019
53cd56c
augment args to unstrict
malcolmbarrett Apr 7, 2019
d68710e
change col names: q* -> cochran.q*, del -> loo
malcolmbarrett Apr 7, 2019
3bcf4fb
fix templating, testing, binding, rownames
malcolmbarrett Apr 7, 2019
b6d23b5
fix augment.rma tests and bugs, change to templates
malcolmbarrett Apr 7, 2019
5b4e6d7
switch to return*() for docs
malcolmbarrett Apr 7, 2019
9f31411
add comments and rearrange `tidy.rma`
malcolmbarrett Apr 7, 2019
15d50ab
Merge pull request #5 from softloud/add-unstrict-aug
softloud Apr 8, 2019
9218953
Merge pull request #7 from softloud/alex-changes
softloud Apr 8, 2019
2ba35ff
Merge pull request #6 from softloud/tweak-broom-names
softloud Apr 8, 2019
fdd77be
Merge branch 'master' into add-tests
softloud Apr 8, 2019
d857e57
Update R/rma-tidiers.R
alexpghayes Apr 8, 2019
d5834cb
fix a couple bugs, tweak docs, remove attribute stripping
malcolmbarrett Apr 8, 2019
5f896bb
Merge pull request #8 from softloud/last-sweep
softloud Apr 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 14 additions & 2 deletions DESCRIPTION
Expand Up @@ -199,7 +199,17 @@ Authors@R:
family = "Krauska",
role = "ctb",
email = "krauskae@gmail.com",
comment = c(ORCID = "0000-0002-1466-5850")))
comment = c(ORCID = "0000-0002-1466-5850")),
person(given = "Malcolm",
family = "Barrett",
role = "ctb",
email = "malcolmbarrett@gmail.com",
comment = c(ORCID = "0000-0003-0299-5825")),
person(given = "Charles",
family = "Gray",
role = "ctb",
email = "charlestigray@gmail.com",
comment = c(ORCID = "0000-0002-9978-011X")))
Description: Summarizes key information about statistical
objects in tidy tibbles. This makes it easy to report results, create
plots and consistently work with large numbers of models at once.
Expand Down Expand Up @@ -304,7 +314,8 @@ Suggests:
testthat,
tseries,
xergm,
zoo
zoo,
metafor
VignetteBuilder:
knitr
Remotes:
Expand Down Expand Up @@ -378,6 +389,7 @@ Collate:
'quantreg-nlrq-tidiers.R'
'quantreg-rq-tidiers.R'
'quantreg-rqs-tidiers.R'
'rma-tidiers.R'
'robust-glmrob-tidiers.R'
'robust-lmrob-tidiers.R'
'sp-tidiers.R'
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Expand Up @@ -26,6 +26,7 @@ S3method(augment,poLCA)
S3method(augment,polr)
S3method(augment,prcomp)
S3method(augment,rlm)
S3method(augment,rma)
S3method(augment,rq)
S3method(augment,rqs)
S3method(augment,smooth.spline)
Expand Down Expand Up @@ -81,6 +82,7 @@ S3method(glance,polr)
S3method(glance,pyears)
S3method(glance,ridgelm)
S3method(glance,rlm)
S3method(glance,rma)
S3method(glance,rq)
S3method(glance,rqs)
S3method(glance,smooth.spline)
Expand Down Expand Up @@ -178,6 +180,7 @@ S3method(tidy,ref.grid)
S3method(tidy,regsubsets)
S3method(tidy,ridgelm)
S3method(tidy,rlm)
S3method(tidy,rma)
S3method(tidy,roc)
S3method(tidy,rq)
S3method(tidy,rqs)
Expand Down
4 changes: 2 additions & 2 deletions NEWS.md
Expand Up @@ -67,9 +67,9 @@ TODO: sort out what happens to `glance.aov()`
- `tidy.lsmobj()` gained a `conf.int` argument

## New tidiers, features and bugfixes


- Added tidiers for `rma` objects from the `metafor` package (#674, @malcolmbarrett, @softloud)
alexpghayes marked this conversation as resolved.
Show resolved Hide resolved
- `ordinal` tidier rewrite

- Added tidiers for `pam` objects from the `cluster` package. (#637)
- Previously, F-statistics for weak instruments were returned through `glance.ivreg()`. F-statistics are now returned through `tidy.ivreg(instruments = TRUE)`. Default is `tidy.ivreg(instruments = FALSE)`. `glance.ivreg()` still returns Wu-Hausman and Sargan test statistics.
- Added `tidy.regsubsets()` for best subsets linear regression from the `leaps` package
Expand Down
222 changes: 222 additions & 0 deletions R/rma-tidiers.R
@@ -0,0 +1,222 @@
#' Tidying methods for meta-analyis objects
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the templating trick to pull in standard boilerplate. i.e.:

#' @templateVar class rma
#' @template title_desc_tidy

rather than this first line.

#'
#' These methods tidy the results of meta-analysis objects from the metafor package
#'
#' @param x An `rma` created by the `metafor` package.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would link to all the function that create rma objects here. That is, something along the lines of:

#' @param x An rma such as those created by [metafor::function1()], [metafor::function2()] or ....

#' @inheritParams tidy.lm
#' @param include_studies Logical. Should individual studies be included in the
#' output?
softloud marked this conversation as resolved.
Show resolved Hide resolved
#' @param ... Additional arguments
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do these arguments get based too? Link to the documentation of that function to make it easier for users trying to track that down.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, they actually aren't used but need to be there to match the generic. I seem to recall some methods in broom saying something like Additional arguments (not used)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they aren't getting used the standard way to document them is with @template param_unused_dots

#' @param measure Measure type. See [metafor::rma()]
#'
#' @return A `tibble`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at https://github.com/tidymodels/broom/blob/master/R/betareg-tidiers.R#L8 for an example of how to document the tidier return. There are three separate functions return_tidy(), ..., return_augment() to look at. Also I definitely need to document how to use these better.

#' @export
#'
#' @examples
#'
#' library(metafor)
#'
#' df <-
#' escalc(
#' measure = "RR",
#' ai = tpos,
#' bi = tneg,
#' ci = cpos,
#' di = cneg,
#' data = dat.bcg
#' )
#'
#' meta_analysis <- rma(yi, vi, data = df, method = "EB")
#'
#' tidy(meta_analysis)
#'
#' @rdname tidiers
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#' @rdname tidiers
#' @rdname metafor_tidiers

#'
tidy.rma <- function(x, conf.int = FALSE, conf.level = 0.95, exponentiate = FALSE,
include_studies = TRUE, measure = "GEN", ...) {
alexpghayes marked this conversation as resolved.
Show resolved Hide resolved

estimates <- metafor::escalc(yi = x$yi.f, vi = x$vi.f, measure = measure) %>%
summary(level = conf.level * 100) %>%
as.data.frame(stringsAsFactors = FALSE)

estimates <- cbind(x$slab, "study", estimates[, c("yi", "sei", "zi")], NA,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I ask why the NA here? Also, would you consider using bind_cols() instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NA is for the p values for the individual studies, which aren't in the model. Only the summary estimate returns a p value. I don't mind switching to bind_cols(), but I'll have to change a few things since it's strict about all the objects having the same length

estimates[, c("ci.lb", "ci.ub")], stringsAsFactors = FALSE)
names(estimates) <- c("study", "type", "estimate", "std.error", "statistic",
"p.value", "conf.low", "conf.high")
estimates <- tibble::as_tibble(estimates)

betas <- x$beta
if (!is.null(nrow(betas)) && nrow(betas) > 1) {
# get estimate type and fix spelling
study <- rownames(betas)
swap <- grepl("intrcpt", study)
study[swap] <- "intercept"
betas <- as.double(betas)
} else {
study <- "overall"
betas <- betas[1]
}

results <- tibble::tibble(study = study, type = "summary",
estimate = betas, std.error = x$se,
statistic = x$zval, p.value = x$pval,
conf.low = x$ci.lb, conf.high = x$ci.ub)
.data <- if (include_studies) rbind(estimates, results) else results

if (exponentiate) {
.data$estimate <- exp(.data$estimate)
.data$conf.low <- exp(.data$conf.low)
.data$conf.high <- exp(.data$conf.high)
}

if (!conf.int) {
.data <- .data[-which(names(.data) %in% c("conf.low", "conf.high"))]
}

attributes(.data$study) <- NULL

tibble::remove_rownames(.data)
}

#' Construct a one-row summary of meta-analysis model fit statistics.
#'
#' `glance()` computes a one-row summary of meta-analysis objects,
#' including estimates of heterogenity and model fit.
#'
#' @param x An `rma` created by the `metafor` package.
#' @param ... Additional arguments
#'
#' @return a `tibble`
#' @export
#'
#' @examples
#'
#' library(metafor)
#'
#' df <-
#' escalc(
#' measure = "RR",
#' ai = tpos,
#' bi = tneg,
#' ci = cpos,
#' di = cneg,
#' data = dat.bcg
#' )
#'
#' meta_analysis <- rma(yi, vi, data = df, method = "EB")
#'
#' glance(meta_analysis)
#'
glance.rma <- function(x, ...) {
fit_stats <- metafor::fitstats(x)
fit_stats <- fit_stats %>%
t() %>%
as.data.frame()
names(fit_stats) <-
stringr::str_replace(names(fit_stats), "\\:", "")

list(
nobs = x$k,
measure = x$measure,
method = x$method,
i.squared = x$I2,
h.squared = x$H2,
tau.squared = x$tau2,
tau.squared.se = x$se.tau2,
qe = x$QE,
p.value.qe = x$QEp,
qm = x$QM,
p.value.qm = x$QMp,
fit_stats
) %>%
# get rid of null values
purrr::discard(is.null) %>%
# don't include multivariate model stats
purrr::discard(~length(.x) >= 2) %>%
# change to tibble with correct column and row names
as.data.frame() %>%
tibble::as_tibble() %>%
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think tibble::remove_rownames() is necessary if you use tibble::as_tibble()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right! These are leftover from when I first wrote these, which was prior to broom using tibble

tibble::remove_rownames()
}

#' Augment data with values from a meta-analysis model
#'
#' Augment the original data with model residuals, fitted values, and influence
#' statistics.
#'
#' @param x An `rma` created by the `metafor` package.
#' @param ... additional arguments
#'
#' @return a `tibble`
#' @export
#'
#' @examples
#'
#' library(metafor)
#'
#' df <-
#' escalc(
#' measure = "RR",
#' ai = tpos,
#' bi = tneg,
#' ci = cpos,
#' di = cneg,
#' data = dat.bcg
#' )
#'
#' meta_analysis <- rma(yi, vi, data = df, method = "EB")
#'
#' augment(meta_analysis)
#'
#' @rdname augmenters
augment.rma <- function(x, ...) {
blup0 <- purrr::possibly(metafor::blup, NULL)
alexpghayes marked this conversation as resolved.
Show resolved Hide resolved
residuals0 <- purrr::possibly(stats::residuals, NULL)
influence0 <- purrr::possibly(stats::influence, NULL)

y <- x$yi
pred <- blup0(x)
if (is.null(pred)) pred <- predict(x)
pred <- as.data.frame(pred)

# fix names
names(pred)[1:4] <- c(".fitted", ".se.fit", ".conf.low", ".conf.high")
credible_intervals <- names(pred) %in% c("cr.lb", "cr.ub")
names(pred)[credible_intervals] <- c(".cred.low", ".cred.high")
moderator <- names(pred) == "X"
names(pred)[moderator] <- ".moderator"

res <- residuals0(x)
inf <- influence0(x)
if (!is.null(inf)) {
inf <- cbind(as.data.frame(inf$inf), dfbetas = inf$dfbs$intrcpt)
inf <- dplyr::select(
inf,
.hat = hat,
.cooksd = cook.d,
.std.resid = rstudent,
.dffits = dffits,
.cov.ratio = cov.r,
.tau.squared.del = tau2.del,
.qe.del = QE.del,
.weight = weight,
.dfbetas = dfbetas
)
}

ret <- cbind(
.rownames = x$slab,
y,
pred,
.resid = res
)

ret <- tibble::as_tibble(ret) %>%
tibble::remove_rownames()

if (all(ret$.rownames == seq_along(ret$.rownames))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a has_rownames() helper somewhere that you can use here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to change the approach in these lines to make it a little clearer what is happening because the rownames aren't coming from the usual place in this model

ret$.rownames <- NULL
}

ret
}
10 changes: 10 additions & 0 deletions R/utilities.R
Expand Up @@ -458,8 +458,12 @@ globalVariables(
"comparison",
"conf.high",
"conf.low",
"cook.d",
"cov.r",
"cutoffs",
"data",
"dffits",
"dfbetas",
"df.residual",
"distance",
"effect",
Expand All @@ -470,6 +474,7 @@ globalVariables(
"GCV",
"group1",
"group2",
"hat",
"index",
"Intercept",
"item1",
Expand All @@ -490,11 +495,14 @@ globalVariables(
"p.value",
"PC",
"percent",
"P-perm (1-tailed)",
"pvalue",
"QE.del",
"rd_roclet",
"rhs",
"rmsea.ci.upper",
"rowname",
"rstudent",
"se",
"series",
"Slope",
Expand All @@ -504,13 +512,15 @@ globalVariables(
"step",
"stratum",
"surv",
"tau2.del",
"term",
"type",
"value",
"Var1",
"Var2",
"variable",
"wald.test",
"weight",
"y",
"z"
)
Expand Down
39 changes: 39 additions & 0 deletions man/augmenters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/broom.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.