Skip to content

Commit 9bb0b99

Browse files
committed
Rel 0.1.0
1 parent 24f1ca2 commit 9bb0b99

10 files changed

Lines changed: 200 additions & 183 deletions

File tree

docs/src/walkthrough.md

Lines changed: 22 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ d_string = "DAG(
2424
statistics ~ algebra+analysis,
2525
analysis ~ algebra)"
2626

27-
dag = DAG("marks", d_string, df);
27+
dag = DAG("marks", d_string; df=df);
2828
show(dag)
2929
```
3030

@@ -50,7 +50,6 @@ OrderedDict{Symbol,Union{Nothing, Array{Symbol,1}, Symbol}} with 4 entries:
5050

5151
Internally, a DAG object will always contain an OrderedDict representation of the DAG. This representation is used in all functions. In the definition of the OrderedDict, read `=>` as `~` in regression models or `<-` in causal models.
5252

53-
5453
Optional display the DAG using GraphViz:
5554
```julia
5655
fname = ProjDir * "/marks.dot"
@@ -60,7 +59,7 @@ Sys.isapple() && run(`open -a GraphViz.app $(fname)`)
6059

6160
The DAG pdf is [here](https://github.com/StatisticalRethinkingJulia/StructuralCausalModels.jl/blob/master/docs/src/marks.pdf).
6261

63-
In this case a DataFrame with observed values has been provided and the related covariance matrix has been computed and stored in the DAG object:
62+
In this example a DataFrame with observed values has been provided and the related covariance matrix will be computed and stored in the DAG object:
6463
```julia
6564
display(dag.s)
6665

@@ -93,68 +92,27 @@ Although this initial version of StructuralCausalModels does not support latent
9392

9493
# Directed separation
9594

96-
Given a causal graph, `d_separation(dag, f, l, cond)` determines if the vertices in set `f` are `d-separated` from the vertices in set `l` given the conditioning set `cond`.
95+
Given a causal graph `dag`, `d_separation(dag, f, l, cond)` determines if the vertices in set `f` are `d-separated` from the vertices in set `l` given the conditioning set `cond`.
9796

9897
Show several `d_separation` results for the marks model:
9998
```julia
100-
f = [:statistics]; s = [:mechanics]; sel = vcat(f, s)
101-
cond = [:algebra]
99+
f = [:statistics]; s = [:mechanics];
102100

103-
e = d_separation(dag, f, s, cond)
104-
println("d_separation($(dag.name), $f, $s, $cond) = $e")
101+
e = d_separation(dag, f, s; cond=:algebra);
102+
println("d_separation($(dag.name), $f, $s; cond=:algebra) = $e")
105103

106-
e = d_separation(dag, f, s)
104+
e = d_separation(dag, f, s);
107105
println("d_separation($(dag.name), $f, $s) = $e")
108106

109-
print("d_separation($(dag.name), [:statistics], [:mechanics], [:vectors]) = ")
110-
println(d_separation(dag, [:statistics], [:mechanics], [:vectors]))
111-
112-
print("d_separation($(dag.name), [:statistics], [:mechanics], [:analysis, :vectors]) = ")
113-
println(d_separation(dag, [:statistics], [:mechanics], [:analysis, :vectors]))
114-
115-
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics], [:algebra]) = ")
116-
println(d_separation(dag, [:statistics, :analysis], [:mechanics], [:algebra]))
117-
118-
print("d_separation($(dag.name), [:statistics], [:mechanics, :vectors], [:algebra]) = ")
119-
println(d_separation(dag, [:statistics], [:mechanics, :vectors], [:algebra]))
120-
121-
print("d_separation($(dag.name), [:statistics], [:mechanics, :analysis], [:algebra]) = ")
122-
println(d_separation(dag, [:statistics], [:mechanics, :analysis], [:algebra]))
123-
124-
print("d_separation($(dag.name), [:analysis], [:vectors]) = ")
125-
println(d_separation(dag, [:analysis], [:vectors]))
126-
127-
print("d_separation($(dag.name), [:analysis], [:vectors], [:algebra]) = ")
128-
println(d_separation(dag, [:analysis], [:vectors], [:algebra]))
129-
130-
print("d_separation($(dag.name), [:vectors], [:statistics], [:algebra]) = ")
131-
println(d_separation(dag, [:analysis], [:vectors], [:algebra]))
132-
133-
print("d_separation($(dag.name), [:statistics], [:algebra], [:analysis]) = ")
134-
println(d_separation(dag, [:statistics], [:algebra], [:analysis]))
135-
136-
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics, :vectors]) = ")
137-
println(d_separation(dag, [:statistics, :analysis], [:mechanics, :vectors]))
138-
139-
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics, :vectors], [:algebra]) = ")
140-
println(d_separation(dag, [:statistics, :analysis], [:mechanics, :vectors], [:algebra]))
107+
print("d_separation($(dag.name), [:statistics], [:mechanics, :analysis]; cond=[:algebra]) = ");
108+
println(d_separation(dag, [:statistics], [:mechanics, :analysis]; cond=[:algebra]))
141109
```
142110

143111
will produce:
144112
```julia
145-
d_separation(marks, [:statistics], [:mechanics], [:algebra]) = false
113+
d_separation(marks, [:statistics], [:mechanics]; cond=:algebra) = false
146114
d_separation(marks, [:statistics], [:mechanics]) = true
147-
d_separation(marks, [:statistics], [:mechanics], [:vectors]) = true
148-
d_separation(marks, [:statistics], [:mechanics], [:analysis, :vectors]) = true
149-
d_separation(marks, [:statistics, :analysis], [:mechanics], [:algebra]) = false
150-
d_separation(marks, [:statistics], [:mechanics, :vectors], [:algebra]) = false
151-
d_separation(marks, [:statistics], [:mechanics, :analysis], [:algebra]) = false
152-
d_separation(marks, [:analysis], [:vectors]) = true
153-
d_separation(marks, [:analysis], [:vectors], [:algebra]) = false
154-
d_separation(marks, [:vectors], [:statistics], [:algebra]) = false
155-
d_separation(marks, [:statistics], [:algebra], [:analysis]) = false
156-
d_separation(marks, [:statistics, :analysis], [:mechanics, :vectors]) = true
157-
d_separation(marks, [:statistics, :analysis], [:mechanics, :vectors], [:algebra]) = false
115+
d_separation(marks, [:statistics], [:mechanics, :analysis]: cond=[:algebra]) = false
158116
```
159117

160118
# Basis set
@@ -176,19 +134,9 @@ BasisSet[
176134
]
177135
```
178136

179-
# Shipley test
180-
181-
Perform the Shipley test:
182-
```julia
183-
t = shipley_test(dag)
184-
display(t)
185-
186-
(ctest = 2.816854003338401, df = 8, pv = 0.9453198036802164)
187-
```
188-
189137
# Adjustment sets
190138

191-
D_separation provides a set of conditional independencies given the causal model. The conditioning set closes (blocks) all paths. It provides ways to test the chosen causal model given observational data.
139+
The function `basis_set()` provides a set of conditional independencies given the causal model. The conditioning set closes ("blocks the flow of causal info") all paths. The conditioning_set can be empty. It provides ways to test the chosen causal model given observational data.
192140

193141
The function `adjustment_sets()` answers a related question, i.e. how to prevent confounding in multiple regression models assuming the chosen causal model is correct.
194142

@@ -231,6 +179,16 @@ Adjustment sets:
231179
[:a, :m]
232180
```
233181

182+
# Shipley test
183+
184+
Perform the Shipley test:
185+
```julia
186+
t = shipley_test(dag)
187+
display(t)
188+
189+
(ctest = 2.816854003338401, df = 8, pv = 0.9453198036802164)
190+
```
191+
234192
# Paths
235193

236194
Adjustment_sets is based on several path manipulations and checks:

examples/dagitty/confounding_triangle.jl

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,17 @@ using StructuralCausalModels
33
ProjDir = @__DIR__
44
cd(ProjDir) #do
55

6-
d = OrderedDict(
7-
:w => :s,
8-
:d => [:a, :w, :m],
9-
:m => [:a, :s],
10-
:a => :s
11-
);
12-
136
d_string = "dag {A -> {E Z}; B -> {D Z}; Z -> {D E}; E -> D}"
147

158
dag = DAG("conf_triangles", d_string);
169
show(dag)
1710

11+
to_ggm(dag) |> display
12+
println()
13+
1814
fname = ProjDir * "/conf_triangles.dot"
1915
to_graphviz(dag, fname)
20-
#Sys.isapple() && run(`open -a GraphViz.app $(fname)`)
16+
Sys.isapple() && run(`open -a GraphViz.app $(fname)`)
2117

2218
println("Basis set:")
2319
bs = basis_set(dag)
@@ -30,13 +26,13 @@ A ⊥ B
3026

3127
f = :A; s = :B;
3228

33-
e = d_separation(dag, f, s, nothing)
34-
println("d_separation($(dag.name), $f, $s) = $e\n")
29+
e = d_separation(dag, f, s)
30+
fprintln("d_separation($(dag.name), $f, $s) = $e\n")
3531

3632
f = [:B]; s = [:E];
3733

38-
e = d_separation(dag, f, s, [:A, :Z])
39-
println("d_separation($(dag.name), $f, $s, [:A, :Z]) = $e\n")
34+
e = d_separation(dag, f, s; cond=[:A, :Z])
35+
println("d_separation($(dag.name), $f, $s; cond=[:A, :Z]) = $e\n")
4036

4137
ap = all_paths(dag, :E, :D)
4238
ap |> display
@@ -46,10 +42,6 @@ bp = backdoor_paths(dag, ap, :E)
4642
bp |> display
4743
println()
4844

49-
op = open_paths(dag, bp, Symbol[])
50-
op |> display
51-
println()
52-
5345
adjustmentsets = adjustment_sets(dag, :E, :D)
5446
println("Adjustment sets:")
5547
adjustmentsets |> display

examples/marks/marks.jl

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ d_string = "DAG(
3535
statistics ~ algebra+analysis,
3636
analysis ~ algebra)"
3737

38-
dag = DAG("marks", d_string, df);
38+
dag = DAG("marks", d_string, df=df);
3939
show(dag)
4040

4141
fname = ProjDir * "/marks.dot"
@@ -55,44 +55,44 @@ display("pcor_test = $pt"); println()
5555

5656
f = [:statistics]; s = [:mechanics]; sel = vcat(f, s)
5757

58-
e = d_separation(dag, f, s, :algebra)
59-
println("d_separation($(dag.name), $f, $s, :algebra) = $e")
58+
e = d_separation(dag, f, s; cond=:algebra)
59+
println("d_separation($(dag.name), $f, $s; cond=:algebra) = $e")
6060

6161
e = d_separation(dag, f, s)
6262
println("d_separation($(dag.name), $f, $s) = $e")
6363

64-
print("d_separation($(dag.name), [:statistics], [:mechanics], [:vectors]) = ")
65-
println(d_separation(dag, [:statistics], [:mechanics], [:vectors]))
64+
print("d_separation($(dag.name), [:statistics], [:mechanics]; cond=[:vectors]) = ")
65+
println(d_separation(dag, [:statistics], [:mechanics]; cond=[:vectors]))
6666

67-
print("d_separation($(dag.name), [:statistics], [:mechanics], [:analysis, :vectors]) = ")
68-
println(d_separation(dag, [:statistics], [:mechanics], [:analysis, :vectors]))
67+
print("d_separation($(dag.name), [:statistics], [:mechanics]; cond=[:analysis, :vectors]) = ")
68+
println(d_separation(dag, [:statistics], [:mechanics]; cond=[:analysis, :vectors]))
6969

70-
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics], [:algebra]) = ")
71-
println(d_separation(dag, [:statistics, :analysis], [:mechanics], [:algebra]))
70+
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics]; cond=[:algebra]) = ")
71+
println(d_separation(dag, [:statistics, :analysis], [:mechanics]; cond=[:algebra]))
7272

73-
print("d_separation($(dag.name), [:statistics], [:mechanics, :vectors], [:algebra]) = ")
74-
println(d_separation(dag, [:statistics], [:mechanics, :vectors], [:algebra]))
73+
print("d_separation($(dag.name), [:statistics], [:mechanics, :vectors]; cond=[:algebra]) = ")
74+
println(d_separation(dag, [:statistics], [:mechanics, :vectors]; cond=[:algebra]))
7575

76-
print("d_separation($(dag.name), [:statistics], [:mechanics, :analysis], [:algebra]) = ")
77-
println(d_separation(dag, [:statistics], [:mechanics, :analysis], [:algebra]))
76+
print("d_separation($(dag.name), [:statistics], [:mechanics, :analysis]; cond=[:algebra]) = ")
77+
println(d_separation(dag, [:statistics], [:mechanics, :analysis]; cond=[:algebra]))
7878

7979
print("d_separation($(dag.name), [:analysis], [:vectors]) = ")
8080
println(d_separation(dag, [:analysis], [:vectors]))
8181

82-
print("d_separation($(dag.name), [:analysis], [:vectors], [:algebra]) = ")
83-
println(d_separation(dag, [:analysis], [:vectors], [:algebra]))
82+
print("d_separation($(dag.name), [:analysis], [:vectors]; cond=[:algebra]) = ")
83+
println(d_separation(dag, [:analysis], [:vectors]; cond=[:algebra]))
8484

85-
print("d_separation($(dag.name), [:vectors], [:statistics], [:algebra]) = ")
86-
println(d_separation(dag, [:analysis], [:vectors], [:algebra]))
85+
print("d_separation($(dag.name), [:vectors], [:statistics]; cond=[:algebra]) = ")
86+
println(d_separation(dag, [:analysis], [:vectors]; cond=[:algebra]))
8787

88-
print("d_separation($(dag.name), [:statistics], [:algebra], [:analysis]) = ")
89-
println(d_separation(dag, [:statistics], [:algebra], [:analysis]))
88+
print("d_separation($(dag.name), [:statistics], [:algebra]; cond=[:analysis]) = ")
89+
println(d_separation(dag, [:statistics], [:algebra]; cond=[:analysis]))
9090

9191
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics, :vectors]) = ")
9292
println(d_separation(dag, [:statistics, :analysis], [:mechanics, :vectors]))
9393

94-
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics, :vectors], [:algebra]) = ")
95-
println(d_separation(dag, [:statistics, :analysis], [:mechanics, :vectors], [:algebra]))
94+
print("d_separation($(dag.name), [:statistics, :analysis], [:mechanics, :vectors]; cond=[:algebra]) = ")
95+
println(d_separation(dag, [:statistics, :analysis], [:mechanics, :vectors]; cond=[:algebra]))
9696

9797
adjustmentsets = adjustment_sets(dag, :statistics, :mechanics)
9898
println("\nAdjustment sets:")

src/methods/d_separation.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ d_separation(
1818
1919
### Keyword arguments
2020
```julia
21-
* `cond::SymbolListOrNothing=nothing` : Conditioning set
21+
* `cset::SymbolListOrNothing=nothing` : Conditioning set
2222
* `debug=false` : Trace execution
2323
```
2424
@@ -45,7 +45,7 @@ d = OrderedDict(
4545
);
4646
4747
dag = DAG("marks", d, df);
48-
d_separation(marks, [:statistics], [:mechanics], [:algebra]))
48+
d_separation(marks, [:statistics], [:mechanics]; cset=[:algebra]))
4949
```
5050
### Acknowledgements
5151
@@ -62,9 +62,9 @@ The Julia translation is licenced under: MIT.
6262
Part of the API, exported.
6363
"""
6464
function d_separation(d::DAG, first::SymbolList, second::SymbolList;
65-
cond::SymbolListOrNothing=nothing, debug=false)
66-
67-
e = induced_covariance_graph(d, vcat(first, second), SymbolList[]; debug=debug)
65+
cset::SymbolListOrNothing=nothing, debug=false)
66+
67+
e = induced_covariance_graph(d, vcat(first, second), cset; debug=debug)
6868
sum(e[first, second]) == 0
6969

7070
end

src/methods/induced_covariance_graph.jl

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,45 +57,49 @@ $(SIGNATURES)
5757
5858
Internal
5959
"""
60-
function induced_covariance_graph(d::DAG, sel, cond; debug=false)
60+
function induced_covariance_graph(d::DAG, sel, cset; debug=false)
6161

6262
@assert all([c in d.vars for c in sel]) "Selection nodes are not among vertices."
6363

64-
if isnothing(cond)
65-
cond = []
66-
elseif typeof(cond) == Symbol
67-
cond = [cond]
68-
elseif length(cond) > 0
69-
@assert all([c in d.vars for c in cond]) "Conditioning nodes are not among vertices."
70-
@assert !all([c in sel for c in cond]) "Conditioning nodes in selected nodes."
64+
if isnothing(cset)
65+
cs = []
66+
elseif typeof(cset) == Symbol
67+
cs = [cset]
68+
elseif length(cset) > 0
69+
cs = cset
7170
end
71+
@assert all([c in d.vars for c in cs]) "Conditioning nodes are not among vertices."
72+
#@assert !(length([cs in sel]) > 0) "Conditioning nodes in selected nodes."
7273

73-
l = setdiff(d.vars, union(sel, cond))
74+
l = setdiff(d.vars, union(sel, cs))
7475
debug && println(l)
7576
l = union(l, sel)
7677
debug && println(l)
77-
r = union(sel, cond)
78+
r = union(sel, cs)
7879
debug && println(r)
7980

8081
e = edge_matrix(d.a) # From adjacency matrix to edge matrix
8182
debug && println(e)
82-
al = ancester_graph(e[l, l])
83+
al = transpose(StructuralCausalModels.ancester_graph(e[l, l]))
8384
debug && println(al)
84-
if length(cond) > 0
85-
trl = indicator_matrix( e[cond, l] * al)
85+
if length(cs) > 0
86+
trl = StructuralCausalModels.indicator_matrix( transpose(e[l, cs]) * al)
8687
else
8788
trl = al - al
8889
end
8990
debug && println(trl)
90-
dlr = indicator_matrix(I(length(l)) + transpose(trl) * trl)
91+
dlr = StructuralCausalModels.indicator_matrix(I(length(l)) + transpose(trl) * trl)
9192
debug && println(dlr)
92-
cl = transitive_closure(dlr)
93+
cl = StructuralCausalModels.transitive_closure(dlr)
9394
debug && println(cl)
94-
out = indicator_matrix(( al * cl * transpose(al)))
95+
out = StructuralCausalModels.indicator_matrix(( al * cl * transpose(al)))
9596
debug && println(out)
9697
out = out[sel, sel]
9798
debug && println(out)
9899
debug && println(adjacency_matrix(out))
99100
adjacency_matrix(out)
100101

101102
end
103+
104+
export
105+
induced_covariance_graph

0 commit comments

Comments
 (0)