Authors: Lucy
D’Agostino McGowan
License: MIT
# install.packages(devtools)
::install_github("lucymcgowan/tipr") devtools
library("tipr")
After fitting your model, you can determine the unmeasured confounder
needed to tip your analysis. This unmeasured confounder is determined by
two quantities, the association between the exposure and the unmeasured
confounder (if the unmeasured confounder is continuous, this is
indicated with smd
, if binary, with exposed_p
and unexposed_p
), and the association between the
unmeasured confounder and outcome outcome_association
.
Using this 📦, we can fix one of these and solve for the other.
Alternatively, we can fix both and solve for n
, that is,
how many unmeasured confounders of this magnitude would tip the
analysis.
In this example, a model was fit and the exposure-outcome relationship was 1.5 (95% CI: 1.2, 1.8).
We are interested in a continuous unmeasured confounder, so we will
use the tip_with_continuous()
function.
Let’s assume the relationship between the unmeasured confounder and
outcome is 1.5 (outcome_association = 1.5
), let’s solve for
the association between the exposure and unmeasured confounder needed to
tip the analysis (in this case, we are solving for smd
, the
mean difference needed between the exposed and unexposed).
tip(1.2, outcome_association = 1.5)
## The observed effect (1.2) WOULD be tipped by 1 unmeasured confounder
## with the following specifications:
## * estimated difference in scaled means between the unmeasured confounder
## in the exposed population and unexposed population: 0.45
## * estimated association between the unmeasured confounder and the outcome: 1.5
## # A tibble: 1 Ă— 4
## observed_effect smd outcome_association n_unmeasured_confounders
## <dbl> <dbl> <dbl> <dbl>
## 1 1.2 0.450 1.5 1
A hypothetical unobserved continuous confounder that has an
association of 1.5 with the outcome would need a scaled mean difference
between exposure groups of 0.45
to tip this analysis at the
5% level, rendering it inconclusive.
Now we are interested in the binary unmeasured confounder, so we will
use the tip_with_binary()
function.
Let’s assume the unmeasured confounder is prevalent in 25% of the
exposed population (exposed_p = 0.25
) and in 10% of the
unexposed population (unexposed_p = 0.10
) – let’s solve for
the association between the unmeasured confounder and the outcome needed
to tip the analysis (outcome_association
).
tip_with_binary(1.2, exposed_p = 0.25, unexposed_p = 0.10)
## The observed effect (1.2) WOULD be tipped by 1 unmeasured confounder
## with the following specifications:
## * estimated prevalence of the unmeasured confounder in the exposed population: 0.25
## * estimated prevalence of the unmeasured confounder in the unexposed population: 0.1
## * estimated association between the unmeasured confounder and the outcome: 2.54
## # A tibble: 1 Ă— 5
## observed_effect exposed_p unexposed_p outcome_association n_unmeasured_confou…
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1.2 0.25 0.1 2.54 1
A hypothetical unobserved binary confounder that is prevalent in 10% of the unexposed population and 25% of the exposed population would need to have an association with the outcome of 2.5 to tip this analysis at the 5% level, rendering it inconclusive.
Suppose we are concerned that there are many small, independent, continuous, unmeasured confounders present.
tip(1.2, smd = 0.25, outcome_association = 1.05)
## The observed effect (1.2) WOULD be tipped by 15 unmeasured confounders
## with the following specifications:
## * estimated difference in scaled means between the unmeasured confounder
## in the exposed population and unexposed population: 0.25
## * estimated association between the unmeasured confounder and the outcome: 1.05
## # A tibble: 1 Ă— 4
## observed_effect smd outcome_association n_unmeasured_confounders
## <dbl> <dbl> <dbl> <dbl>
## 1 1.2 0.25 1.05 14.9
It would take about 15
independent unmeasured
confounders with a scaled mean difference between exposure groups of
0.25 to and an association with the outcome of 1.05 tip the observed
analysis at the 5% level, rendering it inconclusive.
These functions were created to easily integrate with models tidied
using the broom package. This is not necessary
to use these functions, but a nice feature if you choose to do so. Here
is an example of a logistic regression fit with glm
and
tidied with the tidy
function broom that
can be directly fed into the tip()
function.
if (requireNamespace("broom", quietly = TRUE) && requireNamespace("dplyr", quietly = TRUE)) {
glm(am ~ mpg, data = mtcars, family = "binomial") %>%
::tidy(conf.int = TRUE, exponentiate = TRUE) %>%
broom::filter(term == "mpg") %>%
dplyr::pull(conf.low) %>%
dplyrtip(outcome_association = 2.5)
}
## The observed effect (1.13) WOULD be tipped by 1 unmeasured confounder
## with the following specifications:
## * estimated difference in scaled means between the unmeasured confounder
## in the exposed population and unexposed population: 0.13
## * estimated association between the unmeasured confounder and the outcome: 2.5
## # A tibble: 1 Ă— 4
## observed_effect smd outcome_association n_unmeasured_confounders
## <dbl> <dbl> <dbl> <dbl>
## 1 1.13 0.133 2.5 1