<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/sample_n_of-a-useful-helper-function/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://tjmahr.github.io/sample-n-groups/"> Higher Order Functions</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>Here’s the problem: I have some data with nested time series. Lots of them. It’s<br>
like there’s many, many little datasets inside my data. There are too many<br>
groups to plot all of the time series at once, so I just want to preview a<br>
handful of them.</p>
<p>For a working example, suppose we want to visualize the top 50 American female<br>
baby names over time. I start by adding up the total number of births for each<br>
name, finding the overall top 50 most populous names, and then keeping just the<br>
time series from those top names.</p>
<div>
<div>
<pre><code><span>library</span><span>(</span><span>ggplot2</span><span>)</span><span>
</span><span>library</span><span>(</span><span>dplyr</span><span>,</span><span> </span><span>warn.conflicts</span><span> </span><span>=</span><span> </span><span>FALSE</span><span>)</span><span>
</span><span>babynames</span><span> </span><span>&lt;-</span><span> </span><span>babynames</span><span>::</span><span>babynames</span><span> </span><span>%>%</span><span> </span><span>filter</span><span>(</span><span>sex</span><span> </span><span>==</span><span> </span><span>&quot;F&quot;</span><span>)</span><span>
</span><span>top50</span><span> </span><span>&lt;-</span><span> </span><span>babynames</span><span> </span><span>%>%</span><span> </span><span>group_by</span><span>(</span><span>name</span><span>)</span><span> </span><span>%>%</span><span> </span><span>summarise</span><span>(</span><span>total</span><span> </span><span>=</span><span> </span><span>sum</span><span>(</span><span>n</span><span>))</span><span> </span><span>%>%</span><span> </span><span>top_n</span><span>(</span><span>50</span><span>,</span><span> </span><span>total</span><span>)</span><span> </span><span># keep just rows in babynames that match a row in top50</span><span>
</span><span>top_names</span><span> </span><span>&lt;-</span><span> </span><span>babynames</span><span> </span><span>%>%</span><span>
</span><span>semi_join</span><span>(</span><span>top50</span><span>,</span><span> </span><span>by</span><span> </span><span>=</span><span> </span><span>&quot;name&quot;</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p>Hmm, so what does this look like?</p>
<div>
<div>
<pre><code><span>ggplot</span><span>(</span><span>top_names</span><span>)</span><span> </span><span>+</span><span> </span><span>aes</span><span>(</span><span>x</span><span> </span><span>=</span><span> </span><span>year</span><span>,</span><span> </span><span>y</span><span> </span><span>=</span><span> </span><span>n</span><span>)</span><span> </span><span>+</span><span> </span><span>geom_line</span><span>()</span><span> </span><span>+</span><span> </span><span>facet_wrap</span><span>(</span><span>&quot;name&quot;</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p><img alt="An illegible plot because too many facets are plotted" title="An illegible plot because too many facets are plotted" src="https://i1.wp.com/tjmahr.github.io/figs//2018-05-24-sample-n-groups/overplotted-1.png?w=80%25&ssl=1" data-recalc-dims="1"></p>
<p>Aaack, I can’t read anything! Can’t I just see a few of them?</p>
<p>This is a problem I face frequently, so frequently that I wrote a helper<br>
function to handle this problem: <code>sample_n_of()</code>. This is not a very clever<br>
name, but it works. Below I call the function from my personal R package<br>
and plot just the data from four names.</p>
<div>
<div>
<pre><code><span># For reproducible blogging</span><span>
</span><span>set.seed</span><span>(</span><span>20180524</span><span>)</span><span>
</span><span>top_names</span><span> </span><span>%>%</span><span> </span><span>tjmisc</span><span>::</span><span>sample_n_of</span><span>(</span><span>4</span><span>,</span><span> </span><span>name</span><span>)</span><span> </span><span>%>%</span><span> </span><span>ggplot</span><span>()</span><span> </span><span>+</span><span> </span><span>aes</span><span>(</span><span>x</span><span> </span><span>=</span><span> </span><span>year</span><span>,</span><span> </span><span>y</span><span> </span><span>=</span><span> </span><span>n</span><span>)</span><span> </span><span>+</span><span> </span><span>geom_line</span><span>()</span><span> </span><span>+</span><span> </span><span>facet_wrap</span><span>(</span><span>&quot;name&quot;</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p><img alt="A plot with four faceted timeseries" title="A plot with four faceted timeseries" src="https://i0.wp.com/tjmahr.github.io/figs//2018-05-24-sample-n-groups/sample-plot-1.png?w=80%25&ssl=1" data-recalc-dims="1"></p>
<p>In this post, I walk through how this function works. It’s not very<br>
complicated: It relies on some light tidy evaluation plus one obscure dplyr<br>
function.</p>
<h2>Working through the function</h2>
<p>As usual, let’s start by sketching out the function we want to write:</p>
<div>
<div>
<pre><code><span>sample_n_of</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>data</span><span>,</span><span> </span><span>size</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span># quote the dots</span><span>
</span><span>dots</span><span> </span><span>&lt;-</span><span> </span><span>quos</span><span>(</span><span>...</span><span>)</span><span>
</span><span># ...now make things happen...</span><span>
</span><span>}</span><span>
</span></code></pre>
</div>
</div>
<p>where <code>size</code> are the number of groups to sample and <code>...</code> are the columns names<br>
that define the groups. We use <code>quos(...)</code> to capture and quote those column<br>
names. (<a rel="nofollow" href="https://tjmahr.github.io/set-na-where-nonstandard-evaluation-use-case/" target="_blank">As I wrote before</a>,<br>
quotation is how we bottle up R code so we can deploy it for later.)</p>
<p>For interactive testing, suppose our dataset are the time series from the top 50<br>
names and we want data from a sample of 5 names. In this case, the values for<br>
the arguments would be:</p>
<div>
<div>
<pre><code><span>data</span><span> </span><span>&lt;-</span><span> </span><span>top_names</span><span>
</span><span>size</span><span> </span><span>&lt;-</span><span> </span><span>5</span><span>
</span><span>dots</span><span> </span><span>&lt;-</span><span> </span><span>quos</span><span>(</span><span>name</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p>A natural way to think about this problem is that we want to sample subgroups of<br>
the dataframe. First, we create a grouped version of the dataframe using<br>
<code>group_by()</code>. The function <code>group_by()</code> also takes a <code>...</code> argument where the<br>
dots are typically names of columns in the dataframe. We want to take the<br>
names inside of our <code>dots</code>, unquote them and plug them in to where the <code>...</code><br>
goes in <code>group_by()</code>. This is what the tidy evaluation world calls<br>
<a rel="nofollow" href="https://dplyr.tidyverse.org/articles/programming.html#unquote-splicing" target="_blank"><em>splicing</em></a>.</p>
<p>Think of splicing as doing this:</p>
<div>
<div>
<pre><code><span># Demo function that counts the number of arguments in the dots</span><span>
</span><span>count_args</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>...</span><span>)</span><span> </span><span>length</span><span>(</span><span>quos</span><span>(</span><span>...</span><span>))</span><span>
</span><span>example_dots</span><span> </span><span>&lt;-</span><span> </span><span>quos</span><span>(</span><span>var1</span><span>,</span><span> </span><span>var2</span><span>,</span><span> </span><span>var2</span><span>)</span><span>
</span><span># Splicing turns the first form into the second one</span><span>
</span><span>count_args</span><span>(</span><span>!!!</span><span> </span><span>example_dots</span><span>)</span><span>
</span><span>#> [1] 3</span><span>
</span><span>count_args</span><span>(</span><span>var1</span><span>,</span><span> </span><span>var2</span><span>,</span><span> </span><span>var2</span><span>)</span><span>
</span><span>#> [1] 3</span><span>
</span></code></pre>
</div>
</div>
<p>So, we create a grouped dataframe by splicing our dots into the <code>group_by()</code><br>
function.</p>
<div>
<div>
<pre><code><span>grouped</span><span> </span><span>&lt;-</span><span> </span><span>data</span><span> </span><span>%>%</span><span> </span><span>group_by</span><span>(</span><span>!!!</span><span> </span><span>dots</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p>There is a helper function buried in dplyr called <code>group_indices()</code> which<br>
returns the grouping index for each row in a grouped dataframe.</p>
<div>
<div>
<pre><code><span>grouped</span><span> </span><span>%>%</span><span> </span><span>tibble</span><span>::</span><span>add_column</span><span>(</span><span>group_index</span><span> </span><span>=</span><span> </span><span>group_indices</span><span>(</span><span>grouped</span><span>))</span><span> </span><span>#> # A tibble: 6,407 x 6</span><span>
</span><span>#> # Groups: name [50]</span><span>
</span><span>#> year sex name n prop group_index</span><span>
</span><span>#> </span></code></pre></div></div>