6

I am using the 'forest' package in LaTeX in order to create decision trees with multiple decisions. I've got it mostly how I want it, except for two things:

  1. I'd like the circles at the leaves of the tree to be triangles.
  2. I'd like labels to be centred on the lines and not overlapping with the rectangles or circles.

A simple example (using Beamer):

\documentclass{beamer}

\usepackage{beamerstyle}

\usepackage{tikz}
\usetikzlibrary{trees,shapes,snakes}
\usepackage{forest}


\tikzset{
    decision/.style={rectangle, minimum height=10pt, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    chance/.style={circle, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
  }

\begin{document}


\begin{frame}[fragile]

\scalebox{0.85}{
 \begin{forest}
   my label/.style={
      edge label={node[auto,sloped,pos=.75,anchor=south]{#1}}
    },
    for tree={
      grow=0,
      child anchor=west,
      parent anchor=east,
      line width=0.2pt,
      inner sep=1mm,
      edge={thick, draw=blue},
      l sep+=30mm,
      s sep+=5mm,
      if n children=0{
        before typesetting nodes={
          label/.wrap pgfmath arg={right:#1}{content()},
          content={},
          chance,
        },
      }{},
      edge path={
         \noexpand\path[\forestoption{edge}]
         (!u.parent anchor) -- ([xshift=-1.6cm].child anchor) --    
         (.child anchor)\forestoption{edge label};
  },
    }
    [, decision
      [,decision,my label=Route 1
        [,chance,my label=Fast
           [-900, my label=Accident]
           [100, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-700, my label=Accident]
           [50, my label=No Accident]
        ]
      ]
      [,decision,my label=Route 2
        [,chance,my label=Fast
           [-600, my label=Accident]
           [80, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-500, my label=Accident]
           [70, my label=No Accident]
        ]
      ]
    ]
\end{forest}
}

\end{frame}





\end{document}

enter image description here

Edit: Thanks to @AlanMunn for help with the triangles. I've flipped them around. Also got rid of the angled lines so that the labels look better. I prefer angled lines but can't get it looking good.

\documentclass{beamer}

%\usepackage{beamerstyle}

\usepackage{tikz}
\usetikzlibrary{trees,shapes,snakes}
\usepackage{forest}


\tikzset{
    decision/.style={rectangle, minimum height=10pt, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    chance/.style={circle, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    leaf-chance/.style={isosceles triangle, minimum width=10pt, draw=black, thick, fill=white, inner sep=0pt, shape border rotate=180, outer sep=-\pgflinewidth}
  }

\begin{document}


\begin{frame}[fragile]

\scalebox{0.85}{
 \begin{forest}
   my label/.style={
      edge label={node[auto, sloped,pos=.75,anchor=south]{#1}}
    },
    for tree={
      grow=0,
      child anchor=west,
      anchor=west,
      text ragged,
      inner sep=1mm,
      edge={thick, draw=blue},
      l sep+=30mm,
      s sep+=5mm,
      if n children=0{
        before typesetting nodes={
          label/.wrap pgfmath arg={right:#1}{content()},
          content={},
          leaf-chance,
        },
      }{},
      edge path={
       \noexpand\path [draw, \forestoption{edge}] (!u.parent anchor) |- (.child anchor)\forestoption{edge label};
       % alternatively, with angled lines
        %\noexpand\path[\forestoption{edge}]
        % (!u.parent anchor) -- ([xshift=-2.6cm].child anchor) --    
        % (.child anchor)\forestoption{edge label};
  },
    }
    [, decision
      [,decision,my label=Route 1
        [,chance,my label=Fast
           [-900, my label=Accident]
           [100, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-700, my label=Accident]
           [50, my label=No Accident]
        ]
      ]
      [,decision,my label=Route 2
        [,chance,my label=Fast
           [-600, my label=Accident]
           [80, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-500, my label=Accident]
           [70, my label=No Accident]
        ]
      ]
    ]
\end{forest}
}

\end{frame}
\end{document}

enter image description here

6

2 Answers 2

8

Here's a method which keeps the labels at the end of the lines but changes their anchors, right aligns the numbers labelling the triangles and fiddles a little with the straight line approach:

\documentclass{beamer}
\usepackage{forest}
\usetikzlibrary{shapes}

\tikzset{
    decision/.style={rectangle, minimum height=10pt, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    chance/.style={circle, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    leaf-chance/.style={isosceles triangle, minimum width=10pt, draw=black, thick, fill=white, inner sep=0pt, shape border rotate=180, outer sep=-\pgflinewidth}
  }

\begin{document}

\begin{frame}[fragile]

\scalebox{0.85}{
 \begin{forest}
   my label/.style={
     edge label={node[auto, pos=1, anchor=south east]{#1}}
   },
   for tree={
     grow=0,
     child anchor=west,
     anchor=west,
     text ragged,
     inner sep=1mm,
     edge={ultra thick, draw=blue, rounded corners=2pt},
     l sep+=30mm,
     s sep+=5mm,
     if n children=0{
       before typesetting nodes={
         label/.wrap pgfmath arg={[align=right, text width=25pt]right:#1}{content()},
         content={},
         leaf-chance,
       },
     }{},
     edge path={
       \noexpand\path [draw, \forestoption{edge}] (!u.parent anchor) |- (.child anchor)\forestoption{edge label};
     },
   }
    [, decision
      [,decision,my label=Route 1
        [,chance,my label=Fast
           [-900, my label=Accident]
           [100, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-700, my label=Accident]
           [50, my label=No Accident]
        ]
      ]
      [,decision,my label=Route 2
        [,chance,my label=Fast
           [-600, my label=Accident]
           [80, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-500, my label=Accident]
           [70, my label=No Accident]
        ]
      ]
    ]
\end{forest}
}

\end{frame}
\end{document}

straight lines

Or, with angled lines, perhaps something like this?

angled lines

\documentclass{beamer}
\usepackage{forest}
\usetikzlibrary{shapes}

\tikzset{
    decision/.style={rectangle, minimum height=10pt, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    chance/.style={circle, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    leaf-chance/.style={isosceles triangle, minimum width=10pt, draw=black, thick, fill=white, inner sep=0pt, shape border rotate=180, outer sep=-\pgflinewidth}
  }

\begin{document}

\begin{frame}[fragile]

\scalebox{0.85}{
 \begin{forest}
   my label/.style={
     edge label={node[auto, pos=1, anchor=south east]{#1}}
   },
   for tree={
     grow=0,
     child anchor=west,
     anchor=west,
     text ragged,
     inner sep=1mm,
     edge={thick, draw=blue, rounded corners=2pt},
     s sep+=5mm,
     l sep+=30mm,
     if n children=0{
       before typesetting nodes={
         label/.wrap pgfmath arg={[align=right, text width=25pt]right:#1}{content()},
         content={},
         leaf-chance,
       },
     }{
     },
     edge path={
       \noexpand\path[\forestoption{edge}]
       (!u.parent anchor) -- ([xshift=-22.5mm].child anchor) --  (.child anchor)\forestoption{edge label};
     }
   }
    [, decision
      [,decision,my label=Route 1
        [,chance,my label=Fast
           [-900, my label=Accident]
           [100, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-700, my label=Accident]
           [50, my label=No Accident]
        ]
      ]
      [,decision,my label=Route 2
        [,chance,my label=Fast
           [-600, my label=Accident]
           [80, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-500, my label=Accident]
           [70, my label=No Accident]
        ]
      ]
    ]
\end{forest}
}

\end{frame}
\end{document}

EDIT

If you don't like typing, the following code produces the second tree above more automatically:

 \begin{forest}
   my label/.style={
     edge label={node[auto, pos=1, anchor=south east]{#1}}
   },
   for tree={
     grow=0,
     child anchor=west,
     anchor=west,
     text ragged,
     inner sep=1mm,
     edge={thick, draw=blue, rounded corners=2pt},
     s sep+=5mm,
     l sep+=30mm,
     if n children=0{
       if n=1{my label=Accident}{my label=No Accident},
       before typesetting nodes={
         label/.wrap pgfmath arg={[align=right, text width=25pt]right:#1}{content()},
         content={},
         leaf-chance,
       },
     }{
       if level=2{
         chance,
         if n=1{my label=Fast}{my label=Slow}
       }{decision}
     },
     edge path={
       \noexpand\path[\forestoption{edge}]
       (!u.parent anchor) -- ([xshift=-22.5mm].child anchor) --  (.child anchor)\forestoption{edge label};
     }
   }
    [
      [,my label=Route 1
        [
           [-900]
           [100]
        ]
        [
           [-700]
           [50]
        ]
      ]
      [,my label=Route 2
        [
           [-600]
           [80]
        ]
        [
           [-500]
           [70]
        ]
      ]
    ]
\end{forest}
4
  • This looks really excellent @crf. Thank you! I like the second in particular.
    – fraserbc
    Jun 19, 2015 at 21:37
  • 1
    @fraserbc Note that I'm cfr and not crf. Pinging me won't work if you put them in the wrong order. (I don't care otherwise, but I didn't get pinged when you tried to ping me above.) Here I would get pinged anyway as it is a comment on my answer.
    – cfr
    Jun 19, 2015 at 21:41
  • 1
    @fraserbc Glad you like it. I was going to remove the rounded corners from the angled version but discovered I liked it better with. You can easily delete that bit if you don't like it, though.
    – cfr
    Jun 19, 2015 at 21:43
  • 1
    @fraserbc Note you can also automate things a bit rather than keep typing the same labels out repeatedly. The example I just edited in above produces the second tree more automatically.
    – cfr
    Jun 19, 2015 at 22:02
5

There's no optimum solution for your edge labels, I don't think. The easiest solution is to just move them a little to the left with the pos= argument. Here I've set it to .4 to accommodate the length of the 'No Accident' label. To make the triangles you can add another style using the isosceles triangle shape:

\documentclass{beamer}

%\usepackage{beamerstyle} % not standard style

\usepackage{tikz}
\usetikzlibrary{trees,shapes,snakes}
\usepackage{forest}


\tikzset{
    decision/.style={rectangle, minimum height=10pt, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    chance/.style={circle, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt},
    leaf-chance/.style={isosceles triangle, minimum width=10pt, draw=black, fill=black!30!white, thick, inner sep=0pt}
  }

\begin{document}

\begin{frame}[fragile]

\scalebox{0.85}{
 \begin{forest}
   my label/.style={
      edge label={node[sloped,pos=.4,anchor=south]{#1}}
    },
    for tree={
      grow=0,
      child anchor=west,
      parent anchor=east,
      line width=0.2pt,
      inner sep=1mm,
      edge={thick, draw=blue},
      l sep+=30mm,
      s sep+=5mm,
      if n children=0{
        before typesetting nodes={
          label/.wrap pgfmath arg={right:#1}{content()},
          content={},
          leaf-chance,
        },
      }{},
      edge path={
         \noexpand\path[\forestoption{edge}]
         (!u.parent anchor) -- ([xshift=-1.6cm].child anchor) --    
         (.child anchor)\forestoption{edge label};
  },
    }
    [, decision
      [,decision,my label=Route 1
        [,chance,my label=Fast
           [-900, my label=Accident]
           [100, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-700, my label=Accident]
           [50, my label=No Accident]
        ]
      ]
      [,decision,my label=Route 2
        [,chance,my label=Fast
           [-600, my label=Accident]
           [80, my label=No Accident]
        ]
        [,chance,my label=Slow
           [-500, my label=Accident]
           [70, my label=No Accident]
        ]
      ]
    ]
\end{forest}
}

\end{frame}

\end{document}

output of code

2
  • Thanks. This is very helpful. I rotated the triangles around so that they are pointing towards their parents.
    – fraserbc
    Jun 19, 2015 at 21:04
  • 1
    It isn't foolproof, but if you use pos=1 and change the anchor of the label to e.g. south east, then you don't have to manually muck around with the left shifting. (Of course, the labels won't be 'centred' then but I wasn't sure that was desirable here.)
    – cfr
    Jun 19, 2015 at 21:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .