16
$\begingroup$

Description of Problem

Mathematica recently added Callout as a new labeling option. While I am preferential to using Tooltip, if one has to use the image in a presentation, it is nice to have labels. Further, while Placed allows one to designate where the label goes in respect to the vertex, I find that it does not exactly do so in a way that is easy to read - especially if some verticies are close together.

Question

So how can one use Callout with Graph option VertexLabels to get a clean labeling of a directed graph?

Example:

A basic graph

simpleGraph = {1 \[DirectedEdge] 2, 2 \[DirectedEdge] 3, 3 \[DirectedEdge] 1, 1 \[DirectedEdge] 4, 4 \[DirectedEdge] 2};

Default labels

Graph[simpleGraph, VertexLabels -> "Name"]

Using Placed works

Graph[simpleGraph, VertexLabels -> Placed["Name", Above]]

Using Callout does not work

Graph[simpleGraph, VertexLabels -> Callout["Name", Above]]

Graph[{1 \[DirectedEdge] 2, 2 \[DirectedEdge] 3, 3 \[DirectedEdge] 1, 1 \[DirectedEdge] 4, 4 \[DirectedEdge] 2}, VertexLabels -> Callout[{1, 2, 3, 4}, {1, 2, 3, 4}, Above]]

Example of Desired Output

enter image description here

Because this image is wide, we will just zoom up at one part of it

enter image description here

Ideal:

enter image description here

I know making a automated label function that minimizes textual overlap with vertices is hard, but hopefully there is a way to at least get text to not overlap.

Sample graph to practice on:

exampleGraph={17835 \[DirectedEdge] 17848, 17848 \[DirectedEdge] 20967, 
 17835 \[DirectedEdge] 17845, 17845 \[DirectedEdge] 20967, 
 17835 \[DirectedEdge] 5779, 5779 \[DirectedEdge] 20967, 
 17835 \[DirectedEdge] 3931, 3931 \[DirectedEdge] 20967, 
 17835 \[DirectedEdge] 3870, 3870 \[DirectedEdge] 20967, 
 17835 \[DirectedEdge] 3554, 3554 \[DirectedEdge] 20967, 
 17835 \[DirectedEdge] 3403, 3403 \[DirectedEdge] 20967, 
 20967 \[DirectedEdge] 12657, 12657 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 9038, 9038 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 5779, 5779 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 3870, 3870 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 3637, 3637 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 3554, 3554 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 3367, 3367 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 1390, 1390 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 560, 560 \[DirectedEdge] 17835, 
 20967 \[DirectedEdge] 482, 482 \[DirectedEdge] 17835};

An unsatisfying approach

Alternatively, a footnote like style would also be ok, e.g. a vertex has a number, and below the graph a table of number name pairs appears...

Functions

getVerticies[edgeList_] := Module[{vertexList = {}},
  Table[AppendTo[
    vertexList, {edgeList[[i]][[1]], edgeList[[i]][[2]]}], {i, 1, 
    Length[edgeList]}];
  Return[DeleteDuplicates[Flatten[vertexList]]]
  ]
makeVertexLabels[vertexList_, labelList_] := 
  Return[Table[
    vertexList[[i]] -> Placed[labelList[[i]], Center], {i, 1, 
     Length[vertexList]}]];
makeEdgeLabels[edgeList_, labelList_] := 
  Return[Table[
    edgeList[[i]] -> Placed[labelList[[i]], "Middle"], {i, 1, 
     Length[edgeList]}]];

Variables

simpleGraph = {1 \[DirectedEdge] 2, 2 \[DirectedEdge] 3, 
  3 \[DirectedEdge] 1, 1 \[DirectedEdge] 4, 4 \[DirectedEdge] 2};
listOfVertexNames = {"One", "Fish", "Two", "Fish"};
listOfEdgeNames = {"Red", "Fish", "Blue", "Fish", "Octupus"};

Graph

Graph[simpleGraph, 
 VertexLabels -> 
  makeVertexLabels[getVerticies[simpleGraph], 
   Range[Length[getVerticies[simpleGraph]]]], 
 EdgeLabels -> 
  makeEdgeLabels[simpleGraph, Range[Length[simpleGraph]]]]

Table

verticies = 
 Prepend[Table[
   Range[Length[getVerticies[simpleGraph]]][[i]] -> 
    listOfVertexNames[[i]], {i, 1, Length[listOfVertexNames]}], 
  "Verticies"]
edges = Prepend[
  Table[Range[Length[simpleGraph]][[i]] -> listOfEdgeNames[[i]], {i, 
    1, Length[listOfEdgeNames]}], "Edges"]
Grid[{{Column[verticies]}, {Column[edges]}}, Alignment -> Left, 
 BaselinePosition -> Top]

Downsides: 1) I don't know how to guarantee that the vertex numbers fit inside the verticies. 2) nor do I know how to offset the edge labels sightly. 3) Requires more space for publications (because of the table), which means the graph has to be smaller.

$\endgroup$
10
  • 1
    $\begingroup$ The simple answer is that Callout doesn't work with graphs. You have to implement such a display yourself. You can write to Wolfram and suggest adding the functionality. $\endgroup$
    – Szabolcs
    Sep 7, 2016 at 9:49
  • $\begingroup$ This is just personal opinion, but I find that your excessive use of headers and boldface harms the readability of your questions. I could skim them faster if you only highlighted what is truly important. $\endgroup$
    – Szabolcs
    Sep 7, 2016 at 9:50
  • $\begingroup$ @Szabolcs I followed your advise about quesiton layout. So how would one do that.... $\endgroup$
    – SumNeuron
    Sep 7, 2016 at 10:13
  • $\begingroup$ It depends on how nice you want it to look ... can you give a mock-up example? If it's sophisticated enough to make it worth doing, I don't expect it to be easy. I assume you want more than placing the label in a fixed position relative to the vertex and connecting it to the vertex with a line. It's a good question, just not easy to implement (in my opinion; maybe someone will surprise us) $\endgroup$
    – Szabolcs
    Sep 7, 2016 at 10:18
  • $\begingroup$ You could make the labels themselves graph nodes and set different styles on them. Use some force directed layout and try to enforce a minimal internode distance using its options. Finally verify that the positions are reasonable in the result. This might be worth a try for certain kinds of graphs. I am assuming that you don't just want to connect the label and node with a line, but you also want smart positioning (which Callout usually does). $\endgroup$
    – Szabolcs
    Sep 7, 2016 at 12:15

1 Answer 1

7
$\begingroup$

One possible way is to ListPlot the vertex coordinates of the Graph object and Show the graph and list plot together:

gr=Graph[exampleGraph];

lp=ListPlot[Callout@@@Transpose[{GraphEmbedding[gr], VertexList[gr]}],
Axes->False, PlotStyle->None];

Show[gr, lp]

Mathematica graphics

However ... without finer control on the Callout parameters, this is not much better than using VertexLabels->"Name":

Graph[exampleGraph, VertexLabels->"Name"]

Mathematica graphics

Using labels as VertexShapeFunction gives a better picture:

Graph[exampleGraph, ImageSize -> 500, VertexShapeFunction -> "Name"]

Mathematica graphics

$\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.