5
$\begingroup$

I am trying to create a multi-coloured tree plot (Mathematica 9) where each branch is coloured according to its parent node value. For example, for a tree plot with three nodes, n1, n2 and n3, coming from a single parent node (n0), the children nodes of n1 should be red & n2 and n3 should be green.

TreePlot[nodes, EdgeRenderingFunction -> ({Red, Line[#1]} &), 
 VertexRenderingFunction -> (Inset[
     Row[{If[Last[#2] > 3, Rotate[Last[#2], 90 Degree], 
        Last[#2]]}], #1, Background -> White] &)]

tree plot

I am using the EdgeRenderingFunction for this within the following expression which will render the edges of the tree plot. The problem, I think, is that the If statement is not evaluated straight away and so the compiler does not recognise the first option (contained within the If statement) as a relevant one. As you can see I'm trying to create a tree plot with red and green branches.

TreePlot[nodes, 
 EdgeRenderingFunction -> ({If[Last[#2] == 1, Red, Green], 
     Line[#1]} &), 
 VertexRenderingFunction -> (Inset[
     Row[{If[Last[#2] > 3, Rotate[Last[#2], 90 Degree], 
        Last[#2]]}], #1, Background -> White] &)]

The error message I get is:

If is not a Graphic Primitive or Directive

enter image description here

Thanks for your time in helping me with this.

$\endgroup$
5
  • $\begingroup$ Does your code work for the edges if you comment out the VertexRenderingFunction part? What is your goal with the vertex rendering? The second parameter passed (#2) is the vertex name, so I'm not sure what your function is trying to do with that. $\endgroup$ Sep 14, 2017 at 12:30
  • $\begingroup$ For a traditional tree there is a single root node, so why aren't all your branches the same color? $\endgroup$ Sep 14, 2017 at 16:43
  • $\begingroup$ aardvark2012 - no, the code does not work if I comment out the VertexRenderingFunciton part. Davis G. Stork - Because each child node represents a different interrelated group. This can be done in the R programming language. $\endgroup$ Sep 15, 2017 at 2:30
  • 1
    $\begingroup$ "coloured according to its parent node value" <- Where and how is this "parent node value" stored? Can you provide sample data? The error is no doubt because == does not evaluate. This indicates that you may have nodes with non-numerical names. The fix may be as simple as using ===, but I can't tell for sure without seeing a complete example (with data). $\endgroup$
    – Szabolcs
    Sep 15, 2017 at 8:16
  • $\begingroup$ Szabolics - Here's a sample of the node list: {{0} -> {0, 1}, {0} -> {0, 2}, {0} -> {0, 3}, {0, 1} -> {0, 1, 4}, {0, 1} -> {0, 1, 5}, {0, 1} -> {0, 1, 6}, {0, 1} -> {0, 1, 7}, {0, 1} -> {0, 1, 8}... The code works with === or TrueQ[If...] instead of == (Thanks!) but I got an unexpected result - all edges were rendered the same colour. So either the EdgeRenderingFunction function renders the whole tree object rather than it's parts or I'll need to include the edge colour into the tree data structure (node table), or use a different way (function) to represent the tree? $\endgroup$ Sep 15, 2017 at 12:14

3 Answers 3

4
+50
$\begingroup$

The problem seems to be at least partially due to how you reference the arguments of EdgeRenderingFunction. We can label the edges with Last[#2] to see what we're working with:

nodes = {{0} -> {0, 1}, {0} -> {0, 2}, {0} -> {0, 3}, {0, 1} -> {0, 1, 4}, {0, 1} -> {0, 1, 5}, {0, 1} -> {0, 1, 6}, 
  {0, 1} -> {0, 1, 7}, {0, 1} -> {0, 1, 8}};
TreePlot[nodes, 
 EdgeRenderingFunction -> ({Line[#1], 
     Inset[Last[#2], Mean[#1], Automatic, Automatic, #[[1]] - #[[2]], 
      Background -> White]} &), 
 VertexRenderingFunction -> (Inset[
     Row[{If[Last[#2] > 3, Rotate[Last[#2], 90 Degree], 
        Last[#2]]}], #1, Background -> White] &)]

enter image description here

So none of these are ever going to be 1. You could, of course, use these for your colouring. You could also use the values in First[#2]:

TreePlot[nodes, 
 EdgeRenderingFunction -> ({Line[#1], 
     Inset[First[#2], Mean[#1], Automatic, Automatic, #[[1]] - #[[2]],
       Background -> White]} &), 
 VertexRenderingFunction -> (Inset[
     Row[{If[Last[#2] > 3, Rotate[Last[#2], 90 Degree], 
        Last[#2]]}], #1, Background -> White] &)]

enter image description here

You should be able to use any of these values to colour the edges however you want. For example, using #2[[1, -1]] for your condition gives:

TreePlot[nodes, 
 EdgeRenderingFunction -> ({If[#2[[1, -1]] == 1, Red, Green], 
     Line[#1]} &), 
 VertexRenderingFunction -> (Inset[
     Row[{If[Last[#2] > 3, Rotate[Last[#2], 90 Degree], 
        Last[#2]]}], #1, Background -> White] &)]

enter image description here

$\endgroup$
1
  • $\begingroup$ Thank you for a great explanation $\endgroup$ Sep 17, 2017 at 11:02
4
$\begingroup$

You code uses rules for edge styles, e.g.:

func[elist_, pn_, col_] := 
 Module[{rules = 
    MapThread[ed_[#1, x_] :> Style[ed[#1, x], #2] &, {pn, col}]},
  elist /. rules]

For example:

TreeGraph[
 func[{0 -> 1, 0 -> 2, 1 -> 3, 1 -> 4, 2 -> 5, 2 -> 6}, {0, 1, 
   2}, {Red, Green, {Purple, Thick, Dashed}}]]

enter image description here

$\endgroup$
1
  • $\begingroup$ Thanks ubpdqn for this example. $\endgroup$ Sep 18, 2017 at 11:48
2
$\begingroup$

There are simpler ways to do this. I do not have Mathematica 9 around, but I think this should work.

First, I recommend representing trees as directed graphs. This way the identity of the root vertex is already encoded. IGDirectedTree from IGraph/M is useful for this (Mathematica 10.0+ only), otherwise you can do a BreadthFirstScan.

Consider this directed tree:

tree = Graph@{1 -> 2, 2 -> 3, 1 -> 4, 1 -> 5, 1 -> 6, 2 -> 7, 2 -> 8, 
    4 -> 9, 2 -> 10, 2 -> 11, 1 -> 12, 12 -> 13, 2 -> 14, 13 -> 15, 
    2 -> 16, 2 -> 17, 9 -> 18, 4 -> 19, 11 -> 20};

Now simply use patterns to select a set of edges to colour:

Graph[tree,
 VertexShapeFunction -> "Name",
 EdgeStyle -> {1 \[DirectedEdge] _ -> Red, 2 \[DirectedEdge] _ -> Blue, 4 \[DirectedEdge] _ -> Green, 12 \[DirectedEdge] _ -> Orange}
 ]

enter image description here

Having a directed tree was useful because we could easily distinguish "down-edges" (away from the root) from "up-edges" (towards the root).

If you do not want the arrowheads displayed, add

EdgeShapeFunction -> "Line",
PerformanceGoal -> "Quality"

Like this:

Graph[tree,
 VertexShapeFunction -> "Name",
 EdgeStyle -> {Thick, (1 \[DirectedEdge] _) -> Red, 2 \[DirectedEdge] _ -> Blue, 4 \[DirectedEdge] _ -> Green, 12 \[DirectedEdge] _ -> Orange},
 EdgeShapeFunction -> "Line",
 PerformanceGoal -> "Quality"
]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.