651 lines
34 KiB
HTML
651 lines
34 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html>
|
||
|
||
<head>
|
||
|
||
<meta charset="utf-8" />
|
||
<meta name="generator" content="pandoc" />
|
||
<meta http-equiv="X-UA-Compatible" content="IE=EDGE" />
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
|
||
|
||
<title>Using ggplot2 in packages</title>
|
||
|
||
<script>// Pandoc 2.9 adds attributes on both header and div. We remove the former (to
|
||
// be compatible with the behavior of Pandoc < 2.8).
|
||
document.addEventListener('DOMContentLoaded', function(e) {
|
||
var hs = document.querySelectorAll("div.section[class*='level'] > :first-child");
|
||
var i, h, a;
|
||
for (i = 0; i < hs.length; i++) {
|
||
h = hs[i];
|
||
if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6
|
||
a = h.attributes;
|
||
while (a.length > 0) h.removeAttribute(a[0].name);
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style type="text/css">
|
||
code{white-space: pre-wrap;}
|
||
span.smallcaps{font-variant: small-caps;}
|
||
span.underline{text-decoration: underline;}
|
||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||
ul.task-list{list-style: none;}
|
||
</style>
|
||
|
||
|
||
|
||
<style type="text/css">
|
||
code {
|
||
white-space: pre;
|
||
}
|
||
.sourceCode {
|
||
overflow: visible;
|
||
}
|
||
</style>
|
||
<style type="text/css" data-origin="pandoc">
|
||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||
.sourceCode { overflow: visible; }
|
||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||
div.sourceCode { margin: 1em 0; }
|
||
pre.sourceCode { margin: 0; }
|
||
@media screen {
|
||
div.sourceCode { overflow: auto; }
|
||
}
|
||
@media print {
|
||
pre > code.sourceCode { white-space: pre-wrap; }
|
||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||
}
|
||
pre.numberSource code
|
||
{ counter-reset: source-line 0; }
|
||
pre.numberSource code > span
|
||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||
pre.numberSource code > span > a:first-child::before
|
||
{ content: counter(source-line);
|
||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||
border: none; display: inline-block;
|
||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||
-khtml-user-select: none; -moz-user-select: none;
|
||
-ms-user-select: none; user-select: none;
|
||
padding: 0 4px; width: 4em;
|
||
color: #aaaaaa;
|
||
}
|
||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||
div.sourceCode
|
||
{ }
|
||
@media screen {
|
||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||
}
|
||
code span.al { color: #ff0000; font-weight: bold; }
|
||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; }
|
||
code span.at { color: #7d9029; }
|
||
code span.bn { color: #40a070; }
|
||
code span.bu { color: #008000; }
|
||
code span.cf { color: #007020; font-weight: bold; }
|
||
code span.ch { color: #4070a0; }
|
||
code span.cn { color: #880000; }
|
||
code span.co { color: #60a0b0; font-style: italic; }
|
||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; }
|
||
code span.do { color: #ba2121; font-style: italic; }
|
||
code span.dt { color: #902000; }
|
||
code span.dv { color: #40a070; }
|
||
code span.er { color: #ff0000; font-weight: bold; }
|
||
code span.ex { }
|
||
code span.fl { color: #40a070; }
|
||
code span.fu { color: #06287e; }
|
||
code span.im { color: #008000; font-weight: bold; }
|
||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; }
|
||
code span.kw { color: #007020; font-weight: bold; }
|
||
code span.op { color: #666666; }
|
||
code span.ot { color: #007020; }
|
||
code span.pp { color: #bc7a00; }
|
||
code span.sc { color: #4070a0; }
|
||
code span.ss { color: #bb6688; }
|
||
code span.st { color: #4070a0; }
|
||
code span.va { color: #19177c; }
|
||
code span.vs { color: #4070a0; }
|
||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; }
|
||
</style>
|
||
<script>
|
||
// apply pandoc div.sourceCode style to pre.sourceCode instead
|
||
(function() {
|
||
var sheets = document.styleSheets;
|
||
for (var i = 0; i < sheets.length; i++) {
|
||
if (sheets[i].ownerNode.dataset["origin"] !== "pandoc") continue;
|
||
try { var rules = sheets[i].cssRules; } catch (e) { continue; }
|
||
var j = 0;
|
||
while (j < rules.length) {
|
||
var rule = rules[j];
|
||
// check if there is a div.sourceCode rule
|
||
if (rule.type !== rule.STYLE_RULE || rule.selectorText !== "div.sourceCode") {
|
||
j++;
|
||
continue;
|
||
}
|
||
var style = rule.style.cssText;
|
||
// check if color or background-color is set
|
||
if (rule.style.color === '' && rule.style.backgroundColor === '') {
|
||
j++;
|
||
continue;
|
||
}
|
||
// replace div.sourceCode by a pre.sourceCode rule
|
||
sheets[i].deleteRule(j);
|
||
sheets[i].insertRule('pre.sourceCode{' + style + '}', j);
|
||
}
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
|
||
|
||
|
||
<style type="text/css">body {
|
||
background-color: #fff;
|
||
margin: 1em auto;
|
||
max-width: 700px;
|
||
overflow: visible;
|
||
padding-left: 2em;
|
||
padding-right: 2em;
|
||
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||
font-size: 14px;
|
||
line-height: 1.35;
|
||
}
|
||
#TOC {
|
||
clear: both;
|
||
margin: 0 0 10px 10px;
|
||
padding: 4px;
|
||
width: 400px;
|
||
border: 1px solid #CCCCCC;
|
||
border-radius: 5px;
|
||
background-color: #f6f6f6;
|
||
font-size: 13px;
|
||
line-height: 1.3;
|
||
}
|
||
#TOC .toctitle {
|
||
font-weight: bold;
|
||
font-size: 15px;
|
||
margin-left: 5px;
|
||
}
|
||
#TOC ul {
|
||
padding-left: 40px;
|
||
margin-left: -1.5em;
|
||
margin-top: 5px;
|
||
margin-bottom: 5px;
|
||
}
|
||
#TOC ul ul {
|
||
margin-left: -2em;
|
||
}
|
||
#TOC li {
|
||
line-height: 16px;
|
||
}
|
||
table {
|
||
margin: 1em auto;
|
||
border-width: 1px;
|
||
border-color: #DDDDDD;
|
||
border-style: outset;
|
||
border-collapse: collapse;
|
||
}
|
||
table th {
|
||
border-width: 2px;
|
||
padding: 5px;
|
||
border-style: inset;
|
||
}
|
||
table td {
|
||
border-width: 1px;
|
||
border-style: inset;
|
||
line-height: 18px;
|
||
padding: 5px 5px;
|
||
}
|
||
table, table th, table td {
|
||
border-left-style: none;
|
||
border-right-style: none;
|
||
}
|
||
table thead, table tr.even {
|
||
background-color: #f7f7f7;
|
||
}
|
||
p {
|
||
margin: 0.5em 0;
|
||
}
|
||
blockquote {
|
||
background-color: #f6f6f6;
|
||
padding: 0.25em 0.75em;
|
||
}
|
||
hr {
|
||
border-style: solid;
|
||
border: none;
|
||
border-top: 1px solid #777;
|
||
margin: 28px 0;
|
||
}
|
||
dl {
|
||
margin-left: 0;
|
||
}
|
||
dl dd {
|
||
margin-bottom: 13px;
|
||
margin-left: 13px;
|
||
}
|
||
dl dt {
|
||
font-weight: bold;
|
||
}
|
||
ul {
|
||
margin-top: 0;
|
||
}
|
||
ul li {
|
||
list-style: circle outside;
|
||
}
|
||
ul ul {
|
||
margin-bottom: 0;
|
||
}
|
||
pre, code {
|
||
background-color: #f7f7f7;
|
||
border-radius: 3px;
|
||
color: #333;
|
||
white-space: pre-wrap;
|
||
}
|
||
pre {
|
||
border-radius: 3px;
|
||
margin: 5px 0px 10px 0px;
|
||
padding: 10px;
|
||
}
|
||
pre:not([class]) {
|
||
background-color: #f7f7f7;
|
||
}
|
||
code {
|
||
font-family: Consolas, Monaco, 'Courier New', monospace;
|
||
font-size: 85%;
|
||
}
|
||
p > code, li > code {
|
||
padding: 2px 0px;
|
||
}
|
||
div.figure {
|
||
text-align: center;
|
||
}
|
||
img {
|
||
background-color: #FFFFFF;
|
||
padding: 2px;
|
||
border: 1px solid #DDDDDD;
|
||
border-radius: 3px;
|
||
border: 1px solid #CCCCCC;
|
||
margin: 0 5px;
|
||
}
|
||
h1 {
|
||
margin-top: 0;
|
||
font-size: 35px;
|
||
line-height: 40px;
|
||
}
|
||
h2 {
|
||
border-bottom: 4px solid #f7f7f7;
|
||
padding-top: 10px;
|
||
padding-bottom: 2px;
|
||
font-size: 145%;
|
||
}
|
||
h3 {
|
||
border-bottom: 2px solid #f7f7f7;
|
||
padding-top: 10px;
|
||
font-size: 120%;
|
||
}
|
||
h4 {
|
||
border-bottom: 1px solid #f7f7f7;
|
||
margin-left: 8px;
|
||
font-size: 105%;
|
||
}
|
||
h5, h6 {
|
||
border-bottom: 1px solid #ccc;
|
||
font-size: 105%;
|
||
}
|
||
a {
|
||
color: #0033dd;
|
||
text-decoration: none;
|
||
}
|
||
a:hover {
|
||
color: #6666ff; }
|
||
a:visited {
|
||
color: #800080; }
|
||
a:visited:hover {
|
||
color: #BB00BB; }
|
||
a[href^="http:"] {
|
||
text-decoration: underline; }
|
||
a[href^="https:"] {
|
||
text-decoration: underline; }
|
||
|
||
code > span.kw { color: #555; font-weight: bold; }
|
||
code > span.dt { color: #902000; }
|
||
code > span.dv { color: #40a070; }
|
||
code > span.bn { color: #d14; }
|
||
code > span.fl { color: #d14; }
|
||
code > span.ch { color: #d14; }
|
||
code > span.st { color: #d14; }
|
||
code > span.co { color: #888888; font-style: italic; }
|
||
code > span.ot { color: #007020; }
|
||
code > span.al { color: #ff0000; font-weight: bold; }
|
||
code > span.fu { color: #900; font-weight: bold; }
|
||
code > span.er { color: #a61717; background-color: #e3d2d2; }
|
||
</style>
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
<body>
|
||
|
||
|
||
|
||
|
||
<h1 class="title toc-ignore">Using ggplot2 in packages</h1>
|
||
|
||
|
||
|
||
<p>This vignette is intended for package developers who use ggplot2
|
||
within their package code. As of this writing, this includes over 2,000
|
||
packages on CRAN and many more elsewhere! Programming with ggplot2
|
||
within a package adds several constraints, particularly if you would
|
||
like to submit the package to CRAN. In particular, programming within an
|
||
R package changes the way you refer to functions from ggplot2 and how
|
||
you use ggplot2’s non-standard evaluation within <code>aes()</code> and
|
||
<code>vars()</code>.</p>
|
||
<div id="referring-to-ggplot2-functions" class="section level2">
|
||
<h2>Referring to ggplot2 functions</h2>
|
||
<p>As with any function from another package, you will have to list
|
||
ggplot2 in your <code>DESCRIPTION</code> under <code>Imports</code> and
|
||
refer to its functions using <code>::</code> (e.g.,
|
||
<code>ggplot2::function_name</code>):</p>
|
||
<div class="sourceCode" id="cb1"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb1-1"><a href="#cb1-1" tabindex="-1"></a>mpg_drv_summary <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb1-2"><a href="#cb1-2" tabindex="-1"></a> ggplot2<span class="sc">::</span><span class="fu">ggplot</span>(ggplot2<span class="sc">::</span>mpg) <span class="sc">+</span> </span>
|
||
<span id="cb1-3"><a href="#cb1-3" tabindex="-1"></a> ggplot2<span class="sc">::</span><span class="fu">geom_bar</span>(ggplot2<span class="sc">::</span><span class="fu">aes</span>(<span class="at">x =</span> .data<span class="sc">$</span>drv)) <span class="sc">+</span> </span>
|
||
<span id="cb1-4"><a href="#cb1-4" tabindex="-1"></a> ggplot2<span class="sc">::</span><span class="fu">coord_flip</span>()</span>
|
||
<span id="cb1-5"><a href="#cb1-5" tabindex="-1"></a>}</span></code></pre></div>
|
||
<p>If you use ggplot2 functions frequently, you may wish to import one
|
||
or more functions from ggplot2 into your <code>NAMESPACE</code>. If you
|
||
use <a href="https://cran.r-project.org/package=roxygen2">roxygen2</a>,
|
||
you can include
|
||
<code>#' @importFrom ggplot2 <one or more object names></code> in
|
||
any roxygen comment block (this will not work for datasets like
|
||
<code>mpg</code>).</p>
|
||
<div class="sourceCode" id="cb2"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb2-1"><a href="#cb2-1" tabindex="-1"></a><span class="co">#' @importFrom ggplot2 ggplot aes geom_bar coord_flip</span></span>
|
||
<span id="cb2-2"><a href="#cb2-2" tabindex="-1"></a>mpg_drv_summary <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb2-3"><a href="#cb2-3" tabindex="-1"></a> <span class="fu">ggplot</span>(ggplot2<span class="sc">::</span>mpg) <span class="sc">+</span> </span>
|
||
<span id="cb2-4"><a href="#cb2-4" tabindex="-1"></a> <span class="fu">geom_bar</span>(<span class="fu">aes</span>(<span class="at">x =</span> drv)) <span class="sc">+</span> </span>
|
||
<span id="cb2-5"><a href="#cb2-5" tabindex="-1"></a> <span class="fu">coord_flip</span>()</span>
|
||
<span id="cb2-6"><a href="#cb2-6" tabindex="-1"></a>}</span></code></pre></div>
|
||
<p>Even if you use many ggplot2 functions in your package, it is unwise
|
||
to use ggplot2 in <code>Depends</code> or import the entire package into
|
||
your <code>NAMESPACE</code> (e.g. with <code>#' @import ggplot2</code>).
|
||
Using ggplot2 in <code>Depends</code> will attach ggplot2 when your
|
||
package is attached, which includes when your package is tested. This
|
||
makes it difficult to ensure that others can use the functions in your
|
||
package without attaching it (i.e., using <code>::</code>). Similarly,
|
||
importing all 450 of ggplot2’s exported objects into your namespace
|
||
makes it difficult to separate the responsibility of your package and
|
||
the responsibility of ggplot2, in addition to making it difficult for
|
||
readers of your code to figure out where functions are coming from!</p>
|
||
</div>
|
||
<div id="using-aes-and-vars-in-a-package-function" class="section level2">
|
||
<h2>Using <code>aes()</code> and <code>vars()</code> in a package
|
||
function</h2>
|
||
<p>To create any graphic using ggplot2 you will probably need to use
|
||
<code>aes()</code> at least once. If your graphic uses facets, you might
|
||
be using <code>vars()</code> to refer to columns in the plot/layer data.
|
||
Both of these functions use non-standard evaluation, so if you try to
|
||
use them in a function within a package they will result in a CMD check
|
||
note:</p>
|
||
<div class="sourceCode" id="cb3"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb3-1"><a href="#cb3-1" tabindex="-1"></a>mpg_drv_summary <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb3-2"><a href="#cb3-2" tabindex="-1"></a> <span class="fu">ggplot</span>(ggplot2<span class="sc">::</span>mpg) <span class="sc">+</span> </span>
|
||
<span id="cb3-3"><a href="#cb3-3" tabindex="-1"></a> <span class="fu">geom_bar</span>(<span class="fu">aes</span>(<span class="at">y =</span> drv)) <span class="sc">+</span> </span>
|
||
<span id="cb3-4"><a href="#cb3-4" tabindex="-1"></a> <span class="fu">facet_wrap</span>(<span class="fu">vars</span>(year))</span>
|
||
<span id="cb3-5"><a href="#cb3-5" tabindex="-1"></a>}</span></code></pre></div>
|
||
<pre><code>N checking R code for possible problems (2.7s)
|
||
mpg_drv_summary: no visible binding for global variable ‘drv’
|
||
Undefined global functions or variables:
|
||
drv</code></pre>
|
||
<p>There are three situations in which you will encounter this
|
||
problem:</p>
|
||
<ul>
|
||
<li>You already know the column name or expression in advance.</li>
|
||
<li>You have the column name as a character vector.</li>
|
||
<li>The user specifies the column name or expression, and you want your
|
||
function to use the same kind of non-standard evaluation used by
|
||
<code>aes()</code> and <code>vars()</code>.</li>
|
||
</ul>
|
||
<p>If you already know the mapping in advance (like the above example)
|
||
you should use the <code>.data</code> pronoun from <a href="https://rlang.r-lib.org/">rlang</a> to make it explicit that you
|
||
are referring to the <code>drv</code> in the layer data and not some
|
||
other variable named <code>drv</code> (which may or may not exist
|
||
elsewhere). To avoid a similar note from the CMD check about
|
||
<code>.data</code>, use <code>#' @importFrom rlang .data</code> in any
|
||
roxygen code block (typically this should be in the package
|
||
documentation as generated by
|
||
<code>usethis::use_package_doc()</code>).</p>
|
||
<div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" tabindex="-1"></a>mpg_drv_summary <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb5-2"><a href="#cb5-2" tabindex="-1"></a> <span class="fu">ggplot</span>(ggplot2<span class="sc">::</span>mpg) <span class="sc">+</span> </span>
|
||
<span id="cb5-3"><a href="#cb5-3" tabindex="-1"></a> <span class="fu">geom_bar</span>(<span class="fu">aes</span>(<span class="at">y =</span> .data<span class="sc">$</span>drv)) <span class="sc">+</span></span>
|
||
<span id="cb5-4"><a href="#cb5-4" tabindex="-1"></a> <span class="fu">facet_wrap</span>(<span class="fu">vars</span>(.data<span class="sc">$</span>year))</span>
|
||
<span id="cb5-5"><a href="#cb5-5" tabindex="-1"></a>}</span></code></pre></div>
|
||
<p>If you have the column name as a character vector (e.g.,
|
||
<code>col = "drv"</code>), use <code>.data[[col]]</code>:</p>
|
||
<div class="sourceCode" id="cb6"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb6-1"><a href="#cb6-1" tabindex="-1"></a>col_summary <span class="ot"><-</span> <span class="cf">function</span>(df, col, by) {</span>
|
||
<span id="cb6-2"><a href="#cb6-2" tabindex="-1"></a> <span class="fu">ggplot</span>(df) <span class="sc">+</span> </span>
|
||
<span id="cb6-3"><a href="#cb6-3" tabindex="-1"></a> <span class="fu">geom_bar</span>(<span class="fu">aes</span>(<span class="at">y =</span> .data[[col]])) <span class="sc">+</span> </span>
|
||
<span id="cb6-4"><a href="#cb6-4" tabindex="-1"></a> <span class="fu">facet_wrap</span>(<span class="fu">vars</span>(.data[[by]]))</span>
|
||
<span id="cb6-5"><a href="#cb6-5" tabindex="-1"></a>}</span>
|
||
<span id="cb6-6"><a href="#cb6-6" tabindex="-1"></a></span>
|
||
<span id="cb6-7"><a href="#cb6-7" tabindex="-1"></a><span class="fu">col_summary</span>(mpg, <span class="st">"drv"</span>, <span class="st">"year"</span>)</span></code></pre></div>
|
||
<p>If the column name or expression is supplied by the user, you can
|
||
also pass it to <code>aes()</code> or <code>vars()</code> using
|
||
<code>{{ col }}</code>. This tidy eval operator captures the expression
|
||
supplied by the user and forwards it to another tidy eval-enabled
|
||
function such as <code>aes()</code> or <code>vars()</code>.</p>
|
||
<div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" tabindex="-1"></a>col_summary <span class="ot"><-</span> <span class="cf">function</span>(df, col, by) {</span>
|
||
<span id="cb7-2"><a href="#cb7-2" tabindex="-1"></a> <span class="fu">ggplot</span>(df) <span class="sc">+</span> </span>
|
||
<span id="cb7-3"><a href="#cb7-3" tabindex="-1"></a> <span class="fu">geom_bar</span>(<span class="fu">aes</span>(<span class="at">y =</span> {{ col }})) <span class="sc">+</span> </span>
|
||
<span id="cb7-4"><a href="#cb7-4" tabindex="-1"></a> <span class="fu">facet_wrap</span>(<span class="fu">vars</span>({{ by }}))</span>
|
||
<span id="cb7-5"><a href="#cb7-5" tabindex="-1"></a>}</span>
|
||
<span id="cb7-6"><a href="#cb7-6" tabindex="-1"></a></span>
|
||
<span id="cb7-7"><a href="#cb7-7" tabindex="-1"></a><span class="fu">col_summary</span>(mpg, drv, year)</span></code></pre></div>
|
||
<p>To summarise:</p>
|
||
<ul>
|
||
<li>If you know the mapping or facet specification is <code>col</code>
|
||
in advance, use <code>aes(.data$col)</code> or
|
||
<code>vars(.data$col)</code>.</li>
|
||
<li>If <code>col</code> is a variable that contains the column name as a
|
||
character vector, use <code>aes(.data[[col]]</code> or
|
||
<code>vars(.data[[col]])</code>.</li>
|
||
<li>If you would like the behaviour of <code>col</code> to look and feel
|
||
like it would within <code>aes()</code> and <code>vars()</code>, use
|
||
<code>aes({{ col }})</code> or <code>vars({{ col }})</code>.</li>
|
||
</ul>
|
||
<p>You will see a lot of other ways to do this in the wild, but the
|
||
syntax we use here is the only one we can guarantee will work in the
|
||
future! In particular, don’t use <code>aes_()</code> or
|
||
<code>aes_string()</code>, as they are deprecated and may be removed in
|
||
a future version. Finally, don’t skip the step of creating a data frame
|
||
and a mapping to pass in to <code>ggplot()</code> or its layers! You
|
||
will see other ways of doing this, but these may rely on undocumented
|
||
behaviour and can fail in unexpected ways.</p>
|
||
</div>
|
||
<div id="best-practices-for-common-tasks" class="section level2">
|
||
<h2>Best practices for common tasks</h2>
|
||
<div id="using-ggplot2-to-visualize-an-object" class="section level3">
|
||
<h3>Using ggplot2 to visualize an object</h3>
|
||
<p>ggplot2 is commonly used in packages to visualize objects (e.g., in a
|
||
<code>plot()</code>-style function). For example, a package might define
|
||
an S3 class that represents the probability of various discrete
|
||
values:</p>
|
||
<div class="sourceCode" id="cb8"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb8-1"><a href="#cb8-1" tabindex="-1"></a>mpg_drv_dist <span class="ot"><-</span> <span class="fu">structure</span>(</span>
|
||
<span id="cb8-2"><a href="#cb8-2" tabindex="-1"></a> <span class="fu">c</span>(</span>
|
||
<span id="cb8-3"><a href="#cb8-3" tabindex="-1"></a> <span class="st">"4"</span> <span class="ot">=</span> <span class="dv">103</span> <span class="sc">/</span> <span class="dv">234</span>,</span>
|
||
<span id="cb8-4"><a href="#cb8-4" tabindex="-1"></a> <span class="st">"f"</span> <span class="ot">=</span> <span class="dv">106</span> <span class="sc">/</span> <span class="dv">234</span>,</span>
|
||
<span id="cb8-5"><a href="#cb8-5" tabindex="-1"></a> <span class="st">"r"</span> <span class="ot">=</span> <span class="dv">25</span> <span class="sc">/</span> <span class="dv">234</span></span>
|
||
<span id="cb8-6"><a href="#cb8-6" tabindex="-1"></a> ),</span>
|
||
<span id="cb8-7"><a href="#cb8-7" tabindex="-1"></a> <span class="at">class =</span> <span class="st">"discrete_distr"</span></span>
|
||
<span id="cb8-8"><a href="#cb8-8" tabindex="-1"></a>)</span></code></pre></div>
|
||
<p>Many S3 classes in R have a <code>plot()</code> method, but it is
|
||
unrealistic to expect that a single <code>plot()</code> method can
|
||
provide the visualization every one of your users is looking for. It is
|
||
useful, however, to provide a <code>plot()</code> method as a visual
|
||
summary that users can call to understand the essence of an object. To
|
||
satisfy all your users, we suggest writing a function that transforms
|
||
the object into a data frame (or a <code>list()</code> of data frames if
|
||
your object is more complicated). A good example of this approach is <a href="https://cran.r-project.org/package=ggdendro">ggdendro</a>, which
|
||
creates dendrograms using ggplot2 but also computes the data necessary
|
||
for users to make their own. For the above example, the function might
|
||
look like this:</p>
|
||
<div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" tabindex="-1"></a>discrete_distr_data <span class="ot"><-</span> <span class="cf">function</span>(x) {</span>
|
||
<span id="cb9-2"><a href="#cb9-2" tabindex="-1"></a> tibble<span class="sc">::</span><span class="fu">tibble</span>(</span>
|
||
<span id="cb9-3"><a href="#cb9-3" tabindex="-1"></a> <span class="at">value =</span> <span class="fu">names</span>(x),</span>
|
||
<span id="cb9-4"><a href="#cb9-4" tabindex="-1"></a> <span class="at">probability =</span> <span class="fu">as.numeric</span>(x)</span>
|
||
<span id="cb9-5"><a href="#cb9-5" tabindex="-1"></a> )</span>
|
||
<span id="cb9-6"><a href="#cb9-6" tabindex="-1"></a>}</span>
|
||
<span id="cb9-7"><a href="#cb9-7" tabindex="-1"></a></span>
|
||
<span id="cb9-8"><a href="#cb9-8" tabindex="-1"></a><span class="fu">discrete_distr_data</span>(mpg_drv_dist)</span>
|
||
<span id="cb9-9"><a href="#cb9-9" tabindex="-1"></a><span class="co">#> # A tibble: 3 × 2</span></span>
|
||
<span id="cb9-10"><a href="#cb9-10" tabindex="-1"></a><span class="co">#> value probability</span></span>
|
||
<span id="cb9-11"><a href="#cb9-11" tabindex="-1"></a><span class="co">#> <chr> <dbl></span></span>
|
||
<span id="cb9-12"><a href="#cb9-12" tabindex="-1"></a><span class="co">#> 1 4 0.440</span></span>
|
||
<span id="cb9-13"><a href="#cb9-13" tabindex="-1"></a><span class="co">#> 2 f 0.453</span></span>
|
||
<span id="cb9-14"><a href="#cb9-14" tabindex="-1"></a><span class="co">#> 3 r 0.107</span></span></code></pre></div>
|
||
<p>In general, users of <code>plot()</code> call it for its
|
||
side-effects: it results in a graphic being displayed. This is different
|
||
than the behaviour of a <code>ggplot()</code>, which is not displayed
|
||
unless it is explicitly <code>print()</code>ed. Because of this, ggplot2
|
||
defines its own generic <code>autoplot()</code>, a call to which is
|
||
expected to return a <code>ggplot()</code> (with no side effects).</p>
|
||
<div class="sourceCode" id="cb10"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" tabindex="-1"></a><span class="co">#' @importFrom ggplot2 autoplot</span></span>
|
||
<span id="cb10-2"><a href="#cb10-2" tabindex="-1"></a>autoplot.discrete_distr <span class="ot"><-</span> <span class="cf">function</span>(object, ...) {</span>
|
||
<span id="cb10-3"><a href="#cb10-3" tabindex="-1"></a> plot_data <span class="ot"><-</span> <span class="fu">discrete_distr_data</span>(object)</span>
|
||
<span id="cb10-4"><a href="#cb10-4" tabindex="-1"></a> <span class="fu">ggplot</span>(plot_data, <span class="fu">aes</span>(.data<span class="sc">$</span>value, .data<span class="sc">$</span>probability)) <span class="sc">+</span></span>
|
||
<span id="cb10-5"><a href="#cb10-5" tabindex="-1"></a> <span class="fu">geom_col</span>() <span class="sc">+</span></span>
|
||
<span id="cb10-6"><a href="#cb10-6" tabindex="-1"></a> <span class="fu">coord_flip</span>() <span class="sc">+</span></span>
|
||
<span id="cb10-7"><a href="#cb10-7" tabindex="-1"></a> <span class="fu">labs</span>(<span class="at">x =</span> <span class="st">"Value"</span>, <span class="at">y =</span> <span class="st">"Probability"</span>)</span>
|
||
<span id="cb10-8"><a href="#cb10-8" tabindex="-1"></a>}</span></code></pre></div>
|
||
<p>Once an <code>autoplot()</code> method has been defined, a
|
||
<code>plot()</code> method can then consist of <code>print()</code>ing
|
||
the result of <code>autoplot()</code>:</p>
|
||
<div class="sourceCode" id="cb11"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb11-1"><a href="#cb11-1" tabindex="-1"></a><span class="co">#' @importFrom graphics plot</span></span>
|
||
<span id="cb11-2"><a href="#cb11-2" tabindex="-1"></a>plot.discrete_distr <span class="ot"><-</span> <span class="cf">function</span>(x, ...) {</span>
|
||
<span id="cb11-3"><a href="#cb11-3" tabindex="-1"></a> <span class="fu">print</span>(<span class="fu">autoplot</span>(x, ...))</span>
|
||
<span id="cb11-4"><a href="#cb11-4" tabindex="-1"></a>}</span></code></pre></div>
|
||
<p>It is considered bad practice to implement an S3 generic like
|
||
<code>plot()</code>, or <code>autoplot()</code> if you don’t own the S3
|
||
class, as it makes it hard for the package developer who does have
|
||
control over the S3 to implement the method themselves. This shouldn’t
|
||
stop you from creating your own functions to visualize these
|
||
objects!</p>
|
||
</div>
|
||
<div id="creating-a-new-theme" class="section level3">
|
||
<h3>Creating a new theme</h3>
|
||
<p>When creating a new theme, it’s always good practice to start with an
|
||
existing theme (e.g. <code>theme_grey()</code>) and then
|
||
<code>%+replace%</code> the elements that should be changed. This is the
|
||
right strategy even if seemingly all elements are replaced, as not doing
|
||
so makes it difficult for us to improve themes by adding new elements.
|
||
There are many excellent examples of themes in the <a href="https://cran.r-project.org/package=ggthemes">ggthemes</a>
|
||
package.</p>
|
||
<div class="sourceCode" id="cb12"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb12-1"><a href="#cb12-1" tabindex="-1"></a><span class="co">#' @importFrom ggplot2 %+replace%</span></span>
|
||
<span id="cb12-2"><a href="#cb12-2" tabindex="-1"></a>theme_custom <span class="ot"><-</span> <span class="cf">function</span>(...) {</span>
|
||
<span id="cb12-3"><a href="#cb12-3" tabindex="-1"></a> <span class="fu">theme_grey</span>(...) <span class="sc">%+replace%</span> </span>
|
||
<span id="cb12-4"><a href="#cb12-4" tabindex="-1"></a> <span class="fu">theme</span>(</span>
|
||
<span id="cb12-5"><a href="#cb12-5" tabindex="-1"></a> <span class="at">panel.border =</span> <span class="fu">element_rect</span>(<span class="at">linewidth =</span> <span class="dv">1</span>, <span class="at">fill =</span> <span class="cn">NA</span>),</span>
|
||
<span id="cb12-6"><a href="#cb12-6" tabindex="-1"></a> <span class="at">panel.background =</span> <span class="fu">element_blank</span>(),</span>
|
||
<span id="cb12-7"><a href="#cb12-7" tabindex="-1"></a> <span class="at">panel.grid =</span> <span class="fu">element_line</span>(<span class="at">colour =</span> <span class="st">"grey80"</span>)</span>
|
||
<span id="cb12-8"><a href="#cb12-8" tabindex="-1"></a> )</span>
|
||
<span id="cb12-9"><a href="#cb12-9" tabindex="-1"></a>}</span>
|
||
<span id="cb12-10"><a href="#cb12-10" tabindex="-1"></a></span>
|
||
<span id="cb12-11"><a href="#cb12-11" tabindex="-1"></a><span class="fu">mpg_drv_summary</span>() <span class="sc">+</span> <span class="fu">theme_custom</span>()</span></code></pre></div>
|
||
<p>It is important that the theme be calculated after the package is
|
||
loaded. If not, the theme object is stored in the compiled bytecode of
|
||
the built package, which may or may not align with the installed version
|
||
of ggplot2! If your package has a default theme for its visualizations,
|
||
the correct way to load it is to have a function that returns the
|
||
default theme:</p>
|
||
<div class="sourceCode" id="cb13"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb13-1"><a href="#cb13-1" tabindex="-1"></a>default_theme <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb13-2"><a href="#cb13-2" tabindex="-1"></a> <span class="fu">theme_custom</span>()</span>
|
||
<span id="cb13-3"><a href="#cb13-3" tabindex="-1"></a>}</span>
|
||
<span id="cb13-4"><a href="#cb13-4" tabindex="-1"></a></span>
|
||
<span id="cb13-5"><a href="#cb13-5" tabindex="-1"></a>mpg_drv_summary2 <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb13-6"><a href="#cb13-6" tabindex="-1"></a> <span class="fu">mpg_drv_summary</span>() <span class="sc">+</span> <span class="fu">default_theme</span>()</span>
|
||
<span id="cb13-7"><a href="#cb13-7" tabindex="-1"></a>}</span></code></pre></div>
|
||
</div>
|
||
<div id="testing-ggplot2-output" class="section level3">
|
||
<h3>Testing ggplot2 output</h3>
|
||
<p>We suggest testing the output of ggplot2 in using the <a href="https://cran.r-project.org/package=vdiffr">vdiffr</a> package,
|
||
which is a tool to manage visual test cases (this is one of the ways we
|
||
test ggplot2). If changes in ggplot2 or your code introduce a change in
|
||
the visual output of a ggplot, tests will fail when you run them locally
|
||
or as part of a Continuous Integration setup. To use vdiffr, make sure
|
||
you are using <a href="https://testthat.r-lib.org/">testthat</a> (you
|
||
can use <code>usethis::use_testthat()</code> to get started) and add
|
||
vdiffr to <code>Suggests</code> in your <code>DESCRIPTION</code>. Then,
|
||
use
|
||
<code>vdiffr::expect_doppleganger(<name of plot>, <ggplot object>)</code>
|
||
to make a test that fails if there are visual changes in
|
||
<code><ggplot object></code>. However, you should consider whether
|
||
visual testing is the best strategy because it adds a dependency on how
|
||
ggplot2 performs its rendering which may change between versions. If
|
||
extracting the layer data using <code>layer_data()</code> and testing
|
||
the values directly is possible it is far better as it more directly
|
||
test the behaviour of your own code.</p>
|
||
<div class="sourceCode" id="cb14"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb14-1"><a href="#cb14-1" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">"output of ggplot() is stable"</span>, {</span>
|
||
<span id="cb14-2"><a href="#cb14-2" tabindex="-1"></a> vdiffr<span class="sc">::</span><span class="fu">expect_doppelganger</span>(<span class="st">"A blank plot"</span>, <span class="fu">ggplot</span>())</span>
|
||
<span id="cb14-3"><a href="#cb14-3" tabindex="-1"></a>})</span></code></pre></div>
|
||
</div>
|
||
<div id="ggplot2-in-suggests" class="section level3">
|
||
<h3>ggplot2 in <code>Suggests</code></h3>
|
||
<p>If you use ggplot2 in your package, most likely you will want to list
|
||
it under <code>Imports</code>. If you would like to list ggplot2 in
|
||
<code>Suggests</code> instead, you will not be able to
|
||
<code>#' @importFrom ggplot2 ...</code> (i.e., you must refer to ggplot2
|
||
objects using <code>::</code>). If you use infix operators from ggplot2
|
||
like <code>%+replace%</code> and you want to keep ggplot2 in
|
||
<code>Suggests</code>, you can assign the operator within the function
|
||
before it is used:</p>
|
||
<div class="sourceCode" id="cb15"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb15-1"><a href="#cb15-1" tabindex="-1"></a>theme_custom <span class="ot"><-</span> <span class="cf">function</span>(...) {</span>
|
||
<span id="cb15-2"><a href="#cb15-2" tabindex="-1"></a> <span class="st">`</span><span class="at">%+replace%</span><span class="st">`</span> <span class="ot"><-</span> ggplot2<span class="sc">::</span><span class="st">`</span><span class="at">%+replace%</span><span class="st">`</span></span>
|
||
<span id="cb15-3"><a href="#cb15-3" tabindex="-1"></a> </span>
|
||
<span id="cb15-4"><a href="#cb15-4" tabindex="-1"></a> ggplot2<span class="sc">::</span><span class="fu">theme_grey</span>(...) <span class="sc">%+replace%</span> </span>
|
||
<span id="cb15-5"><a href="#cb15-5" tabindex="-1"></a> ggplot2<span class="sc">::</span><span class="fu">theme</span>(<span class="at">panel.background =</span> ggplot2<span class="sc">::</span><span class="fu">element_blank</span>())</span>
|
||
<span id="cb15-6"><a href="#cb15-6" tabindex="-1"></a>}</span></code></pre></div>
|
||
<p>Generally, if you add a method for a ggplot2 generic like
|
||
<code>autoplot()</code>, ggplot2 should be in <code>Imports</code>. If
|
||
for some reason you would like to keep ggplot2 in <code>Suggests</code>,
|
||
it is possible to register your generics only if ggplot2 is installed
|
||
using <code>vctrs::s3_register()</code>. If you do this, you should copy
|
||
and paste the source of <code>vctrs::s3_register()</code> into your own
|
||
package to avoid adding a <a href="https://vctrs.r-lib.org/">vctrs</a>
|
||
dependency.</p>
|
||
<div class="sourceCode" id="cb16"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb16-1"><a href="#cb16-1" tabindex="-1"></a>.onLoad <span class="ot"><-</span> <span class="cf">function</span>(...) {</span>
|
||
<span id="cb16-2"><a href="#cb16-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="fu">requireNamespace</span>(<span class="st">"ggplot2"</span>, <span class="at">quietly =</span> <span class="cn">TRUE</span>)) {</span>
|
||
<span id="cb16-3"><a href="#cb16-3" tabindex="-1"></a> vctrs<span class="sc">::</span><span class="fu">s3_register</span>(<span class="st">"ggplot2::autoplot"</span>, <span class="st">"discrete_distr"</span>)</span>
|
||
<span id="cb16-4"><a href="#cb16-4" tabindex="-1"></a> }</span>
|
||
<span id="cb16-5"><a href="#cb16-5" tabindex="-1"></a>}</span></code></pre></div>
|
||
</div>
|
||
</div>
|
||
<div id="read-more" class="section level2">
|
||
<h2>Read more</h2>
|
||
<p>There are other things to consider when taking on a dependency. <a href="https://www.tidyverse.org/blog/2022/09/playing-on-the-same-team-as-your-dependecy/">This
|
||
post</a> goes into detail with many of these using ggplot2 as an example
|
||
and is a good read for anyone developing a package using ggplot2.</p>
|
||
</div>
|
||
|
||
|
||
|
||
<!-- code folding -->
|
||
|
||
|
||
<!-- dynamically load mathjax for compatibility with self-contained -->
|
||
<script>
|
||
(function () {
|
||
var script = document.createElement("script");
|
||
script.type = "text/javascript";
|
||
script.src = "https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
|
||
document.getElementsByTagName("head")[0].appendChild(script);
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|