Creating a graph with variable size of vertices and variable thickness of edges

I'm trying to create a graph to represent transitions between different states of a system. I would like to represent the time the systems spent in a state with the size of the vertex and the frequency of transition between two states with the thickness of the edge connecting the two vertices. In short I need to control the size of vertices and the thickness of edges.

Now that I grasp the difference between Graph and GraphPlot a bit better, I wanted to implement this using the newer Graph functunality, specifically VertexSize and EdgeStyle.

Here is my clumsy attempt:

trans = Uncompress@
"1:eJyVV01vwjAMRfu47U/sv3RKKc2KGsahxw2YOE0aXPff17RJSZD8/HIBga3Efn5+\
dl4/f/rT32q1ujyNH+35cj09+F8v40d1/j1+XY+\
Ht8P30T2Pf9ihbyvn7Y0T3GbjzZtyM5LbYmcP9Els++FdTWJy7EwrOEYzTjbeRkHyODnvB\
mBlzhq9tJrfqolRC9aSDNSzGPwxFv5f05GAldDVoJKbrgBWJsnCIuFMB/\
28JVXNMR7IQsfVvZSTCD07V79Z78Clo1VLIS+YVn5/2H67Q3h4M83wZr2B4W/\
oCmhaFDXSWysUWcVpH4sElyUTFYsEGxnPRl7nlRgTFoFBu0xGjuDToRLAs5GeeDWqes22H\
ORPLDl/FENXLXCmuVWQanrkBwmT88uGjhh8JgIyYRI91EW4aBoqtOIbg60RxD+IAGjuZC+\
QRSeXfC2qkhGor6gqD60WfdoZTOy8SGu9iONKFEJeVO7eELQGMvOD0UDYaXHN5a6DT5Zlv\
GuYlu2TmhIyD726bP5gSnswmEttWR9puG1wH6WE1VuSBI2sVNA6cfxlK6JIx7utFDE7vI5\
lyAL+Kv09FopgpA9t+\
cIcCcjZMXgYV4YWrHd86DFyR2xpTh012aCEY8sSoy3sX3A0OHUHTjWYWgR8fv/wd/kB";
l = VertexList[Graph[trans]];
(*Weigths for vertices*)
r = RandomReal[{0.5, 1}, Length[l]];
vs = Transpose@{l, r} /. List[a_, b_] -> Rule[a, b];
vls = Transpose@{l, r} /.
List[a_, b_] -> Rule[a, Directive[Black, Bold, 20*b]];
(*Weigths for line thicknes*)
rr = RandomReal[{0.1, 1}, Length[DeleteDuplicates@trans]]/100;
es = (First@# -> Directive[Thickness[Last@#], Opacity[.5]]) & /@
Transpose@{DeleteDuplicates@trans, rr};
g = Graph[DeleteDuplicates@trans,
VertexLabels -> Placed["Name", Center],
VertexShapeFunction -> "Capsule", VertexSize -> vs,
VertexLabelStyle -> vls, EdgeStyle -> es,
EdgeShapeFunction ->
GraphElementData["ShortUnfilledArrow", "ArrowSize" -> 0.03]]


But the result is useless (the text is not readable and it's not clear from where to where the arrows are going):

How can I either fix this graph so that it's readable or implement variable vertex sizes and variable edge thickens using GraphLayout?

EDIT @kguler's comment helps. so using:

esf = Function[
a, (First@
Arrow[#1, {.10 + 6 Last@a, .15}]} &))] /@
Transpose@{DeleteDuplicates@trans, rr};
g = Graph[DeleteDuplicates@trans,
VertexLabels -> Placed["Name", Center], VertexSize -> vs,
VertexLabelStyle -> vls, EdgeStyle -> es, EdgeShapeFunction -> esf(*,
EdgeShapeFunction->GraphElementData["ShortUnfilledArrow",
"ArrowSize"->0.03]*)]


I get , which is almost usable. Is there perhaps a way to draw vertices on top of the edges?

-
To get a list of vertices from a Graph you could do VertexList[Graph[trans]]. –  Heike Jun 5 '12 at 10:31
@Heike Thanks, I knew there had to be a simple way (I was looking under Property). –  Ajasja Jun 5 '12 at 10:46
You might want to change the EdgeShapeFunction to something like EdgeShapeFunction -> ({Arrowheads[{{.03, 1}}], Arrow[#1, {.1, .15}]} &) to avoid too much traffic on top of vertices. –  kglr Jun 5 '12 at 11:26
@kguler Thanks, your suggestion is very helpful (See edit). Is there perhaps a way to draw vertices on top of the edges? –  Ajasja Jun 5 '12 at 15:11
Capsule is a difficult shape to deal with - calculation of the appropriate setbacks gets really complicated because it depends on the entire layout. But, for Circle the following works: EdgeShapeFunction -> ({Arrowheads[{{.03, 1}}], Arrow[#1, {.2 #2[[1]] /. vs, .2 #2[[2]] /. vs}]} &). –  kglr Jun 5 '12 at 15:12

Using the setback argument of Arrow in the EdgeShapeFunction to account for different vertex sizes

EdgeShapeFunction ->
({Arrowheads[{{.03, 1}}], Arrow[#1, {.2  #2[[1]] /. vs, .2 #2[[2]] /. vs}]} &)];


takes care of the first problem. One way to approach the second problem (putting vertices on top) is to superimpose an edge-less version of g on top of g.

First get a slightly modified version of your original graph,

l = VertexList[Graph[trans]];
r = RandomReal[{0.5, 1}, Length[l]];
vs = Transpose@{l, r} /. List[a_, b_] -> Rule[a, b];
vls = Transpose@{l, r} /. List[a_, b_] -> Rule[a, Directive[Black, Bold, 16 b]];
rr = RandomReal[{0.1, 1}, Length[DeleteDuplicates@trans]]/100;
es = (First@# -> Directive[Thickness[Last@#], Opacity[.5]]) & /@
Transpose@{DeleteDuplicates@trans, rr};
g = Graph[DeleteDuplicates@trans,
VertexLabels -> Placed["Name", Center],
VertexShapeFunction -> "Circle", VertexStyle -> {Opacity[1]},
VertexSize -> vs, VertexLabelStyle -> vls, EdgeStyle -> es,
EdgeShapeFunction ->
({Arrowheads[{{.03, 1}}], Arrow[#1, {.2  #2[[1]] /. vs, .2 #2[[2]] /. vs}]} &)];


Then get an edge-less version of g using

Fold[SetProperty[{#1, #2}, EdgeStyle -> Opacity[0]] &, g, EdgeList[g]]


Finally, Show the two graphs.

All three graphs in a column:

 Column[{g,
Fold[SetProperty[{#1, #2}, EdgeStyle -> Opacity[0]] &, g, EdgeList[g]],
Show[g, Fold[SetProperty[{#1, #2}, EdgeStyle -> Opacity[0]] &, g, EdgeList[g]]]}]


EDIT: Perhaps, you do not want the edges to be completely occluded by the vertices. In that case, we can play with combinations of the opacity settings of the edges and vertex styles to make vertices look less cluttered. For example, using

es = (First@# -> Directive[Thickness[Last@#], Opacity[.3]]) & /@
Transpose@{DeleteDuplicates@trans, rr};
vstyl = (Sequence @@ {# -> Hue@{.6 # /. vs}} & /@ l);


(where I modified the opacity setting in es to 3 and defined a vertex style setting that uses the values in vs to determined the vertex color), in

 Graph[DeleteDuplicates@trans, VertexLabels -> Placed["Name", Center],
VertexShapeFunction -> "Circle", VertexSize -> vs,
VertexStyle -> vstyl, VertexLabelStyle -> vls, EdgeStyle -> es,
EdgeShapeFunction ->
({Arrowheads[{{.03, 1}}], Arrow[#1, {.2  #2[[1]] /. vs, .2 #2[[2]] /. vs}]} &),
ImageSize -> 600]


gives

Another alternative is to use styles for the vertices in HighlightGraph as follows:

 HighlightGraph[g, {#, Style[#, {Opacity[1], Hue@{.2  # /. vs}}]} & /@ VertexList[g]]


which gives:

-
Thanks! Got something which is quite OK. Are there any options to make the edges go around vertices (like in LayeredGraphPlot)? But this should probably be a whole new question then ;) –  Ajasja Jun 12 '12 at 12:11
@Ajasja,thank you for the accept. And, yes that would be an interesting new question indeed:) –  kglr Jun 12 '12 at 19:20

Since I already have this code, I'm going to show an example using GraphPlot and an adjacency graph based on a matrix nums where the links can take positive values other than 1, or zero. Rather than having two arrows for each link, I use line thickness to indicate the sum of the two link values, color to show the ratio of the net (difference between the two directions’ values) to gross (sum of the two link values), and the size of the arrowhead to show which is the stronger link direction.

As kguler noted in comments, EdgeRenderingFunction (or in the case of Graph rather than GraphPlot, EdgeShapeFunction) can be specified so that the arrowheads don’t overlap the vertex.

GraphPlot[nums, DirectedEdges -> True,
VertexRenderingFunction -> ({White, EdgeForm[Black], Disk[#, .04],
Black, Text[names[[#2]], #1]} &),
EdgeRenderingFunction -> ({AbsoluteThickness[(nums[[#2[[1]], #2[[
2]]]] + nums[[#2[[2]], #2[[1]]]])/(0.08 maxthick)],
RGBColor[
Abs[(nums[[#2[[1]], #2[[2]]]] -
nums[[#2[[2]], #2[[1]]]])]/(0.8 (nums[[#2[[1]], #2[[2]]]] +
nums[[#2[[2]], #2[[1]]]])), 0.1, 0.7],
0.010 + 0.004 Sign@(nums[[#2[[1]], #2[[2]]]] -
nums[[#2[[2]], #2[[1]]]])], Arrow[#1, 0.05]} &),
VertexLabeling -> True, ImageSize -> 900,
PlotLabel -> Style["Plot Label", Bold, 14, FontFamily -> "Arial"],


I've just labelled the vertices with names = Range[20] but you could use strings or whatever you like.

Here is an example with multiple arrows, where thickness, gray shade and arrowhead size all depend on the value of the element in the adjaceny matrix.

GraphPlot[Sign[nums], DirectedEdges -> True, MultiedgeStyle -> True,
VertexRenderingFunction -> ({White, EdgeForm[Black], Disk[#, .04],
Black, Text[names[[#2]], #1]} &),
EdgeRenderingFunction -> (With[{relexp = (nums[[#2[[1]], #2[[2]]]])/
maxthick}, {AbsoluteThickness[relexp*20.],
RGBColor[relexp*0.8, relexp*0.8, relexp*0.8],
Arrowheads[0.06 relexp + 0.008], Arrow[#1, 0.05]}] &),
VertexLabeling -> True, ImageSize -> 900,
PlotLabel -> Style["Plot Heading", Bold, 14, FontFamily -> "Arial"],


Setting a numerical value for the MultiEdgeStyle option can bring the arrow pairs closer together and tidy up the look considerably.

 GraphPlot[Sign[nums], DirectedEdges -> True, MultiedgeStyle -> 0.02,
VertexRenderingFunction -> ({White, EdgeForm[Black], Disk[#, .04],
Black, Text[names[[#2]], #1]} &),
EdgeRenderingFunction -> (With[{relexp = (nums[[#2[[1]], #2[[2]]]])/
maxthick}, {AbsoluteThickness[relexp*20.],
RGBColor[relexp*0.8, relexp*0.8, relexp*0.8],
Arrowheads[0.06 relexp + 0.008], Arrow[#1, 0.05]}] &),
VertexLabeling -> True, ImageSize -> 700, ImagePadding -> 0,