When starting my research in causality, I did not know how to draw causal models. Most people I asked drew their graphs in powerpoint or some other software, and then exported them as png.
Drawing the graphs from scratch was tedious, and they often ended up looking fairly amateurish. To combat this, a couple years ago, I created a web-based app to draw graphs and export them to png, which is available here.
The exported pngs were easy to make, but there was the issue of needing to align nodes each time I needed to make a change, and the resulting graphs were passable, but did not look beautiful.
Since then, members of our group and I have figured out how to make these graphs directly in latex, and I think they look really great.
This post serves as an example-based intro to causal graphs in tikz.
Here is a full template document with a tikz graph. The graph is called the “bow graph”, and it represents a causal effect with latent confounding.
\documentclass[10pt]{article}
\usepackage{color}
\usepackage{tikz}
% Tikz settings optimized for causal graphs.
% Just copy-paste this part
\usetikzlibrary{shapes,decorations,arrows,calc,arrows.meta,fit,positioning}
\tikzset{
-Latex,auto,node distance =1 cm and 1 cm,semithick,
state/.style ={ellipse, draw, minimum width = 0.7 cm},
point/.style = {circle, draw, inner sep=0.04cm,fill,node contents={}},
bidirected/.style={Latex-Latex,dashed},
el/.style = {inner sep=2pt, align=left, sloped}
}
\begin{document}
\begin{tikzpicture}
% x node set with absolute coordinates
\node[state] (x) at (0,0) {$X$};
% y node set relative to x.
% Locations can be:
% right,left,above,below,
% above left,below right, etc
\node[state] (y) [right =of x] {$Y$};
% Directed edge
\path (x) edge (y);
% Bidirected edge
\path[bidirected] (x) edge[bend left=60] (y);
\end{tikzpicture}
\end{document}
The majority of the above code can be simply copy-pasted into your own tex file without modification. The only thing that you need to change for your own graphs is within the tikzpicture
.
You might have noticed that the output of the given code is a pdf, while the graph shown above is an svg. You can use \documentclass{standalone}
and pdf2svg
to generate a high-quality svg of your graph. The actual code used for the above graph is given here, and the image can be generated on linux using:
pdflatex -shell-escape templatesvg.tex
All the below graphs assume to have the same tikz settings as shown in the template above, with just the tikzpicture
portion replaced.
More complex graphs can look better if they use dots for their nodes, with labels to the side:
\begin{tikzpicture}
\node (x) at (0,0) [label=left:X,point];
\node (y) at (1.3,0) [label=right:Y,point];
\path (x) edge (y);
\path[bidirected] (x) edge[bend left=60] (y);
\end{tikzpicture}
The label directions are same as for node positioning. The label text can have spaces in it:
\node (x) at (0,0) [label=above left:This is a valid label,point];
\begin{tikzpicture}
\node[state,red] (x) at (0,0) {$X$};
\node[state] (y) [right=of x] {$Y$};
\path (x) edge (y);
\path[blue,line width=1.5] (x) edge[bend right=30] (y);
\path[bidirected] (x) edge[bend left=60] (y);
\end{tikzpicture}
\begin{tikzpicture}
\node[state] (1) {$Z$};
\node[state] (2) [right =of 1] {$X$};
\node[state] (3) [right =of 2] {$Y$};
\node[state] (4) [above right =of 1,xshift=-0.3cm,yshift=-0.3cm] {$W$};
\path (1) edge node[above] {$\lambda_{zx}$} (2);
\path (2) edge node[above] {$\lambda_{xy}$} (3);
\path[bidirected] (2) edge[bend left=60] node[above] {$\epsilon_{xy}$} (3);
\path (4) edge node[el,above] {$\lambda_{wz}$} (1);
\path[bidirected] (4) edge[bend left=50] node[el,above] {$\epsilon_{wy}$} (3);
\end{tikzpicture}
\begin{tikzpicture}
\node (1) at (0,0) {Proximity};
\node (2) [right = of 1] {Tutoring};
\node (3) [right = of 2] {GPA};
\node (4) [below = of 2] {Library};
\path (1) edge (2);
\path (2) edge (3);
\path[bidirected] (2) edge[bend left=50] (3);
\path (4) edge (3);
\path (1) edge (4);
\end{tikzpicture}
\begin{tikzpicture}
\node[state] (p) at (0,2) {$S$};
\node[state,rectangle] (k) at (-1.5,1) {$K$};
\node[state] (x) at (0,0) {$L$};
\node[state] (y) at (1.5,0) {$M$};
\path (x) edge (y);
\path (p) edge (k);
\path[bidirected] (p) edge[bend left=20] (y);
\path[bidirected] (p) edge[bend left=20] (x);
\path[bidirected] (k) edge[bend left=20] (y);
\path (k) edge (x);
\node[draw=blue,dotted,fit=(x) (y), inner sep=0.2cm] (machine) {};
\end{tikzpicture}
This example, courtesy of Carlos Cinelli from Judea Pearl’s group, really goes to show how nice these graphs can look.
\begin{tikzpicture}[node distance =0.85 cm and 0.85 cm]
\node (z) [label = below:z, point];
\node (w) [above = of z,yshift=0.3cm, label = above:w, point];
\node (x) [label = below:x, below left = of z, point];
\node (y) [label = below:y, point, below right = of z];
\path (z) edge node[above, el] {$\lambda_{zw}$} (w);
\path (z) edge node[above, el] {$\lambda_{zx}$} (x);
\path (x) edge node[below] {$\lambda_{xy}$} (y);
\path[bidirected] (z) edge[bend left= 60] node[above, el] {$\epsilon_{zy}$} (y);
\path[bidirected] (z) edge[bend right=60] node[above, el] {$\epsilon_{zx}$} (x);
\path[bidirected] (z) edge[bend left= 60] node[above, el] {$\epsilon_{zw}$} (w);
\path[bidirected] (w) edge[bend left= 60] node[above, el] {$\epsilon_{wy}$} (y);
\end{tikzpicture}