I wonder how to draw such path diagrams with minimal effort.
-
11Here's an answer with minimal effort: use TikZ.– Matthew LeingangCommented Aug 28, 2011 at 23:12
-
2I think there's an example very much along these lines in the TikZ manual, actually.– hoylandCommented Aug 28, 2011 at 23:15
-
Maybe this looks a bit silly but could someone let me know how I can remove the tags V,E, etc in the output? I know how to write my own text but the V, E, etc come in the diagrams as a fixture. Thanks,– ShahrazCommented May 10, 2013 at 15:34
2 Answers
Here is another solution using TiKZ
, but without operating on a grid. [It has been edited somewhat to simplify the code, based on the comments.]
TiKZ is an awesome package for making diagrams. Because you do it programmatically rather than interactively, you can make images very precisely, and with higher-level concepts; but it involves some forethought if you want to do it quickly. I will describe the workflow, in order to describe not just what code would produce this image, but to indicate how you can go about designing code to produce this image.
Obligatory pre-amble
Use something like this at the beginning of your document.
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,shapes}
\begin{document}
The command \usetikzlibrary
should always accompany \usepackage{tikz}
, because there's always some library you're going to want to use. In our case, we want calc
to compute relative positions, and shapes
to get e.g. elliptical nodes. The excellent documentation for TiKZ and PGF will always let you know what libraries you need to put things in your picture.
Defining styles
Your picture has several nodes (boxes, ellipses) with common features. We define different styles of nodes which more-or-less duplicate them. You should name them more intelligently than I have. All of these are settings which describe how they ought to look. (The "inner sep
" parameter defines how far the border must be from the text from the default; all of the values were achieved by trial and error.)
\tikzstyle{input}=[font=\scriptsize\sffamily\bfseries]
\tikzstyle{yelbox}=[rectangle,draw=black,fill=yellow,
font=\scriptsize\sffamily\bfseries,inner sep=6pt]
\tikzstyle{yelball}=[ellipse,draw=black,fill=yellow,
font=\scriptsize\sffamily\bfseries,inner sep=-1pt]
\tikzstyle{whiteball}=[ellipse,draw=black,fill=white,
font=\scriptsize\sffamily\bfseries,inner sep=-1pt]
There are also different kinds of arrows which are repeated: we define styles for those as well. (Here, "latex
" is just the name of the triangular arrow-head; I would prefer to use "stealth
" myself, but I'm duplicating your image in this case.)
\tikzstyle{arr}=[-latex,black,line width=1pt]
\tikzstyle{doublearr}=[latex-latex,black,line width=1pt]
\tikzstyle{bluearr}=[-latex,blue,line width=1pt]
Ad-hoc commands
Some of your nodes have features such as labels with multiple lines. We're going to make a macro to produce those labels nicely. The default width of 3em
was achieved by trial-and-error.
\newcommand\nodelabel[3][3em]{\parbox{#1}{\centering #2 \\ #3 }}
In order to more quickly prototype the image, I want fast control over the spacing of the nodes vertically, which is where there is the most variation (and the most non-uniformity). I will define a scaling factor that I can adjust for this (set to a negative number, to change the normal TiKZ
convention so that larger numbers correspond to larger distances downward).
\newcommand\vertspacing{-1.5}
Building the picture
Here we go:
\begin{tikzpicture}[scale=0.8]
The scale was found by trial-and-error to get it to fit on a single page. You can also independently control the x-scaling and y-scaling, but I didn't do it that way, because I wanted to reverse the directions for co-ordinates anyway, and could do the scaling another way. (You can do this with a negative y-scaling, but then it messes up with the corners of the nodes, such as .north
and .south
which I will use below.)
While you're building this, you should be re-compiling all the time to find out whether your placement is working well, and to adjust the styles of the nodes to make their appearance work better. But don't spend too much time making everything perfect; get things just almost-right first, and then make them perfect at the end.
Building the E nodes
These are the nodes with E labels on the left and right. We will give them names so that we can refer to the nodes when drawing arrows.
I eye-balled the relative vertical spacing of the nodes; wherever there was a larger gap, I settled on an incremental spacing of 0.3
or 0.4
. The horizontal positions were also eye-balled (and tweaked slightly), using negative/positive values to ensure symmetry.
\foreach \label/\row in
{E16/1, E17/2.3, E14/3.3, E15/4.7, E18/5.7, E19/7}
\node [input] (\label) at ($(-7.5,\vertspacing*\row)$) {\label};
\foreach \label/\row in
{E10/1, E11/2.3, E8/3.3, E9/4.7, E12/5.7, E13/7}
\node [input] (\label) at ($(7.5,\vertspacing*\row)$) {\label};
The (\label)
assigns the name of the node which we will use later; the {\label}
tells what label the nodes should be printed with.
Building the V nodes
These are the nodes with V labels, which will be the names of the nodes. They also have separate titles, which are the Axxx#-style part of their printed labels. They have similar vertical positioning as the input nodes. We produce their labels using the \nodelabel
macro we defined earlier.
\foreach \title/\label/\row in
{GSSC1/V16/1, GSCR1/V17/2.3, ASCC1/V14/3.3, ASCB1/V15/4.7, AAET1/V18/5.7, AAMA1/V19/7}
\node [yelbox] (\label) at ($(-5,\vertspacing*\row)$) {\nodelabel{\title}{\label}};
\foreach \title/\label/\row in
{GSSC2/V10/1, GSCR2/V11/2.3, ASCC2/V8/3.3, ASCB2/V9/4.7, AAET2/V12/5.7, AAMA2/V13/7}
\node [yelbox] (\label) at ($(5,\vertspacing*\row)$) {\nodelabel{\title}{\label}};
We use the V# part of the label to name the nodes, because it's shorter, and because they match up with the names of the neighboring E nodes; this will be convenient later.
Building the F nodes
These are the elliptical nodes with the F labels. This is again similar to the V nodes. Their vertical positioning is basically at the gaps of the V nodes; this was computed by hand, though if you were slightly fancier you could have TiKZ
do it for you. The width of 3.5em
is chosen to force the ellipse to have about the same shape as in the original diagram.
\foreach \title/\label/\row in
{GSC/F1/1.65/4, ASC/F2/4/4, AA/F3/6.35/4}
\node [whiteball] (\label) at ($(-2,\vertspacing*\row)$)
{\nodelabel[3.5em]{\title \\ I}{\label}};
\foreach \title/\label/\row in
{GSC/F4/1.65, ASC/F5/4, AA/F6/6.35}
\node [yelball] (\label) at ($(2,\vertspacing*\row)$)
{\nodelabel[3.5em]{\title \\ II}{\label}};
Building the D nodes
For each of the F nodes on the right-hand side, there is a D node above and to the right. We place a D node for each one at a fixed position relative to the corresponding F node.
\foreach \label/\parent/\row in
{D4/F4/.7, D5/F5/3, D6/F6/5.4}
\node [input] (\label) at ($(\parent) + (0.5,-1*\vertspacing)$) {\label};
If you were to end the picture here by adding \end{tikzpicture}\end{document}
, it would look like this:
Arrow drawing
Here, we take advantage of the labelling scheme for the nodes. In this case, because the labels are fairly uniform throughout the diagram, we basically just need to say "connect each E node with the appropriate V node", and similarly for the D nodes to the F nodes (with the appropriate style of arrow).
\foreach \j in {16, 17, 14, 15, 18, 19, 10, 11, 8, 9, 12, 13}
\draw [bluearr] (E\j) to (V\j);
\foreach \j in {4, 5, 6}
\draw [bluearr] (D\j) to (F\j);
The V and F nodes don't have such a simple naming scheme, but that's okay; we just specify the full node names. Also, we would like the arrows to terminate precisely at the middle of the vertical sides of the boxes; we do this by specifying the "east
" or "west
" anchors for these nodes, as appropriate.
\foreach \F/\Va/\Vb in {F1/V16/V17, F2/V14/V15, F3/V18/V19}
{ \draw [arr] (\F) to (\Va.east); \draw [arr] (\F) to (\Vb.east); }
\foreach \F/\Va/\Vb in {F4/V10/V11, F5/V8/V9, F6/V12/V13}
{ \draw [arr] (\F) to (\Va.west); \draw [arr] (\F) to (\Vb.west); }
The most complicated arrow to do in the diagram is between F1 and F3; we want this arrow to be anchored precisely at the centre-right, and to curve. We can do this by specifying the "east
" anchor, and by specifying the angles (relative to due east) that the arrow should arc from. The angles were chosen by eye-balling.
\draw [doublearr, out=-60, in=60] (F1.east) to (F3.east);
The other arrows are fairly simple, and use the techniques described thus far.
\draw [doublearr] (F1) to (F2);
\draw [doublearr] (F2) to (F3);
\draw [arr] (F1) to (F5.north);
\draw [arr] (F1) to (F6.north);
\draw [arr] (F2) to (F4.west);
\draw [arr] (F2) to (F6.west);
\draw [arr] (F3) to (F4.south);
\draw [arr] (F3) to (F5.south);
Finishing up
And that's it!
\end{tikzpicture}
\end{document}
The resulting figure:
It's a pretty faithful representation of the original, if I so say so myself; And I made it in one hour, starting from nothing but the original to go by — some of which was spent looking up forgotten bits of TiKZ as I went!
-
Excellent answer! Two comments: TikZ loads
color
automatically, so you don't have to manually include that package, and for the curved arrow you could also use\path (F1.east) edge [tofro,bend left] (F3.east);
, which eliminates the need to manually find appropriate support coordinates.– JakeCommented Aug 29, 2011 at 1:57 -
Thanks Jake; I've removed
color
to simplify the answer. I didn't know aboutbend left
, that's cool. Also, I'm interested to learn that\path
is more reliable in interoperation with anchors and directions than\draw
is (neitherbend left
norout=-60, in=+60
interoperate with using the anchor.east
). Still, specifying a precise control will allow you to determine just how far left and the nature of the curve. Commented Aug 29, 2011 at 2:06 -
Hm, that's strange. For me,
\draw [tofro] (F1.east) to [bend left] (F3.east);
connects theeast
anchors ofF1
andF3
as expected. The curvature of the curve can be specified usingbend left=<angle>
.– JakeCommented Aug 29, 2011 at 2:12 -
1Ah, I was trying to do it the same place I has
[tofro]
, which works when I don't have the anchors.[out=-60, in=60]
works with anchors as well, provided you put it in the middle of the path rather than at the beginning with the arrow type. How peculiar. Edited to add: I've now found it's because I was using--
rather thanto
! Commented Aug 29, 2011 at 2:21 -
Look at the second tutorial in the TikZ manual: it's titeled "A Petri-Net For Hagen", if you work through the example you'll be able to do something like this with ease: