Creating a graph with variable size of vertices and variable thickness of edges
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],
Arrowheads[
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"],
ImagePadding -> 0, PlotRangePadding -> 0.02]
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"],
ImagePadding -> 0, PlotRangePadding -> 0.02]
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,
PlotRangePadding -> 0.02]
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:
To fix the arrowhead positioning, use
PerformanceGoal -> "Quality"
If I remove the EdgeShapeFunction
and add the above option, your code produces this:
Using the latest version of IGraph/M (prerelease), you can also simplify and structure your styling code. We start by creating a graph with edge and vertex weights:
g = Graph[trans, VertexWeight -> r, EdgeWeight -> rr]
Then each IGEdgeMap
and IGVertexMap
operator sets one style property based on another one. We chain multiple operators for each styling step. You can use //
instead of /*
, but the Front End indents more nicely with /*
.
Graph[g, (* set static style options *)
VertexShapeFunction -> "Capsule",
VertexLabels -> Placed["Name", Center],
PerformanceGoal -> "Quality", ImageSize -> Large
] //
IGEdgeMap[ (* scale edge thickness and arrowhead size based on edge weight *)
Directive[Thickness[#], Arrowheads[4 #]] &,
EdgeStyle -> IGEdgeProp[EdgeWeight]
] /*
IGVertexMap[ (* scale vertices based on vertex weight *)
# &,
VertexSize -> IGVertexProp[VertexWeight]
] /*
IGVertexMap[ (* scale vertex labels based on vertex weight *)
FontSize -> 15 # &,
VertexLabelStyle -> IGVertexProp[VertexWeight]
]