skimr
Version:
CLI EDA for CSVs
910 lines (874 loc) • 44.7 kB
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" />
<meta name="author" content="Yihui Xie" />
<meta name="date" content="2023-10-26" />
<title>Custom Print Methods</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: 800px;
overflow: visible;
padding-left: 2em;
padding-right: 2em;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
}
#header {
text-align: center;
}
#TOC {
clear: both;
margin: 0 0 10px 0;
padding: 4px;
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:not([class]) {
margin: auto;
min-width: 40%;
border-width: 1px;
border-color: #DDDDDD;
border-style: outset;
border-collapse: collapse;
}
table[summary="R argblock"] {
width: 100%;
border: none;
}
table:not([class]) th {
border-width: 2px;
padding: 5px;
border-style: inset;
}
table:not([class]) td {
border-width: 1px;
border-style: inset;
line-height: 18px;
padding: 5px 5px;
}
table:not([class]), table:not([class]) th, table:not([class]) td {
border-left-style: none;
border-right-style: none;
}
table:not([class]) tr.odd {
background-color: #f7f7f7;
}
p {
margin: 0.5em 0;
}
blockquote {
background-color: #f6f6f6;
padding: 13px;
padding-bottom: 1px;
}
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: #f5f5f5;
border-radius: 3px;
color: #333;
}
pre {
overflow-x: auto;
border-radius: 3px;
margin: 5px 0 10px 0;
padding: 10px;
}
pre:not([class]) {
background-color: white;
border: #f5f5f5 1px solid;
}
pre:not([class]) code {
color: #444;
background-color: white;
}
code {
font-family: monospace;
font-size: 90%;
}
p > code, li > code {
padding: 2px 4px;
color: #d14;
border: 1px solid #e1e1e8;
white-space: inherit;
}
div.figure {
text-align: center;
}
table > caption, div.figure p.caption {
font-style: italic;
}
table > caption span, div.figure p.caption span {
font-style: normal;
font-weight: bold;
}
p {
margin: 0 0 10px;
}
table:not([class]) {
margin: auto auto 10px auto;
}
img:not([class]) {
background-color: #FFFFFF;
padding: 2px;
border-radius: 3px;
border: 1px solid #CCCCCC;
margin: 0 5px;
max-width: 100%;
}
h1 {
margin-top: 0;
font-size: 35px;
line-height: 40px;
}
h2 {
border-bottom: 4px solid #f5f5f5;
padding-top: 10px;
padding-bottom: 2px;
font-size: 145%;
}
h3 {
border-bottom: 2px solid #f5f5f5;
padding-top: 10px;
font-size: 120%;
}
h4 {
border-bottom: 1px solid #f5f5f5;
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; }
div.r-help-page {
background-color: #f9f9f9;
border-bottom: #ddd 1px solid;
margin-bottom: 10px;
padding: 10px;
}
div.r-help-page:hover {
background-color: #f4f4f4;
}
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">Custom Print Methods</h1>
<h4 class="author">Yihui Xie</h4>
<h4 class="date">2023-10-26</h4>
<div id="TOC">
<ul>
<li><a href="#customize-printing" id="toc-customize-printing">Customize
Printing</a></li>
<li><a href="#a-low-level-explanation" id="toc-a-low-level-explanation">A Low-level Explanation</a>
<ul>
<li><a href="#the-render-option" id="toc-the-render-option">The
<code>render</code> option</a></li>
<li><a href="#metadata" id="toc-metadata">Metadata</a></li>
<li><a href="#for-package-authors" id="toc-for-package-authors">For
package authors</a></li>
</ul></li>
</ul>
</div>
<p>Before <strong>knitr</strong> v1.6, printing objects in R code chunks
basically emulates the R console. For example, a data frame is printed
like this<a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a>:</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><span class="fu">head</span>(mtcars)</span></code></pre></div>
<pre><code> mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1</code></pre>
<p>The text representation of the data frame above may look very
familiar with most R users, but for reporting purposes, it may not be
satisfactory – often times we want to see a table representation
instead. That is the problem that the chunk option <code>render</code>
and the S3 generic function <code>knit_print()</code> try to solve.</p>
<div id="customize-printing" class="section level2">
<h2>Customize Printing</h2>
<p>After we evaluate each R expression in a code chunk, there is an
object returned. For example, <code>1 + 1</code> returns <code>2</code>.
This object is passed to the chunk option <code>render</code>, which is
a function with two arguments, <code>x</code> and <code>options</code>,
or <code>x</code> and <code>...</code>. The default value for the
<code>render</code> option is <code>knit_print</code>, an S3 function in
<strong>knitr</strong>:</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><span class="fu">library</span>(knitr)</span>
<span id="cb3-2"><a href="#cb3-2" tabindex="-1"></a>knit_print <span class="co"># an S3 generic function</span></span></code></pre></div>
<pre><code>## function (x, ...)
## {
## if (need_screenshot(x, ...)) {
## html_screenshot(x)
## }
## else {
## UseMethod("knit_print")
## }
## }
## <bytecode: 0x11ba3fe70>
## <environment: namespace:knitr></code></pre>
<div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" tabindex="-1"></a><span class="fu">methods</span>(knit_print)</span></code></pre></div>
<pre><code>## [1] knit_print.css* knit_print.data.frame*
## [3] knit_print.default* knit_print.grouped_df*
## [5] knit_print.html* knit_print.html_dependency*
## [7] knit_print.knit_asis* knit_print.knit_asis_url*
## [9] knit_print.knitr_kable* knit_print.output_format_dependency*
## [11] knit_print.rowwise_df* knit_print.sass*
## [13] knit_print.shiny.tag* knit_print.shiny.tag.list*
## [15] knit_print.tbl_sql*
## see '?methods' for accessing help and source code</code></pre>
<div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" tabindex="-1"></a><span class="fu">getS3method</span>(<span class="st">'knit_print'</span>, <span class="st">'default'</span>) <span class="co"># the default method</span></span></code></pre></div>
<pre><code>## function (x, ..., inline = FALSE)
## {
## if (inline)
## x
## else normal_print(x)
## }
## <bytecode: 0x12bf8dbc8>
## <environment: namespace:knitr></code></pre>
<div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" tabindex="-1"></a>normal_print</span></code></pre></div>
<pre><code>## function (x, ...)
## if (isS4(x)) methods::show(x) else print(x)
## <bytecode: 0x11bdd4bc0>
## <environment: namespace:evaluate></code></pre>
<p>As we can see, <code>knit_print()</code> has a <code>default</code>
method, which is basically <code>print()</code> or <code>show()</code>,
depending on whether the object is an S4 object. This means it does
nothing special when printing R objects:</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="fu">knit_print</span>(<span class="dv">1</span><span class="sc">:</span><span class="dv">10</span>)</span>
<span id="cb11-2"><a href="#cb11-2" tabindex="-1"></a><span class="do">## [1] 1 2 3 4 5 6 7 8 9 10</span></span>
<span id="cb11-3"><a href="#cb11-3" tabindex="-1"></a><span class="fu">knit_print</span>(<span class="fu">head</span>(mtcars))</span>
<span id="cb11-4"><a href="#cb11-4" tabindex="-1"></a><span class="do">## mpg cyl disp hp drat wt qsec vs am gear carb</span></span>
<span id="cb11-5"><a href="#cb11-5" tabindex="-1"></a><span class="do">## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4</span></span>
<span id="cb11-6"><a href="#cb11-6" tabindex="-1"></a><span class="do">## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4</span></span>
<span id="cb11-7"><a href="#cb11-7" tabindex="-1"></a><span class="do">## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1</span></span>
<span id="cb11-8"><a href="#cb11-8" tabindex="-1"></a><span class="do">## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1</span></span>
<span id="cb11-9"><a href="#cb11-9" tabindex="-1"></a><span class="do">## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2</span></span>
<span id="cb11-10"><a href="#cb11-10" tabindex="-1"></a><span class="do">## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1</span></span></code></pre></div>
<p>S3 generic functions are extensible in the sense that we can define
custom methods for them. A method <code>knit_print.foo()</code> will be
applied to the object that has the class <code>foo</code>. Here is quick
example of how we can print data frames as tables:</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="fu">library</span>(knitr)</span>
<span id="cb12-2"><a href="#cb12-2" tabindex="-1"></a><span class="co"># define a method for objects of the class data.frame</span></span>
<span id="cb12-3"><a href="#cb12-3" tabindex="-1"></a>knit_print.data.frame <span class="ot">=</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb12-4"><a href="#cb12-4" tabindex="-1"></a> res <span class="ot">=</span> <span class="fu">paste</span>(<span class="fu">c</span>(<span class="st">''</span>, <span class="st">''</span>, <span class="fu">kable</span>(x)), <span class="at">collapse =</span> <span class="st">'</span><span class="sc">\n</span><span class="st">'</span>)</span>
<span id="cb12-5"><a href="#cb12-5" tabindex="-1"></a> <span class="fu">asis_output</span>(res)</span>
<span id="cb12-6"><a href="#cb12-6" tabindex="-1"></a>}</span>
<span id="cb12-7"><a href="#cb12-7" tabindex="-1"></a><span class="co"># register the method</span></span>
<span id="cb12-8"><a href="#cb12-8" tabindex="-1"></a><span class="fu">registerS3method</span>(<span class="st">"knit_print"</span>, <span class="st">"data.frame"</span>, knit_print.data.frame)</span></code></pre></div>
<p>If you define a method in a code chunk in a <strong>knitr</strong>
document, the call to <code>registerS3method()</code> will be necessary
for R >= 3.5.0, because the S3 dispatch mechanism has changed since R
3.5.0. If you are developing an R package, see the section <a href="#for-package-authors">For package authors</a> below.</p>
<p>We expect the print method to return a character vector, or an object
that can be coerced into a character vector. In the example above, the
<code>kable()</code> function returns a character vector, which we pass
to the <code>asis_output()</code> function so that later
<strong>knitr</strong> knows that this result needs no special treatment
(just write it as is), otherwise it depends on the chunk option
<code>results</code> (<code>= 'asis'</code> / <code>'markup'</code> /
<code>'hide'</code>) how a normal character vector should be written.
The function <code>asis_output()</code> has the same effect as
<code>results = 'asis'</code>, but saves us the effort to provide this
chunk option explicitly. Now we check how the printing behavior is
changed. We print a number, a character vector, a list, a data frame,
and write a character value using <code>cat()</code> in the chunk
below:</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><span class="dv">1</span> <span class="sc">+</span> <span class="dv">1</span></span></code></pre></div>
<pre><code>## [1] 2</code></pre>
<div class="sourceCode" id="cb15"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb15-1"><a href="#cb15-1" tabindex="-1"></a><span class="fu">head</span>(letters)</span></code></pre></div>
<pre><code>## [1] "a" "b" "c" "d" "e" "f"</code></pre>
<div class="sourceCode" id="cb17"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb17-1"><a href="#cb17-1" tabindex="-1"></a><span class="fu">list</span>(<span class="at">a =</span> <span class="dv">1</span>, <span class="at">b =</span> <span class="dv">9</span><span class="sc">:</span><span class="dv">4</span>)</span></code></pre></div>
<pre><code>## $a
## [1] 1
##
## $b
## [1] 9 8 7 6 5 4</code></pre>
<div class="sourceCode" id="cb19"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb19-1"><a href="#cb19-1" tabindex="-1"></a><span class="fu">head</span>(mtcars)</span></code></pre></div>
<table style="width:100%;">
<colgroup>
<col width="26%" />
<col width="7%" />
<col width="5%" />
<col width="7%" />
<col width="5%" />
<col width="7%" />
<col width="8%" />
<col width="8%" />
<col width="4%" />
<col width="4%" />
<col width="7%" />
<col width="7%" />
</colgroup>
<thead>
<tr class="header">
<th align="left"></th>
<th align="right">mpg</th>
<th align="right">cyl</th>
<th align="right">disp</th>
<th align="right">hp</th>
<th align="right">drat</th>
<th align="right">wt</th>
<th align="right">qsec</th>
<th align="right">vs</th>
<th align="right">am</th>
<th align="right">gear</th>
<th align="right">carb</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left">Mazda RX4</td>
<td align="right">21.0</td>
<td align="right">6</td>
<td align="right">160</td>
<td align="right">110</td>
<td align="right">3.90</td>
<td align="right">2.620</td>
<td align="right">16.46</td>
<td align="right">0</td>
<td align="right">1</td>
<td align="right">4</td>
<td align="right">4</td>
</tr>
<tr class="even">
<td align="left">Mazda RX4 Wag</td>
<td align="right">21.0</td>
<td align="right">6</td>
<td align="right">160</td>
<td align="right">110</td>
<td align="right">3.90</td>
<td align="right">2.875</td>
<td align="right">17.02</td>
<td align="right">0</td>
<td align="right">1</td>
<td align="right">4</td>
<td align="right">4</td>
</tr>
<tr class="odd">
<td align="left">Datsun 710</td>
<td align="right">22.8</td>
<td align="right">4</td>
<td align="right">108</td>
<td align="right">93</td>
<td align="right">3.85</td>
<td align="right">2.320</td>
<td align="right">18.61</td>
<td align="right">1</td>
<td align="right">1</td>
<td align="right">4</td>
<td align="right">1</td>
</tr>
<tr class="even">
<td align="left">Hornet 4 Drive</td>
<td align="right">21.4</td>
<td align="right">6</td>
<td align="right">258</td>
<td align="right">110</td>
<td align="right">3.08</td>
<td align="right">3.215</td>
<td align="right">19.44</td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">3</td>
<td align="right">1</td>
</tr>
<tr class="odd">
<td align="left">Hornet Sportabout</td>
<td align="right">18.7</td>
<td align="right">8</td>
<td align="right">360</td>
<td align="right">175</td>
<td align="right">3.15</td>
<td align="right">3.440</td>
<td align="right">17.02</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">3</td>
<td align="right">2</td>
</tr>
<tr class="even">
<td align="left">Valiant</td>
<td align="right">18.1</td>
<td align="right">6</td>
<td align="right">225</td>
<td align="right">105</td>
<td align="right">2.76</td>
<td align="right">3.460</td>
<td align="right">20.22</td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">3</td>
<td align="right">1</td>
</tr>
</tbody>
</table>
<div class="sourceCode" id="cb20"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb20-1"><a href="#cb20-1" tabindex="-1"></a><span class="fu">cat</span>(<span class="st">'This is cool.'</span>)</span></code></pre></div>
<pre><code>## This is cool.</code></pre>
<p>We see all objects except the data frame were printed “normally”<a href="#fn2" class="footnote-ref" id="fnref2"><sup>2</sup></a>. The data
frame was printed as a real table. Note you do not have to use
<code>kable()</code> to create tables – there are many other options
such as <strong>xtable</strong>. Just make sure the print method returns
a character string.</p>
<p>The <a href="https://github.com/yihui/printr"><strong>printr</strong></a>
package is a companion to <strong>knitr</strong> containing printing
methods for some common objects like matrices and data frames. Users
only need to load this package to get attractive printed results. A
major factor to consider (which has been considered in
<strong>printr</strong>) when defining a printing method is the output
format. For example, the table syntax can be entirely different when the
output is LaTeX vs when it is Markdown.</p>
<p>It is strongly recommended that your S3 method has a <code>...</code>
argument, so that your method can safely ignore arguments that are
passed to <code>knit_print()</code> but not defined in your method. At
the moment, a <code>knit_print()</code> method can have two optional
arguments:</p>
<ul>
<li>the <code>options</code> argument takes a list of the current chunk
options;</li>
<li>the <code>inline</code> argument indicates if the method is called
in code chunks or inline R code;</li>
</ul>
<p>Depending on your application, you may optionally use these
arguments. Here are some examples:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb22-1"><a href="#cb22-1" tabindex="-1"></a>knit_print.classA <span class="ot">=</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb22-2"><a href="#cb22-2" tabindex="-1"></a> <span class="co"># ignore options and inline</span></span>
<span id="cb22-3"><a href="#cb22-3" tabindex="-1"></a>}</span>
<span id="cb22-4"><a href="#cb22-4" tabindex="-1"></a>knit_print.classB <span class="ot">=</span> <span class="cf">function</span>(x, options, ...) {</span>
<span id="cb22-5"><a href="#cb22-5" tabindex="-1"></a> <span class="co"># use the chunk option out.height</span></span>
<span id="cb22-6"><a href="#cb22-6" tabindex="-1"></a> <span class="fu">asis_output</span>(<span class="fu">paste0</span>(</span>
<span id="cb22-7"><a href="#cb22-7" tabindex="-1"></a> <span class="st">'<iframe src="https://yihui.org" height="'</span>, options<span class="sc">$</span>out.height, <span class="st">'"></iframe>'</span>,</span>
<span id="cb22-8"><a href="#cb22-8" tabindex="-1"></a> ))</span>
<span id="cb22-9"><a href="#cb22-9" tabindex="-1"></a>}</span>
<span id="cb22-10"><a href="#cb22-10" tabindex="-1"></a>knit_print.classC <span class="ot">=</span> <span class="cf">function</span>(x, <span class="at">inline =</span> <span class="cn">FALSE</span>, ...) {</span>
<span id="cb22-11"><a href="#cb22-11" tabindex="-1"></a> <span class="co"># different output according to inline=TRUE/FALSE</span></span>
<span id="cb22-12"><a href="#cb22-12" tabindex="-1"></a> <span class="cf">if</span> (inline) {</span>
<span id="cb22-13"><a href="#cb22-13" tabindex="-1"></a> <span class="st">'inline output for classC'</span></span>
<span id="cb22-14"><a href="#cb22-14" tabindex="-1"></a> } <span class="cf">else</span> {</span>
<span id="cb22-15"><a href="#cb22-15" tabindex="-1"></a> <span class="st">'chunk output for classC'</span></span>
<span id="cb22-16"><a href="#cb22-16" tabindex="-1"></a> }</span>
<span id="cb22-17"><a href="#cb22-17" tabindex="-1"></a>}</span>
<span id="cb22-18"><a href="#cb22-18" tabindex="-1"></a>knit_print.classD <span class="ot">=</span> <span class="cf">function</span>(x, options, <span class="at">inline =</span> <span class="cn">FALSE</span>, ...) {</span>
<span id="cb22-19"><a href="#cb22-19" tabindex="-1"></a> <span class="co"># use both options and inline</span></span>
<span id="cb22-20"><a href="#cb22-20" tabindex="-1"></a>}</span></code></pre></div>
<p>Note that when <em>using</em> your (or another)
<code>knit_print()</code> method <em>inline</em> (if it supports that),
you must not call <code>knit_print()</code> on the object, but just have
it return. For example, your inline code should read
<code>`r c("foo")`</code> and <em>not</em>
<code>`r knit_print(c("foo"))`</code>. The latter inline code would
yield the methods’ result for <em>in-chunk</em> (not inline), because,
as set up in the above, <code>knit_print()</code> methods default to
<code>inline = FALSE</code>. This default gets overwritten depending on
the context in which <code>knit_print()</code> is called (inline or
in-chunk), only when <code>knit_print()</code> is called by
<strong>knitr</strong> (not you) via the <code>render</code> option (see
below). You can, of course, always manually set the inline option
<code>`r knit_print(c("foo"), inline = TRUE)`</code>, but that’s a lot
of typing.</p>
</div>
<div id="a-low-level-explanation" class="section level2">
<h2>A Low-level Explanation</h2>
<p>You can skip this section if you do not care about the low-level
implementation details.</p>
<div id="the-render-option" class="section level3">
<h3>The <code>render</code> option</h3>
<p>As mentioned before, the chunk option <code>render</code> is a
function that defaults to <code>knit_print()</code>. We can certainly
use other render functions. For example, we create a dummy function that
always says “I do not know what to print” no matter what objects it
receives:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb23-1"><a href="#cb23-1" tabindex="-1"></a>dummy_print <span class="ot">=</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb23-2"><a href="#cb23-2" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">"I do not know what to print!"</span>)</span>
<span id="cb23-3"><a href="#cb23-3" tabindex="-1"></a> <span class="co"># this function implicitly returns an invisible NULL</span></span>
<span id="cb23-4"><a href="#cb23-4" tabindex="-1"></a>}</span></code></pre></div>
<p>Now we use the chunk option <code>render = dummy_print</code>:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb24-1"><a href="#cb24-1" tabindex="-1"></a><span class="dv">1</span> <span class="sc">+</span> <span class="dv">1</span></span>
<span id="cb24-2"><a href="#cb24-2" tabindex="-1"></a><span class="do">## I do not know what to print!</span></span>
<span id="cb24-3"><a href="#cb24-3" tabindex="-1"></a><span class="fu">head</span>(letters)</span>
<span id="cb24-4"><a href="#cb24-4" tabindex="-1"></a><span class="do">## I do not know what to print!</span></span>
<span id="cb24-5"><a href="#cb24-5" tabindex="-1"></a><span class="fu">list</span>(<span class="at">a =</span> <span class="dv">1</span>, <span class="at">b =</span> <span class="dv">9</span><span class="sc">:</span><span class="dv">4</span>)</span>
<span id="cb24-6"><a href="#cb24-6" tabindex="-1"></a><span class="do">## I do not know what to print!</span></span>
<span id="cb24-7"><a href="#cb24-7" tabindex="-1"></a><span class="fu">head</span>(mtcars)</span>
<span id="cb24-8"><a href="#cb24-8" tabindex="-1"></a><span class="do">## I do not know what to print!</span></span>
<span id="cb24-9"><a href="#cb24-9" tabindex="-1"></a><span class="fu">cat</span>(<span class="st">'This is cool.'</span>)</span>
<span id="cb24-10"><a href="#cb24-10" tabindex="-1"></a><span class="do">## This is cool.</span></span></code></pre></div>
<p>Note the <code>render</code> function is only applied to visible
objects. There are cases in which the objects returned are invisible,
e.g. those wrapped in <code>invisible()</code>.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb25-1"><a href="#cb25-1" tabindex="-1"></a><span class="dv">1</span> <span class="sc">+</span> <span class="dv">1</span></span></code></pre></div>
<pre><code>## [1] 2</code></pre>
<div class="sourceCode" id="cb27"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb27-1"><a href="#cb27-1" tabindex="-1"></a><span class="fu">invisible</span>(<span class="dv">1</span> <span class="sc">+</span> <span class="dv">1</span>)</span>
<span id="cb27-2"><a href="#cb27-2" tabindex="-1"></a><span class="fu">invisible</span>(<span class="fu">head</span>(mtcars))</span>
<span id="cb27-3"><a href="#cb27-3" tabindex="-1"></a>x <span class="ot">=</span> <span class="dv">1</span><span class="sc">:</span><span class="dv">10</span> <span class="co"># invisibly returns 1:10</span></span></code></pre></div>
</div>
<div id="metadata" class="section level3">
<h3>Metadata</h3>
<p>The print function can have a side effect of passing “metadata” about
objects to <strong>knitr</strong>, and <strong>knitr</strong> will
collect this information as it prints objects. The motivation of
collecting metadata is to store external dependencies of the objects to
be printed. Normally we print an object only to obtain a text
representation, but there are cases that can be more complicated. For
example, a <a href="https://ggvis.rstudio.com/"><strong>ggvis</strong></a> graph
requires external JavaScript and CSS dependencies such as
<code>ggvis.js</code>. The graph itself is basically a fragment of
JavaScript code, which will not work unless the required libraries are
loaded (in the HTML header). Therefore we need to collect the
dependencies of an object beside printing the object itself.</p>
<p>One way to specify the dependencies is through the <code>meta</code>
argument of <code>asis_output()</code>. Here is a pseudo example:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb28-1"><a href="#cb28-1" tabindex="-1"></a><span class="co"># pseudo code</span></span>
<span id="cb28-2"><a href="#cb28-2" tabindex="-1"></a>knit_print.ggvis <span class="ot">=</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb28-3"><a href="#cb28-3" tabindex="-1"></a> res <span class="ot">=</span> ggvis<span class="sc">::</span><span class="fu">print_this_object</span>(x)</span>
<span id="cb28-4"><a href="#cb28-4" tabindex="-1"></a> knitr<span class="sc">::</span><span class="fu">asis_output</span>(res, <span class="at">meta =</span> <span class="fu">list</span>(</span>
<span id="cb28-5"><a href="#cb28-5" tabindex="-1"></a> <span class="at">ggvis =</span> <span class="fu">list</span>(</span>
<span id="cb28-6"><a href="#cb28-6" tabindex="-1"></a> <span class="at">version =</span> <span class="st">'0.1.0'</span>,</span>
<span id="cb28-7"><a href="#cb28-7" tabindex="-1"></a> <span class="at">js =</span> <span class="fu">system.file</span>(<span class="st">'www'</span>, <span class="st">'js'</span>, <span class="st">'ggvis.js'</span>, <span class="at">package =</span> <span class="st">'ggvis'</span>),</span>
<span id="cb28-8"><a href="#cb28-8" tabindex="-1"></a> <span class="at">css =</span> <span class="fu">system.file</span>(<span class="st">'www'</span>, <span class="st">'www'</span>, <span class="st">'ggvis.css'</span>, <span class="at">package =</span> <span class="st">'ggvis'</span>)</span>
<span id="cb28-9"><a href="#cb28-9" tabindex="-1"></a> )</span>
<span id="cb28-10"><a href="#cb28-10" tabindex="-1"></a> ))</span>
<span id="cb28-11"><a href="#cb28-11" tabindex="-1"></a>}</span></code></pre></div>
<p>Then when <strong>knitr</strong> prints a <strong>ggvis</strong>
object, the <code>meta</code> information will be collected and stored.
After knitting is done, we can obtain a list of all the dependencies via
<code>knit_meta()</code>. It is very likely that there are duplicate
entries in the list, and it is up to the package authors to clean them
up, and process the metadata list in their own way (e.g. write the
dependencies into the HTML header). We give a few more quick and dirty
examples below to see how <code>knit_meta()</code> works.</p>
<p>Now we define a print method for <code>foo</code> objects:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb29-1"><a href="#cb29-1" tabindex="-1"></a><span class="fu">library</span>(knitr)</span>
<span id="cb29-2"><a href="#cb29-2" tabindex="-1"></a>knit_print.foo <span class="ot">=</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb29-3"><a href="#cb29-3" tabindex="-1"></a> res <span class="ot">=</span> <span class="fu">paste</span>(<span class="st">'> **This is a `foo` object**:'</span>, x)</span>
<span id="cb29-4"><a href="#cb29-4" tabindex="-1"></a> <span class="fu">asis_output</span>(res, <span class="at">meta =</span> <span class="fu">list</span>(</span>
<span id="cb29-5"><a href="#cb29-5" tabindex="-1"></a> <span class="at">js =</span> <span class="fu">system.file</span>(<span class="st">'www'</span>, <span class="st">'shared'</span>, <span class="st">'shiny.js'</span>, <span class="at">package =</span> <span class="st">'shiny'</span>),</span>
<span id="cb29-6"><a href="#cb29-6" tabindex="-1"></a> <span class="at">css =</span> <span class="fu">system.file</span>(<span class="st">'www'</span>, <span class="st">'shared'</span>, <span class="st">'shiny.css'</span>, <span class="at">package =</span> <span class="st">'shiny'</span>)</span>
<span id="cb29-7"><a href="#cb29-7" tabindex="-1"></a> ))</span>
<span id="cb29-8"><a href="#cb29-8" tabindex="-1"></a>}</span></code></pre></div>
<p>See what happens when we print <code>foo</code> objects:</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb30-1"><a href="#cb30-1" tabindex="-1"></a>new_foo <span class="ot">=</span> <span class="cf">function</span>(x) <span class="fu">structure</span>(x, <span class="at">class =</span> <span class="st">'foo'</span>)</span>
<span id="cb30-2"><a href="#cb30-2" tabindex="-1"></a><span class="fu">new_foo</span>(<span class="st">'hello'</span>)</span></code></pre></div>
<blockquote>
<p><strong>This is a <code>foo</code> object</strong>: hello</p>
</blockquote>
<p>Check the metadata now:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb31-1"><a href="#cb31-1" tabindex="-1"></a><span class="fu">str</span>(<span class="fu">knit_meta</span>(<span class="at">clean =</span> <span class="cn">FALSE</span>))</span></code></pre></div>
<pre><code>## List of 2
## $ js : chr "/Users/yihui/R/shiny/www/shared/shiny.js"
## $ css: chr ""
## - attr(*, "knit_meta_id")= chr [1:2] "unnamed-chunk-9" "unnamed-chunk-9"</code></pre>
<p>Another <code>foo</code> object:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb33-1"><a href="#cb33-1" tabindex="-1"></a><span class="fu">new_foo</span>(<span class="st">'world'</span>)</span></code></pre></div>
<blockquote>
<p><strong>This is a <code>foo</code> object</strong>: world</p>
</blockquote>
<p>Similarly for <code>bar</code> objects:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb34-1"><a href="#cb34-1" tabindex="-1"></a>knit_print.bar <span class="ot">=</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb34-2"><a href="#cb34-2" tabindex="-1"></a> <span class="fu">asis_output</span>(x, <span class="at">meta =</span> <span class="fu">list</span>(<span class="at">head =</span> <span class="st">'<script>console.log("bar!")</script>'</span>))</span>
<span id="cb34-3"><a href="#cb34-3" tabindex="-1"></a>}</span>
<span id="cb34-4"><a href="#cb34-4" tabindex="-1"></a>new_bar <span class="ot">=</span> <span class="cf">function</span>(x) <span class="fu">structure</span>(x, <span class="at">class =</span> <span class="st">'bar'</span>)</span>
<span id="cb34-5"><a href="#cb34-5" tabindex="-1"></a><span class="fu">new_bar</span>(<span class="st">'> **hello** world!'</span>)</span></code></pre></div>
<blockquote>
<p><strong>hello</strong> world!</p>
</blockquote>
<div class="sourceCode" id="cb35"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb35-1"><a href="#cb35-1" tabindex="-1"></a><span class="fu">new_bar</span>(<span class="st">'> hello **world**!'</span>)</span></code></pre></div>
<blockquote>
<p>hello <strong>world</strong>!</p>
</blockquote>
<p>The final version of the metadata, and clean it up:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb36-1"><a href="#cb36-1" tabindex="-1"></a><span class="fu">str</span>(<span class="fu">knit_meta</span>())</span></code></pre></div>
<pre><code>## List of 6
## $ js : chr "/Users/yihui/R/shiny/www/shared/shiny.js"
## $ css : chr ""
## $ js : chr "/Users/yihui/R/shiny/www/shared/shiny.js"
## $ css : chr ""
## $ head: chr "<script>console.log(\"bar!\")</script>"
## $ head: chr "<script>console.log(\"bar!\")</script>"
## - attr(*, "knit_meta_id")= chr [1:6] "unnamed-chunk-9" "unnamed-chunk-9" "unnamed-chunk-11" "unnamed-chunk-11" ...</code></pre>
<div class="sourceCode" id="cb38"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb38-1"><a href="#cb38-1" tabindex="-1"></a><span class="fu">str</span>(<span class="fu">knit_meta</span>()) <span class="co"># empty now, because clean = TRUE by default</span></span></code></pre></div>
<pre><code>## list()</code></pre>
</div>
<div id="for-package-authors" class="section level3">
<h3>For package authors</h3>
<p>If you are implementing a custom print method in your own package,
here are two tips:</p>
<ol style="list-style-type: decimal">
<li><p>With R >= 3.6.0 (2019-04-26), you can declare the S3 method in
the package <code>NAMESPACE</code> with
<code>S3method(knitr::knit_print, class)</code>. If you use
<strong>roxygen2</strong> package, that means a roxygen comment like
this:</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb40-1"><a href="#cb40-1" tabindex="-1"></a><span class="co">#' @exportS3Method knitr::knit_print</span></span>
<span id="cb40-2"><a href="#cb40-2" tabindex="-1"></a>knit_print.class <span class="ot"><-</span> </span></code></pre></div>
<p>With this method, you do not need to import <strong>knitr</strong> to
your package, i.e., <strong>knitr</strong> can be listed in
<code>Suggests</code> and not necessarily <code>Imports</code> in the
package <code>DESCRIPTION</code>. The S3 methods will be automatically
registered when <strong>knitr</strong> is actually loaded.</p>
<p>For R < 3.6.0, you need to import <code>knit_print</code> in your
package namespace via <code>importFrom(knitr, knit_print)</code> (or
roxygen: <code>#' @importFrom knitr knit_print</code>) (see <a href="https://github.com/yihui/printr">the <strong>printr</strong>
package</a> for an example).</p></li>
<li><p><code>asis_output()</code> is simply a function that marks an
object with the class <code>knit_asis</code>, and you do not have to
import this function to your package, either—just let your print method
return <code>structure(x, class = 'knit_asis')</code>, and if there are
additional metadata, just put it in the <code>knit_meta</code>
attribute; here is the source code of this function:</p>
<div class="sourceCode" id="cb41"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb41-1"><a href="#cb41-1" tabindex="-1"></a>knitr<span class="sc">::</span>asis_output</span></code></pre></div>
<pre><code>## function (x, meta = NULL, cacheable = NA)
## {
## structure(x, class = "knit_asis", knit_meta = meta, knit_cacheable = cacheable)
## }
## <bytecode: 0x10b02cb00>
## <environment: namespace:knitr></code></pre>
<p>You may put <strong>knitr</strong> in the <code>Suggests</code> field
in <code>DESCRIPTION</code>, and use <code>knitr::asis_output()</code>,
so that you can avoid the “hard” dependency on
<strong>knitr</strong>.</p></li>
</ol>
</div>
</div>
<div class="footnotes footnotes-end-of-document">
<hr />
<ol>
<li id="fn1"><p>Note R prints an object without an explicit
<code>print()</code> call when it is <em>visible</em>; see
<code>?invisible</code><a href="#fnref1" class="footnote-back">↩︎</a></p></li>
<li id="fn2"><p>The two hashes <code>##</code> were from the chunk
option <code>comment</code>; you can turn them off by
<code>comment = ''</code>.<a href="#fnref2" class="footnote-back">↩︎</a></p></li>
</ol>
</div>
<script type="text/javascript">
window.onload = function() {
var i, fig = 1, caps = document.getElementsByClassName('caption');
for (i = 0; i < caps.length; i++) {
var cap = caps[i];
if (cap.parentElement.className !== 'figure' || cap.nodeName !== 'P')
continue;
cap.innerHTML = '<span>Figure ' + fig + ':</span> ' + cap.innerHTML;
fig++;
}
fig = 1;
caps = document.getElementsByTagName('caption');
for (i = 0; i < caps.length; i++) {
var cap = caps[i];
if (cap.parentElement.nodeName !== 'TABLE') continue;
cap.innerHTML = '<span>Table ' + fig + ':</span> ' + cap.innerHTML;
fig++;
}
}
</script>
<!-- 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>