<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/writing-better-r-functions-part-four-april-17-2018/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://ibecav.github.io/betterfunctions4/"> Chuck Powell</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>In my last four posts I have been working at automating a process, that<br>
I am likely to repeat many times, by turning it into a proper <code>R<br>
function</code>. <a rel="nofollow" href="https://ibecav.github.io/betterfunctions3/" target="_blank">In my last post</a><br>
I overcame some real performance problems, combined two sub-functions<br>
into one and generally had a workable piece of code. In the final post<br>
in this series today I’ll accomplish two more important tasks. I’ll once<br>
again refactor the code to streamline it, and I’ll give the user a lot<br>
more flexibility on how they input their request.</p>
<blockquote>
<p><strong>I’m going to output all the plots in a smaller size for the benefit<br>
of you the readers. I’m doing that via RMarkdown and it won’t happen<br>
automatically for you if you download and use the code. I’ll initially<br>
be using, fig.width=4.5, fig.height=2 and later slighly larger than<br>
that</strong></p>
</blockquote>
<h2>Background and catch-up</h2>
<p>We originally started with a simple task – using <code>dplyr</code> and <code>ggplot2</code><br>
in the console. Take two of the <code>mtcars</code> variables, in this case <code>am</code><br>
and <code>cyl</code>, and conduct a cross tabulation and then plot it. Since it’s<br>
the sort of thing I’m likely to do often seemed like a good candidate to<br>
write a function for. Then I decided that I might very well want the<br>
function to allow me to input more than two variables at a time. It<br>
would be very tedious to execute the command 25 times if I had 5<br>
dependent variables and 5 independent variables and needed to fully<br>
cross them.</p>
<p>So the code grew and you can see below I address those two use cases in<br>
the code. I have very deliberately left comments in the code so that you<br>
trace the process. Roughly speaking if you follow the code here’s what’s<br>
happening.</p>
<ol>
<li>Error checking. Do we have the right packages? The right number of<br>
arguments passed in? Does the first object even exist and is it a<br>
data frame?</li>
<li>Which type of plot does the user want?</li>
<li>Some <code>if</code> logic that says in effect if both the parameters passed in<br>
as bare names are columns (variables) in the dataframe, make the<br>
plot and leave the function or <code>return</code>.</li>
<li>If the user has given us two column numbers, or vectors e.g.<br>
<code>c(8:15)</code> of column numbers as input then run them through some<br>
<code>for-loops</code> to completely cross them and print the plots.</li>
<li>If you make it that far then exit the function and <code>return</code> the list<br>
of arguments we were passed in to begin with.</li>
</ol>
<p></p>
<div>
<div>
<pre><code><span>PlotMeX</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>dataframe</span><span>,</span><span> </span><span>xwhich</span><span>,</span><span> </span><span>ywhich</span><span>,</span><span> </span><span>plottype</span><span> </span><span>=</span><span> </span><span>&quot;side&quot;</span><span>){</span><span>
</span><span># error checking</span><span>
</span><span>if</span><span> </span><span>(</span><span>!</span><span>require</span><span>(</span><span>ggplot2</span><span>))</span><span> </span><span>{</span><span>
</span><span>stop</span><span>(</span><span>&quot;Can't continue can't load ggplot2&quot;</span><span>)</span><span>
</span><span>}</span><span>
</span><span>theme_set</span><span>(</span><span>theme_bw</span><span>())</span><span>
</span><span>if</span><span> </span><span>(</span><span>!</span><span>require</span><span>(</span><span>dplyr</span><span>))</span><span> </span><span>{</span><span>
</span><span>stop</span><span>(</span><span>&quot;Can't continue can't load dplyr&quot;</span><span>)</span><span>
</span><span>}</span><span>
</span><span>if</span><span> </span><span>(</span><span>length</span><span>(</span><span>match.call</span><span>())</span><span> </span><span>&lt;=</span><span> </span><span>3</span><span>)</span><span> </span><span>{</span><span>
</span><span>stop</span><span>(</span><span>&quot;Not enough arguments passed... requires a dataframe, plus at least two variables&quot;</span><span>)</span><span>
</span><span>}</span><span>
</span><span>argList</span><span> </span><span>&lt;-</span><span> </span><span>as.list</span><span>(</span><span>match.call</span><span>()[</span><span>-1</span><span>])</span><span>
</span><span>if</span><span> </span><span>(</span><span>!</span><span>exists</span><span>(</span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>dataframe</span><span>))))</span><span> </span><span>{</span><span>
</span><span>stop</span><span>(</span><span>&quot;The first object in your list does not exist. It should be a dataframe&quot;</span><span>)</span><span>
</span><span>}</span><span>
</span><span>if</span><span> </span><span>(</span><span>!</span><span>is</span><span>(</span><span>dataframe</span><span>,</span><span> </span><span>&quot;data.frame&quot;</span><span>))</span><span> </span><span>{</span><span>
</span><span>stop</span><span>(</span><span>&quot;The first name you passed does not appear to be a data frame&quot;</span><span>)</span><span>
</span><span>}</span><span>
</span><span>switch</span><span>(</span><span>plottype</span><span>,</span><span>
</span><span>side</span><span> </span><span>=</span><span> </span><span>list</span><span>(</span><span>geom_bar</span><span>(</span><span>position</span><span>=</span><span>&quot;dodge&quot;</span><span>,</span><span> </span><span>stat</span><span>=</span><span>&quot;identity&quot;</span><span>),</span><span>
</span><span>ylab</span><span>(</span><span>&quot;Count&quot;</span><span>))</span><span> </span><span>-></span><span> </span><span>whichbar</span><span>,</span><span>
</span><span>stack</span><span> </span><span>=</span><span> </span><span>list</span><span>(</span><span>geom_bar</span><span>(</span><span>stat</span><span>=</span><span>&quot;identity&quot;</span><span>),</span><span>
</span><span>ylab</span><span>(</span><span>&quot;Count&quot;</span><span>))</span><span> </span><span>-></span><span> </span><span>whichbar</span><span>,</span><span>
</span><span>percent</span><span> </span><span>=</span><span> </span><span>list</span><span>(</span><span>geom_bar</span><span>(</span><span>stat</span><span>=</span><span>&quot;identity&quot;</span><span>,</span><span> </span><span>position</span><span>=</span><span>&quot;fill&quot;</span><span>),</span><span>
</span><span>ylab</span><span>(</span><span>&quot;Percent&quot;</span><span>))</span><span> </span><span>-></span><span> </span><span>whichbar</span><span>
</span><span>)</span><span>
</span><span># If both variables are found in the dataframe immediately print the plot</span><span>
</span><span>if</span><span> </span><span>(</span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>xwhich</span><span>))</span><span> </span><span>%in%</span><span> </span><span>names</span><span>(</span><span>dataframe</span><span>)</span><span> </span><span>&amp;</span><span> </span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>ywhich</span><span>))</span><span> </span><span>%in%</span><span> </span><span>names</span><span>(</span><span>dataframe</span><span>))</span><span> </span><span>{</span><span>
</span><span>aaa</span><span> </span><span>&lt;-</span><span> </span><span>enquo</span><span>(</span><span>xwhich</span><span>)</span><span>
</span><span>bbb</span><span> </span><span>&lt;-</span><span> </span><span>enquo</span><span>(</span><span>ywhich</span><span>)</span><span>
</span><span>xname</span><span> </span><span>&lt;-</span><span> </span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>xwhich</span><span>))</span><span>
</span><span>yname</span><span> </span><span>&lt;-</span><span> </span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>ywhich</span><span>))</span><span>
</span><span>dfname</span><span> </span><span>&lt;-</span><span> </span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>dataframe</span><span>))</span><span>
</span><span>dataframe</span><span> </span><span>%>%</span><span>
</span><span>filter</span><span>(</span><span>!</span><span>is.na</span><span>(</span><span>!!</span><span> </span><span>aaa</span><span>),</span><span> </span><span>!</span><span>is.na</span><span>(</span><span>!!</span><span> </span><span>bbb</span><span>))</span><span> </span><span>%>%</span><span>
</span><span>mutate</span><span>(</span><span>!!</span><span>quo_name</span><span>(</span><span>aaa</span><span>)</span><span> </span><span>:=</span><span> </span><span>factor</span><span>(</span><span>!!</span><span>aaa</span><span>),</span><span> </span><span>!!</span><span>quo_name</span><span>(</span><span>bbb</span><span>)</span><span> </span><span>:=</span><span> </span><span>factor</span><span>(</span><span>!!</span><span>bbb</span><span>))</span><span> </span><span>%>%</span><span>
</span><span>group_by</span><span>(</span><span>!!</span><span> </span><span>aaa</span><span>,</span><span>!!</span><span> </span><span>bbb</span><span>)</span><span> </span><span>%>%</span><span>
</span><span>count</span><span>()</span><span> </span><span>-></span><span> </span><span>tempdf</span><span>
</span><span>tempdf</span><span> </span><span>%>%</span><span>
</span><span>ggplot</span><span>(</span><span>aes_</span><span>(</span><span>fill</span><span>=</span><span>aaa</span><span>,</span><span> </span><span>y</span><span>=~</span><span>n</span><span>,</span><span> </span><span>x</span><span>=</span><span>bbb</span><span>))</span><span> </span><span>+</span><span>
</span><span>whichbar</span><span> </span><span>+</span><span>
</span><span>ggtitle</span><span>(</span><span>bquote</span><span>(</span><span>&quot;Crosstabs dataset: &quot;</span><span>*</span><span>.</span><span>(</span><span>dfname</span><span>)</span><span>*</span><span>&quot; variables &quot;</span><span>*</span><span>.</span><span>(</span><span>xname</span><span>)</span><span>~</span><span>&quot;by &quot;</span><span>*</span><span>.</span><span>(</span><span>yname</span><span>)))</span><span> </span><span>-></span><span> </span><span>p</span><span>
</span><span>return</span><span>(</span><span>p</span><span>)</span><span>
</span><span>}</span><span>
</span><span># If the user has given us integers indicating the column numbers rather than bare variable names</span><span>
</span><span># we need to build a list of what is to be plotted and then do the plotting</span><span>
</span><span># Build two lists</span><span>
</span><span>indvars</span><span>&lt;-</span><span>list</span><span>()</span><span> </span><span># create empty list to add to</span><span>
</span><span>depvars</span><span>&lt;-</span><span>list</span><span>()</span><span> </span><span># create empty list to add to</span><span>
</span><span>totalcombos</span><span> </span><span>&lt;-</span><span> </span><span>1</span><span> </span><span># keep track of where we are</span><span>
</span><span>message</span><span>(</span><span>&quot;Creating the variable pairings...&quot;</span><span>)</span><span>
</span><span>for</span><span> </span><span>(</span><span>j</span><span> </span><span>in</span><span> </span><span>seq_along</span><span>(</span><span>xwhich</span><span>))</span><span> </span><span>{</span><span>
</span><span>for</span><span> </span><span>(</span><span>k</span><span> </span><span>in</span><span> </span><span>seq_along</span><span>(</span><span>ywhich</span><span>))</span><span> </span><span>{</span><span>
</span><span>depvarsbare</span><span> </span><span>&lt;-</span><span> </span><span>as.name</span><span>(</span><span>colnames</span><span>(</span><span>dataframe</span><span>[</span><span>xwhich</span><span>[[</span><span>j</span><span>]]]))</span><span>
</span><span>indvarsbare</span><span> </span><span>&lt;-</span><span> </span><span>as.name</span><span>(</span><span>colnames</span><span>(</span><span>dataframe</span><span>[</span><span>ywhich</span><span>[[</span><span>k</span><span>]]]))</span><span>
</span><span>cat</span><span>(</span><span>&quot;Pairing #&quot;</span><span>,</span><span> </span><span>totalcombos</span><span>,</span><span> </span><span>&quot; &quot;</span><span>,</span><span> </span><span>as.name</span><span>(</span><span>colnames</span><span>(</span><span>dataframe</span><span>[</span><span>xwhich</span><span>[[</span><span>j</span><span>]]])),</span><span>
</span><span>&quot; with &quot;</span><span>,</span><span> </span><span>as.name</span><span>(</span><span>colnames</span><span>(</span><span>dataframe</span><span>[</span><span>ywhich</span><span>[[</span><span>k</span><span>]]])),</span><span> </span><span>&quot;\n&quot;</span><span>,</span><span> </span><span>sep</span><span> </span><span>=</span><span> </span><span>&quot;&quot;</span><span>)</span><span>
</span><span>aaa</span><span> </span><span>&lt;-</span><span> </span><span>enquo</span><span>(</span><span>depvarsbare</span><span>)</span><span>
</span><span>bbb</span><span> </span><span>&lt;-</span><span> </span><span>enquo</span><span>(</span><span>indvarsbare</span><span>)</span><span>
</span><span>xname</span><span> </span><span>&lt;-</span><span> </span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>depvarsbare</span><span>))</span><span>
</span><span>yname</span><span> </span><span>&lt;-</span><span> </span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>indvarsbare</span><span>))</span><span>
</span><span>dfname</span><span> </span><span>&lt;-</span><span> </span><span>deparse</span><span>(</span><span>substitute</span><span>(</span><span>dataframe</span><span>))</span><span>
</span><span>dataframe</span><span> </span><span>%>%</span><span>
</span><span>filter</span><span>(</span><span>!</span><span>is.na</span><span>(</span><span>!!</span><span> </span><span>aaa</span><span>),</span><span> </span><span>!</span><span>is.na</span><span>(</span><span>!!</span><span> </span><span>bbb</span><span>))</span><span> </span><span>%>%</span><span>
</span><span>mutate</span><span>(</span><span>!!</span><span>quo_name</span><span>(</span><span>aaa</span><span>)</span><span> </span><span>:=</span><span> </span><span>factor</span><span>(</span><span>!!</span><span>aaa</span><span>),</span><span> </span><span>!!</span><span>quo_name</span><span>(</span><span>bbb</span><span>)</span><span> </span><span>:=</span><span> </span><span>factor</span><span>(</span><span>!!</span><span>bbb</span><span>))</span><span> </span><span>%>%</span><span>
</span><span>group_by</span><span>(</span><span>!!</span><span> </span><span>aaa</span><span>,</span><span>!!</span><span> </span><span>bbb</span><span>)</span><span> </span><span>%>%</span><span>
</span><span>count</span><span>()</span><span> </span><span>-></span><span> </span><span>tempdf</span><span>
</span><span>tempdf</span><span> </span><span>%>%</span><span>
</span><span>ggplot</span><span>(</span><span>aes_</span><span>(</span><span>fill</span><span>=</span><span>aaa</span><span>,</span><span> </span><span>y</span><span>=~</span><span>n</span><span>,</span><span> </span><span>x</span><span>=</span><span>bbb</span><span>))</span><span> </span><span>+</span><span>
</span><span>whichbar</span><span> </span><span>+</span><span>
</span><span>ggtitle</span><span>(</span><span>bquote</span><span>(</span><span>&quot;Crosstabs dataset: &quot;</span><span>*</span><span>.</span><span>(</span><span>dfname</span><span>)</span><span>*</span><span>&quot; variables &quot;</span><span>*</span><span>.</span><span>(</span><span>xname</span><span>)</span><span>~</span><span>&quot;by &quot;</span><span>*</span><span>.</span><span>(</span><span>yname</span><span>)))</span><span> </span><span>-></span><span> </span><span>p</span><span>
</span><span>print</span><span>(</span><span>p</span><span>)</span><span>
</span><span>totalcombos</span><span> </span><span>&lt;-</span><span> </span><span>totalcombos</span><span> </span><span>+1</span><span>
</span><span>}</span><span>
</span><span>}</span><span>
</span><span>return</span><span>(</span><span>argList</span><span>)</span><span>
</span><span>}</span><span>
</span><span>PlotMeX</span><span>(</span><span>mtcars</span><span>,</span><span> </span><span>am</span><span>,</span><span> </span><span>cyl</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p><img alt="" src="https://i2.wp.com/ibecav.github.io/image/best1-1.png?w=456" data-recalc-dims="1"></p>
<div>
<div>
<pre><code><span># exact same thing using column numbers rather than names</span><span>
</span><span>PlotMeX</span><span>(</span><span>mtcars</span><span>,</span><span> </span><span>9</span><span>,</span><span> </span><span>2</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## Creating the variable pairings...

## Pairing #1 am with cyl
</code></pre>
</div>
</div>
<p><img alt="" src="https://i1.wp.com/ibecav.github.io/image/best1-2.png?w=456" data-recalc-dims="1"></p>
<div>
<div>
<pre><code>## $dataframe
## mtcars
## 
## $xwhich
## [1] 9
## 
## $ywhich
## [1] 2
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span># a more complex example and using a different plot type</span><span>
</span><span>PlotMeX</span><span>(</span><span>mtcars</span><span>,</span><span> </span><span>c</span><span>(</span><span>9</span><span>,</span><span> </span><span>2</span><span>),</span><span> </span><span>c</span><span>(</span><span>8</span><span>,</span><span>10</span><span>),</span><span> </span><span>&quot;percent&quot;</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## Creating the variable pairings...

## Pairing #1 am with vs
</code></pre>
</div>
</div>
<p><img alt="" src="https://i2.wp.com/ibecav.github.io/image/best1-3.png?w=456" data-recalc-dims="1"></p>
<div>
<div>
<pre><code>## Pairing #2 am with gear
</code></pre>
</div>
</div>
<p><img alt="" src="https://i1.wp.com/ibecav.github.io/image/best1-4.png?w=456" data-recalc-dims="1"></p>
<div>
<div>
<pre><code>## Pairing #3 cyl with vs
</code></pre>
</div>
</div>
<p><img alt="" src="https://i0.wp.com/ibecav.github.io/image/best1-5.png?w=456" data-recalc-dims="1"></p>
<div>
<div>
<pre><code>## Pairing #4 cyl with gear
</code></pre>
</div>
</div>
<p><img alt="" src="https://i2.wp.com/ibecav.github.io/image/best1-6.png?w=456" data-recalc-dims="1"></p>
<div>
<div>
<pre><code>## $dataframe
## mtcars
## 
## $xwhich
## c(9, 2)
## 
## $ywhich
## c(8, 10)
## 
## $plottype
## [1] &quot;percent&quot;
</code></pre>
</div>
</div>
<p>If you’d like a detailed review of the sub components it’s covered in<br>
earlier posts. In particular, if you’re not familiar with the<br>
<strong>“tricks”</strong> of working with <code>dplyr</code> and <code>ggplot2</code> inside of<br>
functions <a rel="nofollow" href="https://ibecav.github.io/betterfunctions2/" target="_blank">you may want to look at this<br>
post</a> if you’re not familiar<br>
with <code>enquo</code> or the <code>!!</code> notation.</p>
<p>As you can see from the output, it works fine as far as we can tell on<br>
the <code>mtcars</code> data set for the two cases we have identified so far. It’s<br>
not pretty and there is clearly some repetition but hey it is good<br>
enough for my needs at this point.</p>
<p>Except it isn’t…</p>
<h2>Complexity and simplicity and user choices</h2>
<p>Turns out I needed more. In my line of work it’s not uncommon to have a<br>
small number of dependent variables and a much larger number of<br>
independent variables that you want to examine. Although it’s a very<br>
contrived example let’s keep using the <code>mtcars</code> dataset (I promise I’ll<br>
use something more fun in a bit). Let’s say that I’m interested in<br>
plotting the relationship between <code>cyl</code> (the number of cylinders) and<br>
all of <code>vs</code>, <code>am</code>, <code>gear</code> &amp; <code>carb</code>. That will be a total of four plots.<br>
My current function would allow me to call <code>PlotMeX(mtcars, 2, c(8:11),<br>
&quot;percent&quot;)</code> which is all well and good. But, I know my working<br>
preferences and I know at some point I will think <code>PlotMeX(mtcars, cyl,<br>
c(8:11), &quot;percent&quot;)</code>. So lets see what happens.</p>
<div>
<div>
<pre><code><span>PlotMeX</span><span>(</span><span>mtcars</span><span>,</span><span> </span><span>cyl</span><span>,</span><span> </span><span>c</span><span>(</span><span>8</span><span>:</span><span>11</span><span>),</span><span> </span><span>&quot;percent&quot;</span><span>)</span><span>
</span><span># Quitting from lines 32-116 (betterfunctions4git.Rmd) </span><span>
</span><span># Error in PlotMeX(mtcars, cyl, c(8:11), &quot;percent&quot;) : </span><span>
</span><span># object 'cyl' not found</span><span>
</span><span># Calls: </span></code></pre></div></div>