if(!requireNamespace("fabricatr", quietly = TRUE)) {
install.packages("fabricatr")
}
library(CausalQueries)
library(fabricatr)
library(knitr)Generating: To make a model you need to provide a
DAG statement to make_model.
For instance
"X->Y""X -> M -> Y <- X" or"Z -> X -> Y <-> X".# examples of models
xy_model <- make_model("X -> Y")
iv_model <- make_model("Z -> X -> Y <-> X")Graphing: Once you have made a model you can inspect the DAG:
plot(iv_model)Inspecting: The model has a set of parameters and a default distribution over these.
xy_model$parameters_df |> kable()| param_names | node | gen | param_set | nodal_type | given | param_value | priors |
|---|---|---|---|---|---|---|---|
| X.0 | X | 1 | X | 0 | 0.50 | 1 | |
| X.1 | X | 1 | X | 1 | 0.50 | 1 | |
| Y.00 | Y | 2 | Y | 00 | 0.25 | 1 | |
| Y.10 | Y | 2 | Y | 10 | 0.25 | 1 | |
| Y.01 | Y | 2 | Y | 01 | 0.25 | 1 | |
| Y.11 | Y | 2 | Y | 11 | 0.25 | 1 |
Tailoring: These features can be edited using
set_restrictions, set_priors and
set_parameters. Here is an example of setting a
monotonicity restriction (see ?set_restrictions for
more):
Here is an example of setting a monotonicity restriction (see
?set_restrictions for more):
iv_model <-
iv_model |> set_restrictions(decreasing('Z', 'X'))Here is an example of setting priors (see ?set_priors
for more):
iv_model <-
iv_model |> set_priors(distribution = "jeffreys")
#> No specific parameters to alter values for specified. Altering all parameters.Simulation: Data can be drawn from a model like this:
data <- make_data(iv_model, n = 4)
data |> kable()| Z | X | Y |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 1 |
| 1 | 1 | 0 |
| 1 | 1 | 1 |
Updating: Update using update_model.
You can pass all rstan arguments to
update_model.
df <- fabricatr::fabricate(N = 100, X = rbinom(N, 1, .5), Y = rbinom(N, 1, .25 + X*.5))
xy_model <-
xy_model |>
update_model(df, refresh = 0)Inspecting: You can access the posterior distribution on model parameters directly thus:
xy_model$posterior_distribution |>
head() |> kable()| X.0 | X.1 | Y.00 | Y.10 | Y.01 | Y.11 |
|---|---|---|---|---|---|
| 0.4554032 | 0.5445968 | 0.2133428 | 0.0533077 | 0.6377230 | 0.0956265 |
| 0.5088665 | 0.4911335 | 0.1799062 | 0.1110084 | 0.6423045 | 0.0667809 |
| 0.5843434 | 0.4156566 | 0.1351546 | 0.0203205 | 0.6599764 | 0.1845485 |
| 0.5781881 | 0.4218119 | 0.2660972 | 0.1237393 | 0.5374835 | 0.0726800 |
| 0.5771600 | 0.4228400 | 0.1700487 | 0.0794219 | 0.6463647 | 0.1041647 |
| 0.4761543 | 0.5238457 | 0.2182718 | 0.0959705 | 0.6140564 | 0.0717014 |
where each row is a draw of parameters.
Querying: You ask arbitrary causal queries of the model.
Examples of unconditional queries:
xy_model |>
query_model("Y[X=1] > Y[X=0]", using = c("priors", "posteriors")) |>
kable()| query | given | using | case_level | mean | sd | cred.low | cred.high |
|---|---|---|---|---|---|---|---|
| Y[X=1] > Y[X=0] | - | priors | FALSE | 0.2538130 | 0.1957065 | 0.0090679 | 0.7107930 |
| Y[X=1] > Y[X=0] | - | posteriors | FALSE | 0.6226124 | 0.0857011 | 0.4449809 | 0.7725678 |
Examples of conditional queries:
xy_model |>
query_model("Y[X=1] > Y[X=0]", using = c("priors", "posteriors"),
given = "X==1 & Y == 1") |>
kable()| query | given | using | case_level | mean | sd | cred.low | cred.high |
|---|---|---|---|---|---|---|---|
| Y[X=1] > Y[X=0] | X==1 & Y == 1 | priors | FALSE | 0.4959161 | 0.2907514 | 0.0218644 | 0.9752001 |
| Y[X=1] > Y[X=0] | X==1 & Y == 1 | posteriors | FALSE | 0.8710234 | 0.0837827 | 0.6865481 | 0.9921468 |
Queries can even be conditional on counterfactual quantities. Here the probability of a positive effect given some effect:
xy_model |>
query_model("Y[X=1] > Y[X=0]", using = c("priors", "posteriors"),
given = "Y[X=1] != Y[X=0]") |>
kable()| query | given | using | case_level | mean | sd | cred.low | cred.high |
|---|---|---|---|---|---|---|---|
| Y[X=1] > Y[X=0] | Y[X=1] != Y[X=0] | priors | FALSE | 0.4970856 | 0.2895303 | 0.0271811 | 0.9728868 |
| Y[X=1] > Y[X=0] | Y[X=1] != Y[X=0] | posteriors | FALSE | 0.8852670 | 0.0646467 | 0.7582853 | 0.9931525 |