Learn R Programming

cna (version 2.2.3)

cyclic: Detect cyclic substructures in complex solution formulas (csf)

Description

Given a character vector x specifying complex solution formula(s) (csf), cyclic(x) checks whether x contains cyclic substructures. The function can be used, for instance, to filter cyclic causal models out of cna solution objects (e.g. in order to reduce ambiguities).

Usage

cyclic(x, cycle.type = c("factor", "value"), use.names = TRUE, verbose = FALSE)

Arguments

x

Character vector specifying one or several csf.

cycle.type

Character string specifying what type of cycles to be detected: "factor" (the default) or "value".

use.names

Logical; if TRUE, names are added to the result (see examples).

verbose

Logical; if TRUE, the checked causal paths are printed to the console.

Value

A logical vector: TRUE for a csf with at least one cyclic substructure, FALSE for a csf without any cyclic substructures.

Details

Detecting causal cycles is one of the most challenging tasks in causal data analysis---in all methodological traditions. In a nutshell, the reason is that factors in a cyclic structure are so highly interdependent that, even under optimal discovery conditions, the diversity of (observational) data tends to be too limited to draw informative conclusions about the data-generating structure. In consequence, various methods (most notably, Bayes nets methods, cf. Spirtes et al. 2000) assume that data-generating structures are acyclic.

cna outputs cyclic complex solutions formulas (csf) if they fit the data. Typically, however, the causal modeling of configurational data that can be modeled in terms of cycles is massively ambiguous. Therefore, if there are independent reasons to assume that the data are not generated by a cyclic structure, the function cyclic can be used to reduce the ambiguities in a cna output by filtering out all csf with cyclic substructures.

A causal structure has a cyclic substructure if, and only if, it contains a directed causal path from at least one cause back to itself. A regularity theory of causation in the vein of Mackie (1974) spells this criterion out as follows: a csf x has a cyclic substructure if, and only if, x contains a sequence <Z1, Z2,..., Zn> every element of which is an INUS condition of its successor and Z1=Zn. Accordingly, the function cyclic searches for sequences <Z1, Z2,..., Zn> of factors or factor values in a csf x such that (i) every Zi is contained in the antecedent (i.e. the left-hand side of "<->") of and atomic solution formula (asf) of Zi+1 in x, and (ii) Zn is identical to Z1. The function returns TRUE if, and only if, at least one such sequence (i.e. directed causal path) is found in x.

The cycle.type argument controls whether the sequence <Z1, Z2,..., Zn> is composed of factors (cycle.type = "factor") or factor values (cycle.type = "value"). To illustrate, if cycle.type = "factor", the following csf is considered cyclic: (A + B <-> C)*(c + D <-> A). The factor A (with value 1) appears in the antecedent of an asf of C (with value 1), and the factor C (with value 0) appears in the antecedent of an asf of A (with value 1). But if cycle.type = "value", that same csf does not pass as cyclic. Although the factor value 1 of A appears in the antecedent of an asf of the factor value 1 of C, that same value of C does not appear in the antecedent of an asf of A; rather, the value 0 of C appears in the antecedent of A.

If verbose = TRUE, the sequences (paths) tested for cyclicity are output to the console. Note that the search for cycles is stopped as soon as one cyclic sequence (path) has been detected. Accordingly, not all sequences (paths) contained in x may be output to the console.

References

Mackie, John L. 1974. The Cement of the Universe: A Study of Causation. Oxford: Oxford University Press.

Spirtes, Peter, Clark Glymour, and Richard Scheines. 2000. Causation, Prediction, and Search (second ed.). Cambridge MA: MIT Press.

Examples

Run this code
# NOT RUN {
# cna infers two csf from the d.educate data, neither of which has a cyclic
# substructure.
cnaedu <- cna(d.educate)
cyclic(csf(cnaedu)$condition)

# At con = .82 and cov = .82, cna infers 605 csf from the d.jobsecurity data, all
# of which are cyclic.
cnajob <- fscna(d.jobsecurity, con = 0.82, cov = 0.82)
cyclic(csf(cnajob)$condition)  # first 20 csf
any(!cyclic(csf(cnajob, Inf)$condition))  # No acyclic csf!

# At con = .82 and cov = .82, cna infers 126 csf for the d.pacts data, some
# of which are cyclic, others are acyclic. If there are independent
# reasons to assume acyclicity, here is how to extract all acyclic csf.
cnapacts <- fscna(d.pacts, con = .82, cov = .82, inus.only = TRUE)
subset(csf(cnapacts, Inf), !cyclic(csf(cnapacts, Inf)$condition))

# With verbose = TRUE, the tested sequences (causal paths) are printed.
cyclic("(L=1 + G=1 <-> E=2)*(U=5 + D=3 <-> L=1)*(E=2*G=4 <-> D=3)", verbose = TRUE) 
cyclic("(e*G + F*D + E*c*g*f <-> A)*(d + f*e + c*a <-> B)*(A*e + G*a*f <-> C)", verbose = TRUE)

# Argument cycle.type = "factor" or "value".
cyclic("(A*b + C -> D)*(d + E <-> A)")
cyclic("(A*b + C -> D)*(d + E <-> A)", cycle.type = "value")

cyclic("(L=1 + G=1 <-> E=2)*(U=5 + D=3 <-> L=2)*(E=2 + G=3 <-> D=3)") 
cyclic("(L=1 + G=1 <-> E=2)*(U=5 + D=3 <-> L=2)*(E=2 + G=3 <-> D=3)", cycle.type = "v") 

cyclic("a <-> A")
cyclic("a <-> A", cycle.type = "v")

sol1 <- "(A*X1 + Y1 <-> B)*(b*X2 + Y2 <-> C)*(C*X3 + Y3 <-> A)"
cyclic(sol1)
cyclic(sol1, cycle.type = "value") 

sol2 <- "(A*X1 + Y1 <-> B)*(B*X2 + Y2 <-> C)*(C*X3 + Y3 <-> A)"
cyclic(sol2)
cyclic(sol2, cycle.type = "value")

# Argument use.names.
cyclic("a*b + C -> A", use.names = FALSE)

# More examples.
cyclic("(L + G <-> E)*(U + D <-> L)*(A <-> U)")
cyclic("(L + G <-> E)*(U + D <-> L)*(A <-> U)*(B <-> G)")
cyclic("(L + G <-> E)*(U + D <-> L)*(A <-> U)*(B <-> G)*(L <-> G)")
cyclic("(L + G <-> E)*(U + D <-> L)*(A <-> U)*(B <-> G)*(L <-> C)")
cyclic("(D -> A)*(A -> B)*(A -> C)*(B -> C)")

cyclic("(B + d*f <-> A)*(E + F*g <-> B)*(G*e + D*A <-> C)")
cyclic("(B*E + d*f <-> A)*(A + E*g + f <-> B)*(G*e + D*A <-> C)")
cyclic("(B + d*f <-> A)*(C + F*g <-> B)*(G*e + D*A <-> C)")
cyclic("(e*G + F*D + E*c*g*f <-> A)*(d + f*e + c*a <-> B)*(A*e + G*a*f <-> C)")
cyclic("(e*G + F*D + E*c*g*f <-> A)*(d + f*e + c*a <-> B)*(A*e + G*a*f <-> C)", 
       verbose = TRUE)
# }

Run the code above in your browser using DataLab