Plotting collocation networks with R and ggraph
In a previous post, I showed how to use the igraph
package for R to plot a graph of the nominal collocates of the verbs hoard and stockpile in the Coronavirus Corpus (Davies, 2020). In this post, I show how to do the same with the ggraph
package.
ggraph
On February 2017, Thomas Lin Pedersen announced the release of ggraph
on CRAN. The motivation for ggraph
is the following:
The grammar of graphics as implemented in
https://cran.r-project.org/web/packages/ggraph/index.htmlggplot2
is a poor fit for graph and network visualizations due to its reliance on tabular data input.ggraph
is an extension of theggplot2
API tailored to graph visualizations and provides the same flexible approach to building up plots layer by layer.
Although presented as an improvement upon ggplot2
, ggraph
also plays the role of an igraph
add-on to bring the flexibility of the tidyverse grammar and more graphics settings into it. Naturally, because of its native tidyverse coloration, ggraph
is well suited to the logic of tidygraph
.
Walkthrough
We download, install, and load three packages: igraph
, ggraph
, and tidygraph
.
rm(list=ls(all=TRUE)) install.library("igraph") install.library("ggraph") install.library("tidygraph") library(igraph) library(ggraph) library(tidygraph)
The data is the same as used previously. It consists of two files: a table of vertex attributes (v.attr
) and an edge list (edges_data
). We load them.
v.attr <- read.table("https://www.nakala.fr/data/11280/1f0d5c4c", header=T, sep="\t") edges_data <- read.table("https://www.nakala.fr/data/11280/cfd3793e", header=T, sep="\t")
We make a graph object with the graph_from_data_frame
function from igraph
. The function takes three arguments:
d
, the data frame with theedges_data
;vertices
, a data frame withv.attr
in the first column;directed
which, set to FALSE, specifies that we want an undirected graph.
ig <- graph_from_data_frame(d=edges_data, vertices=v.attr, directed = FALSE)
Let us inspect the graph object:
> ig IGRAPH 4bca3fb UN-- 154 202 -- + attr: name (v/c), coll_freq (v/n), MI (e/n) + edges from 4bca3fb (vertex names): [1] food --hoard hoard --masks hoard --supplies hoard --toilet [5] hoard --paper cash --hoard hoard --items goods --hoard [9] groceries --hoard hoard --ventilators hoard --price hoard --profiteering [13] face --hoard hoard --wealth equipment --hoard hoard --products [17] commodities--hoard hoard --profiteers hoard --things hoard --panic [21] hoard --misuse essentials --hoard hand --hoard bottles --hoard [25] drugs --hoard drug --hoard behaviour --hoard devices --hoard [29] hoard --overpricing amounts --hoard hoard --sanitizer dollars --hoard + ... omitted several edges
The output says that we have a graph that is undirected (U) and named (N) with 154 nodes and 202 edges. The vertices have coll_freq
(i.e. collocation frequency) as a quantitative attribute, and the edges are weighted with MI
(i.e. mutual information).
For the sake of illustration, let us compare the igraph
object to its tidygraph
counterpart:
tg <- tidygraph::as_tbl_graph(ig) %>% activate(nodes) %>% mutate(label=name)
R outputs the following:
> tg # A tbl_graph: 154 nodes and 202 edges # # An undirected simple graph with 1 component # # Node Data: 154 x 3 (active) name coll_freq label <chr> <int> <chr> 1 alcohol 17 alcohol 2 allocation 4 allocation 3 ammunition 3 ammunition 4 amounts 24 amounts 5 antibiotics 5 antibiotics 6 anticipation 8 anticipation # … with 148 more rows # # Edge Data: 202 x 3 from to MI <int> <int> <dbl> 1 45 60 6.41 2 60 76 5.59 3 60 137 6.72 # … with 199 more rows
tg
is a tibble graph (tbl_graph
). As the tidygraph
documentation indicates:
The
https://rdrr.io/cran/tidygraph/man/tbl_graph.htmltbl_graph
class is a thin wrapper around anigraph
object that provides methods for manipulating the graph using the tidy API. As it is just a subclass ofigraph
every igraph method will work as expected.
tidygraph
bridges the gap between igraph
and the tidyverse, which means that we can rely on igraph
while benefiting from the intuitive grammar of the tidyverse.
We index the size of each node on collocation frequency,
v.size <- V(tg)$coll_freq
and specify that mutual information is used to weight the edges of the graph.
E(tg)$weight <- E(tg)$MI
As previously, we use eigenvector centrality to spot the most influential nodes by:
- rearranging the centrality scores into quantized buckets known as ‘bins;
- assigning a color to each bin along a heat continuum skewed to the reds (for higher centrality scores) and yellows (for lower centrality scores).
eigenCent <- evcent(tg)$vector bins <- unique(quantile(eigenCent, seq(0,1,length.out=30))) vals <- cut(eigenCent, bins, labels=FALSE, include.lowest=TRUE) colorVals <- rev(heat.colors(length(bins)))[vals]
Finally, we plot the graph with the desired parameters. Note that the grammar is typical of the tidyverse, as shown by the pipe operator (%>%
).
tg %>% ggraph(layout="stress") + geom_edge_diagonal(alpha = .2, color='white') + geom_node_point(size=log(v.size)*2, color=colorVals) + geom_node_text(aes(label = name), repel = TRUE, point.padding = unit(0.2, "lines"), size=log(v.size), colour='white') + theme_graph(background = 'grey20')
Here is what each line means:
ggraph(layout="stress")
: “This layout is related to the stress-minimization algorithm known as Kamada-Kawai (available as the ‘kk’ layout), but uses another optimization strategy.”(https://rdrr.io/cran/ggraph/man/layout_tbl_graph_stress.html);geom_edge_diagonal(alpha = .2, color='white')
: this draws edges as diagonal bezier curves rather than straight lines. A level of transparency is added (alpha = .2
) and the edges are printed in white (color='white'
);geom_node_point(size=log(v.size)*2, color=colorVals)
: the size of each node is indexed on the log of collocation frequency times two (size=log(v.size)*2
) and the color of each node is indexed on its centrality score (color=colorVals
).geom_node_text(aes(label = name), repel = TRUE, point.padding = unit(0.2, "lines"), size=log(v.size), colour='white')
: each node is assigned a label (aes(label = name)
). The position of labels is optimized to avoid overlapping text (repel = TRUE
). Labels are in white (colour='white
‘). Padding is added around the labeled data points (point.padding = unit(0.2, "lines")
);theme_graph(background = 'grey20')
: the background of the graph is colored in a dark shade of grey.
The results is an elegant graph (Fig. 1). See this previous post for a short analysis.
Note that we have not indexed edge width on MI. To do so, remove the geom_edge_diagonal
parameter and replace it with: geom_edge_link(alpha = .25, colour='white, aes(width = weight))
. The code is now:
tg %>% ggraph(layout="stress") + geom_edge_link(alpha = .25, colour='white', aes(width = weight)) + geom_node_point(size=log(v.size)*2, color=colorVals) + geom_node_text(aes(label = name), repel = TRUE, point.padding = unit(0.2, "lines"), size=log(v.size), colour="white") + theme_graph(background = 'grey20')
In a future post, I plan to conduct a diachronic study of collocation shifts to see how panick-buying has evolved along the whole COVID-19 crisis.
References
Davies, Mark. 2020. The Coronavirus Corpus. https://www.english-corpora.org/corona/ (last accessed 05.17.2020).
Pedersen, Thomas Lin. 2020. ggraph
: An Implementation of Grammar of Graphics for Graphs and Networks. R package version 2.0.2. https://CRAN.R-project.org/package=ggraph
OpenEdition vous propose de citer ce billet de la manière suivante :
Guillaume Desagulier (19 mai 2020). Plotting collocation networks with R and ggraph. Around the word. Consulté le 22 janvier 2025 à l’adresse https://doi.org/10.58079/n4v1