<p><a href="https://twitter.com/drsimonj">@drsimonj</a> here to make pretty scatter plots of correlated variables with ggplot2!</p>
<p>We’ll learn how to create plots that look like this:</p>
<p><a href="https://svbtleusercontent.com/nvvxuthhlzemg.png"></a></p>
<h2><a href="https://drsimonj.svbtle.com/#data_2"> </a>Data</h2>
<p>In a data.frame <code>d</code>, we’ll simulate two correlated variables <code>a</code> and <code>b</code> of length <code>n</code>:</p>
<pre><code>set.seed(170513)
n &lt;- 200
d &lt;- data.frame(a = rnorm(n))
d$b &lt;- .4 * (d$a + rnorm(n))

head(d)
#>            a           b
#> 1 -0.9279965 -0.03795339
#> 2  0.9133158  0.21116682
#> 3  1.4516084  0.69060249
#> 4  0.5264596  0.22471694
#> 5 -1.9412516 -1.70890512
#> 6  1.4198574  0.30805526
</code></pre>
<h2><a href="https://drsimonj.svbtle.com/#basic-scatter-plot_2"> </a>Basic scatter plot</h2>
<p>Using ggplot2, the basic scatter plot (with <code>theme_minimal</code>) is created via:</p>
<pre><code>library(ggplot2)

ggplot(d, aes(a, b)) +
  geom_point() +
  theme_minimal()
</code></pre>
<p><a href="https://svbtleusercontent.com/hbhsdeonvjlvw.jpeg"></a></p>
<h2><a href="https://drsimonj.svbtle.com/#shape-and-size_2"> </a>Shape and size</h2>
<p>There are many ways to tweak the <code>shape</code> and <code>size</code> of the points. Here’s the combination I settled on for this post:</p>
<pre><code>ggplot(d, aes(a, b)) +
  geom_point(shape = 16, size = 5) +
  theme_minimal()
</code></pre>
<p><a href="https://svbtleusercontent.com/prd80u5fwjacw.jpeg"></a></p>
<h2><a href="https://drsimonj.svbtle.com/#color_2"> </a>Color</h2>
<p>We want to color the points in a way that helps to visualise the correlation between them.</p>
<p>One option is to <code>color</code> by one of the variables. For example, color by <code>a</code> (and hide legend):</p>
<pre><code>ggplot(d, aes(a, b, color = a)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE) +
  theme_minimal()
</code></pre>
<p><a href="https://svbtleusercontent.com/iijlkiihme6rlw.jpeg"></a></p>
<p>Although it’s subtle in this plot, the problem is that the color is changing as the points go from left to right. Instead, we want the color to change in a direction that characterises the correlation - diagonally in this case.</p>
<p>To do this, we can color points by the <strong>first</strong> principal component. Add it to the data frame as a variable <code>pc</code> and use it to color like so:</p>
<pre><code>d$pc &lt;- predict(prcomp(~a+b, d))[,1]

ggplot(d, aes(a, b, color = pc)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE) +
  theme_minimal()
</code></pre>
<p><a href="https://svbtleusercontent.com/acgkiogvjxz9w.jpeg"></a></p>
<p>Now we can add color, let’s pick something nice with the help of the <code>scale_color_gradient</code> functions and some nice hex codes (check out <a href="http://www.color-hex.com/">color-hex</a> for inspriation). For example:</p>
<pre><code>ggplot(d, aes(a, b, color = pc)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE) +
  theme_minimal() +
  scale_color_gradient(low = &quot;#0091ff&quot;, high = &quot;#f0650e&quot;)
</code></pre>
<p><a href="https://svbtleusercontent.com/y3ffjbkmsdmr8w.jpeg"></a></p>
<h2><a href="https://drsimonj.svbtle.com/#transparency_2"> </a>Transparency</h2>
<p>Now it’s time to get rid of those offensive mushes by adjusting the transparency with <code>alpha</code>.</p>
<p>We could adjust it to be the same for every point:</p>
<pre><code>ggplot(d, aes(a, b, color = pc)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE, alpha = .4) +
  theme_minimal() +
  scale_color_gradient(low = &quot;#0091ff&quot;, high = &quot;#f0650e&quot;)
</code></pre>
<p><a href="https://svbtleusercontent.com/s7ukxd5q8lttqa.jpeg"></a></p>
<p>This is fine most of the time. However, what if you have many points? Let’s try with 5,000 points:</p>
<pre><code># Simulate data
set.seed(170513)
n &lt;- 5000
d &lt;- data.frame(a = rnorm(n))
d$b &lt;- .4 * (d$a + rnorm(n))

# Compute first principal component
d$pc &lt;- predict(prcomp(~a+b, d))[,1]

# Plot
ggplot(d, aes(a, b, color = pc)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE, alpha = .4) +
  theme_minimal() +
  scale_color_gradient(low = &quot;#0091ff&quot;, high = &quot;#f0650e&quot;)
</code></pre>
<p><a href="https://svbtleusercontent.com/gqbax0bvlmxqyq.jpeg"></a></p>
<p>We’ve got another big mush. What if we take <code>alpha</code> down really low to .05?</p>
<pre><code>ggplot(d, aes(a, b, color = pc)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE, alpha = .05) +
  theme_minimal() +
  scale_color_gradient(low = &quot;#0091ff&quot;, high = &quot;#f0650e&quot;)
</code></pre>
<p><a href="https://svbtleusercontent.com/zliuy0syanmwow.jpeg"></a></p>
<p>Better, except it’s now hard to see extreme points that are alone in space.</p>
<p>To solve this, we’ll map <code>alpha</code> to the <strong>inverse</strong> point density. That is, turn down <code>alpha</code> wherever there are lots of points! The trick is to use bivariate density, which can be added as follows:</p>
<pre><code># Add bivariate density for each point
d$density &lt;- fields::interp.surface(
  MASS::kde2d(d$a, d$b), d[,c(&quot;a&quot;, &quot;b&quot;)])
</code></pre>
<p>Now plot with <code>alpha</code> mapped to <code>1/density</code>:</p>
<pre><code>ggplot(d, aes(a, b, color = pc, alpha = 1/density)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE) +
  theme_minimal() +
  scale_color_gradient(low = &quot;#0091ff&quot;, high = &quot;#f0650e&quot;)
</code></pre>
<p><a href="https://svbtleusercontent.com/cez6zj7g3gnpfa.jpeg"></a></p>
<p>You can see that distant points are now too vibrant. Our final fix is to use <code>scale_alpha</code> to tweak the alpha range. By default, this range is 0 to 1, making the most distant points have an alpha close to 1. Let’s restrict it to something better:</p>
<pre><code>ggplot(d, aes(a, b, color = pc, alpha = 1/density)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE) +
  theme_minimal() +
  scale_color_gradient(low = &quot;#0091ff&quot;, high = &quot;#f0650e&quot;) +
  scale_alpha(range = c(.05, .25))
</code></pre>
<p><a href="https://svbtleusercontent.com/hlqpu7p0exdqg.jpeg"></a></p>
<p>Much better! No more mushy patches or lost points.</p>
<h2><a href="https://drsimonj.svbtle.com/#bringing-it-together_2"> </a>Bringing it together</h2>
<p>Here’s a complete example with new data and colors:</p>
<pre><code># Simulate data
set.seed(170513)
n &lt;- 2000
d &lt;- data.frame(a = rnorm(n))
d$b &lt;- -(d$a + rnorm(n, sd = 2))

# Add first principal component
d$pc &lt;- predict(prcomp(~a+b, d))[,1]

# Add density for each point
d$density &lt;- fields::interp.surface(
  MASS::kde2d(d$a, d$b), d[,c(&quot;a&quot;, &quot;b&quot;)])

# Plot
ggplot(d, aes(a, b, color = pc, alpha = 1/density)) +
  geom_point(shape = 16, size = 5, show.legend = FALSE) +
  theme_minimal() +
  scale_color_gradient(low = &quot;#32aeff&quot;, high = &quot;#f2aeff&quot;) +
  scale_alpha(range = c(.25, .6))
</code></pre>
<p><a href="https://svbtleusercontent.com/3svtcqfu4ynitg.png"></a></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>