<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Cédric Scherer</title>
    <link>https://www.cedricscherer.com/</link>
    <description>Recent content on Cédric Scherer</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 01 Apr 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://www.cedricscherer.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>30DayChartChallenge</title>
      <link>https://www.cedricscherer.com/tags/30daychartchallenge/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/30daychartchallenge/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Cédric Scherer</title>
      <link>https://www.cedricscherer.com/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/</guid>
      <description></description>
    </item>
    
    <item>
      <title>DataViz</title>
      <link>https://www.cedricscherer.com/tags/dataviz/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/dataviz/</guid>
      <description></description>
    </item>
    
    <item>
      <title>DataWrapper</title>
      <link>https://www.cedricscherer.com/tags/datawrapper/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/datawrapper/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Exploring &#34;The Simpsons&#34; with DataWrapper for the 30DayChartChallenge 2024</title>
      <link>https://www.cedricscherer.com/2024/04/01/contributions-30daychartchallenge-2024/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/2024/04/01/contributions-30daychartchallenge-2024/</guid>
      <description>&lt;h2 id=&#34;whats-the-challenge-about&#34;&gt;What&amp;rsquo;s the Challenge About?&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;#30DayChartChallenge&lt;/strong&gt; is a data visualization challenge with the aim to create a data visualization on a certain topic for each day of April. Anyone is welcome to contribute, no matter which data source or tool is used to create the visualizations. This year, Dominic and I are hosting the fourth edition with a new set of prompts:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cedricscherer.com/img/banner/30daychartchallenge-prompts.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Find out more about the history of the challenge and explore inspirational contributions in my &lt;a href=&#34;https://nightingaledvs.com/the-30daychartchallenge-year-three/&#34;&gt;Nightingale article from last year&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I will not participate on every day this year as there are too many other responsibilities I am involved in currently. But for the few prompts I am participating in, my goal is to explore the possibilities to create interactive, responsive visualizations with &lt;a href=&#34;https://www.datawrapper.de/&#34;&gt;DataWrapper&lt;/a&gt;. And to keep it simple so I&amp;rsquo;ve decided to set some constraints for my contributions.&lt;/p&gt;
&lt;h5 id=&#34;self-imposed-rules&#34;&gt;Self-Imposed Rules&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data:&lt;/strong&gt; visualize a &amp;ldquo;The Simpsons&amp;rdquo; data set (&lt;a href=&#34;https://github.com/rfordatascience/tidytuesday/blob/master/data/2019/2019-08-27/simpsons-guests.csv&#34;&gt;1&lt;/a&gt;, &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_The_Simpsons_episodes#endnote_april&#34;&gt;2&lt;/a&gt;, and &lt;a href=&#34;https://github.com/jcrodriguez1989/thesimpsons&#34;&gt;3&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool:&lt;/strong&gt; use &lt;a href=&#34;https://www.datawrapper.de/&#34;&gt;DataWrapper&lt;/a&gt; to create the charts (and resist to use ggplot2 👀)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Colors:&lt;/strong&gt; pick colors from &amp;ldquo;The Simpsons&amp;rdquo; (&lt;a href=&#34;https://www.designboom.com/design/did-the-simpsons-predict-every-pantone-color-of-the-year-12-23-2019/&#34;&gt;1&lt;/a&gt; and &lt;a href=&#34;https://hypebeast.com/2015/9/the-simpsons-pantone&#34;&gt;2&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fun:&lt;/strong&gt; choose a suitable quote from &amp;ldquo;The Simpsons&amp;rdquo; for the title&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://www.cedricscherer.com/img/dataviz-posts/30daychartchallenge-wrap-up.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day1&#34;&gt;Day 1: Part-to-Whole&lt;/h3&gt;
&lt;iframe title=&#34;Ah, geez!!!&#34; aria-label=&#34;Donut Chart&#34; id=&#34;datawrapper-chart-n0YjQ&#34; src=&#34;https://datawrapper.dwcdn.net/n0YjQ/5/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;492&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day2&#34;&gt;Day 2: Neo*&lt;/h3&gt;
&lt;iframe title=&#34;Hello! Hello, Springfield! Look at me and my purple hair!&#34; aria-label=&#34;Scatter Plot&#34; id=&#34;datawrapper-chart-M99F9&#34; src=&#34;https://datawrapper.dwcdn.net/M99F9/11/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;502&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;&lt;br&gt;&lt;br&gt;
&lt;div style=&#39;font-size:11pt;line-height:1.2;&#39;&gt;* The prompt was often interpreted as &#34;new&#34; (or &#34;Neo&#34; from &#34;Matrix&#34;) but Dominic&#39;s original idea was &#34;neon colors&#34; :)&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day4&#34;&gt;Day 4: Waffle&lt;/h3&gt;
&lt;iframe title=&#34;I want more bananas on my waffles!!&#34; aria-label=&#34;Scatter Plot&#34; id=&#34;datawrapper-chart-n66UU&#34; src=&#34;https://datawrapper.dwcdn.net/n66UU/12/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;513&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day4&#34;&gt;Day 5: Diverging&lt;/h3&gt;
&lt;iframe title=&#34;Welcome to Diversity Tales&#34; aria-label=&#34;Split Bars&#34; id=&#34;datawrapper-chart-m6S0p&#34; src=&#34;https://datawrapper.dwcdn.net/m6S0p/4/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;1104&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day9&#34;&gt;Day 9: Major/Minor&lt;/h3&gt;
&lt;iframe title=&#34;We happen to be watching this very educational cartoon.&#34; aria-label=&#34;Interactive line chart&#34; id=&#34;datawrapper-chart-gUrI7&#34; src=&#34;https://datawrapper.dwcdn.net/gUrI7/10/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;525&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day10&#34;&gt;Day 10: Physical&lt;/h3&gt;
&lt;iframe title=&#34;Home Sweet Homediddly-Dum-Doodily&#34; aria-label=&#34;Range Plot&#34; id=&#34;datawrapper-chart-gD19A&#34; src=&#34;https://datawrapper.dwcdn.net/gD19A/15/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;604&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;div style=&#39;line-height:1.3;&#39;&gt;&lt;br&gt;&lt;b&gt;Note:&lt;/b&gt; While trying to create a lollipop chart, I ended up with this column chart with gradient fills—it&#39;s actually a &lt;a href=&#39;https://www.datawrapper.de/charts/range-plot&#39; target=&#34;_blank&#34;&gt;range plot&lt;/a&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day11&#34;&gt;Day 11: Mobile-friendly&lt;/h3&gt;
&lt;iframe title=&#34;I&#39;d rather call you by your normal name, if that&#39;s okay.&#34; aria-label=&#34;Multiple Pies&#34; id=&#34;datawrapper-chart-MTNQJ&#34; src=&#34;https://datawrapper.dwcdn.net/MTNQJ/7/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;427&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;br&gt;
&lt;h5 id=&#34;stacked-bars-for-members-of-the-no-pie-chart-gang&#34;&gt;Stacked Bars for Members of the &amp;ldquo;No Pie Chart Gang&amp;rdquo;&lt;/h5&gt;
&lt;iframe title=&#34;I&#39;d rather call you by your normal name, if that&#39;s okay.&#34; aria-label=&#34;Stacked Bars&#34; id=&#34;datawrapper-chart-S0ggI&#34; src=&#34;https://datawrapper.dwcdn.net/S0ggI/6/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;483&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day13&#34;&gt;Day 13: Family&lt;/h3&gt;
&lt;iframe title=&#34;He gets it from your side of the family, you know. No monsters on my side.&#34; aria-label=&#34;Scatter Plot&#34; id=&#34;datawrapper-chart-ZHm4s&#34; src=&#34;https://datawrapper.dwcdn.net/ZHm4s/12/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;612&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;div style=&#39;line-height:1.3;&#39;&gt;&lt;br&gt;&lt;b&gt;Note:&lt;/b&gt; The family tree is build with a &lt;a href=&#39;https://academy.datawrapper.de/article/146-customizing-your-scatter-plot-annotate&#39; target=&#34;_blank&#34;&gt;scatter plot with lots of custom annotation lines&lt;/a&gt;.&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day14&#34;&gt;Day 14: Heatmap&lt;/h3&gt;
&lt;iframe title=&#34;Hey, what do you want from me? I do a kid show! And it&#39;s a classic.&#34; aria-label=&#34;Map&#34; id=&#34;datawrapper-chart-4fprx&#34; src=&#34;https://datawrapper.dwcdn.net/4fprx/12/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;952&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;div style=&#39;line-height:1.3;&#39;&gt;&lt;br&gt;&lt;b&gt;Note:&lt;/b&gt; The chart is actually a &lt;a href=&#39;https://www.datawrapper.de/maps/choropleth-map&#39; target=&#34;_blank&#34;&gt;choropleth map&lt;/a&gt; to ensure that the heatmap looks good independent of the screen width.&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day19&#34;&gt;Day 19: Dinosaurs&lt;/h3&gt;
&lt;iframe title=&#34;I&#39;m starting to think we&#39;ll never see him again.&#34; aria-label=&#34;Multiple Lines&#34; id=&#34;datawrapper-chart-c0kk2&#34; src=&#34;https://datawrapper.dwcdn.net/c0kk2/3/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;970&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day20&#34;&gt;Day 20: Correlation&lt;/h3&gt;
&lt;iframe title=&#34;Now, at the risk of being unpopular, this reporter places the blame for all this squarely on you, the viewers.&#34; aria-label=&#34;Scatter Plot&#34; id=&#34;datawrapper-chart-GukTP&#34; src=&#34;https://datawrapper.dwcdn.net/GukTP/5/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;614&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day22&#34;&gt;Day 22: Mobility&lt;/h3&gt;
&lt;iframe title=&#34;Beep beep! Out of my way! I’m a motorist!&#34; aria-label=&#34;Table&#34; id=&#34;datawrapper-chart-yp6fa&#34; src=&#34;https://datawrapper.dwcdn.net/yp6fa/4/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;878&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day28&#34;&gt;Day 28: Trend&lt;/h3&gt;
&lt;iframe title=&#34;You talk too much. Abraca-blab-ra.&#34; aria-label=&#34;Interactive line chart&#34; id=&#34;datawrapper-chart-rF4Vr&#34; src=&#34;https://datawrapper.dwcdn.net/rF4Vr/3/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;585&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;day29&#34;&gt;Day 29: Black&amp;rsquo;n&amp;rsquo;White &lt;b style=&#39;color:#818181;&#39;&gt;(or Day 27: Good/Bad)&lt;/b&gt;&lt;/h3&gt;
&lt;iframe title=&#34;Why should I spend half my Sunday hearing about how I&#39;m going to Hell?&#34; aria-label=&#34;Grouped Columns&#34; id=&#34;datawrapper-chart-PQxAx&#34; src=&#34;https://datawrapper.dwcdn.net/PQxAx/5/&#34; scrolling=&#34;no&#34; frameborder=&#34;0&#34; style=&#34;width: 0; min-width: 100% !important; border: none;&#34; height=&#34;560&#34; data-external=&#34;1&#34;&gt;&lt;/iframe&gt;&lt;script type=&#34;text/javascript&#34;&gt;!function(){&#34;use strict&#34;;window.addEventListener(&#34;message&#34;,(function(a){if(void 0!==a.data[&#34;datawrapper-height&#34;]){var e=document.querySelectorAll(&#34;iframe&#34;);for(var t in a.data[&#34;datawrapper-height&#34;])for(var r=0;r&lt;e.length;r++)if(e[r].contentWindow===a.source){var i=a.data[&#34;datawrapper-height&#34;][t]+&#34;px&#34;;e[r].style.height=i}}}))}();&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Posts</title>
      <link>https://www.cedricscherer.com/post/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/post/</guid>
      <description></description>
    </item>
    
    <item>
      <title>R</title>
      <link>https://www.cedricscherer.com/tags/r/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/r/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Tags</title>
      <link>https://www.cedricscherer.com/tags/</link>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Annotations</title>
      <link>https://www.cedricscherer.com/tags/annotations/</link>
      <pubDate>Thu, 26 Oct 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/annotations/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Ggplot2</title>
      <link>https://www.cedricscherer.com/tags/ggplot2/</link>
      <pubDate>Thu, 26 Oct 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/ggplot2/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Tidyverse</title>
      <link>https://www.cedricscherer.com/tags/tidyverse/</link>
      <pubDate>Thu, 26 Oct 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/tidyverse/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Tutorial</title>
      <link>https://www.cedricscherer.com/tags/tutorial/</link>
      <pubDate>Thu, 26 Oct 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/tutorial/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Yet Another How-to on Labelling Bar Graphs in ggplot2</title>
      <link>https://www.cedricscherer.com/2023/10/26/yet-another-how-to-on-labelling-bar-graphs-in-ggplot2/</link>
      <pubDate>Thu, 26 Oct 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/2023/10/26/yet-another-how-to-on-labelling-bar-graphs-in-ggplot2/</guid>
      <description>


&lt;p style=&#34;font-size:14px&#34;&gt;
Header visualization from &lt;a href=&#34;https://www.daserste.de/information/talk/maischberger/sendung/maischberger-838.html&#34; target=_blank&gt;“maischberger”&lt;/a&gt; (see my &lt;a href=&#34;https://www.cedricscherer.com/2023/10/26/yet-another-how-to-on-labelling-bar-graphs-in-ggplot2/#note-header&#34;&gt;note&lt;/a&gt; below)
&lt;/p&gt;
&lt;div id=&#34;table-of-content&#34; class=&#34;section level5&#34;&gt;
&lt;h5&gt;Table of Content&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#intro&#34;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#data&#34;&gt;Data Preparation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#basic-bars&#34;&gt;Create a Basic Bar Chart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#theming&#34;&gt;Style the Visualization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#placing-labels&#34;&gt;Place Category Labels on the Top&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#styling&#34;&gt;Bonus: Style the Bars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#alternative-approach&#34;&gt;Alternative Approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#conclusion&#34;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;div id=&#34;intro&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Yes, &lt;a href=&#34;https://www.cedricscherer.com/2021/07/05/a-quick-how-to-on-labelling-bar-graphs-in-ggplot2/&#34;&gt;I have written about creating bar charts with &lt;code&gt;{ggplot2}&lt;/code&gt; before&lt;/a&gt;. As one of the most common chart types, creating bar charts is a task that thousands of people likely face every day. In an old blog post I’ve shown various ways&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;how to &lt;a href=&#34;https://www.cedricscherer.com/2021/07/05/a-quick-how-to-on-labelling-bar-graphs-in-ggplot2/#how-to-1&#34;&gt;calculate the percentage values&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;how to &lt;a href=&#34;https://www.cedricscherer.com/2021/07/05/a-quick-how-to-on-labelling-bar-graphs-in-ggplot2/#how-to-2&#34;&gt;position the percentage labels inside&lt;/a&gt;, and&lt;/li&gt;
&lt;li&gt;how to &lt;a href=&#34;https://www.cedricscherer.com/2021/07/05/a-quick-how-to-on-labelling-bar-graphs-in-ggplot2/#how-to-3&#34;&gt;color the bars using different colors&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Inspired by a question by one of my clients, I am now extending that list by showcasing&lt;/p&gt;
&lt;ol start=&#34;4&#34; style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;how to &lt;strong&gt;place the category labels above the bars&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;div id=&#34;data&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Data Preparation&lt;/h2&gt;
&lt;p&gt;I am using the &lt;code&gt;diamonds&lt;/code&gt; data set from the &lt;code&gt;{ggplot2}&lt;/code&gt; package to generate shares of diamonds for five different categories describing the quality of the cut. In a first step, I am calculating the shares per quality and turn the categories into a factor ordered by that metric.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(dplyr)
library(ggplot2)

diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, prop))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 5 × 2
##   cut         prop
##   &amp;lt;ord&amp;gt;      &amp;lt;dbl&amp;gt;
## 1 Ideal     0.400 
## 2 Premium   0.256 
## 3 Good      0.0910
## 4 Very Good 0.224 
## 5 Fair      0.0298&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are multiple other ways to calculate the shares, including &lt;code&gt;diamonds |&amp;gt; mutate(n = n()) |&amp;gt; summarize(prop = n() / unique(n), .by = cut)&lt;/code&gt;. Instead of using the experimental &lt;code&gt;.by&lt;/code&gt; argument you can also group your data first with &lt;code&gt;group_by(cut)&lt;/code&gt; before summarizing per cut quality.&lt;/p&gt;
&lt;p&gt;The last step is not needed in our example case here as the ranking by shares follows the defined order of the cut qualities. However, in most other cases you likely have to sort your categories on your own.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;basic-bars&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Create a Basic Bar Chart&lt;/h2&gt;
&lt;p&gt;Now, I can easily pass the summarized data set to &lt;code&gt;ggplot()&lt;/code&gt; and create a simple horizontal bar graph:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col()&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/basic-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
The default horizontal bar chart, ranked by shares.
&lt;/figcaption&gt;
&lt;p&gt;Alternatively, you can transform the complete data set on the fly instead of calculating shares first:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(diamonds, aes(y = cut, x = after_stat(count / sum(count)))) +
  geom_bar()&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/basic-after-stat-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
The same default horizontal bar chart, this time created with &lt;code&gt;geom_bar()&lt;/code&gt; and &lt;code&gt;after_stat()&lt;/code&gt;.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div id=&#34;theming&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Style the Visualization&lt;/h2&gt;
&lt;p&gt;If you know me a bit, you know that before moving on I &lt;strong&gt;have to&lt;/strong&gt; modify the theme and fix the grid lines (read: remove them all together in this case).&lt;/p&gt;
&lt;p&gt;Also, I am modifying the x axis range and labels. Instead of showing proportions, I decide to show percentages (0-100). Also, to follow good practice I am adding the percentage label to the axis using &lt;code&gt;label_percent()&lt;/code&gt; from the &lt;code&gt;{scales}&lt;/code&gt; package. I am also removing the padding on the left and right of the bars and adjust the limits so that the 40% label is shown as well.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;theme_set(theme_minimal(base_family = &amp;quot;Spline Sans&amp;quot;))
theme_update(
  panel.grid.minor = element_blank(),
  panel.grid.major = element_blank(),
  axis.line.x = element_line(color = &amp;quot;grey80&amp;quot;, linewidth = .4),
  axis.ticks.x = element_line(color = &amp;quot;grey80&amp;quot;, linewidth = .4),
  axis.title.y = element_blank(),
  plot.margin = margin(10, 15, 10, 15)
)

diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col() +
  scale_x_continuous(
    expand = c(0, 0), limits = c(0, .4),
    labels = scales::label_percent(),
    name = &amp;quot;Proportion&amp;quot;
  ) &lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/theming-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
The same bar chart with a modified theme and a polished x axis.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div id=&#34;placing-labels&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Place Category Labels on the Top&lt;/h2&gt;
&lt;p&gt;The approach I take to now to move the labels to the top of the bars is: faceting!&lt;/p&gt;
&lt;p&gt;There are multiple options including placing the labels with geom_text and shifting them upwards. But by far the fastest way (and also likely the one that breaks last when the number of bars changes) is using the facet functionality of &lt;code&gt;{ggplot2}&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col() +
  facet_wrap(~ cut) +
  scale_x_continuous(
    expand = c(0, 0), limits = c(0, .4),
    labels = scales::label_percent(),
  )&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/facet-default-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
Creating small multiples based on the variable mapped to the y axis leads to a set of mostly empty panels with redundant labels by default.
&lt;/figcaption&gt;
&lt;p&gt;It doesn’t work “out of the box”, however. But that’s a quick fix if you know about the &lt;code&gt;ncol&lt;/code&gt; and the &lt;code&gt;scales&lt;/code&gt; arguments in the &lt;code&gt;facet_wrap()&lt;/code&gt; function! The trick is that we force all small multiples in a single column (so that bars share a common baseline again) by setting &lt;code&gt;ncol = 1&lt;/code&gt;. By default, the axis ranges are kept constant across small multiples. By setting &lt;code&gt;scales = &#34;free_y&#34;&lt;/code&gt; we can &lt;em&gt;free&lt;/em&gt; the axis range which removes redundant, empty groups and all the resulting white space.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, -prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col() +
  facet_wrap(~ cut, ncol = 1, scales = &amp;quot;free_y&amp;quot;) +
  scale_x_continuous(
    name = &amp;quot;Proportion&amp;quot;, expand = c(0, 0), 
    limits = c(0, .4), labels = scales::label_percent()
  )&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/facet-adjust-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
Now, our facets looks like a regular bar chart. However, we have redundant labels which we remove in the next step.
&lt;/figcaption&gt;
&lt;p&gt;Note that we also have to flip the order of our categories as now they’re ordered top to bottom, not bottom to top anymore.&lt;/p&gt;
&lt;p&gt;The final step is cleaning up the labels. First, let’s remove the category names on the y axis by passing &lt;code&gt;guide = &#34;none&#34;&lt;/code&gt; in &lt;code&gt;scale_y_discrete()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To modify the new labels, the so-called strip texts, we address the text element &lt;code&gt;strip.text&lt;/code&gt; via &lt;code&gt;theme()&lt;/code&gt;. The margin of zero on the left ensures that, together with the horizontal justification (&lt;code&gt;hjust = 0&lt;/code&gt;) that the strip text labels are full left-aligned with the baseline of the bars. The small margin at the top and the bottom ensure that the labels are not clipped (e.g. that the descender of y is shown completely).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, -prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col() +
  facet_wrap(~ cut, ncol = 1, scales = &amp;quot;free_y&amp;quot;) +
  scale_x_continuous(
    name = &amp;quot;Proportion&amp;quot;, expand = c(0, 0), 
    limits = c(0, .4), labels = scales::label_percent()
  ) +
  scale_y_discrete(guide = &amp;quot;none&amp;quot;) +
  theme(strip.text = element_text(
    hjust = 0, margin = margin(1, 0, 1, 0), 
    size = rel(1.1), face = &amp;quot;bold&amp;quot;
  ))&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/strip-text-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
The polished new y axis labels, originally strip text of small multiples, replace the original axis labels.
&lt;/figcaption&gt;
&lt;p&gt;To add some spacing between the last bar and the axis line, one can adjust the vertical padding of each panel by passing &lt;code&gt;expansion(add = c(.8, .6)&lt;/code&gt; to the &lt;code&gt;expand&lt;/code&gt; argument in &lt;code&gt;scale_y_discrete()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, -prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col() +
  facet_wrap(~ cut, ncol = 1, scales = &amp;quot;free_y&amp;quot;) +
  scale_x_continuous(
    name = &amp;quot;Proportion&amp;quot;, expand = c(0, 0), 
    limits = c(0, .4), labels = scales::label_percent()
  ) +
  scale_y_discrete(
    guide = &amp;quot;none&amp;quot;, expand = expansion(add = c(.8, .6))
  ) +
  theme(strip.text = element_text(
    hjust = 0, margin = margin(1, 0, 1, 0), 
    size = rel(1.1), face = &amp;quot;bold&amp;quot;
  ))&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/expansion-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
The final version with polished category labels by adjusting the strip text of the facets.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div id=&#34;styling&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Bonus: Style the Bars&lt;/h2&gt;
&lt;p&gt;Let’s merge this new approach with some of the &lt;a href=&#34;https://www.cedricscherer.com/2021/07/05/a-quick-how-to-on-labelling-bar-graphs-in-ggplot2/&#34;&gt;tricks from my previous blog post&lt;/a&gt;. We add direct labels and highlight the top-ranked category.&lt;/p&gt;
&lt;div id=&#34;highlight-top-ranked-category&#34; class=&#34;section level4&#34;&gt;
&lt;h4&gt;Highlight Top-Ranked Category&lt;/h4&gt;
&lt;p&gt;By mapping the cut variable to fill, bars would be colored by categories. To color only the first, top ranked bar, I am making use of the rank which is equal to the factor level. Thus, mapping the fill to &lt;code&gt;as.numeric(cut) == 1)&lt;/code&gt; returns &lt;code&gt;TRUE&lt;/code&gt; for “Ideal” and &lt;code&gt;FALSE&lt;/code&gt; otherwise. To customize the fill colors, we add &lt;code&gt;scale_fill_manual()&lt;/code&gt; to pass a vector of two custom colors. As we don’t need a legend, we also set &lt;code&gt;guide = &#34;none&#34;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p &amp;lt;- 
  diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, -prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col(aes(fill = as.numeric(cut) == 1)) +
  facet_wrap(~ cut, ncol = 1, scales = &amp;quot;free_y&amp;quot;) +
  scale_x_continuous(
    name = &amp;quot;Proportion&amp;quot;, expand = c(0, 0), 
    limits = c(0, .4), labels = scales::label_percent()
  ) +
  scale_y_discrete(guide = &amp;quot;none&amp;quot;, expand = expansion(add = c(.8, .6))) +
  scale_fill_manual(values = c(&amp;quot;grey50&amp;quot;, &amp;quot;#1D785A&amp;quot;), guide = &amp;quot;none&amp;quot;) +
  theme(strip.text = element_text(
    hjust = 0, margin = margin(1, 0, 1, 0), 
    size = rel(1.1), face = &amp;quot;bold&amp;quot;
  ))

p&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/color-encoding-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
Drawing attention to the top-ranked category by using a different fill color.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div id=&#34;add-percentages-as-direct-labels&#34; class=&#34;section level4&#34;&gt;
&lt;h4&gt;Add Percentages as Direct Labels&lt;/h4&gt;
&lt;p&gt;Similarly, we can pass an expression to &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;hjust&lt;/code&gt; inside the &lt;code&gt;geom_text()&lt;/code&gt; component that we use to add the direct labels. As &lt;code&gt;TRUE&lt;/code&gt; is encoded as &lt;code&gt;1&lt;/code&gt;, all group that have a share lower than 5% are right-aligned while all others are left-aligned (as &lt;code&gt;FALSE&lt;/code&gt; = &lt;code&gt;0&lt;/code&gt;). To move the labels a bit more inside and outside, respectively, I am cheating by adding some spaces before and after the label.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p +
  geom_text(
    aes(label = paste0(&amp;quot;  &amp;quot;, sprintf(&amp;quot;%2.1f&amp;quot;, prop * 100), &amp;quot;%  &amp;quot;), 
        color = prop &amp;gt; .05, hjust = prop &amp;gt; .05),
    size = 4, fontface = &amp;quot;bold&amp;quot;, family = &amp;quot;Spline Sans&amp;quot;
  ) +
  scale_color_manual(values = c(&amp;quot;black&amp;quot;, &amp;quot;white&amp;quot;), guide = &amp;quot;none&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/percentage-labels-a-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
Now, the bars are labelled directly including a rule that automatically places the labels inside the bars as long as they are wide enough to fit the label.
&lt;/figcaption&gt;
&lt;p&gt;Alternatively, you can pass the value for &lt;code&gt;hjust&lt;/code&gt; directly by using an &lt;code&gt;ifelse&lt;/code&gt;or &lt;code&gt;if_else&lt;/code&gt; condition: &lt;code&gt;hjust = if_else(prop &amp;gt; .05, 1.2, -.2)&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p +
  geom_text(
    aes(label = paste0(sprintf(&amp;quot;%2.1f&amp;quot;, prop * 100), &amp;quot;%&amp;quot;), 
        color = prop &amp;gt; .05, hjust = if_else(prop &amp;gt; .05, 1.2, -.2)),
    size = 4, fontface = &amp;quot;bold&amp;quot;, family = &amp;quot;Spline Sans&amp;quot;
  ) +
  scale_color_manual(values = c(&amp;quot;black&amp;quot;, &amp;quot;white&amp;quot;), guide = &amp;quot;none&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same logic applies when we want to control the text color, which is recommended here to increase the contrast. With the final &lt;code&gt;scale_color_manual()&lt;/code&gt; I change the text color to white in case the label is placed inside the bar and black otherwise.&lt;/p&gt;
&lt;p&gt;Another way to style the labels would be &lt;code&gt;scales::label_percent(accuracy = .1, prefix = &#34;  &#34;, suffix = &#34;%  &#34;)(prop)&lt;/code&gt; (or make use of the superseded &lt;code&gt;scales::percent()&lt;/code&gt;) but that’s rather long and also not that easy to remember.&lt;/p&gt;
&lt;p&gt;One could of course also remove the x axis as the values are now shown as direct labels.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p +
  geom_text(
    aes(label = paste0(&amp;quot;  &amp;quot;, sprintf(&amp;quot;%2.1f&amp;quot;, prop * 100), &amp;quot;%  &amp;quot;), 
        color = prop &amp;gt; .05, hjust = prop &amp;gt; .05),
    size = 4, fontface = &amp;quot;bold&amp;quot;, family = &amp;quot;Spline Sans&amp;quot;
  ) +
  scale_x_continuous(guide = &amp;quot;none&amp;quot;, name = NULL, expand = c(0, 0)) +
  scale_color_manual(values = c(&amp;quot;black&amp;quot;, &amp;quot;white&amp;quot;), guide = &amp;quot;none&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/no-x-axis-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
A version in which the x axis has been removed as it shows redundant information.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;alternative-approach&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Alternative Approach&lt;/h2&gt;
&lt;p&gt;Here is an approach using &lt;code&gt;geom_text()&lt;/code&gt;. The trick here is to (i) reduce the width (read: height in our case) of the bars to allow for space for the labels and (ii) add the labels with &lt;code&gt;geom_text()&lt;/code&gt; in combination with a custom &lt;code&gt;vjust&lt;/code&gt; or &lt;code&gt;nudge_y&lt;/code&gt; setting.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;diamonds |&amp;gt; 
  summarize(prop = n() / nrow(diamonds), .by = cut) |&amp;gt; 
  mutate(cut = forcats::fct_reorder(cut, prop)) |&amp;gt; 
  ggplot(aes(prop, cut)) +
  geom_col(width = .5) +
  geom_text(
    aes(label = cut, x = 0),
    family = &amp;quot;Spline Sans&amp;quot;, fontface = &amp;quot;bold&amp;quot;,
    hjust = 0, vjust = -1.7, size = 4.5
  ) +
  scale_x_continuous(
    expand = c(0, 0), limits = c(0, .4),
    labels = scales::label_percent(),
    name = &amp;quot;Proportion&amp;quot;
  ) +
  scale_y_discrete(guide = &amp;quot;none&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/alternative-1.png&#34; width=&#34;576&#34; /&gt;
&lt;figcaption&gt;
An example using &lt;code&gt;geom_text()&lt;/code&gt; to place the category labels in combination with &lt;code&gt;vjust&lt;/code&gt;.
&lt;/figcaption&gt;
&lt;p&gt;That’s a great solution, too. I see some potential issues coming up here, for example problems in case the labels become larger (can be fixed by removing the clipping and adding some margin) or the number of bars increases (and that may be especially a problem in an automated workflow). In the latter case, the space between bars may become too small and/or the placement of the labels, adjusted via &lt;code&gt;vjust&lt;/code&gt; or &lt;code&gt;nudge_y&lt;/code&gt;, is not perfectly above the bars anymore.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusion&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;To illustrate the different behavior of the two approaches, let’s run the exact same codes on a new data set with more categories:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;p1 &amp;lt;- 
  mpg |&amp;gt; 
  filter(year == &amp;quot;2008&amp;quot;) |&amp;gt; 
  summarize(prop = n() / nrow(mpg), .by = manufacturer) |&amp;gt; 
  mutate(manufacturer = forcats::fct_reorder(stringr::str_to_title(manufacturer), -prop)) |&amp;gt; 
  ggplot(aes(prop, manufacturer)) +
  geom_col() +
  facet_wrap(~ manufacturer, ncol = 1, scales = &amp;quot;free_y&amp;quot;) +
  scale_x_continuous(
    name = &amp;quot;Proportion&amp;quot;, expand = c(0, 0), 
    limits = c(0, .1), labels = scales::label_percent()
  ) +
  scale_y_discrete(
    guide = &amp;quot;none&amp;quot;, expand = expansion(add = c(.8, .6))
  ) +
  theme(strip.text = element_text(
    hjust = 0, margin = margin(1, 0, 1, 0), 
    size = rel(1.1), face = &amp;quot;bold&amp;quot;
  ))

p2 &amp;lt;- 
  mpg |&amp;gt; 
  filter(year == &amp;quot;2008&amp;quot;) |&amp;gt; 
  summarize(prop = n() / nrow(mpg), .by = manufacturer) |&amp;gt; 
  mutate(manufacturer = forcats::fct_reorder(stringr::str_to_title(manufacturer), prop)) |&amp;gt; 
  ggplot(aes(prop, manufacturer)) +
  geom_col(width = .5) +
  geom_text(
    aes(label = manufacturer, x = 0),
    family = &amp;quot;Spline Sans&amp;quot;, fontface = &amp;quot;bold&amp;quot;,
    hjust = 0, vjust = -1.7, size = 4.5
  ) +
  scale_x_continuous(
    name = &amp;quot;Proportion&amp;quot;, expand = c(0, 0), 
    limits = c(0, .1), labels = scales::label_percent()
  ) +
  scale_y_discrete(guide = &amp;quot;none&amp;quot;)

library(patchwork)
p1 + p2&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/compare-1-1.png&#34; width=&#34;1152&#34; /&gt;
&lt;figcaption&gt;
Applying both codes to a different data set illustrates nicely the differences of the two approaches to place labels on top. The facet approach (left) ensures that labels are placed above, while the bars get thinner. The geom approach (right) uses a fixed bar width and thus the labels overlap at some point.
&lt;/figcaption&gt;
&lt;p&gt;Both approaches have their pros and cons. In circumstances, where you can tweak the exact setting of bar widths, font sizes, and vertical justification, the &lt;code&gt;geom_text()&lt;/code&gt; approach might be easier to set up.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;facet_wrap()&lt;/code&gt; approach ensures that the labels are always above the bars and that the labels are not clipped by the panel or plot border. This is especially powerful in case the data changes and charts need to be updated without any further modifications. Or if you want to apply a function to multiple data sets without the need to include further arguments to modify the widths and spacing. At the same time, the thinner bars make it more difficult to place labels inside the bars. However, the same issue would pop up when adjusting the widths and font sizes in the &lt;code&gt;geom_text()&lt;/code&gt; example.&lt;/p&gt;
&lt;p&gt;Finally, I should note that also the facet approach will break at some point: if the figure height is not sufficient, no bars are visible at all. But scaling the figure height based on the number of categories is something one can easy automate as well.&lt;/p&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-10-26-how-to-label-bar-graphs-in-ggplot2-yet-again_files/figure-html/compare-2-1.png&#34; width=&#34;1152&#34; /&gt;
&lt;figcaption&gt;
Both approaches, the facet approach on the left and the geom approach on the right, need sufficient figure height to work nicely.
&lt;/figcaption&gt;
&lt;hr&gt;
&lt;div id=&#34;note-header&#34; class=&#34;section level4&#34;&gt;
&lt;h4&gt;Note on the Header Image&lt;/h4&gt;
&lt;p&gt;I’ve seen this bar chart on the german TV talk show &lt;a href=&#34;https://www.daserste.de/information/talk/maischberger/sendung/maischberger-838.html&#34; target=_blank&gt;“maischberger”&lt;/a&gt;, airing on September 27, 2023. A wonderful revival of 3D-bars, combined with a glossy, transparent gradient style. It shows the number of newly constructed apartments per year over time.&lt;/p&gt;
&lt;details&gt;
&lt;summary style=&#34;font-size:10pt;&#34;&gt;
R Session Info
&lt;/summary&gt;
&lt;pre&gt;&lt;code&gt;## R version 4.3.0 (2023-04-21)
## Platform: aarch64-apple-darwin20 (64-bit)
## Running under: macOS Ventura 13.2.1
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Europe/Berlin
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] patchwork_1.1.2   ggplot2_3.4.3     dplyr_1.1.0       systemfonts_1.0.4
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.4      jsonlite_1.8.7    highr_0.10        compiler_4.3.0    tidyselect_1.2.0  stringr_1.5.0     jquerylib_0.1.4   scales_1.2.1     
##  [9] textshaping_0.3.6 yaml_2.3.7        fastmap_1.1.1     R6_2.5.1          labeling_0.4.2    generics_0.1.3    knitr_1.42        forcats_1.0.0    
## [17] tibble_3.2.1      bookdown_0.35     munsell_0.5.0     bslib_0.5.1       pillar_1.9.0      rlang_1.1.1       utf8_1.2.3        stringi_1.7.12   
## [25] cachem_1.0.8      xfun_0.40         sass_0.4.7        cli_3.6.1         withr_2.5.0       magrittr_2.0.3    digest_0.6.33     grid_4.3.0       
## [33] rstudioapi_0.15.0 lifecycle_1.0.3   vctrs_0.6.3       evaluate_0.20     glue_1.6.2        farver_2.1.1      blogdown_1.18     ragg_1.2.5       
## [41] fansi_1.0.4       colorspace_2.1-0  rmarkdown_2.20    tools_4.3.0       pkgconfig_2.0.3   htmltools_0.5.6&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;/div&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Automation</title>
      <link>https://www.cedricscherer.com/tags/automation/</link>
      <pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/automation/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Efficiency</title>
      <link>https://www.cedricscherer.com/tags/efficiency/</link>
      <pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/efficiency/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Efficiency and Consistency: Automate Subset Graphics with ggplot2 and purrr</title>
      <link>https://www.cedricscherer.com/2023/07/05/efficiency-and-consistency-automate-subset-graphics-with-ggplot2-and-purrr/</link>
      <pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/2023/07/05/efficiency-and-consistency-automate-subset-graphics-with-ggplot2-and-purrr/</guid>
      <description>


&lt;p style=&#34;font-size:14px&#34;&gt;
Header image by &lt;a href=&#39;https://www.youtube.com/watch?v=kLvZUXtVXQ0&#39; target=&#34;_blank&#34;&gt;Purrple Cat&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;During a consulting session a few weeks ago, we discussed automated plot generation with &lt;code&gt;{ggplot2}&lt;/code&gt;. What does that mean? If you have a bunch of variables, you may want to create a set of explorative charts for different variables.
Or you may want to create a set of explanatory charts, one for each category of your data set. For both use cases, this requires redundant work as the plots use &lt;em&gt;almost&lt;/em&gt; the same code to generate the visualizations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Instead of copying and pasting the same code and adjusting the variables, iterate over a vector of groups (variables, categories, numeric ranges) to generate the same visual for different data sets by using a custom function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are several use cases why you may want to use a functional programming approach to generate the same chart for different data subsets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;explore distributions or relationships of different variables&lt;/li&gt;
&lt;li&gt;communicate results for different groups&lt;/li&gt;
&lt;li&gt;generate charts for custom reports using various data (sub)sets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a great &lt;a href=&#34;https://aosmith.rbind.io/2018/08/20/automating-exploratory-plots/&#34; target=&#34;_blank&#34;&gt;blogpost by Ariel Muldoon on automating explorative plots&lt;/a&gt; by iterating over different variables. This blog post here focuses more on the generation of polished charts to visualize subsets of the same data set. In the end, I will also share some &lt;a href=&#34;#variables&#34;&gt;examples for working with variables&lt;/a&gt; and some &lt;a href=&#34;#shortcuts&#34;&gt;shortcuts for exploring data sets visually&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;div id=&#34;table-of-content&#34; class=&#34;section level5&#34;&gt;
&lt;h5&gt;Table of Content&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#setup&#34;&gt;The Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#case&#34;&gt;The Use Case&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#function&#34;&gt;The Function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#automation&#34;&gt;The Automation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#complex&#34;&gt;A More Complex Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#variables&#34;&gt;An Example with Variables as Inputs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;div id=&#34;setup&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;The Setup&lt;/h2&gt;
&lt;p&gt;We are going to visualize relationships for different numeric variables of the &lt;code&gt;mpg&lt;/code&gt; data set which features fuel economy data of popular car models for different years, manufacturers, and car types. In this tutorial, we are using only data from 2008.&lt;/p&gt;
&lt;p&gt;For some data-wrangling steps, we make use of the &lt;code&gt;{dplyr}&lt;/code&gt; packages. To visualize the data, we use the packages &lt;code&gt;{ggplot2}&lt;/code&gt; and &lt;code&gt;{patchwork}&lt;/code&gt;. We will also use some functions of other packages, namely &lt;code&gt;{tidyr}&lt;/code&gt;, &lt;code&gt;{stringr}&lt;/code&gt;, &lt;code&gt;{prismatic}&lt;/code&gt; and &lt;code&gt;{ggforce}&lt;/code&gt; which we address via the namespace.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
Unfold to explore setup steps
&lt;/summary&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ggplot2)   ## for plotting
library(purrr)     ## for iterative tasks
library(dplyr)     ## for data wrangling
library(patchwork) ## for multi-panel plots

## customize plot style
theme_set(theme_minimal(base_size = 15, base_family = &amp;quot;Anybody&amp;quot;))
theme_update(
  axis.title.x = element_text(margin = margin(12, 0, 0, 0), color = &amp;quot;grey30&amp;quot;),
  axis.title.y = element_text(margin = margin(0, 12, 0, 0), color = &amp;quot;grey30&amp;quot;),
  panel.grid.minor = element_blank(),
  panel.border = element_rect(color = &amp;quot;grey45&amp;quot;, fill = NA, linewidth = 1.5),
  panel.spacing = unit(.9, &amp;quot;lines&amp;quot;),
  strip.text = element_text(size = rel(1)),
  plot.title = element_text(size = rel(1.4), face = &amp;quot;bold&amp;quot;, hjust = .5),
  plot.title.position = &amp;quot;plot&amp;quot;
)

## adjust data set
mpg &amp;lt;-
  ggplot2::mpg |&amp;gt; 
  filter(year == 2008) |&amp;gt; 
  mutate(manufacturer = stringr::str_to_title(manufacturer))&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;/div&gt;
&lt;div id=&#34;case&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;The Use Case&lt;/h2&gt;
&lt;p&gt;Let’s visualize the 2008 car fuel data and explore the relationship of displacement and highway miles per gallon per manufacturer.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;g &amp;lt;- 
  ggplot(mpg, aes(x = hwy, y = displ)) +
  scale_x_continuous(breaks = 2:8*5) +
  labs(x = &amp;quot;Highway miles per gallon&amp;quot;, y = &amp;quot;Displacement in litres&amp;quot;, color = NULL)

g + geom_point(aes(color = manufacturer), alpha = .5, size = 3)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/scatter-all-color-1.png&#34; width=&#34;768&#34; /&gt;
&lt;figcaption&gt;
A (too) colorful scatterplot of displacement versus highway miles per gallon with points colored by manufacturer.
&lt;/figcaption&gt;
&lt;p&gt;Two issues arise here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;too many categories:&lt;/strong&gt; the use of color encoding is not useful given the large number of manufacturers (a usual recommended limit of categorical colors is 5-8)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;too many data points:&lt;/strong&gt; the number of observations, especially with identical value combinations, leads to* overplotting and color mixing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A common solution to circumvent these issues are small multiples:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt; g + 
  geom_point(alpha = .5, size = 2) +
  facet_wrap(~ manufacturer, ncol = 3)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/scatter-facet-1.png&#34; width=&#34;960&#34; /&gt;
&lt;figcaption&gt;
Small multiples visualizing the relationship of displacement versus highway miles per gallon per manufaturer as a grid of scatter plots.
&lt;/figcaption&gt;
&lt;p&gt;While it solves the mentioned issues, the resulting small multiple is likely too dense to effectively communicate the relationships for each manufacturer. Due to the large number of manufacturers, each plot also becomes rather small.&lt;/p&gt;
&lt;p&gt;To focus on a single manufacturer, we may decide to create a plot for a subset of the data. To allow for comparison, we also plot all other car models as smaller, grey circles.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;g +
  ## filter for manufacturer of interest
  geom_point(data = filter(mpg, manufacturer == &amp;quot;Audi&amp;quot;), 
             color = &amp;quot;#007cb1&amp;quot;, alpha = .5, size = 4) +
  ## add shaded points for other data
  geom_point(data = filter(mpg, manufacturer != &amp;quot;Audi&amp;quot;), 
             shape = 1, color = &amp;quot;grey45&amp;quot;, size = 2) +
  ## add title manually
  ggtitle(&amp;quot;Audi&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/scatter-single-audi-1.png&#34; width=&#34;672&#34; /&gt;
&lt;figcaption&gt;
A scatterplot highlighting the car models manufactured by Audi. The grey circles represent all other car models featured in the data set.
&lt;/figcaption&gt;
&lt;p&gt;To communicate the relationship for all manufacturers, e.g. a dedicated section for each in a report or revealing the results step by step in a presentation, we now need to repeat the same code 15 times and replace the filter conditions and title. Or we iterate the process.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;function&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;The Function&lt;/h2&gt;
&lt;p&gt;The first step is to create a custom function that takes the subset condition as an input and creates the plot with the filtered data. The code inside the user-defined function below is basically the same as the one to create the Audi chart. The only difference is that we do not specify Audi but create a placeholder, here called &lt;code&gt;group&lt;/code&gt;, to control the filtering condition and title: In the &lt;code&gt;geom_point()&lt;/code&gt; calls, we subset our data based on &lt;code&gt;group&lt;/code&gt;; in the &lt;code&gt;labs()&lt;/code&gt; function we use this string as the plot title.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_manufacturer &amp;lt;- function(group) {
  
  ## check if input is valid
  if (!group %in% mpg$manufacturer) stop(&amp;quot;Manufacturer not listed in the data set.&amp;quot;)
  
  ggplot(mapping = aes(x = hwy, y = displ)) +
    ## filter for manufacturer of interest
    geom_point(data = filter(mpg, manufacturer %in% group), 
               color = &amp;quot;#007cb1&amp;quot;, alpha = .5, size = 4) +
    ## add shaded points for other data
    geom_point(data = filter(mpg, !manufacturer %in% group), 
               shape = 1, color = &amp;quot;grey45&amp;quot;, size = 2) +
    scale_x_continuous(breaks = 2:8*5) +
    ## add title automatically based on subset choice
    labs(x = &amp;quot;Highway gallons&amp;quot;, y = &amp;quot;Displacement&amp;quot;, 
         title = group, color = NULL)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can run the function manually for each manufacturer featured in the data set:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;## run function for specific subsets
plot_manufacturer(&amp;quot;Audi&amp;quot;)
plot_manufacturer(&amp;quot;Chevrolet&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/run-function-scatter-highlight-plot-1.png&#34; width=&#34;1152&#34; /&gt;
&lt;figcaption&gt;
Using the custom function, we can create plots for specific manufacturers without the need to copy-paste and adjust the ggplot code manually.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div id=&#34;automation&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;The Automation&lt;/h2&gt;
&lt;p&gt;The final step is simple: we use the &lt;code&gt;map()&lt;/code&gt; function from the &lt;code&gt;{purrr}&lt;/code&gt; package to iterate over a vector of manufacturers and pass the elements to our &lt;code&gt;plot_manufacturer()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;groups &amp;lt;- unique(mpg$manufacturer)
map(groups, ~plot_manufacturer(group = .x))&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/purrr-function-scatter-highlight-plots-1.png&#34; width=&#34;1920&#34; /&gt;
&lt;figcaption&gt;
All 15 scatter plots for the manufacturers featured in the 2008 car fuel data.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div id=&#34;complex&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;A More Complex Example&lt;/h2&gt;
&lt;p&gt;Let’s consider a more complex visualization which consists of multiple plots. As before, we first check the input and filter the data. Afterwards, we create three plots and combine them with the help of the &lt;code&gt;{patchwork}&lt;/code&gt; package.&lt;/p&gt;
&lt;p&gt;The main plot is again a scatter plot of displacement versus highway miles per gallon. This time, we do not add the other car models to our plot. To ensure that the plots cover the full range of the data, I set the axis limits based on the variable ranges of the full data set (&lt;code&gt;lims_x&lt;/code&gt; and &lt;code&gt;lims_y&lt;/code&gt; in &lt;code&gt;scale_x_continuous()&lt;/code&gt; and &lt;code&gt;scale_y_continuous()&lt;/code&gt;, respectively). The color of the points is mapped to the car type. To avoid inconsistent coloring of the points due to different car types listed per manufacturer, I create a named color vector (&lt;code&gt;pal&lt;/code&gt;) to match the colors by class (&lt;code&gt;scale_color_manual()&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;To both axes, I add marginal box plots showing the 1-D summary statistics of the variables. Again, I am applying the limits based on the full data set to match the axis ranges of the scatter plot. I also remove all non-data elements (&lt;code&gt;theme_void()&lt;/code&gt;) and the axis guides (&lt;code&gt;guide = &#34;none&#34;&lt;/code&gt; in the positional scales).&lt;/p&gt;
&lt;p&gt;Another function argument allows to control whether the plot is saved to disk (&lt;code&gt;save = TRUE&lt;/code&gt;) or not (&lt;code&gt;save = FALSE&lt;/code&gt;).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_manufacturer_marginal &amp;lt;- function(group, save = FALSE) {
  
  ## check if input is valid
  if (!group %in% mpg$manufacturer) stop(&amp;quot;Manufacturer not listed in the data set.&amp;quot;)
  if (!is.logical(save)) stop(&amp;quot;save should be either TRUE or FALSE.&amp;quot;)
  
  ## filter data
  data &amp;lt;- filter(mpg, manufacturer %in% group)
  
  ## set limits
  lims_x &amp;lt;- range(mpg$hwy) 
  lims_y &amp;lt;- range(mpg$displ)
  
  ## define colors
  pal &amp;lt;- RColorBrewer::brewer.pal(n = n_distinct(mpg$class), name = &amp;quot;Dark2&amp;quot;)
  names(pal) &amp;lt;- unique(mpg$class)
  
  ## scatter plot
  main &amp;lt;- ggplot(data, aes(x = hwy, y = displ, color = class)) +
    geom_point(size = 3, alpha = .5) +
    scale_x_continuous(limits = lims_x, breaks = 2:8*5) +
    scale_y_continuous(limits = lims_y) +
    scale_color_manual(values = pal, name = NULL) +
    labs(x = &amp;quot;Highway miles per gallon&amp;quot;, y = &amp;quot;Displacement&amp;quot;) +
    theme(legend.position = &amp;quot;bottom&amp;quot;)
  
  ## boxplots
  right &amp;lt;- ggplot(data, aes(x = manufacturer, y = displ)) +
    geom_boxplot(linewidth = .7, color = &amp;quot;grey45&amp;quot;) +
    scale_y_continuous(limits = lims_y, guide = &amp;quot;none&amp;quot;, name = NULL) +
    scale_x_discrete(guide = &amp;quot;none&amp;quot;, name = NULL) +
    theme_void()
  
  top &amp;lt;- ggplot(data, aes(x = hwy, y = manufacturer)) +
    geom_boxplot(linewidth = .7, color = &amp;quot;grey45&amp;quot;) +
    scale_x_continuous(limits = lims_x, guide = &amp;quot;none&amp;quot;, name = NULL) +
    scale_y_discrete(guide = &amp;quot;none&amp;quot;, name = NULL) +
    theme_void()
  
  ## combine plots
  p &amp;lt;- top + plot_spacer() + main + right + 
    plot_annotation(title = group) + 
    plot_layout(widths = c(1, .05), heights = c(.1, 1))
  
  ## save multi-panel plot
  if (isTRUE(save)) {
    ggsave(p, filename = paste0(group, &amp;quot;.pdf&amp;quot;), 
           width = 6, height = 6, device = cairo_pdf)
  }
  
  return(p)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can apply the function to manufacturers line by line:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_manufacturer_marginal(&amp;quot;Dodge&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/run-function-scatter-boxplots-dodge-1.png&#34; width=&#34;864&#34; /&gt;
&lt;figcaption&gt;
There are 11 cars listed that are produced by Dodge: 5 minivans, 12 pickups, and 4 SUVs.
&lt;/figcaption&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_manufacturer_marginal(&amp;quot;Nissan&amp;quot;) &lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/run-function-scatter-boxplots-nissan-1.png&#34; width=&#34;864&#34; /&gt;
&lt;figcaption&gt;
The 7 Nissan cars belong either to the midsize or the SUV class.
&lt;/figcaption&gt;
&lt;p&gt;Or we iterate over the vector of manufacturers with the &lt;code&gt;{purrr}&lt;/code&gt; package.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;map(groups, ~plot_manufacturer_marginal(.x))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you wish to only save the visualizations but not plot them, use the &lt;code&gt;walk()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;walk(groups, ~plot_manufacturer_marginal(.x, save = TRUE))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;variables&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;An Example with Variables as Inputs&lt;/h2&gt;
&lt;p&gt;To wrap up, let’s consider the use case of exploring the data set. We create a general function that works with any data set and two numeric variables. Based on these three inputs, the function generates a scatter plot with a linear fitting.&lt;/p&gt;
&lt;p&gt;In addition, we allow to define a variable to encode points by color as well as to control the size and transparency of the points. If the user passes a column for color encoding, we either use (i) a categorical palette and linear fittings for each group for qualitative variables or (ii) a sequential palette with a single smoothing line for quantitative variables.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_scatter_lm &amp;lt;- function(data, var1, var2, pointsize = 2, transparency = .5, color = &amp;quot;&amp;quot;) {
  
  ## check if inputs are valid
  if (!exists(substitute(data))) stop(&amp;quot;data needs to be a data frame.&amp;quot;)
  if (!is.data.frame(data)) stop(&amp;quot;data needs to be a data frame.&amp;quot;)
  if (!is.numeric(pull(data[var1]))) stop(&amp;quot;Column var1 needs to be of type numeric, passed as string.&amp;quot;)
  if (!is.numeric(pull(data[var2]))) stop(&amp;quot;Column var2 needs to be of type numeric, passed as string.&amp;quot;)
  if (!is.numeric(pointsize)) stop(&amp;quot;pointsize needs to be of type numeric.&amp;quot;)
  if (!is.numeric(transparency)) stop(&amp;quot;transparency needs to be of type numeric.&amp;quot;)
  if (color != &amp;quot;&amp;quot;) { if (!color %in% names(data)) stop(&amp;quot;Column color needs to be a column of data, passed as string.&amp;quot;) }
  
  g &amp;lt;- 
    ggplot(data, aes(x = !!sym(var1), y = !!sym(var2))) +
    geom_point(aes(color = !!sym(color)), size = pointsize, alpha = transparency) +
    geom_smooth(aes(color = !!sym(color), color = after_scale(prismatic::clr_darken(color, .3))), 
                method = &amp;quot;lm&amp;quot;, se = FALSE) +
    theme_minimal(base_family = &amp;quot;Roboto Condensed&amp;quot;, base_size = 15) +
    theme(panel.grid.minor = element_blank(),
          legend.position = &amp;quot;top&amp;quot;)
   
  if (color != &amp;quot;&amp;quot;) { 
    if (is.numeric(pull(data[color]))) {
      g &amp;lt;- g + scale_color_viridis_c(direction = -1, end = .85) +
        guides(color = guide_colorbar(
          barwidth = unit(12, &amp;quot;lines&amp;quot;), barheight = unit(.6, &amp;quot;lines&amp;quot;), title.position = &amp;quot;top&amp;quot;
        ))
    } else {
      g &amp;lt;- g + scale_color_brewer(palette = &amp;quot;Set2&amp;quot;)
    }
  }
    
  return(g)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because I want (better have to) pass the aesthetics as strings with &lt;code&gt;{purrr}&lt;/code&gt;, I first turn the input into symbols by using the &lt;code&gt;sym()&lt;/code&gt; function and then those get evaluated with &lt;code&gt;!!&lt;/code&gt; (so-called “bang-bang”). Note that, in case you just want to use the function without iteration, you can also use &lt;code&gt;{{ }}&lt;/code&gt; (so-called embracing) to pass unquoted variables.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
Unfold to see &lt;code&gt;{{ }}&lt;/code&gt; example
&lt;/summary&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_scatter_lm_embraced &amp;lt;- function(data, var1, var2, color = NULL) {
  
  v1 &amp;lt;- deparse(substitute(var1))
  v2 &amp;lt;- deparse(substitute(var2))
  v3 &amp;lt;- deparse(substitute(color))
  
  ## check if inputs are valid
  if (!exists(substitute(data))) stop(&amp;quot;data needs to be a data frame.&amp;quot;)
  if (!is.data.frame(data)) stop(&amp;quot;data needs to be a data frame.&amp;quot;)
  if (!v1 %in% names(data)) stop(&amp;quot;Column var1 needs to be a column of data.&amp;quot;)
  if (!v2 %in% names(data)) stop(&amp;quot;Column var2 needs to be a column of data.&amp;quot;)
  if (!is.numeric(pull(data[v1]))) stop(&amp;quot;Column var1 needs to be of type numeric.&amp;quot;)
  if (!is.numeric(pull(data[v2]))) stop(&amp;quot;Column var2 needs to be of type numeric.&amp;quot;)
  if (!v3 %in% c(names(data), &amp;quot;NULL&amp;quot;)) stop(&amp;quot;Column color needs to be a column of data.&amp;quot;)
  
  g &amp;lt;- 
    ggplot(data, aes(x = {{ var1 }}, y = {{ var2 }})) +
    geom_point(aes(color = {{ color }}), alpha = .5) +
    geom_smooth(aes(color = {{ color }}), method = &amp;quot;lm&amp;quot;, se = FALSE) +
    theme_minimal(base_family = &amp;quot;Roboto Condensed&amp;quot;, base_size = 15) +
    theme(panel.grid.minor = element_blank(),
          legend.position = &amp;quot;top&amp;quot;)
   
  if (v3 != &amp;quot;NULL&amp;quot;) {
    if (is.numeric(pull(data[v3])))  {
      g &amp;lt;- g + scale_color_viridis_c(direction = -1, end = .85) +
        guides(color = guide_colorbar(
          barwidth = unit(12, &amp;quot;lines&amp;quot;), barheight = unit(.6, &amp;quot;lines&amp;quot;), title.position = &amp;quot;top&amp;quot;
        ))
    } else {
      g &amp;lt;- g + scale_color_brewer(palette = &amp;quot;Set2&amp;quot;)
    }
  }
  
  return(g)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_scatter_lm_embraced(data = mpg, var1 = displ, var2 = hwy)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/run-embraced-function-1-1.png&#34; width=&#34;672&#34; /&gt;
&lt;figcaption&gt;
An example using the function with embracing without passing a color column.
&lt;/figcaption&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_scatter_lm_embraced(data = mpg, var1 = displ, var2 = hwy, color = cyl)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/run-embraced-function-2-1.png&#34; width=&#34;672&#34; /&gt;
&lt;figcaption&gt;
An example using the function with embracing, passing a &lt;b&gt;quantitative variable&lt;/b&gt; to color.
&lt;/figcaption&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot_scatter_lm_embraced(data = mpg, var1 = displ, var2 = hwy, color = class)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/run-embraced-function-3-1.png&#34; width=&#34;672&#34; /&gt;
&lt;figcaption&gt;
An example using the function with embracing, passing a &lt;b&gt;qualitative variable&lt;/b&gt; to color.
&lt;/figcaption&gt;
&lt;/details&gt;
&lt;p&gt;To iterate over the function, we have two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;we can fix one variable and pass the other as vector with &lt;code&gt;map()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;we can pass two variables as vectors to &lt;code&gt;var1&lt;/code&gt; and &lt;code&gt;var2&lt;/code&gt; with &lt;code&gt;map2()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s consider an example in which we vary both positional variables, using the 2008 car fuel data:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;map2(
  c(&amp;quot;displ&amp;quot;, &amp;quot;displ&amp;quot;, &amp;quot;hwy&amp;quot;), 
  c(&amp;quot;hwy&amp;quot;, &amp;quot;cty&amp;quot;, &amp;quot;cty&amp;quot;),
  ~plot_scatter_lm(
    data = mpg, var1 = .x, var2 = .y, 
    color = &amp;quot;cyl&amp;quot;, pointsize = 3.5
  )
)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/purrr-function-scatter-lm-plots-1.png&#34; width=&#34;1440&#34; /&gt;
&lt;figcaption&gt;
Scatter plot showing the linear trends for different combinations of &lt;code&gt;displ&lt;/code&gt;, &lt;code&gt;cty&lt;/code&gt;, and &lt;code&gt;hwy&lt;/code&gt; for the 2008 car fuel data. As the number of cylinders is encoded as numeric, the sequential green-purple viridis scale is applied to the points.
&lt;/figcaption&gt;
&lt;p&gt;Now we are going to apply our function to a different data set, the &lt;a href=&#34;https://allisonhorst.github.io/palmerpenguins/&#34;&gt;Palmer penguins&lt;/a&gt; to visualize x-y relationships per species. We automatically iterate over all combinations of a set of chosen numeric variables: we first generate a vector containing the column names of interest (&lt;code&gt;names&lt;/code&gt;) and then create a data frame with all possible combinations (&lt;code&gt;names_set&lt;/code&gt;) with the help of &lt;code&gt;expand_grid()&lt;/code&gt; from the &lt;code&gt;{tidyr}&lt;/code&gt; package.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;## set up variables of interest
names &amp;lt;- c(&amp;quot;bill_length_mm&amp;quot;, &amp;quot;bill_depth_mm&amp;quot;, &amp;quot;flipper_length_mm&amp;quot;, &amp;quot;body_mass_g&amp;quot;)
## ... and create all possible combinations
names_set &amp;lt;- tidyr::expand_grid(names, names)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using another mapping function from the &lt;code&gt;{purrr}&lt;/code&gt; package called &lt;code&gt;pmap()&lt;/code&gt;, we can map over multiple arguments simultaneously:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(palmerpenguins)

pmap(
  names_set, ~plot_scatter_lm(
    data = penguins, 
    var1 = .x, var2 = .y, color = &amp;quot;species&amp;quot;
  )
)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/purrr-function-scatter-lm-penguins-plots-1.png&#34; width=&#34;1920&#34; /&gt;
&lt;figcaption&gt;
Scatter plots for each combination of four numeric variables of the Palmer penguins data set. Passing the categorical variable species to &lt;code&gt;color&lt;/code&gt; in our custom function returns linear smoothing lines for each of the three groups which are encoded with a categorical palette from the ColorBrewer project.
&lt;/figcaption&gt;
&lt;div id=&#34;shortcuts&#34; class=&#34;section level4&#34;&gt;
&lt;h4&gt;Shortcuts for Explorative Plots&lt;/h4&gt;
&lt;div id=&#34;plot&#34; class=&#34;section level5&#34;&gt;
&lt;h5&gt;&lt;code&gt;plot()&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;I am not a fan of base R plots but one big advantage is the behavior when applying the &lt;code&gt;plot()&lt;/code&gt; function to a full data set. The output is a grid of plots showing the relationship of all variable combinations:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;plot(penguins)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/base-plot-data-1.png&#34; width=&#34;672&#34; /&gt;
&lt;figcaption&gt;
Running &lt;code&gt;plot()&lt;/code&gt; on a data frame returns a grid of scatter plots visualizing the x-y relationships of all possible combinations of variables.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;div id=&#34;facet_matrix&#34; class=&#34;section level5&#34;&gt;
&lt;h5&gt;&lt;code&gt;facet_matrix()&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;A similar, and more flexible take on this idea is the &lt;code&gt;facet_matrix()&lt;/code&gt; functionality from the &lt;a href=&#34;https://ggforce.data-imaginist.com/&#34;&gt;&lt;code&gt;{ggforce}&lt;/code&gt; package&lt;/a&gt;. Instead of passing variable names in the aesthetics, we specify placeholders called &lt;code&gt;.panel_x&lt;/code&gt; and &lt;code&gt;.panel_y&lt;/code&gt;. We then create our ggplot as usual by adding layers and scales.&lt;/p&gt;
&lt;p&gt;Finally, we specify the variables to use in the facet inside the &lt;code&gt;facet_matrix()&lt;/code&gt; component. You can also specify specific layers for different areas (upper, diagonal, lower) inside &lt;code&gt;facet_matrix()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(penguins, aes(x = .panel_x, y = .panel_y)) +
  geom_point(aes(color = species), alpha = .5) +
  geom_smooth(aes(color = species), method = &amp;quot;lm&amp;quot;) +
  ggforce::geom_autodensity(aes(color = species, fill = after_scale(color)), alpha = .7) +
  scale_color_brewer(palette = &amp;quot;Set2&amp;quot;, name = NULL) +
  ggforce::facet_matrix(vars(names), layer.lower = 2, layer.diag = 3)&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&#34;https://www.cedricscherer.com/post/2023-07-07-automate-plotting-ggplot-purrr_files/figure-html/facet-matrix-layers-1.png&#34; width=&#34;1152&#34; /&gt;
&lt;figcaption&gt;
&lt;code&gt;facet_matrix()&lt;/code&gt; from the &lt;code&gt;{ggforce}&lt;/code&gt; package offers a great way to generate similar grids with &lt;code&gt;{ggplot2}&lt;/code&gt;, allowing for the usual flexibility in terms of fine-tuning and polishing. By specifying different layers for the three areas , we can combine all kind of chart types to visualize the relationships of the variables.
&lt;/figcaption&gt;
&lt;hr /&gt;
&lt;details&gt;
&lt;summary style=&#34;font-size:10pt;&#34;&gt;
R Session Info
&lt;/summary&gt;
&lt;pre&gt;&lt;code&gt;## R version 4.2.3 (2023-03-15)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Big Sur ... 10.16
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] palmerpenguins_0.1.1 patchwork_1.1.2      dplyr_1.1.2          purrr_1.0.1          ggplot2_3.4.2       
## 
## loaded via a namespace (and not attached):
##  [1] tidyselect_1.2.0   xfun_0.39          bslib_0.5.0        splines_4.2.3      lattice_0.20-45    colorspace_2.1-0   vctrs_0.6.2        generics_0.1.3    
##  [9] htmltools_0.5.4    viridisLite_0.4.1  yaml_2.3.7         mgcv_1.8-42        utf8_1.2.3         rlang_1.1.1        jquerylib_0.1.4    pillar_1.9.0      
## [17] glue_1.6.2         withr_2.5.0        tweenr_2.0.2       RColorBrewer_1.1-3 lifecycle_1.0.3    stringr_1.5.0      munsell_0.5.0      blogdown_1.18     
## [25] gtable_0.3.1       ragg_1.2.5         evaluate_0.20      labeling_0.4.2     knitr_1.42         fastmap_1.1.1      fansi_1.0.4        highr_0.10        
## [33] Rcpp_1.0.10        scales_1.2.1       cachem_1.0.7       jsonlite_1.8.4     farver_2.1.1       systemfonts_1.0.4  textshaping_0.3.6  ggforce_0.4.1     
## [41] digest_0.6.31      stringi_1.7.12     bookdown_0.34      polyclip_1.10-4    grid_4.2.3         cli_3.6.0          tools_4.2.3        magrittr_2.0.3    
## [49] sass_0.4.5         tibble_3.2.1       tidyr_1.3.0        pkgconfig_2.0.3    MASS_7.3-58.2      Matrix_1.5-3       rmarkdown_2.20     rstudioapi_0.14   
## [57] R6_2.5.1           prismatic_1.1.1    nlme_3.1-162       compiler_4.2.3&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Tips</title>
      <link>https://www.cedricscherer.com/tags/tips/</link>
      <pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/tips/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Workflow</title>
      <link>https://www.cedricscherer.com/tags/workflow/</link>
      <pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/workflow/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Challenge</title>
      <link>https://www.cedricscherer.com/tags/challenge/</link>
      <pubDate>Tue, 28 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/challenge/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Community</title>
      <link>https://www.cedricscherer.com/tags/community/</link>
      <pubDate>Tue, 28 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.cedricscherer.com/tags/community/</guid>
      <description></description>
    </item>
    
  </channel>
</rss>
