<p><a href="https://twitter.com/drsimonj">@drsimonj</a> here to show you how to use ggraph and corrr to create correlation network plots like these:</p>
<p><a href="https://svbtleusercontent.com/vkzxys0qozbww.jpeg"></a></p>
<p><a href="https://svbtleusercontent.com/tzrjm2atvoy3a.jpeg"></a></p>
<h2><a href="https://drsimonj.svbtle.com/#ggraph-and-corrr_2"> </a>ggraph and corrr</h2>
<p>The <a href="https://github.com/thomasp85/ggraph">ggraph</a> package by <a href="https://twitter.com/thomasp85">Thomas Lin Pedersen</a>, has just been published on CRAN and it’s so hot right now! What does it do?</p>
<blockquote><p>“ggraph is an extension of ggplot2 aimed at supporting relational data structures such as networks, graphs, and trees.”</p>
</blockquote>
<p>A relational metric I work with a lot is correlations. Becuase of this, I created the <a href="https://github.com/drsimonj/corrr">corrr</a> package, which helps to explore correlations by leveraging data frames and tidyverse tools rather than matrices.</p>
<p>So…</p>
<ul><li> corrr creates relational data frames of correlations intended to work with tidyverse tools like ggplot2.</li>
<li> ggraph extends ggplot2 to help plot relational structures.</li>
</ul><p>Seems like a perfect match!</p>
<h2><a href="https://drsimonj.svbtle.com/#libraries_2"> </a>Libraries</h2>
<p>We’ll be using the following libraries:</p>
<pre><code>library(tidyverse)
library(corrr)
library(igraph)
library(ggraph)
</code></pre>
<h2><a href="https://drsimonj.svbtle.com/#basic-approach_2"> </a>Basic approach</h2>
<p>Given a data frame <code>d</code> of numeric variables for which we want to plot the correlations in a network, here’s a basic approach:</p>
<pre><code># Create a tidy data frame of correlations
tidy_cors &lt;- d %>% 
  correlate() %>% 
  stretch()

# Convert correlations stronger than some value
# to an undirected graph object
graph_cors &lt;- tidy_cors %>% 
  filter(abs(r) > `VALUE_BETWEEN_0_AND_1`) %>% 
  graph_from_data_frame(directed = FALSE)

# Plot
ggraph(graph_cors) +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name), repel = TRUE) +
  theme_graph()
</code></pre>
<h2><a href="https://drsimonj.svbtle.com/#example-1-correlating-variables-in-mtcars_2"> </a>Example 1: correlating variables in mtcars</h2>
<p>Let’s follow this for the <code>mtcars</code> data set. By default, all variables are numeric, so we don’t need to do any pre-processing.</p>
<p>We first create a tidy data frame of correlations to be converted to a graph object. We do this with two corrr functions: <code>correlate()</code>, to create a correlation data frame, and <code>stretch()</code>, to convert it into a tidy format:</p>
<pre><code>tidy_cors &lt;- mtcars %>% 
  correlate() %>% 
  stretch()

tidy_cors
#> # A tibble: 121 × 3
#>        x     y          r
#>    &lt;chr> &lt;chr>      &lt;dbl>
#> 1    mpg   mpg         NA
#> 2    mpg   cyl -0.8521620
#> 3    mpg  disp -0.8475514
#> 4    mpg    hp -0.7761684
#> 5    mpg  drat  0.6811719
#> 6    mpg    wt -0.8676594
#> 7    mpg  qsec  0.4186840
#> 8    mpg    vs  0.6640389
#> 9    mpg    am  0.5998324
#> 10   mpg  gear  0.4802848
#> # ... with 111 more rows
</code></pre>
<p>Next, we convert these values to an undirected graph object. The graph is undirected because correlations do not have a direction. For example, correlations do not assume cause or effect. This is done using the <a href="http://igraph.org/redirect.html">igraph</a> function, <code>graph_from_data_frame(directed = FALSE)</code>.</p>
<p>Because, we typically don’t want to see ALL of the correlations, we first <code>filter()</code> out any correlations with an absolute value less than some threshold. For example, let’s include correlations that are .3 or stronger (positive OR negative):</p>
<pre><code>graph_cors &lt;- tidy_cors %>%
  filter(abs(r) > .3) %>%
  graph_from_data_frame(directed = FALSE)

graph_cors
#> IGRAPH UN-- 11 88 -- 
#> + attr: name (v/c), r (e/n)
#> + edges (vertex names):
#>  [1] mpg --cyl  mpg --disp mpg --hp   mpg --drat mpg --wt   mpg --qsec
#>  [7] mpg --vs   mpg --am   mpg --gear mpg --carb mpg --cyl  cyl --disp
#> [13] cyl --hp   cyl --drat cyl --wt   cyl --qsec cyl --vs   cyl --am  
#> [19] cyl --gear cyl --carb mpg --disp cyl --disp disp--hp   disp--drat
#> [25] disp--wt   disp--qsec disp--vs   disp--am   disp--gear disp--carb
#> [31] mpg --hp   cyl --hp   disp--hp   hp  --drat hp  --wt   hp  --qsec
#> [37] hp  --vs   hp  --carb mpg --drat cyl --drat disp--drat hp  --drat
#> [43] drat--wt   drat--vs   drat--am   drat--gear mpg --wt   cyl --wt  
#> + ... omitted several edges
</code></pre>
<p>We now plot this object with ggraph. Here’s a basic plot:</p>
<pre><code>ggraph(graph_cors) +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name))
</code></pre>
<p><a href="https://svbtleusercontent.com/gwlcvahoprqpw.jpeg"></a></p>
<p>and here’s one that’s polished to look nicer:</p>
<pre><code>ggraph(graph_cors) +
  geom_edge_link(aes(edge_alpha = abs(r), edge_width = abs(r), color = r)) +
  guides(edge_alpha = &quot;none&quot;, edge_width = &quot;none&quot;) +
  scale_edge_colour_gradientn(limits = c(-1, 1), colors = c(&quot;firebrick2&quot;, &quot;dodgerblue2&quot;)) +
  geom_node_point(color = &quot;white&quot;, size = 5) +
  geom_node_text(aes(label = name), repel = TRUE) +
  theme_graph() +
  labs(title = &quot;Correlations between car variables&quot;)
</code></pre>
<p><a href="https://svbtleusercontent.com/3yezgp3udlv9a.jpeg"></a></p>
<p>For an excellent resource on how these graphing parts work, Thomas has some great posts like <a href="http://www.data-imaginist.com/2017/Announcing-ggraph/">this one</a> on his blog, <a href="http://www.data-imaginist.com/">data-imaginist.com</a>.</p>
<h2><a href="https://drsimonj.svbtle.com/#example-2-countries-with-similar-drinking-hab_2"> </a>Example 2: countries with similar drinking habits</h2>
<p>This example requires some data pre-processing, and we’ll only look at strong positive correlations.</p>
<p>I’m about to finish my job in Australia and am looking for work elsewhere. As is typical of Australians, a friend suggested I look for work in countries where people drink like us. This is probably not the best approach for job hunting, but it makes for a fun example!</p>
<p>Conveniently, <a href="https://fivethirtyeight.com/datalab/dear-mona-followup-where-do-people-drink-the-most-beer-wine-and-spirits/">FiveThirtyEight</a> did a story on the amount of beer, wine, and spirits, drunk by countries around the world. Even more conveniently, the data is included in the <a href="https://github.com/rudeboybert/fivethirtyeight">fivethirtyeight</a> package! Let’s take a look:</p>
<pre><code>library(fivethirtyeight)

drinks
#> # A tibble: 193 × 5
#>              country beer_servings spirit_servings wine_servings
#>                &lt;chr>         &lt;int>           &lt;int>         &lt;int>
#> 1        Afghanistan             0               0             0
#> 2            Albania            89             132            54
#> 3            Algeria            25               0            14
#> 4            Andorra           245             138           312
#> 5             Angola           217              57            45
#> 6  Antigua &amp; Barbuda           102             128            45
#> 7          Argentina           193              25           221
#> 8            Armenia            21             179            11
#> 9          Australia           261              72           212
#> 10           Austria           279              75           191
#> # ... with 183 more rows, and 1 more variables:
#> #   total_litres_of_pure_alcohol &lt;dbl>
</code></pre>
<p>I wanted to find which countries in Europe and the Americas had similar patterns of beer, wine, and spirit drinking, and where Australia fit in. Using the <a href="https://github.com/vincentarelbundock/countrycode">countrycode</a> package to bind continent information and find the countries I’m interested, let’s get this data into shape for correlations:</p>
<pre><code>library(countrycode)

# Get relevant data for Australia and countries
# in Europe and the Americas
d &lt;- drinks %>% 
  mutate(continent = countrycode(country, &quot;country.name&quot;, &quot;continent&quot;)) %>% 
  filter(continent %in% c(&quot;Europe&quot;, &quot;Americas&quot;) | country == &quot;Australia&quot;) %>% 
  select(country, contains(&quot;servings&quot;))

# Scale data to examine relative amounts,
# rather than absolute volume, of
# beer, wine and spirits drunk
scaled_data &lt;- d %>% mutate_if(is.numeric, scale)

# Tidy the data
tidy_data &lt;- scaled_data %>% 
  gather(type, litres, -country) %>% 
  drop_na() %>% 
  group_by(country) %>% 
  filter(sd(litres) > 0) %>% 
  ungroup()

# Widen into suitable format for correlations
wide_data &lt;- tidy_data %>% 
  spread(country, litres) %>% 
  select(-type)

wide_data
#> # A tibble: 3 × 78
#>      Albania     Andorra `Antigua &amp; Barbuda`   Argentina  Australia
#> *      &lt;dbl>       &lt;dbl>               &lt;dbl>       &lt;dbl>      &lt;dbl>
#> 1 -1.0798330  0.68479335          -0.9327808  0.09658458  0.8657807
#> 2 -0.1146881 -0.04560957          -0.1607405 -1.34658934 -0.8054739
#> 3 -0.4577044  2.16796347          -0.5492974  1.24185582  1.1502628
#> # ... with 73 more variables: Austria &lt;dbl>, Bahamas &lt;dbl>,
#> #   Barbados &lt;dbl>, Belarus &lt;dbl>, Belgium &lt;dbl>, Belize &lt;dbl>,
#> #   Bolivia &lt;dbl>, `Bosnia-Herzegovina` &lt;dbl>, Brazil &lt;dbl>,
#> #   Bulgaria &lt;dbl>, Canada &lt;dbl>, Chile &lt;dbl>, Colombia &lt;dbl>, `Costa
#> #   Rica` &lt;dbl>, Croatia &lt;dbl>, Cuba &lt;dbl>, `Czech Republic` &lt;dbl>,
#> #   Denmark &lt;dbl>, Dominica &lt;dbl>, `Dominican Republic` &lt;dbl>,
#> #   Ecuador &lt;dbl>, `El Salvador` &lt;dbl>, Estonia &lt;dbl>, Finland &lt;dbl>,
#> #   France &lt;dbl>, Germany &lt;dbl>, Greece &lt;dbl>, Grenada &lt;dbl>,
#> #   Guatemala &lt;dbl>, Guyana &lt;dbl>, Haiti &lt;dbl>, Honduras &lt;dbl>,
#> #   Hungary &lt;dbl>, Iceland &lt;dbl>, Ireland &lt;dbl>, Italy &lt;dbl>,
#> #   Jamaica &lt;dbl>, Latvia &lt;dbl>, Lithuania &lt;dbl>, Luxembourg &lt;dbl>,
#> #   Macedonia &lt;dbl>, Malta &lt;dbl>, Mexico &lt;dbl>, Moldova &lt;dbl>,
#> #   Monaco &lt;dbl>, Montenegro &lt;dbl>, Netherlands &lt;dbl>, Nicaragua &lt;dbl>,
#> #   Norway &lt;dbl>, Panama &lt;dbl>, Paraguay &lt;dbl>, Peru &lt;dbl>, Poland &lt;dbl>,
#> #   Portugal &lt;dbl>, Romania &lt;dbl>, `Russian Federation` &lt;dbl>, `San
#> #   Marino` &lt;dbl>, Serbia &lt;dbl>, Slovakia &lt;dbl>, Slovenia &lt;dbl>,
#> #   Spain &lt;dbl>, `St. Kitts &amp; Nevis` &lt;dbl>, `St. Lucia` &lt;dbl>, `St.
#> #   Vincent &amp; the Grenadines` &lt;dbl>, Suriname &lt;dbl>, Sweden &lt;dbl>,
#> #   Switzerland &lt;dbl>, `Trinidad &amp; Tobago` &lt;dbl>, Ukraine &lt;dbl>, `United
#> #   Kingdom` &lt;dbl>, Uruguay &lt;dbl>, USA &lt;dbl>, Venezuela &lt;dbl>
</code></pre>
<p>This data includes the z-scores of the amount of beer, wine and spirits drunk in each country.</p>
<p>We can now go ahead with our standard approach. Because I’m only interested in which countries are really similar, we’ll <code>filter(r > .9)</code>:</p>
<p><a href="https://svbtleusercontent.com/ledio2c1v3ql2q.jpeg"></a></p>
<p>It looks like the drinking behaviour of these countries group into three clusters. I’ll leave it to you do think about what defines those clusters!</p>
<p>The important thing for my friend: Australia appears in the top left cluster along with many West and North European countries like the United Kingdom, France, Netherlands, Norway, and Sweden. Perhaps this is the region I should look for work if I want to keep up Aussie drinking habits!</p>
<h2><a href="https://drsimonj.svbtle.com/#sign-off_2"> </a>Sign off</h2>
<p>Thanks for reading and I hope this was useful for you.</p>
<p>For updates of recent blog posts, follow <a href="https://twitter.com/drsimonj">@drsimonj</a> on Twitter, or email me at <a href="mailto:drsimonjackson@gmail.com">drsimonjackson@gmail.com</a> to get in touch.</p>
<p>If you’d like the code that produced this blog, check out the <a href="https://github.com/drsimonj/blogR">blogR GitHub repository</a>.</p>