Title: | Identification of Counterfactual Queries in Causal Models |
---|---|
Description: | Facilitates the identification of counterfactual queries in structural causal models via the ID* and IDC* algorithms by Shpitser, I. and Pearl, J. (2007, 2008) <arXiv:1206.5294>, <https://jmlr.org/papers/v9/shpitser08a.html>. Provides a simple interface for defining causal diagrams and counterfactual conjunctions. Construction of parallel worlds graphs and counterfactual graphs is carried out automatically based on the counterfactual query and the causal diagram. See Tikka, S. (2023) <doi:10.32614/RJ-2023-053> for a tutorial of the package. |
Authors: | Santtu Tikka [aut, cre] |
Maintainer: | Santtu Tikka <[email protected]> |
License: | GPL (>= 3) |
Version: | 0.1.7 |
Built: | 2024-11-10 03:28:59 UTC |
Source: | https://github.com/santikka/cfid |
cfid
packageIdentification of Counterfactual Queries in Causal Models
This package provides tools necessary for identifying counterfactual queries in causal models. Causal graphs, counterfactual variables, and counterfactual conjunctions are defined via simple interfaces.
In simple terms, counterfactual are statements involving multiple
conceptual "worlds" where the observed state of the worlds is different.
As an example, consider two variables, Y = "headache", and X = "aspirin".
A counterfactual statement could be "If I have a headache and did not take
aspirin, would I not have a headache, had I taken aspirin instead".
This statement involves two worlds: the real or "actual" world, where
aspirin was not taken, and a hypothetical world, where it was taken.
In more formal terms, this statement involves a counterfactual variable
that attains two different values in two different worlds, forming
a counterfactual conjunction:
, where
and
are two different values of
, and
and
are
two different values of
.
Pearl's ladder of causation consists of the associational,
interventional and counterfactual levels, with counterfactual being
the highest level. The goal of identification is to find a transformation
of a higher level query into a lower level one.
For the interventional case, this transformation is known as causal effect
identifiability, where interventional distributions are expressed in terms
of observational quantities. Tools for this type of identification are
readily available,
such as in the causaleffect
, dagitty
, pcalg
, and dosearch
packages. Transformation from the highest counterfactual level, is more
difficult, both conceptually and computationally, since to reach the
observational level, we must first find a transformation of our
counterfactual query into a interventional query, and then transform this
yet again to observational. Also, there transformations may not always
exist, for example in the presence of latent unobserved confounders, meaning
that the queries are non-identifiable. This package deals with the first
transformation, i.e., expressing the counterfactual queries in terms of
interventional queries (and observational, when possible), as well as the
second one, transforming interventional distributions to observational
quantities.
Identification is carried out in terms of and
and where
is a directed acyclic graph (DAG) depicting the causal model
in question (a causal graph for short), and
is the set of all interventional distributions
in causal models inducing
. Identification is carried
out by the ID* and IDC* algorithms by Shpitser and Pearl (2008) which aim to
convert the input counterfactual probability into an expression which can
be represented solely in terms of interventional distributions. These
algorithms are sound and complete, meaning that their output is always
correct, and in the case of a non-identifiable counterfactual, one can
always construct a counterexample, witnessing non-identifiability.
The causal graph associated with the causal model is given via a simple
text-based interface, similar to dagitty
package syntax. Directed edges
are given as X -> Y
, and bidirected edges as X <-> Y
, which is a
shorthand notation for latent confounders. For more details on graph
construction, see dag()
.
Counterfactual variables are defined by their name, value and the conceptual world that they belong to. A world is defined by a unique set of actions (interventions) via the do-operator (Pearl, 2009). We can define the two counterfactual variables of the headache/aspirin example as follows:
cf(var = "Y", obs = 0, sub = c(X = 0)) cf(var = "Y", obs = 1, sub = c(X = 1))
Here, var
defines the name of the variable, obs
gives level the variable
is assigned to (not the actual value), and sub
defines the vector of
interventions that define the counterfactual world. For more details,
see counterfactual_variable()
. Counterfactual conjunctions on the
other hand, are simply counterfactual statements (variables) that are
observed at the same time. For more details, see
counterfactual_conjunction()
.
For complete examples of identifiable counterfactual queries, see
identifiable()
, which is the main function of the package.
Tikka, S. (2023).
Identifying counterfactual queries with the R package cfid
.
The R Journal, 15(2):330–343.
Pearl, J. (1995) Causal diagrams for empirical research. Biometrika, 82(4):669–688.
Pearl, J. (2009) Causality: Models, Reasoning, and Inference. Cambridge University Press, 2nd edition.
Shpitser, I. and Pearl, J. (2007). What counterfactuals can be tested. In Proceedings of the 23rd Conference on Uncertainty in Artificial Intelligence, 352–359.
Shpitser, I. and Pearl, J. (2008). Complete identification methods for the causal hierarchy. Journal of Machine Learning Research, 9(64):1941–1979.
Makhlouf, K., Zhioua, S. and Palamidessi, C. (2021). Survey on causal-based machine learning fairness notions. arXiv:2010.09553
Identify a causal effect of the form from
in
.
causal_effect(g, y, x = character(0), z = character(0), v = integer(0))
causal_effect(g, y, x = character(0), z = character(0), v = integer(0))
g |
A |
y |
A |
x |
A |
z |
An optional |
v |
An optional named |
An object of class query
which is a list
with the following
components:
id
A logical
value that is TRUE
if the query is identifiable and
FALSE
otherwise.
formula
A functional
object expressing the causal effect in terms
of the joint probability distribution for identifiable queries
or
NULL
if the query is not identifiable.
data
The available data, for causal_effect
this is always
"observations"
causaleffect
The original query as a
probability
object.
undefined
A logical
value, this is always FALSE
for
causaleffect
conj
defines a conjunction of counterfactual statements (variables).
cf
defines a counterfactual variable .
counterfactual_conjunction(...) ## S3 method for class 'counterfactual_conjunction' format(x, var_sep = " /\\ ", ...) ## S3 method for class 'counterfactual_conjunction' print(x, ...) ## S3 method for class 'counterfactual_conjunction' e1 + e2 ## S3 method for class 'counterfactual_conjunction' x[i] ## S3 method for class 'counterfactual_variable' e1 + e2 conj(...) counterfactual_variable(var, obs = integer(0L), sub = integer(0L)) ## S3 method for class 'counterfactual_variable' format(x, use_primes = TRUE, ...) ## S3 method for class 'counterfactual_variable' print(x, ...) cf(var, obs = integer(0L), sub = integer(0L))
counterfactual_conjunction(...) ## S3 method for class 'counterfactual_conjunction' format(x, var_sep = " /\\ ", ...) ## S3 method for class 'counterfactual_conjunction' print(x, ...) ## S3 method for class 'counterfactual_conjunction' e1 + e2 ## S3 method for class 'counterfactual_conjunction' x[i] ## S3 method for class 'counterfactual_variable' e1 + e2 conj(...) counterfactual_variable(var, obs = integer(0L), sub = integer(0L)) ## S3 method for class 'counterfactual_variable' format(x, use_primes = TRUE, ...) ## S3 method for class 'counterfactual_variable' print(x, ...) cf(var, obs = integer(0L), sub = integer(0L))
... |
Additional arguments passed to
|
x |
A |
var_sep |
A |
e1 |
A |
e2 |
A |
i |
An |
var |
A character vector of length one naming the variable
(i.e., |
obs |
An integer vector of length one or zero. If given, denotes
the observed value of |
sub |
A named integer vector where the names correspond to the
variables intervened on (via |
use_primes |
A |
conj
returns an object of class counterfactual_conjunction
.
cf
returns an object of class counterfactual_variable
.
A counterfactual conjunction is a conjunction (or a set in some contexts) of counterfactual statements that are assumed to hold simultaneously.
For example, the statement "The value of was observed to
be
, and the value of
was observed to be
under the intervention
" consists of two variables:
variable
without intervention, and
under the intervention
(which is
). This conjunction can be succinctly
written as
.
Conjunctions can also be constructed
via the alias conj
or iteratively from counterfactual_variable
objects (see examples).
Assume that is a single variable and
is a vector
of variables. Here, The notation
means that the variable
(
var
) attains the value (
obs
) under the
intervention (
sub
).
Note that different values of obs
for a two variables with the same var
and the same sub
do not denote their actual values, but the levels
(i.e., obs = 0
is different from obs = 1
, but the variables do not
actually attain values 0 and 1). In other words, if var
is different for
two counterfactual variables, but they have the same value obs
, this
does not mean that these variables have the same value. They will only
actually have the same value if they share both var
and obs
.
For more information about the
-operator, see Pearl (2009). The shortcut alias
cf
can also
be used to construct counterfactual variables.
# The conjunction described under 'details' v1 <- cf("Y", 0) v2 <- cf("Y", 1, c("X" = 0)) c1 <- conj(v1, v2) # Alternative construction c1 <- v1 + v2 # Adding further variables v3 <- cf("X", 1) c2 <- c1 + v3 # A specific value of a variable (a unique combination of `var` and `sub`) # can only appear once in a given conjunction, # otherwise the conjunction would be trivially inconsistent v4 <- cf("Y", 0, c("X" = 0)) v5 <- cf("Y", 1, c("X" = 0)) c3 <- try(conj(v4, v5)) # Y without an assigned value or any interventions cf("Y") # Y with a value assignment y, but no interventions cf("Y", 0) # Y with a different value y', but no interventions cf("Y", 1) # Y with the same value as the previous under the intervention do(X = x) cf("Y", 1, c("X" = 0)) # Y with yet another value y'', under the intervention # do(X = x', Z = z), i.e., the intervention on X has a different value # than the previous (x != x') and Z is also assigned the value z cf("Y", 2, c("X" = 1, "Z" = 0))
# The conjunction described under 'details' v1 <- cf("Y", 0) v2 <- cf("Y", 1, c("X" = 0)) c1 <- conj(v1, v2) # Alternative construction c1 <- v1 + v2 # Adding further variables v3 <- cf("X", 1) c2 <- c1 + v3 # A specific value of a variable (a unique combination of `var` and `sub`) # can only appear once in a given conjunction, # otherwise the conjunction would be trivially inconsistent v4 <- cf("Y", 0, c("X" = 0)) v5 <- cf("Y", 1, c("X" = 0)) c3 <- try(conj(v4, v5)) # Y without an assigned value or any interventions cf("Y") # Y with a value assignment y, but no interventions cf("Y", 0) # Y with a different value y', but no interventions cf("Y", 1) # Y with the same value as the previous under the intervention do(X = x) cf("Y", 1, c("X" = 0)) # Y with yet another value y'', under the intervention # do(X = x', Z = z), i.e., the intervention on X has a different value # than the previous (x != x') and Z is also assigned the value z cf("Y", 2, c("X" = 1, "Z" = 0))
Define a directed acyclic graph (DAG) describing the causal model.
dag(x, u = character(0L)) ## S3 method for class 'dag' print(x, ...)
dag(x, u = character(0L)) ## S3 method for class 'dag' print(x, ...)
x |
A |
u |
A |
... |
Not used |
The syntax for x
follows the dagitty
package closely for compatibility.
However, not all features of dagitty
graphs are supported.
The resulting adjacency matrix of the definition is checked for cycles.
Directed edges are defined as X -> Y
meaning that there is an edge from
X
to Y
in the graph. Edges can be combined in sequence to create paths
for concise descriptions, for example X -> Y <- Z -> W
.
Unobserved latent confounders are defined using bidirected edges as
X <-> Y
which means that there is an additional variable U[X,Y]
in
the graph, and the edges X <- U[X,Y] -> Y
, respectively.
Different statements in x
can be distinguished from one
another using either semicolons, line breaks, or spaces.
Subgraphs can be defined by enclosing the definition within
curly braces. For example X -> {Y Z}
defines an edge
from X
to both Y
and Z
. Individual statements within a subgraph can be
separated by a comma or semicolon, but this is optional.
Edges can also be defined within subgraphs, and subgraphs can be nested.
For example, X -> {Z -> Y}
is the same definition as
X -> Z; X -> Y; Z -> Y
. Similarly X <-> {Z -> {A B}} -> Y
is the same as
X <-> {Z A B} -> Y; Z -> {A B}
.
Note that in the context of this package, vertex labels will always be
converted into upper case, meaning that typing Z
or z
will
always represent the same variable. This is done to enforce the notation
of counterfactual variables, where capital letters denote variables
and small letters denote their value assignments.
An object of class dag
, which is a square adjacency matrix
with the following attributes:
labels
A character
vector (or a list) of vertex labels.
latent
A logical
vector indicating latent variables.
order
An integer
vector giving a topological order for the vertices.
text
A character
string giving representing the DAG.
.
dag("X -> {Y Z} <- W <-> G") # Subgraphs can appear on both sides of an edge dag("{X Z} -> {Y W}") # Semicolons can be used to distinguish individual statements dag("X -> Z -> Y; X <-> Y") # Commas can be used to distinguish variables within groups if there # are no edges within the group dag("{X, Y, Z} -> W") # Edges within subgraphs is supported dag("{X -> Z} -> {Y <-> W}") # Nested subgraphs are supported dag("{X -> {Z -> {Y <-> W}}}") # Line breaks are also supported for statement separation dag(" Z -> W X -> Y ")
dag("X -> {Y Z} <- W <-> G") # Subgraphs can appear on both sides of an edge dag("{X Z} -> {Y W}") # Semicolons can be used to distinguish individual statements dag("X -> Z -> Y; X <-> Y") # Commas can be used to distinguish variables within groups if there # are no edges within the group dag("{X, Y, Z} -> W") # Edges within subgraphs is supported dag("{X -> Z} -> {Y <-> W}") # Nested subgraphs are supported dag("{X -> {Z -> {Y <-> W}}}") # Line breaks are also supported for statement separation dag(" Z -> W X -> Y ")
Convert a valid graph object into a supported external format.
export_graph( g, type = c("dagitty", "causaleffect", "dosearch"), use_bidirected = TRUE, ... )
export_graph( g, type = c("dagitty", "causaleffect", "dosearch"), use_bidirected = TRUE, ... )
g |
An object of class |
type |
A character string matching one of the following:
|
use_bidirected |
A logical value indicating if bidirected edges
should be used in the resulting object.
If |
... |
Additional arguments passed to |
If type
is "dagitty"
, returns a dagitty
object.
If type
is "causaleffect"
, returns an igraph
graph, with its edge
attributes set according to the causaleffect
package syntax. If type
is "dosearch"
, returns a character vector of length one that describes
g
in the dosearch
package syntax.
Identifying functionals are more complicated probabilistic expressions
that cannot be expressed as simple observational or interventional
probabilities using probability()
.
functional(sumset = NULL, terms = NULL, numerator = NULL, denominator = NULL) ## S3 method for class 'functional' format(x, ...) ## S3 method for class 'functional' print(x, ...)
functional(sumset = NULL, terms = NULL, numerator = NULL, denominator = NULL) ## S3 method for class 'functional' format(x, ...) ## S3 method for class 'functional' print(x, ...)
sumset |
A |
terms |
A |
numerator |
A |
denominator |
A |
x |
A |
... |
Additional arguments passed to |
When formatted via print
or format
, the arguments are
prioritized in the following order if conflicting definitions are given:
(sumset
, terms
), (numerator
, denominator
).
An object of class functional
, which is a list
containing
all of the arguments of the constructor.
A character
representation of the functional
object
in LaTeX syntax.
Determine the identifiability of a (conditional) counterfactual conjunction.
identifiable( g, gamma, delta = NULL, data = c("interventions", "observations", "both") )
identifiable( g, gamma, delta = NULL, data = c("interventions", "observations", "both") )
g |
A |
gamma |
An R object that can be coerced into a
|
delta |
An R object that can be coerced into a
|
data |
A |
To identify a non-conditional conjunction , the argument
delta
should be NULL
.
To identify a conditional conjunction , both
gamma
and delta
should be specified.
First, a parallel worlds graph is constructed based on the query. In a
parallel worlds graph, for each -action that appears in
(and
) a copy of the original graph is created with the new
observational variables attaining their post-interventional values
but sharing the latent variables. This graph is known as a parallel worlds
graph. From the parallel worlds graph, a counterfactual graph
is derived such that each variable is unique, which might not be the case
in a parallel worlds graph.
Finally, the ID* (or IDC*) algorithm is applied to determine identifiability
of the query. Similar to the ID and IDC algorithms for causal effects,
these algorithms exploit the so called c-component factorization to split
the query into smaller subproblems, which are then solved recursively.
If argument data
is "observations"
or "both"
, identification of
interventional probabilities in the resulting functional is further
attempted in terms of the joint probability distribution by using the
ID and IDC algorithms (see causal_effect).
An object of class query
which is a list
containing
one or more of the following:
id
A logical
value that is TRUE
if the query is identifiable and
FALSE
otherwise from the available data
in g
.
Note that in cases where gamma
itself is
inconsistent, the query will be identifiable, but with probability 0.
formula
An object of class functional
giving the identifying
functional of the query in LaTeX syntax via format
or print
,
if identifiable. This expression is given in terms of the
available data
. Variables bound by summation are distinguished by a
superscript asterisk. For tautological statements, the resulting
probability is 1, and for inconsistent statements, the resulting
probability is 0. For formatting options, see
format.functional()
and format.probability()
.
undefined
A logical value that is TRUE
if
a conditional conjunction is undefined,
for example when
, and
FALSE
otherwise.
gamma
The original counterfactual conjunction..
delta
The original conditioning counterfactual conjunction.
data
The original data.
dag()
, counterfactual_variable()
,
probability()
, functional()
# Examples that appears in Shpitser and Pearl (2008) g1 <- dag("X -> W -> Y <- Z <- D X <-> Y") g2 <- dag("X -> W -> Y <- Z <- D X <-> Y X -> Y") v1 <- cf("Y", 0, c(X = 0)) v2 <- cf("X", 1) v3 <- cf("Z", 0, c(D = 0)) v4 <- cf("D", 0) c1 <- conj(v1) c2 <- conj(v2, v3, v4) c3 <- conj(v1, v2, v3, v4) # Identifiable conditional conjunction identifiable(g1, c1, c2) # Identifiable conjunction identifiable(g1, c3) # Non-identifiable conjunction identifiable(g2, c3)
# Examples that appears in Shpitser and Pearl (2008) g1 <- dag("X -> W -> Y <- Z <- D X <-> Y") g2 <- dag("X -> W -> Y <- Z <- D X <-> Y X -> Y") v1 <- cf("Y", 0, c(X = 0)) v2 <- cf("X", 1) v3 <- cf("Z", 0, c(D = 0)) v4 <- cf("D", 0) c1 <- conj(v1) c2 <- conj(v2, v3, v4) c3 <- conj(v1, v2, v3, v4) # Identifiable conditional conjunction identifiable(g1, c1, c2) # Identifiable conjunction identifiable(g1, c3) # Non-identifiable conjunction identifiable(g2, c3)
Import and construct a valid DAG from an external format. Accepts
dagitty
graphs, igraph
graphs in the causaleffect
package syntax,
and character strings in the dosearch
package syntax.
import_graph(x)
import_graph(x)
x |
A graph object in a valid external format. |
A dag
object.
Defines an interventional or observational (conditional) probability
. For formatting options, see
format.probability()
.
probability(val = NULL, var = NULL, do = NULL, cond = NULL) ## S3 method for class 'probability' format(x, use_do = FALSE, ...) ## S3 method for class 'probability' print(x, ...)
probability(val = NULL, var = NULL, do = NULL, cond = NULL) ## S3 method for class 'probability' format(x, use_do = FALSE, ...) ## S3 method for class 'probability' print(x, ...)
val |
An |
var |
A |
do |
A |
cond |
A |
x |
A |
use_do |
A |
... |
Additional arguments passed to |
An object of class probability
, which is a list
containing
all of the arguments of the constructor.
A character
representation of the probability
object
in LaTeX syntax.
counterfactual_variable()
, functional()
# Example from Makhlouf, Zhioua and Palamidessi (2021) g2 <- dag("C -> A -> Y; C -> Y") v1 <- cf("Y", 0, c(A = 1)) v2 <- cf("A", 0) c1 <- conj(v1) c2 <- conj(v2) f <- identifiable(g2, c1, c2)$formula # Default, using primes and subscript notation format(f) # Without primes, no do-operator format(f, use_primes = FALSE) # Primes, with do-operator format(f, use_do = TRUE) # Without primes, with do-operator format(f, use_primes = FALSE, use_do = TRUE)
# Example from Makhlouf, Zhioua and Palamidessi (2021) g2 <- dag("C -> A -> Y; C -> Y") v1 <- cf("Y", 0, c(A = 1)) v2 <- cf("A", 0) c1 <- conj(v1) c2 <- conj(v2) f <- identifiable(g2, c1, c2)$formula # Default, using primes and subscript notation format(f) # Without primes, no do-operator format(f, use_primes = FALSE) # Primes, with do-operator format(f, use_do = TRUE) # Without primes, with do-operator format(f, use_primes = FALSE, use_do = TRUE)
Objects of class query
describe the output of identifiable
and
causal_effect
. They are list
objects with a custom print
method and
contain data related to the identifiability results. See
identifiable and causal_effect for details.
## S3 method for class 'query' print(x, ...)
## S3 method for class 'query' print(x, ...)
x |
A |
... |
Arguments passed to format.functional and format.counterfactual_conjunction |