<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/network-centrality-in-r-an-introduction/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://blog.schochastics.net/post/network-centrality-in-r-introduction/"> schochastics</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>This is the first post of a series on the concept of “network centrality” with<br>
applications in R and the package <code>netrankr</code>. There is already a rudimentary <a rel="nofollow" href="https://schochastics.github.io/netrankr/" target="_blank">tutorial</a> for the package, but I wanted to extend<br>
it to a broader tutorial for network centrality. The main focus of the<br>
blog series will be the applications in R and conceptual considerations will only play a minor role.<br>
An extended version of the series will be made available as a <a rel="nofollow" href="http://centrality.schochastics.net/" target="_blank">bookdown</a><br>
version (hopefully) early next year.</p>
<pre><code>library(igraph)
library(ggraph)
library(tidyverse)
library(netrankr)</code></pre>
<div>
<h1>Introduction</h1>
<p>Research involving networks has found its place in a lot of disciplines. From<br>
the social sciences to the natural sciences, the buzz-phrase “networks are everywhere”, is everywhere.<br>
One of the many tools to analyze networks are measures of <em>centrality</em>.<br>
In a nutshell, a measure of centrality is an index that assigns a numeric values to<br>
the nodes of the network. The higher the value, the more central the node (A more thorough introduction is given in the extended tutorial). Related ideas can already be found in <a rel="nofollow" href="http://www.asgpp.org/docs/wss/wss.html" target="_blank">Moreno’s work</a> in the 1930s, but most relevant ground work was done in the late 1940s to end 1970s. This period includes the work of <a rel="nofollow" href="http://www.analytictech.com/networks/commstruc.htm" target="_blank">Bavelas and Leavit</a> on group communication experiments and of course the seminal paper <a rel="nofollow" href="https://www.bebr.ufl.edu/sites/default/files/Centrality%20in%20Social%20Networks.pdf" target="_blank">“Centrality in Social Networks: Conceptual Clarification”</a> of Linton Freeman.<br>
If you work, or intend to work, with centrality and haven’t read it, I urge you to do so.<br>
Freeman does not mince matters and goes all-in on criticizing contemporary work of that time.<br>
He calls existing indices “nearly impossible to interpret”, “hideously complex”, “seriously flawed”, “unacceptable”, and “arbitrary and uninterpretable”. His rant culminates in the following statement.</p>
<blockquote>
<p>The several measures are often only vaguely related to the intuitive ideas they purport to index,<br>
and many are so complex that it is difficult or impossible to discover what,<br>
if anything, they are measuring.</p>
</blockquote>
<p>and he concludes:</p>
<blockquote>
<p>There is certainly no unanimity on exactly what centrality is or on its conceptual foundations,<br>
and there is very little agreement in the proper procedure for its measurement.</p>
</blockquote>
<p>So, up to the 1970s, many different centrality indices have already been proposed, which assessed different<br>
structural features of networks in order to determine central nodes. But Freeman found this<br>
measures hardly appropriate and criticized their overabundance. He argued that the vast array of indices can be<br>
boiled down to three indices: degree, betweenness and closeness. Everything else was just an awkward<br>
variant of one of these measures. Note though, the indices already existed and were not designed<br>
by him (well, betweenness kind of was; <a rel="nofollow" href="http://moreno.ss.uci.edu/23.pdf" target="_blank">PDF</a>). How was this work received?<br>
As of November 2019, the paper has been cited over 14000 times. It is by far the most<br>
the most influential work in the area of centrality. But did it change how we handle<br>
centrality today? Not really. Consider, for instance, the review paper of<br>
<a rel="nofollow" href="https://arxiv.org/pdf/1607.01134" target="_blank">Lü et al.</a> (or my <a rel="nofollow" href="http://schochastics.net/sna/periodic.html" target="_blank">periodic table</a><br>
of indices.). The list of existing centrality indices is huge and new centrality indices are<br>
still crafted on a regular basis. Thus, it seems that Freeman’s “no unanimity” statement is still<br>
relevant today. The ambiguities surrounding centrality poses many challenges for empirical work.<br>
Some of the reoccurring questions are:</p>
<ul>
<li>Which index do I choose for my analysis?</li>
<li>Should I maybe design a new one?</li>
<li>How do I validate that the chosen index is appropriate?</li>
</ul>
<p>While the answer to the second question is easy (<strong>Don’t do it!</strong>), the others are a bit more tricky.</p>
<p>In this post, I will review existing R Package that are relevant for centrality related analyses<br>
and illustrate why considering the above mentioned questions is necessary.</p>
</div>
<div>
<h1>R packages for centrality</h1>
<p>(<em>This section lists a great variety of different indices. If you are interested in the technical details,<br>
consult the help of the function and check out the references</em>)</p>
<p>There are several packages that implement centrality indices for R.<br>
Of course, there are the big network and graph packages such as<br>
<code>igraph</code>,<code>sna</code>, <code>qgraph</code>, and <code>tidygraph</code>, which are designed as general purpose packages<br>
for network analysis. Hence, they also implement some centrality indices.</p>
<p><code>igraph</code> contains the following 10 indices:</p>
<ul>
<li>degree (<code>degree()</code>)</li>
<li>weighted degree (<code>graph.strength()</code>)</li>
<li>betweenness (<code>betweenness()</code>)</li>
<li>closeness (<code>closeness()</code>)</li>
<li>eigenvector (<code>eigen_centrality()</code>)</li>
<li>alpha centrality (<code>alpha_centrality()</code>)</li>
<li>power centrality (<code>power_centrality()</code>)</li>
<li>PageRank (<code>page_rank()</code>)</li>
<li>eccentricity (<code>eccentricity()</code>)</li>
<li>hubs and authorities (<code>authority_score()</code> and <code>hub_score()</code>)</li>
<li>subgraph centrality (<code>subgraph_centrality()</code>)</li>
</ul>
<p>In most cases, parameters can be adjusted to account for directed/undirected and<br>
weighted/unweighted networks.</p>
<p>The <code>sna</code> package implements roughly the same indices together with:</p>
<ul>
<li>flow betweenness (<code>flowbet()</code>)</li>
<li>load centrality (<code>loadcent()</code>)</li>
<li>Gil-Schmidt Power Index (<code>gilschmidt()</code>)</li>
<li>information centrality (<code>infocent()</code>)</li>
<li>stress centrality (<code>stresscent()</code>)</li>
</ul>
<p><code>qgraph</code> specializes on weighted networks. It has a generic function <code>centrality_auto()</code><br>
which returns, depending on the network, the following indices:</p>
<ul>
<li>degree</li>
<li>strength (weighted degree)</li>
<li>betweenness</li>
<li>closeness</li>
</ul>
<p>The package also contains the function <code>centrality()</code>, which calculates a non-linear combination<br>
of unweighted and weighted indices using a tuning parameter <span>\(\alpha\)</span> (See <a rel="nofollow" href="https://www.sciencedirect.com/science/article/pii/S0378873310000183" target="_blank">Opsahl et al.</a>).</p>
<p>There are also some dedicated centrality packages, such as <code>centiserve</code>, <code>CINNA</code>, <code>influenceR</code> and <code>keyplayer</code>.<br>
The biggest in terms of implemented indices is currently <code>centiserve</code> with a total of 33 indices.</p>
<pre><code>as.character(lsf.str(&quot;package:centiserve&quot;))</code></pre>
<pre><code>##  [1] &quot;averagedis&quot;            &quot;barycenter&quot;           
##  [3] &quot;bottleneck&quot;            &quot;centroid&quot;             
##  [5] &quot;closeness.currentflow&quot; &quot;closeness.freeman&quot;    
##  [7] &quot;closeness.latora&quot;      &quot;closeness.residual&quot;   
##  [9] &quot;closeness.vitality&quot;    &quot;clusterrank&quot;          
## [11] &quot;communibet&quot;            &quot;communitycent&quot;        
## [13] &quot;crossclique&quot;           &quot;decay&quot;                
## [15] &quot;diffusion.degree&quot;      &quot;dmnc&quot;                 
## [17] &quot;entropy&quot;               &quot;epc&quot;                  
## [19] &quot;geokpath&quot;              &quot;hubbell&quot;              
## [21] &quot;katzcent&quot;              &quot;laplacian&quot;            
## [23] &quot;leaderrank&quot;            &quot;leverage&quot;             
## [25] &quot;lincent&quot;               &quot;lobby&quot;                
## [27] &quot;markovcent&quot;            &quot;mnc&quot;                  
## [29] &quot;pairwisedis&quot;           &quot;radiality&quot;            
## [31] &quot;salsa&quot;                 &quot;semilocal&quot;            
## [33] &quot;topocoefficient&quot;</code></pre>
<p>The package is maintained by the team behind <a rel="nofollow" href="http://www.centiserver.org/" target="_blank">centiserver</a>,<br>
the “comprehensive centrality resource and server for centralities calculation”.<br>
The website collects indices found in the literature. Currently (December 2018), it lists 235 different indices.<br>
That’s…a lot.</p>
<p><code>CINNA</code> is a relatively new package (first CRAN submission in 2017). The package description says<br>
“Functions for computing, comparing and demonstrating top informative centrality measures within a network.”<br>
Most of the indices in the package are imported from other package, such as <code>centiserve</code>.<br>
In addition, there are:</p>
<ul>
<li>Dangalchev closeness (<code>dangalchev_closeness_centrality()</code>)</li>
<li>group centrality (<code>group_centrality()</code>)</li>
<li>harmonic closeness (<code>harmonic_centrality()</code>)</li>
<li>local bridging centrality (<code>local_bridging_centrality()</code>)</li>
</ul>
<p>The function <code>calculate_centralities()</code> can be used to calculate all applicable indices<br>
to a network. The primary purpose of the package is to facilitate the choice of indices<br>
by visual and statistical tools. If you are interested in the details, see this <a rel="nofollow" href="https://www.datacamp.com/community/tutorials/centrality-network-analysis-R?tap_a=5644-dce66f&tap_s=10907-287229" target="_blank">tutorial</a><br>
and this <a rel="nofollow" href="https://cran.r-project.org/web/packages/CINNA/vignettes/CINNA.html" target="_blank">vignette</a>.</p>
<p><code>influenceR</code> and <code>keyplayer</code> are comparably small packages which implement only a small<br>
number of indices.</p>
<p><em>(For now, I deliberately leave out my package <code>netrankr</code>. While it implements a great variety of indices,<br>
it is not its primary purpose to provide a set of predefined measures. We will come to that in<br>
the next part.)</em></p>
<p>You really have the agony of choice if you look at this exhaustive list of possibilities.</p>
</div>
<div>
<h1>A small example</h1>
<p>Let us start with a fairly simple example. Consider the following two small networks.</p>
<pre><code>#data can be found here: https://github.com/schochastics/centrality_tutorial
g1 &lt;- readRDS(&quot;example_1.rds&quot;)
g2 &lt;- readRDS(&quot;example_2.rds&quot;)</code></pre>
<p><img src="https://i1.wp.com/blog.schochastics.net/post/2018-12-07-network-centrality-in-r-introduction_files/figure-html/examplenets-1.png?w=450" data-recalc-dims="1"><img src="https://i0.wp.com/blog.schochastics.net/post/2018-12-07-network-centrality-in-r-introduction_files/figure-html/examplenets-2.png?w=450" data-recalc-dims="1"></p>
<p>Now, without any empirical context, we want to determine the most central node in both networks.<br>
I wrote a small function (code at the end of this post), which calculates 35 of the above mentioned<br>
indices. We blindly apply them to both networks and see what happens.</p>
<pre><code>res1 &lt;- all_indices(g1)
res2 &lt;- all_indices(g2)</code></pre>
<p>The chart below shows a breakdown for how many indices return a specific node as the most central one.</p>
<p><img src="https://i1.wp.com/blog.schochastics.net/post/2018-12-07-network-centrality-in-r-introduction_files/figure-html/examplecent-1.png?w=450" data-recalc-dims="1"></p>
<p>In network 1, five different nodes are considered to be “the most central node” by different<br>
indices. In network 2, on the other hand, all 35 indices agree on node eleven as the most<br>
central one. The take away message from network 1 is clearly that choice matters.<br>
Depending on which index we choose, we can obtain very different results. This is hardly<br>
surprising. Why else would there be so many different indices? Five different centers are, however,<br>
a lot for such a tiny network. Network 2 paints a completely different picture.<br>
All indices agree upon the most central node. Even better (or worse?), they all<br>
induce the same ranking. We can check that with the function <code>compare_ranks()</code> in<br>
<code>netrankr</code> by counting the wrongly ordered (discordant) pairs of nodes for pairs of indices <span>\(x\)</span> and <span>\(y\)</span>.<br>
That is, <span>\(x\)</span> ranks a node <span>\(i\)</span> before <span>\(j\)</span> but <span>\(y\)</span> ranks <span>\(j\)</span> before <span>\(i\)</span>.</p>
<p>(<em>The function is unfortunately not properly vectorised yet, so we need to resort to some for looping</em>)</p>
<pre><code>discordant &lt;- rep(1,35*34)
k &lt;- 0
for(i in 1:(ncol(res2)-1)){
  for(j in (i=1):ncol(res2)){
    k &lt;- k+1
    discordant[k] &lt;- compare_ranks(res2[,i],res2[,j])$discordant
  }
}
any(discordant>0)</code></pre>
<pre><code>## [1] FALSE</code></pre>
<p>So, the indices not only agree upon the most central node, but also on the rest of the ranking!</p>
<p>
You may be wondering, why we are only looking at the ranking and not the actual values.<br>
Effectively, the values themselves don’t have any meaning. There is no such thing as<br>
a “unit of centrality”, if we look at it from a measurement perspective. For instance, we can’t say that<br>
a node is “twice as between” as another if its betweenness value is twice as high. Centrality<br>
should thus not be considered to be on an interval scale, but rather an ordinal one.<br>
This might seem like a restriction at first, but we will see later on that it facilitates<br>
many theoretical examinations.
</p>
<p>The two networks illustrate the big problem of choice. We have “only” tried 35 different<br>
indices, so we actually can’t make any conclusive statements about central nodes.<br>
After all, 35 indices can in the best case produce 35 completely different rankings.<br>
But theoretically, there are <span>\(11! =\)</span> 39,916,800 possibilities<br>
to rank the nodes of the network without allowing ties, which indices actually do.<br>
So, what if we missed hundreds of thousands of potential indices that would rank, say,<br>
node nine on top for network 1? What if those 35 indices are exactly the ones<br>
that rank node eleven on top for network 2, but no other index does that?</p>
<p>In the next example, we add some (made up) empirical context to illustrate the<br>
problem of how to validate the appropriateness of chosen indices.</p>
</div>
<div>
<h1>An almost realistic example</h1>
<p>Centrality indices are commonly used as an explanatory variable for some observed<br>
phenomenon or node attribute in a network. Let’s say we have the following abstract research question.<br>
Given a network where each node is equipped with a binary attribute, which could<br>
signify the presence or absence of some property. Can a centrality index “explain” the presence<br>
of this attribute?</p>
<pre><code>#data can be found here: https://github.com/schochastics/centrality_tutorial
g3 &lt;- readRDS(&quot;example_3.rds&quot;)</code></pre>
<p><img src="https://i0.wp.com/blog.schochastics.net/post/2018-12-07-network-centrality-in-r-introduction_files/figure-html/example3-1.png?w=450" data-recalc-dims="1"></p>
<p>Instead of 35 indices, we here focus on the more common indices.</p>
<pre><code>cent &lt;- tibble(nodes=1:vcount(g3),attr=V(g3)$attr)
cent$degree &lt;- igraph::degree(g3)
cent$betweenness &lt;- igraph::betweenness(g3)
cent$closeness &lt;- igraph::closeness(g3)
cent$eigen &lt;- igraph::eigen_centrality(g3)$vector
cent$subgraph &lt;- igraph::subgraph_centrality(g3)
cent$infocent &lt;- sna::infocent(get.adjacency(g3,sparse=F))

glimpse(cent)</code></pre>
<pre><code>## Observations: 2,224
## Variables: 8
## $ nodes       </code></pre></div>