These methods allow standard dplyr operations on estimates_vcov objects while keeping the variance-covariance matrix synchronized with the data.
Supported operations:
Arguments
- .data, x
An
estimates_vcovobject.- y
A data frame to join against.
- ...
Passed on to the corresponding dplyr verb.
- .preserve, .by_group, .key, .keep, n, prop, var, name
Passed on to the corresponding dplyr verb.
- by, copy, suffix, keep, na_matches, multiple, unmatched, relationship
Passed on to the corresponding dplyr join.
- .before, .after
Passed on to
dplyr::relocate().
Value
An estimates_vcov object, except for pull(), which returns a
vector, and nest_by(), which returns a rowwise tibble whose .key column
holds one estimates_vcov object per group.
Details
These methods keep the vcov row-aligned (subsetting and reordering the rows
also subset/reorder the matrix), but they never transform the matrix. In
particular mutate() can change the values of a column without touching the
vcov, so mutate(estimate = -estimate) (or any rescaling of estimate) leaves
the vcov inconsistent with the estimates. To sign-flip or rescale estimates and
keep the covariances valid, use rescale_estimates_vcov(), which updates the
vcov as \(\mathrm{diag}(s)\, V\, \mathrm{diag}(s)\).
Joins that could change the row set are refused rather than allowed to
desynchronize the object: inner_join() and full_join() can drop or add
rows, and right_join() changes which rows are kept. left_join() is
supported, and errors if the join turns out to duplicate rows. Use filter()
when the intent is to remove estimates.
See also
rescale_estimates_vcov() to transform estimate values and the vcov
together, and estimates_vcov for what the object guarantees.
Other estimates_vcov objects:
as_estimates_vcov(),
bind_estimates_vcov(),
estimates_vcov,
make_estimates_vcov(),
rescale_estimates_vcov()
Examples
library(dplyr)
set.seed(1)
dat_1 <- data.frame(Y = rnorm(60), Z = factor(rep(c("T0", "T1"), each = 30)))
dat_2 <- data.frame(Y = rnorm(90), Z = factor(rep(c("T0", "T1", "T2"), each = 30)))
ev <- as_estimates_vcov(bind_rows(
study_1 = prep_fit(lm(Y ~ Z, dat_1), term = "ZT1"),
study_2 = prep_fit(lm(Y ~ Z, dat_2), term = c("ZT1", "ZT2")),
.id = "study"
))
# Row operations subset or reorder the vcov along with the estimates
dim(get_vcov(ev))
#> [1] 3 3
dim(get_vcov(filter(ev, study == "study_2")))
#> [1] 2 2
rownames(get_vcov(arrange(ev, estimate)))
#> [1] "3" "2" "1"
# Column operations leave the vcov alone, because the rows cannot change
ev |> mutate(abs_estimate = abs(estimate))
#> <estimates_vcov>
#> # 3 estimates with 3x3 vcov matrix
#>
#> # A tibble: 3 × 8
#> id study term estimate std.error statistic p.value abs_estimate
#> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1 study_1 ZT1 0.0503 0.223 0.226 0.822 0.0503
#> 2 2 study_2 ZT1 0.00305 0.238 0.0128 0.990 0.00305
#> 3 3 study_2 ZT2 -0.440 0.238 -1.85 0.0679 0.440
ev |> select(id, study, term, estimate)
#> <estimates_vcov>
#> # 3 estimates with 3x3 vcov matrix
#>
#> # A tibble: 3 × 4
#> id study term estimate
#> <chr> <chr> <chr> <dbl>
#> 1 1 study_1 ZT1 0.0503
#> 2 2 study_2 ZT1 0.00305
#> 3 3 study_2 ZT2 -0.440
# nest_by() gives one self-contained object per group, each with its own vcov
nested <- ev |> nest_by(study)
nested$data[[2]]
#> <estimates_vcov>
#> # 2 estimates with 2x2 vcov matrix
#>
#> # A tibble: 2 × 6
#> id term estimate std.error statistic p.value
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 2 ZT1 0.00305 0.238 0.0128 0.990
#> 2 3 ZT2 -0.440 0.238 -1.85 0.0679
dim(get_vcov(nested$data[[2]]))
#> [1] 2 2
# left_join() adds columns; a join that duplicated rows would be an error
ev |> left_join(data.frame(study = c("study_1", "study_2"),
region = c("north", "south")), by = "study")
#> <estimates_vcov>
#> # 3 estimates with 3x3 vcov matrix
#>
#> # A tibble: 3 × 8
#> id study term estimate std.error statistic p.value region
#> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr>
#> 1 1 study_1 ZT1 0.0503 0.223 0.226 0.822 north
#> 2 2 study_2 ZT1 0.00305 0.238 0.0128 0.990 south
#> 3 3 study_2 ZT2 -0.440 0.238 -1.85 0.0679 south
# Study 2's two arms covary: that is the entry a sign flip has to update.
get_vcov(ev)[2, 3]
#> [1] 0.02836191
# mutate() does NOT transform the vcov, so this desynchronizes the object:
get_vcov(mutate(ev, estimate = -estimate))[2, 3] # unchanged, now wrong
#> [1] 0.02836191
# rescale_estimates_vcov() updates it. Flipping one arm and not the other
# flips the sign of their covariance, which is the case easiest to get wrong:
get_vcov(rescale_estimates_vcov(ev, by = if_else(id == "2", -1, 1)))[2, 3]
#> [1] -0.02836191