{"id":2856,"date":"2023-01-24T07:00:19","date_gmt":"2023-01-24T07:00:19","guid":{"rendered":"http:\/\/optimumsportsperformance.com\/blog\/?p=2856"},"modified":"2023-01-24T15:32:10","modified_gmt":"2023-01-24T15:32:10","slug":"loop-function-to-save-multiple-plots-as-svg-files","status":"publish","type":"post","link":"https:\/\/optimumsportsperformance.com\/blog\/loop-function-to-save-multiple-plots-as-svg-files\/","title":{"rendered":"Loop function to save multiple plots as SVG files"},"content":{"rendered":"<p>I&#8217;ve discussed using loops for a number of statistical tasks (<strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/optimumsportsperformance.com\/blog\/tidyx-48-monte-carlo-simulation-for-nba-match-ups\/\">simulation<\/a><\/span><\/strong>, <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/optimumsportsperformance.com\/blog\/optimization-algorithms-in-r-returning-model-fit-metrics\/\">optimization<\/a><\/span><\/strong>, <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/optimumsportsperformance.com\/blog\/bayesian-simple-linear-regression-by-hand-gibbs-sampler\/\">Gibbs sampling<\/a><\/span><\/strong>) as well as data processing tasks, such as <span style=\"color: #0000ff;\"><strong><a style=\"color: #0000ff;\" href=\"https:\/\/optimumsportsperformance.com\/blog\/r-tips-tricks-write-data-to-separate-excel-sheet-tabs\/\">writing data outputs to separate excel tabs within one excel file<\/a><\/strong><\/span> and <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/optimumsportsperformance.com\/blog\/r-tips-tricks-creating-a-multipage-pdf-with-ggplot2\/\">creating a multiple page PDF with a plot on each page<\/a><\/span><\/strong>.<\/p>\n<p>Today, I want to expand the loop function to produce separate SVG file plots and have R save those directly to a folder stored on my computer. The goal here is to have the separate plots in one place so that I can upload those files directly to a web app and allow them to be viewable for a decision-maker.<\/p>\n<p><strong>NOTE: <\/strong>You can save these files in other formats (e.g., jpeg, png). I chose SVG because it was the primary file type I had been working with.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Data<\/strong><\/span><\/p>\n<p>To keep the example simple, we will be using the {<strong>mtcars<\/strong>} data set, which is freely available in R. I&#8217;m going to set the cylinder (cyl) variable to be a factor as that is the variable that we will build our separate plot files for. In the sport setting, you can think of this as player names or player IDs, where you are building a plot for each individual, looping over them and producing a separate plot file.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\nlibrary(tidyverse)\r\nlibrary(patchwork)\r\n\r\ntheme_set(theme_bw())\r\n\r\n## data\r\ndat &lt;- mtcars %&gt;%\r\n  mutate(cyl = as.factor(cyl))\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Example Plots<\/strong><\/span><\/p>\n<p>Here is an example of the three types of plots we will build. We will wrap the three plots together using the {<strong>patchwork<\/strong>} package. The below plot is using all of the data but our goal will be to produce a loop function that creates the same plot layout using data for each of the three cylinder types.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\np1 &lt;- dat %&gt;%\r\n  ggplot(aes(x = drat, y = hp)) +\r\n  geom_point(size = 5) +\r\n  geom_smooth(method = &quot;lm&quot;,\r\n              se = FALSE) +\r\n  ggtitle(&quot;hp ~ drat&quot;)\r\n\r\np2 &lt;- dat %&gt;%\r\n  count(carb) %&gt;%\r\n  mutate(carb = as.factor(carb)) %&gt;%\r\n  ggplot(aes(x = n, y = reorder(carb, n))) +\r\n  geom_col() +\r\n  labs(x = &quot;Count&quot;,\r\n       y = &quot;Carb&quot;,\r\n       title = &quot;Carb Count&quot;)\r\n\r\np3 &lt;- dat %&gt;%\r\n  ggplot(aes(x = wt)) +\r\n  geom_histogram(fill = &quot;light grey&quot;,\r\n                 color = &quot;black&quot;,\r\n                 bins = 5) +\r\n  ggtitle(&quot;Engine Weight&quot;)\r\n\r\n\r\n(p2 | p3) \/ p1\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.29.37-AM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-2857\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.29.37-AM-1024x930.png\" alt=\"\" width=\"625\" height=\"568\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.29.37-AM-1024x930.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.29.37-AM-300x273.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.29.37-AM-768x698.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.29.37-AM-624x567.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.29.37-AM.png 1618w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Creating the loop for plotting<\/strong><\/span><\/p>\n<p>First, we create a function that produces the plots above. Basically, I&#8217;m taking the plotting code from above and wrapping it in a function. The function takes in input, <strong>i<\/strong>, and runs through the three plots for that input, at the end using the <strong>ggsave()<\/strong> function to save each plot to the dedicated file path.<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n# create a plot function for each cyl\r\nplt_func &lt;- function(i){\r\n  p1 &lt;- i %&gt;%\r\n    ggplot(aes(x = drat, y = hp)) +\r\n    geom_point(size = 5) +\r\n    geom_smooth(method = &quot;lm&quot;,\r\n                se = FALSE) +\r\n    ggtitle(&quot;hp ~ drat&quot;)\r\n  \r\n  p2 &lt;- i %&gt;%\r\n    count(carb) %&gt;%\r\n    mutate(carb = as.factor(carb)) %&gt;%\r\n    ggplot(aes(x = n, y = reorder(carb, n))) +\r\n    geom_col() +\r\n    labs(x = &quot;Count&quot;,\r\n         y = &quot;Carb&quot;,\r\n         title = &quot;Carb Count&quot;)\r\n  \r\n  p3 &lt;- i %&gt;%\r\n    ggplot(aes(x = wt)) +\r\n    geom_histogram(fill = &quot;light grey&quot;,\r\n                   color = &quot;black&quot;,\r\n                   bins = 5) +\r\n    ggtitle(&quot;Engine Weight&quot;)\r\n  \r\n  three_plt &lt;- (p2 | p3) \/ p1\r\n  \r\n  \r\n  ggsave(three_plt, file = paste0(unique(i$cyl), &quot;.svg&quot;))\r\n}\r\n<\/pre>\n<p>Then, we use the <strong>split()<\/strong> function to split the data frame into a named list with each cylinder type being it&#8217;s own list that contains a data frame. The <strong>map()<\/strong> function then creates the loop over that list and for each element of the list (for each cylinder type) it runs our plot function above and saves the results. Notice that I&#8217;ve specified <strong>setwd()<\/strong> to indicate where I want the files to be saved to. If you are saving thousands of files at once and you don&#8217;t specify this and have your working directory defaulted to your desktop, it becomes a mess pretty quick (trust me!).<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n# setwd(&quot;name of the file path where you want to save the files goes here&quot;)\r\ndat %&gt;% \r\n  split(.$cyl) %&gt;% \r\n  map(plt_func)\r\n<\/pre>\n<p>Once you&#8217;ve run the loop, your R output should look like this, where we see that each list element (cylinder) is being saved as an SVG file.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.41.50-AM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-2858\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.41.50-AM-1024x585.png\" alt=\"\" width=\"625\" height=\"357\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.41.50-AM-1024x585.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.41.50-AM-300x172.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.41.50-AM-768x439.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.41.50-AM-624x357.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.41.50-AM.png 1172w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p>Our folder has the plot outputs:<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.07-AM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2859\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.07-AM.png\" alt=\"\" width=\"386\" height=\"282\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.07-AM.png 386w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.07-AM-300x219.png 300w\" sizes=\"auto, (max-width: 386px) 100vw, 386px\" \/><\/a><\/p>\n<p>If I click on any one of the SVG files I get the desired plot.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.58-AM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-2860\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.58-AM-985x1024.png\" alt=\"\" width=\"625\" height=\"650\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.58-AM-985x1024.png 985w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.58-AM-289x300.png 289w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.58-AM-768x798.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.58-AM-624x648.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-8.44.58-AM.png 1276w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p>The above is for a 4 cylinder vehicle. Notice that I didn&#8217;t specify this at the top of the plot because my initial assumption was that I would be uploading the individual SVG files to a web application where there is a webpage dedicated to each cylinder type. Therefore, naming the plots by cylinder type would be redundant. However, if you want to add a plot to the {<strong>pathwork<\/strong>} layout about, you can use the <strong>plot_annotation()<\/strong> function.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n(p2 | p3) \/ p1 + plot_annotation(title = &quot;Engine cylinders&quot;)\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.26.58-AM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-2861\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.26.58-AM-934x1024.png\" alt=\"\" width=\"625\" height=\"685\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.26.58-AM-934x1024.png 934w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.26.58-AM-274x300.png 274w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.26.58-AM-768x842.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.26.58-AM-624x684.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.26.58-AM.png 1326w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p>We can add the\u00a0<strong>plot_annotation()<\/strong> function to the loop but instead of a generic title, like above, we will need to create a bespoke title within the loop that stores each cylinder type. To do this, we use the <strong>paste()<\/strong>\u00a0function to add the cylinder number in front of the word &#8220;<strong>cylinder&#8221; <\/strong>in our plot title name.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\nplt_func &lt;- function(i){\r\n  p1 &lt;- i %&gt;%\r\n    ggplot(aes(x = drat, y = hp)) +\r\n    geom_point(size = 5) +\r\n    geom_smooth(method = &quot;lm&quot;,\r\n                se = FALSE) +\r\n    ggtitle(&quot;hp ~ drat&quot;)\r\n  \r\n  p2 &lt;- i %&gt;%\r\n    count(carb) %&gt;%\r\n    mutate(carb = as.factor(carb)) %&gt;%\r\n    ggplot(aes(x = n, y = reorder(carb, n))) +\r\n    geom_col() +\r\n    labs(x = &quot;Count&quot;,\r\n         y = &quot;Carb&quot;,\r\n         title = &quot;Carb Count&quot;)\r\n  \r\n  p3 &lt;- i %&gt;%\r\n    ggplot(aes(x = wt)) +\r\n    geom_histogram(fill = &quot;light grey&quot;,\r\n                   color = &quot;black&quot;,\r\n                   bins = 5) +\r\n    ggtitle(&quot;Engine Weight&quot;)\r\n  \r\n  cyl_name &lt;- i %&gt;% \r\n    select(cyl) %&gt;%\r\n    distinct(cyl) %&gt;%\r\n    pull(cyl)\r\n  \r\n  three_plt &lt;- (p2 | p3) \/ p1 + plot_annotation(title = paste(cyl_name, &quot;cylinder&quot;, sep = &quot; &quot;)) ggsave(three_plt, file = paste0(unique(i$cyl), &quot;.svg&quot;)) } # setwd(&quot;name of the file path where you want to save the files goes here&quot;) dat %&gt;% \r\n  split(.$cyl) %&gt;% \r\n  map(plt_func)\r\n<\/pre>\n<p>Now we have plots with named titles.<\/p>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.47.14-AM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-2862\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.47.14-AM-989x1024.png\" alt=\"\" width=\"625\" height=\"647\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.47.14-AM-989x1024.png 989w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.47.14-AM-290x300.png 290w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.47.14-AM-768x795.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.47.14-AM-624x646.png 624w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/01\/Screen-Shot-2023-01-22-at-9.47.14-AM.png 1300w\" sizes=\"auto, (max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Wrapping Up<\/strong><\/span><\/p>\n<p>During those times where you need to produce several individual plots, rather than doing them one-by-one, leverage R&#8217;s loop functions to rapidly produce multiple plots in one shot.<\/p>\n<p>The full code is accessible on my <strong><span style=\"color: #0000ff;\"><a style=\"color: #0000ff;\" href=\"https:\/\/github.com\/pw2\/R-Tips-Tricks\/blob\/master\/loop%20for%20plotting%20svgs.R\">GITHUB page<\/a><\/span><\/strong>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve discussed using loops for a number of statistical tasks (simulation, optimization, Gibbs sampling) as well as data processing tasks, such as writing data outputs to separate excel tabs within one excel file and creating a multiple page PDF with a plot on each page. Today, I want to expand the loop function to produce [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[45,43,42],"tags":[],"class_list":["post-2856","post","type-post","status-publish","format-standard","hentry","category-r-tips-tricks","category-sports-analytics","category-sports-science"],"_links":{"self":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/2856","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/comments?post=2856"}],"version-history":[{"count":3,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/2856\/revisions"}],"predecessor-version":[{"id":2870,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/2856\/revisions\/2870"}],"wp:attachment":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/media?parent=2856"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/categories?post=2856"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/tags?post=2856"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}