{"id":2890,"date":"2023-02-01T02:57:23","date_gmt":"2023-02-01T02:57:23","guid":{"rendered":"http:\/\/optimumsportsperformance.com\/blog\/?p=2890"},"modified":"2023-02-01T13:54:03","modified_gmt":"2023-02-01T13:54:03","slug":"removing-columns-with-na-for-fluid-table-building-in-shiny","status":"publish","type":"post","link":"https:\/\/optimumsportsperformance.com\/blog\/removing-columns-with-na-for-fluid-table-building-in-shiny\/","title":{"rendered":"Removing columns with NA for fluid table building in shiny"},"content":{"rendered":"<p>One of the most frustrating aspects of building {<strong>shiny<\/strong>} apps is dealing with columns that have NAs when outputting tables. This is common in sport when dealing with players from different position groups who may have different stats that describe performance for those positions. Rather than writing a long series of <strong>if\/else<\/strong> statements, I prefer to streamline the process by dropping those columns prior to returning the table of data. Not only does this make the app run smoothly but it also is easier to debug or add additional table information without having to deal with a lot of nested <strong>if\/else <\/strong>statements.<\/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\/Removing%20columns%20with%20NA%20for%20fluid%20table%20building%20in%20shiny.R\">GITHUB page<\/a><\/span><\/strong>.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Load Packages &amp; Simulate Data<\/strong><\/span><\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n## Removing columns with NA for fluid table building in shiny\r\n\r\n## packages ---------------------------------------------------\r\nlibrary(tidyverse)\r\nlibrary(shiny)\r\n\r\n## simulate data ----------------------------------------------\r\nd &lt;- tribble(\r\n  ~player, ~position, ~stat1, ~stat2, ~stat3,\r\n  'Frank', 'Pitcher', 10, NA, 33,\r\n  'Tom', 'Batter', NA, 14, 12,\r\n  'Jeff', 'Batter', NA, 5, NA,\r\n  'Harold', 'Pitcher\/Batter', 12, 33, 9\r\n)\r\n\r\nd\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.11.56-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2891\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.11.56-PM.png\" alt=\"\" width=\"487\" height=\"212\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.11.56-PM.png 648w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.11.56-PM-300x131.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.11.56-PM-624x272.png 624w\" sizes=\"auto, (max-width: 487px) 100vw, 487px\" \/><\/a><\/p>\n<p>We can see from our little data set that different players have different stats populated. We really don&#8217;t want our users to deal with having to see NA in the table output. So, we need to devise a way to drop the columns with NA&#8217;s once a specific player has been selected.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Dropping Columns with NA in Base R<\/strong><\/span><\/p>\n<p>Let&#8217;s select on player and attempt to drop their columns with NA. In base R we will use the <strong>colSums() <\/strong>function to produce a count of the number of NA&#8217;s in each column.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n## remove columns with NA for Frank, using Base R --------------------------\r\n\r\nfrank &lt;- d %&gt;% \r\n  filter(player == 'Frank')\r\n\r\n# colSums() can be used to count the NA's in each column\r\ncolSums(is.na(frank))\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.39.11-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2892\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.39.11-PM.png\" alt=\"\" width=\"516\" height=\"95\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.39.11-PM.png 676w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.39.11-PM-300x55.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.39.11-PM-624x114.png 624w\" sizes=\"auto, (max-width: 516px) 100vw, 516px\" \/><\/a><\/p>\n<p>We can see that stat2 has 1 NA while the other 4 columns are complete. We can use this information to retain those four columns and drop stat2.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\nfrank&#x5B; , colSums(is.na(frank)) == 0]\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.41.44-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2893\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.41.44-PM.png\" alt=\"\" width=\"412\" height=\"130\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.41.44-PM.png 596w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.41.44-PM-300x95.png 300w\" sizes=\"auto, (max-width: 412px) 100vw, 412px\" \/><\/a><\/p>\n<p><strong><span style=\"text-decoration: underline;\">Dropping Columns with NA in {dplyr}<\/span><br \/>\n<\/strong><\/p>\n<p>We can perform a similar task using the\u00a0<strong>select_if()\u00a0<\/strong>function within the {<strong>dplyr<\/strong>} package and indicating that we want to select all columns without an NA.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\nfrank %&gt;%\r\n  select_if(~!is.na(.x))\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.44.29-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2894\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.44.29-PM.png\" alt=\"\" width=\"345\" height=\"170\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.44.29-PM.png 454w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.44.29-PM-300x148.png 300w\" sizes=\"auto, (max-width: 345px) 100vw, 345px\" \/><\/a><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Build a shiny app that fluidly retains the columns without NA<\/strong><\/span><\/p>\n<p>Now that we have a few strategies for removing columns with NA, we can build a {<strong>shiny<\/strong>} app that allows the user to select a player and then the server fluidly will drop the columns with NA so that we don&#8217;t need to use a messy <strong>if\/else <\/strong>chain.<\/p>\n<p>Notice that prior to dropping columns with NA I set the names of all of the columns in the table so that they look nicer when the table gets rendered. We can see from the figures that no matter which player is selected, the server intelligently drops columns with missing data, allowing the user to see only the statistics that are meaningful for the individual.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n# UI\r\nui &lt;- fluidPage(\r\n  \r\n  sidebarPanel(\r\n    \r\n    selectInput(inputId = 'player',\r\n                label = &quot;Select Player&quot;,\r\n                choices = sort(unique(d$player)),\r\n                selected = FALSE,\r\n                multiple = FALSE)\r\n    \r\n  ),\r\n  \r\n  mainPanel(\r\n    \r\n    tableOutput(outputId = 'tbl')\r\n  )\r\n)\r\n\r\n# Server\r\nserver &lt;- function(input, output){\r\n  \r\n  # get selected player\r\n  dat_tbl &lt;- reactive({ d %&gt;%\r\n      filter(player == input$player)\r\n    \r\n  })\r\n  \r\n  # build table\r\n  output$tbl &lt;- renderTable({ dat_tbl() %&gt;%\r\n      setNames(c(&quot;Player&quot;, &quot;Position&quot;, &quot;Stat 1&quot;, &quot;Stat 2&quot;, &quot;Stat 3&quot;)) %&gt;%\r\n      select_if(~!is.na(.x))\r\n    \r\n  })\r\n  \r\n}\r\n\r\n\r\n# deploy\r\nshinyApp(ui, server)\r\n<\/pre>\n<p><a href=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.51.40-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-2895\" src=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.51.40-PM-1024x280.png\" alt=\"\" width=\"702\" height=\"192\" srcset=\"https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.51.40-PM-1024x280.png 1024w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.51.40-PM-300x82.png 300w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.51.40-PM-768x210.png 768w, https:\/\/optimumsportsperformance.com\/blog\/wp-content\/uploads\/2023\/02\/Screen-Shot-2023-01-31-at-6.51.40-PM-624x170.png 624w\" sizes=\"auto, (max-width: 702px) 100vw, 702px\" \/><\/a><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Wrapping Up<\/strong><\/span><\/p>\n<p>Instead of having users see columns with NA, make your <strong>renderTable() <\/strong>function fluid and automatically drop columns with missing values to improve the user experience.<\/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\/Removing%20columns%20with%20NA%20for%20fluid%20table%20building%20in%20shiny.R\">GITHUB page<\/a><\/span><\/strong>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the most frustrating aspects of building {shiny} apps is dealing with columns that have NAs when outputting tables. This is common in sport when dealing with players from different position groups who may have different stats that describe performance for those positions. Rather than writing a long series of if\/else statements, I prefer [&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-2890","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\/2890","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=2890"}],"version-history":[{"count":3,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/2890\/revisions"}],"predecessor-version":[{"id":2898,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/posts\/2890\/revisions\/2898"}],"wp:attachment":[{"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/media?parent=2890"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/categories?post=2890"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/optimumsportsperformance.com\/blog\/wp-json\/wp\/v2\/tags?post=2890"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}