Skip to content

Commit 53bd595

Browse files
committed
Merge pull request #10 from thomasp85/master
Update ggraph examples
2 parents e2c420b + e62dde4 commit 53bd595

File tree

1 file changed

+151
-19
lines changed

1 file changed

+151
-19
lines changed

Diff for: ggraph.Rmd

+151-19
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,159 @@ title: "ggplot2 extensions: ggraph"
55
### ggraph
66
<https://github.com/thomasp85/ggraph>
77

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.
917

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
1423

15-
# igraph
16-
# We'll just make up some data
24+
```{r, eval=TRUE, echo=TRUE, warning=FALSE, message=FALSE}
25+
library(ggraph)
1726
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()
29162
```
30163

31-
![ggraph](images/ggraph.png)

0 commit comments

Comments
 (0)