<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/shiny-1-2-0-plot-caching/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://blog.rstudio.com/2018/11/13/shiny-1-2-0/"> RStudio Blog</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>We’re pleased to announce the CRAN release of Shiny v1.2.0! This release features Plot Caching, an important new tool for improving performance and scalability in Shiny apps.</p>
<p>If you’re not familiar with the term “caching”, it just means that when we perform a time-consuming operation, we save (cache) the results so that the next time that operation is requested, we can skip the actual operation and instantly fetch the previously cached results. Shiny’s reactive expressions do some amount of caching for you already, and you can use more explicit techniques to cache the various operations you might do to your data (using the <a rel="nofollow" href="https://github.com/r-lib/memoise" target="_blank">memoise</a> package, or manually saving intermediate data frames to disk as CSV or RDS, for two examples).</p>
<p>Plots, being very common and (potentially) expensive-to-compute outputs, are great candidates for caching, and in theory you can use <code>renderImage</code> to accomplish this. But because Shiny’s <code>renderPlot</code> function contains a lot of complex infrastructure code, it’s actually quite a difficult task. Despite some valiant <a rel="nofollow" href="https://stackoverflow.com/questions/24192570/caching-plots-in-r-shiny" target="_blank">attempts</a>, all of the examples we’ve seen in the wild have had serious limitations we wanted to overcome.</p>
<p>Shiny v1.2.0 introduces a new function, <code>renderCachedPlot</code>, that makes it much easier to add plot caching to your app.</p>
<h3>Using <code>renderCachedPlot</code></h3>
<p>Let’s take a simple, but expensive, plot output:</p>
<pre><code>output$plot &lt;- renderPlot({
  ggplot(diamonds, aes(carat, price, color = !!input$color_by)) +
    geom_point()
})
</code></pre>
<p>The <code>diamonds</code> dataset has 53,940 rows. On my laptop, this takes about 1580 milliseconds (1.58 seconds). Perhaps that’s fast enough for doing exploratory data analysis, but it’s slower than we’d like for a high traffic Shiny app.</p>
<p>We can tell Shiny to cache this plot in two steps.</p>
<ol>
<li>Change <code>renderPlot</code> to <code>renderCachedPlot</code>.</li>
<li>Provide a suitable <code>cacheKeyExpr</code>. This is an expression that Shiny will use to determine which invocations of <code>renderCachedPlot</code> should be considered equivalent to each other. (In this case, two plots with different <code>input$color_by</code> values can’t be considered the “same” plot, so the <code>cacheKeyExpr</code> needs to have <code>input$color_by</code>.)</li>
</ol>
<pre><code>output$plot &lt;- renderCachedPlot({
  ggplot(diamonds, aes(carat, price, color = !!input$color_by)) +
    geom_point()
}, cacheKeyExpr = { input$color_by })
</code></pre>
<p>With these code changes, the first time a plot with a particular <code>input$color_by</code> is requested, it will take the normal amount of time. But the next time it is requested, it will be almost instant, as the previously rendered plot will be reused.</p>
<h3>Benchmarking the results</h3>
<p>To quantify the performance difference between the cached and uncached versions, I turned each into a minimal Shiny app (<a rel="nofollow" href="https://gist.github.com/jcheng5/1f09a0939ae45fd36f286a158bcb0dfb" target="_blank">source</a>). This app simply provides the <code>color_by</code> input using the new <code>varSelectInput</code> control, and then renders the output using either of the two code examples above. Then I used our (still-in-development) <a rel="nofollow" href="https://resources.rstudio.com/rstudio-conf-2018/scaling-shiny-sean-lopp" target="_blank">Shiny load testing tools</a> to record a test script, and “replay” it against both versions of the app, each running in a single R process.</p>
<p>I tested the <code>renderPlot</code> version of the app with 5 concurrent users, and the <code>renderCachedPlot</code> version with 25, 50, and 100 concurrent users. The difference in performance is as dramatic as we’d expect:</p>
<p><img alt="A chart showing that renderCachedPlot with 100 users is faster than renderPlot with 5 users" src="https://i1.wp.com/blog.rstudio.com/images/2018-11-05-shiny-1-2-0.png?w=450&ssl=1" data-recalc-dims="1"></p>
<p>With only five concurrent users, the latency is already pretty bad with the <code>renderPlot</code> version. (Note that this isn’t intended to represent typical performance with Shiny apps in general! We chose a particularly torturous ggplot on purpose.)</p>
<p>On the other hand, the <code>renderCachedPlot</code> version doesn’t break a sweat with 50 concurrent users; and even at 100 concurrent users, the latency is still acceptable.</p>
<h3>When to use plot caching</h3>
<p>A Shiny app is a good candidate for plot caching if:</p>
<ol>
<li>The app has plot outputs that are time-consuming to generate,</li>
<li>These plots are a significant fraction of the total amount of time the app spends thinking, and</li>
<li>Most users are likely to request the same few plots.</li>
</ol>
<p>Since our test has a pretty expensive plot as its only output, and our load testing tools simulate <em>n</em> users all doing the same exact thing, this these numbers reflect a best-case scenario for plot caching.</p>
<p>Shiny can store your cached plots in memory, on disk, or with another backend like <a rel="nofollow" href="https://redis.io/" target="_blank">Redis</a>. You also have a number of options for limiting the size of the cache. Be sure to read <a rel="nofollow" href="http://shiny.rstudio.com/articles/plot-caching.html" target="_blank">this article</a> to get the most out of this feature!</p>
<p>In future releases of Shiny, we hope to build on this foundation we’ve laid to dramatically speed up other areas of Shiny apps, like reactive expressions and non-plot outputs. In the meantime, we hope you find plot caching to be a useful addition to your performance toolkit!</p>
<h2>Other changes in Shiny v1.2.0</h2>
<ul>
<li>
<p>Upgrade FontAwesome from 4.7.0 to 5.3.1 and made <code>icon</code> tags browsable, which means they will display in a web browser or RStudio viewer by default (<a rel="nofollow" href="https://github.com/rstudio/shiny/issues/2186" target="_blank">#2186</a>). Note that if your application or library depends on FontAwesome directly using custom CSS, you may need to make some or all of the changes recommended in <a rel="nofollow" href="https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4" target="_blank">Upgrade from Version 4</a>. Font Awesome icons can also now be used in static R Markdown documents.</p>
</li>
<li>
<p>Address <a rel="nofollow" href="https://github.com/rstudio/shiny/issues/174" target="_blank">#174</a>: Added <code>datesdisabled</code> and <code>daysofweekdisabled</code> as new parameters to <code>dateInput()</code>. This resolves <a rel="nofollow" href="https://github.com/rstudio/shiny/issues/174" target="_blank">#174</a> and exposes the underlying arguments of <a rel="nofollow" href="http://bootstrap-datepicker.readthedocs.io/en/latest/options.html#datesdisabled" target="_blank">Bootstrap Datepicker</a>. <code>datesdisabled</code> expects a character vector with values in <code>yyyy/mm/dd</code> format and <code>daysofweekdisabled</code> expects an integer vector with day interger ids (Sunday=0, Saturday=6). The default value for both is <code>NULL</code>, which leaves all days selectable. Thanks, @nathancday! <a rel="nofollow" href="https://github.com/rstudio/shiny/pull/2147" target="_blank">#2147</a></p>
</li>
</ul>
<p>See the <a rel="nofollow" href="http://shiny.rstudio.com/reference/shiny/1.2.0/upgrade.html" target="_blank">full changelog for Shiny v1.2.0</a> for other minor improvements and bug fixes we’ve made in this release.</p>
	<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://blog.rstudio.com/2018/11/13/shiny-1-2-0/"> RStudio Blog</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a rel="nofollow" href="https://www.r-project.org/" title="The R Project for Statistical Computing">R</a> news and <a rel="nofollow" href="https://www.r-bloggers.com/search/tutorial" title="R tutorials">tutorials</a> on topics such as: <a rel="nofollow" href="https://www.r-bloggers.com/search/data%20science" title="Data science">Data science</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data" title="Big Data">Big Data, <a rel="nofollow" href="https://www.r-users.com/" title="R jobs">R jobs</a>, visualization (<a rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2" title="ggplot and ggplot2 tutorials">ggplot2</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/boxplot" title="Boxplots using lattice and ggplot2 tutorials">Boxplots</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/map" title="Maps and gis">maps</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/animation" title="Animation in R">animation</a>), programming (<a rel="nofollow" href="https://www.r-bloggers.com/search/RStudio" title="RStudio IDE for R">RStudio</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/sweave" title="Sweave and literate programming">Sweave</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX" title="LaTeX in R">LaTeX</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/SQL" title="SQL and databases">SQL</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/eclipse" title="Eclipse IDE for R">Eclipse</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/git" title="git and github, Version Control System">git</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/hadoop" title="Large data in R using Hadoop">hadoop</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping" title="Web Scraping of google, facebook, yahoo, twitter and more using R">Web Scraping</a>) statistics (<a rel="nofollow" href="https://www.r-bloggers.com/search/regression" title="Regressions and ANOVA analysis tutorials">regression</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/PCA" title="principal component analysis tutorial">PCA</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/time+series" title="Time series">time series</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/trading" title="finance trading">trading</a>) and more...
<img width="1" alt="" src="http://feeds.feedburner.com/~r/RBloggers/~4/TuJ02pU7hmk" height="1"></a></div>