6

Is there an easy way to create tree nodes from data in a file? I need the tree to have nodes in the format 1,2,3,4 rather than 1,2,4,8 etc. I can't create the nodes in a loop as there would be two child nodes for each node.

I'm picking up the data in each cell and declaring a variable for each entry. Is there an easier way to create the following tree? Each row in the file is a node, and I will eventually be filling each node with columns from each row.

\documentclass{standalone}
\usepackage{catchfile,tikz}
\usepackage{pgfplotstable}
\usepackage{filecontents}


\begin{filecontents*}{\jobname.dat}
a&1&2&3\\
b&1&2&3\\
c&1&2&3\\
d&1&2&3\\
e&1&2&3\\
f&1&2&3\\
\end{filecontents*}

\begin{document}

\pgfplotstableread[col sep=&,header=false]{\jobname.dat}{\firsttable}   

\pgfplotstablegetelem{0}{[index]0}\of{\firsttable} \let\RowZeroColZero\pgfplotsretval
\pgfplotstablegetelem{1}{[index]0}\of{\firsttable} \let\RowOneColZero\pgfplotsretval
\pgfplotstablegetelem{2}{[index]0}\of{\firsttable} \let\RowTwoColZero\pgfplotsretval
\pgfplotstablegetelem{3}{[index]0}\of{\firsttable} \let\RowThreeColZero\pgfplotsretval
\pgfplotstablegetelem{4}{[index]0}\of{\firsttable} \let\RowFourColZero\pgfplotsretval
\pgfplotstablegetelem{5}{[index]0}\of{\firsttable} \let\RowFiveColZero\pgfplotsretval

\begin{tikzpicture} [grow=right, sloped,dot/.style={circle,fill,inner sep=0.5pt}]
\coordinate 
node{\RowZeroColZero}
    child {
    node {\RowTwoColZero}
            child {node {\RowFiveColZero}}
            child {node[text opacity = 0] {\RowFiveColZero}}
    }
    child {
    node {\RowOneColZero}
            child {node {\RowFourColZero}}
            child {node {\RowThreeColZero}}
    }
; 
\end{tikzpicture}
\end{document}

enter image description here

At each node, if I could even extract the node coordinate and locate a row in the file with the node reference, that would be a great help in reducing the amount of code.

2
  • I've added the figure I get from your code. If it's not what you want, please delete it and explain it better. I've read somewhere in TeX.SX that this result is not formally a tree. So probably there are other solutions. In any case, what happens with non first columns in your data file?
    – Ignasi
    Commented May 27, 2015 at 10:44
  • Did you think about grahpViz? Commented May 30, 2015 at 19:59

1 Answer 1

9
+50

Here's a TikZ solution:

It defines a new command \myTree{} that takes a comma-separated list of data like <column One / column Two / column Three / column Four>.

So to get your example, one would use

\myTree{a///, b///, c///, d///, e///, f///}

enter image description here

This works for a variable number of elements in the list:

\begin{enumerate}
    \item \ \\\myTree{a/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3, d/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3, d/1/2/3, e/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3, d/1/2/3, e/1/2/3, f/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3, d/1/2/3, e/1/2/3, f/1/2/3, g/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3, d/1/2/3, e/1/2/3, f/1/2/3, g/1/2/3, h/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3, d/1/2/3, e/1/2/3, f/1/2/3, g/1/2/3, h/1/2/3, i/1/2/3}
    \item \ \\\myTree{a/1/2/3, b/1/2/3, c/1/2/3, d/1/2/3, e/1/2/3, f/1/2/3, g/1/2/3, h/1/2/3, i/1/2/3, j/1/2/3}
\end{enumerate}

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

The \myTree :

\newcounter{total}
\newcommand{\myTree}[1]{
\begin{tikzpicture}
    \setcounter{total}{0}
    \foreach \colOne/\colTwo/\colThree/\colFour [count=\i] in {#1} {
        \stepcounter{total};
        \pgfmathtruncatemacro{\x}{-1/2+sqrt(\i*2)}; % Sequence A002024
        \pgfmathtruncatemacro{\t}{(-1+sqrt(\i*8-7))/2};
        \pgfmathtruncatemacro{\y}{(\t*\t+3*\t+4)/2-2*\i+\t*(\t+1)/2} % Sequence A114327
        \node at (\x,\y) (\i) {\colOne};
    }
    \pgfmathtruncatemacro{\canUp}{\thetotal-floor((sqrt(\thetotal*8+1)-1)/2)+1} % Sequence A083920
    \pgfmathtruncatemacro{\canDown}{\thetotal-1-floor((sqrt((\thetotal-1)*8+1)-1)/2)+1} % Sequence A083920
    \foreach \colOne/\colTwo/\colThree/\colFour [count=\i] in {#1} {
        \pgfmathtruncatemacro{\up}{\i+round(sqrt(2*\i))}; % Sequence A014132
        \pgfmathtruncatemacro{\down}{\up+1}; % Sequence A080036
        \ifnum \i < \canUp   \draw (\i) -- (\up);  \fi
        \ifnum \i < \canDown \draw (\i) -- (\down);\fi
    }
\end{tikzpicture}
}

To answer your questions:

  • Node coordinates: The easiest way to find the node coordinates is to write some of them down while trying to find a pattern for both the x-coordinate and the y-coordinate. The first 12 nodes have the following coordinates:enter image description here. So for the x-coordinates that is the sequence: 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, .... The y-coordinates become: 0, 1, -1, 2, 0, -2, 3, 1, -1, -3, 4, 2, .... Now all we need to do is find a formula to generate those sequences. Here the On-Line Encyclopedia of Integer Sequences comes to the rescue (see the sequence references in the code comments). And to decide which node to connect to which node, we can use the exact same method (see second for loop).
  • You said "I will eventually be filling each node with columns from each row." To achieve that, one needs to change {\colOne} in the line \node at (\x,\y) (\i) {\colOne}; For example changing it to {$\colOne \times \colTwo^\colThree = \colFour$} will get you:

enter image description here

As one can see, this can get quite crowded. That's why, with the help of xparse, I added two optional arguments: an x-scale and a y-scale.

So using

\myTree[2.2][1.5]{  a / 1 / 2 / 3, 
                    b / 1 / 2 / 3, 
                    c / 1 / 2 / 3, 
                    d / 1 / 2 / 3, 
                    e / 1 / 2 / 3, 
                    f / 1 / 2 / 3}

will get you a way better result:

enter image description here

All code combined, the entire document now looks like:

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\newcounter{total}
\DeclareDocumentCommand{\myTree}{ O{1.0} O{1.0} m }{
    \begin{tikzpicture}
        \setcounter{total}{0}
        \pgfmathsetmacro{\xscale}{#1}
        \pgfmathsetmacro{\yscale}{#2}
        \foreach \colOne/\colTwo/\colThree/\colFour [count=\i] in {#3} {
            \stepcounter{total};
            \pgfmathsetmacro{\x}{\xscale*floor(-1/2+sqrt(\i*2))}; % Sequence A002024
            \pgfmathtruncatemacro{\t}{(-1+sqrt(\i*8-7))/2};
            \pgfmathsetmacro{\y}{\yscale*((\t*\t+3*\t+4)/2-2*\i+\t*(\t+1)/2)} % Sequence A114327
            \node at (\x,\y) (\i) {$\colOne \times \colTwo^\colThree = \colFour$};
        }
        \pgfmathtruncatemacro{\canUp}{\thetotal-floor((sqrt(\thetotal*8+1)-1)/2)+1} % Sequence A083920
        \pgfmathtruncatemacro{\canDown}{\thetotal-1-floor((sqrt((\thetotal-1)*8+1)-1)/2)+1} % Sequence A083920
        \foreach \colOne/\colTwo/\colThree/\colFour [count=\i] in {#3} {
            \pgfmathtruncatemacro{\up}{\i+round(sqrt(2*\i))}; % Sequence A014132
            \pgfmathtruncatemacro{\down}{\up+1}; % Sequence A080036
            \ifnum \i < \canUp   \draw (\i) -- (\up);  \fi
            \ifnum \i < \canDown \draw (\i) -- (\down);\fi
        }
    \end{tikzpicture}
}

\begin{document}
    \myTree[2.2][1.5]{  a / 1 / 2 / 3, 
                        b / 1 / 2 / 3, 
                        c / 1 / 2 / 3, 
                        d / 1 / 2 / 3, 
                        e / 1 / 2 / 3, 
                        f / 1 / 2 / 3}
\end{document}

I changed your input format from a filecontents* .dat file to an argument list because I think it's more convenient. If you want to keep using your filecontents* .dat input format though, you can achieve this with the datatool package and some minor adjustments:

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}
\usepackage{filecontents, datatool}

\begin{filecontents*}{jobname1.dat}
a&1&2&3\\
b&1&2&3\\
c&1&2&3\\
d&1&2&3\\
e&1&2&3\\
f&1&2&3\\
\end{filecontents*}
\begin{filecontents*}{jobname2.dat}
a&1&2&3\\
b&1&2&3\\
c&1&2&3\\
d&1&2&3\\
\end{filecontents*}
\DTLsetseparator{&}

\newcounter{total}
\newcounter{counter}
\DeclareDocumentCommand{\myTree}{ O{1.0} O{1.0} m }{
    \DTLloaddb[noheader]{#3}{#3}
    \begin{tikzpicture}
        \setcounter{total}{0}
        \pgfmathsetmacro{\xscale}{#1}
        \pgfmathsetmacro{\yscale}{#2}
        \DTLforeach*{#3}{\colOne=Column1, \colTwo=Column2, \colThree=Column3, \colFour=Column4}{
            \stepcounter{total};
            \pgfmathsetmacro{\x}{\xscale*floor(-1/2+sqrt(\thetotal*2))}; % Sequence A002024
            \pgfmathtruncatemacro{\t}{(-1+sqrt(\thetotal*8-7))/2};
            \pgfmathsetmacro{\y}{\yscale*((\t*\t+3*\t+4)/2-2*\thetotal+\t*(\t+1)/2)} % Sequence A114327
            \node at (\x,\y) (\thetotal) {$\colOne \times \colTwo^\colThree = \colFour$};
        }
        \pgfmathtruncatemacro{\canUp}{\thetotal-floor((sqrt(\thetotal*8+1)-1)/2)+1} % Sequence A083920
        \pgfmathtruncatemacro{\canDown}{\thetotal-1-floor((sqrt((\thetotal-1)*8+1)-1)/2)+1} % Sequence A083920
        \setcounter{counter}{0}
        \DTLforeach*{#3}{}{
            \stepcounter{counter}
            \pgfmathtruncatemacro{\up}{\thecounter+round(sqrt(2*\thecounter))}; % Sequence A014132
            \pgfmathtruncatemacro{\down}{\up+1}; % Sequence A080036
            \ifnum \thecounter < \canUp   \draw (\thecounter) -- (\up);  \fi
            \ifnum \thecounter < \canDown \draw (\thecounter) -- (\down);\fi
        }
    \end{tikzpicture}
}

\begin{document}
    \myTree{jobname1.dat}
    \myTree[2.2][1.5]{jobname2.dat}
\end{document}

enter image description here

1
  • 2
    I think you are now an officially recognized TeX.SX addict with this ;) You have been warned. Take it easy otherwise you start writing packages.
    – percusse
    Commented Jun 2, 2015 at 0:29

You must log in to answer this question.

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