@@ -5,27 +5,159 @@ title: "ggplot2 extensions: ggraph"
5
5
### ggraph
6
6
< https://github.com/thomasp85/ggraph >
7
7
8
- ggraph is an extension of ggplot2 tailored at plotting graph-like data structures (graphs, networks, trees, hierarchies...).
8
+ ggraph extends the grammar of graphics provided by ggplot2 to cover graph and
9
+ network data. This type of data consists of nodes and edges and are not
10
+ optimally stored in a single data.frame, as expected by ggplot2. Furthermore the
11
+ spatial position of nodes (end thereby edges) are more often defined by the
12
+ graph structure through a layout function, rather than mapped to specific
13
+ parameters. ggraph handles all of these issues in an extensible way that lets
14
+ the user gradually build up their graph visualization with different layers as
15
+ expected from ggplot2, without needing to worry too much about how positions and
16
+ other metrics are derived.
9
17
10
- ``` {r, message=FALSE,warning=FALSE, eval=FALSE}
11
- # Example from https://github.com/thomasp85/ggraph
12
- library(ggplot2)
13
- library(ggraph)
18
+ While node-edge diagrams are the visualization people often relate to graph
19
+ visualizations it is by far the only one, and ggraph have, or will have support
20
+ for all types of common and uncommon visualization types.
21
+
22
+ #### A simple example
14
23
15
- # igraph
16
- # We'll just make up some data
24
+ ``` {r, eval=TRUE, echo=TRUE, warning=FALSE, message=FALSE}
25
+ library(ggraph)
17
26
library(igraph)
18
- gr <- erdos.renyi.game(n=20, p=0.2)
19
- E(gr)$weight <- sample(1:5, gsize(gr), TRUE)
20
- V(gr)$class <- sample(letters[1:3], gorder(gr), TRUE)
21
-
22
- # Plotting this is exactly as plotting a dendrogram
23
- # All igraph layouts are available - here we use Fructerman and Reingold
24
- ggraph(graph = gr, layout = 'fr') +
25
- geom_edge_link(aes(size = weight), color = 'grey', alpha = 0.5) +
26
- geom_node_point(aes(color = class), size = 10) +
27
- coord_fixed() +
28
- ggforce::theme_no_axes()
27
+
28
+ # To show a simple example of the API we consider a hierarchy
29
+ ## Create a graph of the flare class hierarchy
30
+ flareGraph <- graph_from_data_frame(flare$edges, vertices = flare$vertices)
31
+ ggraph(flareGraph, 'igraph', algorithm = 'tree') +
32
+ geom_edge_link() +
33
+ ggforce::theme_no_axes()
34
+ ```
35
+
36
+ Here we use the reingold-tilford algorithm to position the nodes in a hierarchy
37
+ and draw straight edges between them using the ` geom_edge_link ` function. There
38
+ are ample opportunity for modifications though:
39
+
40
+ ``` {r, eval=TRUE, echo=TRUE}
41
+ ggraph(flareGraph, 'igraph', algorithm = 'tree', circular = TRUE) +
42
+ geom_edge_diagonal(aes(alpha = ..index..)) +
43
+ coord_fixed() +
44
+ scale_edge_alpha('Direction', guide = 'edge_direction') +
45
+ geom_node_point(aes(filter = degree(flareGraph, mode = 'out') == 0),
46
+ color = 'steelblue', size = 1) +
47
+ ggforce::theme_no_axes()
48
+ ```
49
+
50
+ Here we transform the layout into a circular representation and choose to draw
51
+ the edges as diagonals instead of straight lines using ` geom_edge_diagonal ` . We
52
+ indicate the direction of the edge by mapping the alpha value to it, and shows
53
+ this using the edge_direction guide. Lastly we plot the leaf nodes using a
54
+ filter function.
55
+
56
+ #### Other layouts
57
+ But what if my data is not hierarchical, you ask. Well ggraph have access to all
58
+ the layouts implemented in igraph, and then some (more on that later). To show
59
+ this we take a look at some colonial Americans:
60
+
61
+ ``` {r, echo=TRUE, eval=TRUE}
62
+ whigsGraph <- graph_from_adjacency_matrix(whigs %*% t(whigs), mode = 'upper',
63
+ weighted = TRUE, diag = FALSE)
64
+ V(whigsGraph)$degree <- degree(whigsGraph)
65
+ ggraph(whigsGraph, 'igraph', algorithm = 'kk') +
66
+ geom_edge_link0(aes(width = weight), edge_alpha = 0.1) +
67
+ geom_node_point(aes(size = degree), colour = 'forestgreen') +
68
+ geom_node_text(aes(label = name, filter = degree > 150), color = 'white',
69
+ size = 3) +
70
+ ggforce::theme_no_axes()
71
+ ```
72
+
73
+ So it seems Paul Reveres was really in the center of it all (more on this [ here] ( http://kieranhealy.org/blog/archives/2013/06/09/using-metadata-to-find-paul-revere/ ) ).
74
+
75
+ Here we use the kamada kawai layout to lay out the nodes and draw the edges
76
+ using the ` geom_edge_link0 ` (notice the trailing 0 - this is a faster version
77
+ than the standard but it doesn't support gradients along the edge). We draw
78
+ the nodes scaling the size to the degree and lastly we add node labels to the
79
+ nodes with a degree larger than 150.
80
+
81
+ * What if I don't like nodes and edges?*
82
+
83
+ Well you could try to make a treemap. Let's turn to our flare hierarchy again:
84
+
85
+ ``` {r, eval=TRUE, echo=TRUE}
86
+ # Well add some additional information using the treeApply helpers
87
+ flareGraph <- treeApply(flareGraph, function(node, parent, depth, tree) {
88
+ tree <- set_vertex_attr(tree, 'depth', node, depth)
89
+ if (depth == 1) {
90
+ tree <- set_vertex_attr(tree, 'class', node, V(tree)$shortName[node])
91
+ } else if (depth > 1) {
92
+ tree <- set_vertex_attr(tree, 'class', node, V(tree)$class[parent])
93
+ }
94
+ tree
95
+ })
96
+ V(flareGraph)$leaf <- degree(flareGraph, mode = 'out') == 0
97
+
98
+ ggraph(flareGraph, 'treemap', weight = 'size') +
99
+ geom_treemap(aes(fill = class, filter = leaf, alpha = depth), colour = NA) +
100
+ geom_treemap(aes(size = depth), fill = NA, colour = 'white') +
101
+ geom_node_text(aes(filter = depth == 1, label = shortName), size = 3) +
102
+ scale_fill_brewer(type = 'qual', palette = 3, guide = 'none') +
103
+ scale_size(range = c(3, 0.2), guide = 'none') +
104
+ scale_alpha(range = c(1, 0.6), guide = 'none') +
105
+ ggforce::theme_no_axes()
106
+ ```
107
+
108
+ * What if I don't use igraph?*
109
+
110
+ There is also support for dendrogram objects and almost anything else can be
111
+ converted to either igraph or dendrogram. Let's look at a dendrogram object:
112
+
113
+ ``` {r, echo=TRUE, eval=TRUE}
114
+ irisCluster <- hclust(dist(iris[, -5]), method = 'average')
115
+ irisCluster <- as.dendrogram(irisCluster)
116
+
117
+ # treeApply also works for dendrogram
118
+ irisCluster <- treeApply(irisCluster, function(node, children, ...) {
119
+ if (is.leaf(node)) {
120
+ index <- as.integer(attr(node, 'label'))
121
+ attr(node, 'nodePar') <- list(species = as.character(iris$Species[index]))
122
+ } else {
123
+ childSpecies <- sapply(children, function(child) {
124
+ attr(child, 'nodePar')$species
125
+ })
126
+ if (length(unique(childSpecies)) == 1 && !anyNA(childSpecies)) {
127
+ attr(node, 'nodePar') <- list(species = childSpecies[1])
128
+ } else {
129
+ attr(node, 'nodePar') <- list(species = NA)
130
+ }
131
+ }
132
+ node
133
+ }, direction = 'up')
134
+
135
+ ggraph(irisCluster, 'dendrogram', repel = TRUE, ratio = 10) +
136
+ geom_edge_elbow2(aes(colour = node.species),
137
+ data = gEdges('long', nodePar = 'species')) +
138
+ scale_edge_colour_brewer('Species', type = 'qual', na.value = 'black') +
139
+ ggforce::theme_no_axes()
140
+ ```
141
+
142
+ Wait, what is that data argument? Most edge geoms comes in three flavors, a
143
+ standard, a fast (the 0 one) and a slow (the 2 one). So why use the slow? It
144
+ makes it possible to set interpolate edge aesthetics between the start and end
145
+ point. Here we tell that colour should be mapped to the species of the nodes,
146
+ and we tell that the edge data should contain the species parameter from the
147
+ start and end nodes within the ` gEdges() ` call. Now we get a nice gradient from
148
+ clusters with a common species towards clusters with mixed species.
149
+
150
+ These are just a couple of examples of what is possible. Due to the layer
151
+ approach to plotting edges and nodes you have full control over the look of your
152
+ plot in a very expressive way. As it is a direct extension of ggplot2, it also
153
+ comes with all the niceties from there, e.g. facetting:
154
+
155
+ ``` {r, echo=TRUE, eval=TRUE}
156
+ highschoolGraph <- graph_from_data_frame(highschool)
157
+
158
+ ggraph(highschoolGraph, 'igraph', algorithm = 'kk') +
159
+ geom_edge_fan(aes(alpha = ..index..)) +
160
+ facet_wrap(~year) +
161
+ ggforce::theme_no_axes()
29
162
```
30
163
31
- ![ ggraph] ( images/ggraph.png )
0 commit comments