<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/pair-programming-statistical-analyses/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://staff.math.su.se/hoehle/blog/2017/09/02/pairprogramming.html"> Theory meets practice...</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<h2>Abstract</h2>
<p>Control calculation ping-pong is the process of iteratively improving a statistical analysis by comparing results from two independent analysis approaches until agreement. We use the <code>daff</code> package to simplify the comparison of the two results and illustrate its use by a case study with two statisticians ping-ponging an analysis using dplyr and SQL, respectively.</p>
<p><center><br>
<img src="https://i2.wp.com/staff.math.su.se/hoehle/blog/figure/source/2017-09-02-pairprogramming/pingpong.png?w=456" data-recalc-dims="1"><br>
</center></p>
<p> <font color="bbbbbb">Clip art is based on work by <a rel="nofollow" href="https://thenounproject.com/term/ping-pong/655102/" target="_blank">Gan Khoon Lay</a> available under a CC BY 3.0 US license.</font></p>
<p> <a rel="nofollow" href="http://creativecommons.org/licenses/by-sa/4.0/" target="_blank"><img alt="Creative Commons License" src="https://i1.wp.com/i.creativecommons.org/l/by-sa/4.0/88x31.png?w=456&ssl=1" data-recalc-dims="1"></a> This work is licensed under a <a rel="nofollow" href="http://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Creative Commons Attribution-ShareAlike 4.0 International License</a>. The markdown+Rknitr source code of this blog is available under a <a rel="nofollow" href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank">GNU General Public License (GPL v3)</a> license from <a rel="nofollow" href="https://github.com/hoehleatsu/hoehleatsu.github.io" target="_blank"><img alt="github" src="https://i2.wp.com/staff.math.su.se/hoehle/blog/images/GitHub-Mark-32px.png?w=456" data-recalc-dims="1"></a>.</p>
<h2>Introduction</h2>
<p>If you are a statistician working in climate science, data driven journalism, official statistics, public health, economics or any related field working with <em>real</em> data, chances are that you have to perform analyses, where you know there is zero tolerance for errors. The easiest way to ensure the correctness of such an analysis is to check your results over and over again (the <strong>iterated 2-eye principle</strong>). A better approach is to pair-program the analysis by either having a colleague read through your code (the <strong>4-eye principle</strong>) or have a skilled colleague completely redo your analysis from scratch using her favorite toolchain (the <strong>2-mindset principle</strong>). Structured software development in the form of, e.g. version control and unit tests, provides valuable inspiration on how to ensure the quality of your code. However, when it comes to pair-programming analyses, surprisingly many steps remain manual. The <code>daff</code> package provides the equivalent of a <code>diff</code> statement on data frames and we shall illustrate its use by automatizing the comparison step of the control calculation ping-pong process.</p>
<h2>The Case Story</h2>
<p>Ada and Bob have to calculate their country’s quadrennial official statistics on the total income and number of employed people in <a rel="nofollow" href="https://www.destatis.de/DE/Publikationen/Qualitaetsberichte/Dienstleistungen/SonstDienstleistungsbereiche2010.pdf?__blob=publicationFile" target="_blank">fitness centers</a>. A sample of fitness centers is asked to fill out a questionnaire containing their yearly sales volume, staff costs and number of employees. The present exposition will for the sake of convenience ignore the complex survey part of the problem and just pretend that the sample corresponds to the population (complete inventory count).</p>
<h3>The Data</h3>
<p>After the questionnaire phase, the following data are available to Ada and Bob.</p>
<table>
<thead>
<tr>
<th>
Id
</th>
<th>
Version
</th>
<th>
Region
</th>
<th>
Sales Volume
</th>
<th>
Staff Costs
</th>
<th>
People
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
01
</td>
<td>
1
</td>
<td>
A
</td>
<td>
23000
</td>
<td>
10003
</td>
<td>
5
</td>
</tr>
<tr>
<td>
02
</td>
<td>
1
</td>
<td>
B
</td>
<td>
12200
</td>
<td>
7200
</td>
<td>
1
</td>
</tr>
<tr>
<td>
03
</td>
<td>
1
</td>
<td>
NA
</td>
<td>
19500
</td>
<td>
7609
</td>
<td>
2
</td>
</tr>
<tr>
<td>
04
</td>
<td>
1
</td>
<td>
A
</td>
<td>
17500
</td>
<td>
13000
</td>
<td>
3
</td>
</tr>
<tr>
<td>
05
</td>
<td>
1
</td>
<td>
B
</td>
<td>
119500
</td>
<td>
90000
</td>
<td>
NA
</td>
</tr>
<tr>
<td>
05
</td>
<td>
2
</td>
<td>
B
</td>
<td>
119500
</td>
<td>
95691
</td>
<td>
19
</td>
</tr>
<tr>
<td>
06
</td>
<td>
1
</td>
<td>
B
</td>
<td>
NA
</td>
<td>
19990
</td>
<td>
6
</td>
</tr>
<tr>
<td>
07
</td>
<td>
1
</td>
<td>
A
</td>
<td>
19123
</td>
<td>
20100
</td>
<td>
8
</td>
</tr>
<tr>
<td>
08
</td>
<td>
1
</td>
<td>
D
</td>
<td>
25000
</td>
<td>
100
</td>
<td>
NA
</td>
</tr>
<tr>
<td>
09
</td>
<td>
1
</td>
<td>
D
</td>
<td>
45860
</td>
<td>
32555
</td>
<td>
9
</td>
</tr>
<tr>
<td>
10
</td>
<td>
1
</td>
<td>
E
</td>
<td>
33020
</td>
<td>
25010
</td>
<td>
7
</td>
</tr>
</tbody>
</table>
<p>Here <code>Id</code> denotes the unique identifier of the sampled fitness center, <code>Version</code> indicates the version of a center’s questionnaire and <code>Region</code> denotes the region in which the center is located. In case a center’s questionnaire lacks information or has inconsistent information, the protocol is to get back to the center and have it send a revised questionnaire. All Ada and Bob now need to do is aggregate the data per region in order to obtain region stratified estimates of:</p>
<ul>
<li>the overall number of fitness centres</li>
<li>total sales volume</li>
<li>total staff cost</li>
<li>total number of people employed in fitness centres</li>
</ul>
<p>However, the analysis protocol instructs that only fitness centers with an annual sales volume larger than or equal to €17,500 are to be included in the analysis. Furthermore, if missing values occur, they are to be ignored in the summations. Since a lot of muscle will be angered in case of errors, Ada and Bob agree on following the 2-mindset procedure and meet after an hour to discuss their results. Here is what each of them came up with.</p>
<h3>Ada</h3>
<p>Ada loves the tidyverse and in particular <code>dplyr</code>. This is her solution:</p>
<div>
<pre><code>ada &lt;-<span> </span>fitness %>%<span> </span><span>na.omit</span>() %>%<span> </span><span>group_by</span>(Region,Id) %>%<span> </span><span>top_n</span>(<span>1</span>,Version) %>%
<span> </span><span>group_by</span>(Region) %>%
<span> </span><span>filter</span>(<span>`</span><span>Sales Volume</span><span>`</span> >=<span> </span><span>17500</span>) %>%
<span> </span><span>summarise</span>(<span>`</span><span>NoOfUnits</span><span>`</span>=<span>n</span>(),
            <span>`</span><span>Sales Volume</span><span>`</span>=<span>sum</span>(<span>`</span><span>Sales Volume</span><span>`</span>),
            <span>`</span><span>Staff Costs</span><span>`</span>=<span>sum</span>(<span>`</span><span>Staff Costs</span><span>`</span>),
            <span>People=</span><span>sum</span>(People))
ada</code></pre>
</div>
<pre><code>## # A tibble: 4 x 5
##   Region NoOfUnits `Sales Volume` `Staff Costs` People
##    </code></pre>