<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="4.1.1">Jekyll</generator><link href="https://carsonwoods.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://carsonwoods.io/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2025-12-18T13:53:45-05:00</updated><id>https://carsonwoods.io/feed.xml</id><title type="html">Carson Woods</title><subtitle>Carson Woods&apos; personal website.</subtitle><author><name>Carson Woods</name><email>your-email@email.com</email></author><entry><title type="html">Building an Interactive Time-Series Globe Visualization in R With Plotly</title><link href="https://carsonwoods.io/plotly-globe-visualization/" rel="alternate" type="text/html" title="Building an Interactive Time-Series Globe Visualization in R With Plotly" /><published>2021-10-21T00:00:00-04:00</published><updated>2021-10-21T00:00:00-04:00</updated><id>https://carsonwoods.io/plotly-globe-visualization</id><content type="html" xml:base="https://carsonwoods.io/plotly-globe-visualization/"><![CDATA[<p>Building interactive visualizations in R is quickly becoming a popular method of visualizing large and complex datasets. In recent months, I have been fascinated by trends in COVID-19 data. Using this as inspiration, this post will be describing how to build an interactive globe in R for visualizing time-series data. In this tutorial, the globe will be both interactive (meaning that it can be moved and panned around on screen, with countries being able to be hovered over for more information), and a slider will be present at the bottom of the screen to move the globe “through time” in the dataset.</p>

<p>To create this visualization, you’ll need the following packages. These can easily be installed with the <code class="language-plaintext highlighter-rouge">install.packages()</code> command.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">plotly</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">countrycode</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">dplyr</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Next, you’ll need to load the data into R. For this visualization I used COVID-19 global case count data from John Hopkins University. In this dataset, each row is data from an individual country, and each column is the cumulative case count for that particular day.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">global_case_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">read.csv</span><span class="p">(</span><span class="s1">'https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv'</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>In this dataset specifically, the United States was denoted as ‘US’. Given that the rest of the countries in this dataset were denoted by their full name, I decided to rename this data point for consistency. This step is entirely optional.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">global_case_data</span><span class="o">$</span><span class="n">Country.Region</span><span class="p">[</span><span class="m">250</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="s2">"United States"</span><span class="w">
</span></code></pre></div></div>

<p>Another quirk of this dataset is that it has two country columns. The first denotes the state or province, the second the country itself. For simplicity, I combined all the rows that were split over different states/provinces, using the code below.</p>

<p>First, I took the second column of the data frame, the country column, as a list, and iterated through it. I then subsetted the data frame to contain just the rows pertaining to that country. If the number of rows in that subsetted data frame was equal to one, then that row was appended to a new data frame (gc_data). However, if the number of rows in the subsetted data frame was greater than one, I then summed the columns of each row to acquire a total day-by-day case count for all the states/provinces in that country.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gc_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">()</span><span class="w">
</span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">country</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">unique</span><span class="p">(</span><span class="n">global_case_data</span><span class="o">$</span><span class="n">Country.Region</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">subset</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">global_case_data</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">filter</span><span class="p">(</span><span class="n">Country.Region</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">country</span><span class="p">)</span><span class="w">
  </span><span class="n">temp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">subset</span><span class="p">[</span><span class="m">1</span><span class="p">,]</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">subset</span><span class="p">)</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">2</span><span class="o">:</span><span class="n">nrow</span><span class="p">(</span><span class="n">subset</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">temp</span><span class="p">[,</span><span class="m">5</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">subset</span><span class="p">)]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">temp</span><span class="p">[,</span><span class="m">5</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">subset</span><span class="p">)]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">subset</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="m">5</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">subset</span><span class="p">)]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="n">gc_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbind</span><span class="p">(</span><span class="n">gc_data</span><span class="p">,</span><span class="w"> </span><span class="n">temp</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Once the consolidation of the data was done, an extra column was appended to the case count data frame that contained the country code of each country in the dataset. This becomes relevant later on, as the country codes are needed to map the data onto the correct country.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gc_data</span><span class="o">$</span><span class="n">Codes</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">countrycode</span><span class="p">(</span><span class="n">gc_data</span><span class="o">$</span><span class="n">Country.Region</span><span class="p">,</span><span class="w"> </span><span class="n">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'country.name'</span><span class="p">,</span><span class="w">
                             </span><span class="n">destination</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'iso3c'</span><span class="p">)</span><span class="w">
</span><span class="n">gc_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbind</span><span class="p">(</span><span class="n">gc_data</span><span class="p">,</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="kc">FALSE</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="p">(</span><span class="n">gc_data</span><span class="p">)))</span><span class="w">
</span></code></pre></div></div>

<p>Now, specify the time intervals that you wish to be present in your slider. I picked the first of every month, starting from March 1st 2020, all the way to April 1st 2021.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dates</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s1">'X3.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X4.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X5.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X6.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X7.1.20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'X8.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X9.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X10.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X11.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X12.1.20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'X1.1.21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X2.1.21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X3.1.21'</span><span class="p">,</span><span class="s1">'X4.1.21'</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>To make the slider look nicer later on, I created labels for each of the dates.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Create print-ready labels for figure</span><span class="w">
</span><span class="n">date_str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s1">'3/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'4/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'5/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'6/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'7/1/20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'8/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'9/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'10/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'11/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'12/1/20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'1/1/21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'2/1/21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'3/1/21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'4/1/21'</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Last (in terms of pre-processing the data), you want to ensure that the first date shown on the figure is visible by default (this is explained in more detail later).</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gc_data</span><span class="p">[</span><span class="n">nrow</span><span class="p">(</span><span class="n">gc_data</span><span class="p">),</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="m">1</span><span class="p">]]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="w">
</span></code></pre></div></div>

<p>In order to create a readable choropleth map, Plotly uses a series of settings that are defined as a list. These settings can of course be tweaked to however you want to create your map, however the following settings were used for the map. The only one that shouldn’t be changed (unless you are not aiming for a globe map view) is the <code class="language-plaintext highlighter-rouge">projection = list(type = 'orthographic’)</code> setting. This is what creates a globe instead of another map view. <a href="https://plotly.com/python/map-configuration/#map-projections">Click here for a list of other map views in Plotly. </a></p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">border_settings</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">toRGB</span><span class="p">(</span><span class="s2">"#000000"</span><span class="p">),</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w">

</span><span class="n">globe_settings</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
  </span><span class="n">showframe</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">,</span><span class="w">
  </span><span class="n">showcoastlines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">,</span><span class="w">
  </span><span class="n">projection</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'orthographic'</span><span class="p">),</span><span class="w">
  </span><span class="n">resolution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'100'</span><span class="p">,</span><span class="w">
  </span><span class="n">showcountries</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">countrycolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#d1d1d1'</span><span class="p">,</span><span class="w">
  </span><span class="n">showocean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">oceancolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#c9d2e0'</span><span class="p">,</span><span class="w">
  </span><span class="n">showlakes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">lakecolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#99c0db'</span><span class="p">,</span><span class="w">
  </span><span class="n">showrivers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">rivercolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#99c0db'</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>After defining the settings for the map, I create the initial, generic map object. Right now, this plot object (named <code class="language-plaintext highlighter-rouge">fig</code> has no settings or context for the data).</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">plot_geo</span><span class="p">(</span><span class="n">gc_data</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>The next block of code is the most complex part of this tutorial. As mentioned above, not every single day in the dataset will be visualized. The next step builds the map overlays for each time slice that I am visualizing. First I create a list to store visibility information for each time slice. The reason for this becomes more apparent later. Then I iterate through the previously defined list of dates via a loop. Every iteration of the loop does a few things. First its important to understand how Plotly handles adding data to a map. It does so via a “trace” which acts as an overlay of data onto countries. Each iteration of this loop, adds a trace by pulling the proper data from the proper date (via the current loop index). It also sets the visibility of that trace to whatever was placed on the bottom of the rows previously (not visible, except for the initial trace). It applies a uniform color scheme to the trace, I used “Sunset” and it turns the Country Region column of the data into the map labels. To map a country’s data to the proper portion of the map, the country codes found earlier is used for the locations parameter. Finally the border settings defined earlier are applied. Additionally, using the <code class="language-plaintext highlighter-rouge">%&gt;%</code> operator, a title, map legend, and the map settings that I created earlier.
This loop also manages the visibility of the layers. As mentioned, I created a steps list that will store the visibility of the traces as I slide through the visualization. This section creates a series of nested lists, that set all visibilities but the specific trace to be visible. This allows for turning off traces as a new time is selected by the slider.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">steps</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">()</span><span class="w">
</span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">dates</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">fig</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">fig</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">add_trace</span><span class="p">(</span><span class="n">z</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">gc_data</span><span class="p">[,</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="n">i</span><span class="p">]],</span><span class="w">
                   </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">gc_data</span><span class="p">[,</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="n">i</span><span class="p">]],</span><span class="w">
                   </span><span class="n">visible</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.logical</span><span class="p">(</span><span class="n">gc_data</span><span class="p">[</span><span class="n">nrow</span><span class="p">(</span><span class="n">gc_data</span><span class="p">),</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="n">i</span><span class="p">]]),</span><span class="w">
                   </span><span class="n">colors</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">hcl.colors</span><span class="p">(</span><span class="m">10</span><span class="p">,</span><span class="w"> </span><span class="s2">"Sunset"</span><span class="p">,</span><span class="w"> </span><span class="n">rev</span><span class="o">=</span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
                   </span><span class="n">text</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">Country.Region</span><span class="p">,</span><span class="w">
                   </span><span class="n">locations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">Codes</span><span class="p">,</span><span class="w">
                   </span><span class="n">marker</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">line</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">border_settings</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">layout</span><span class="p">(</span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'COVID-19 Cases Over Time'</span><span class="p">,</span><span class="w"> </span><span class="n">geo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">globe_settings</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">colorbar</span><span class="p">(</span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Cases:'</span><span class="p">,</span><span class="w"> </span><span class="n">limits</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">31000000</span><span class="p">),</span><span class="w"> </span><span class="n">len</span><span class="o">=</span><span class="m">.5</span><span class="p">,</span><span class="w"> </span><span class="n">which</span><span class="o">=</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="o">=</span><span class="m">.5</span><span class="p">)</span><span class="w">

  </span><span class="n">step</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">args</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="s1">'visible'</span><span class="p">,</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="kc">FALSE</span><span class="p">,</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">dates</span><span class="p">))),</span><span class="w">
               </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'restyle'</span><span class="p">,</span><span class="w"> </span><span class="n">label</span><span class="o">=</span><span class="n">date_str</span><span class="p">[</span><span class="n">i</span><span class="p">])</span><span class="w">
  </span><span class="n">step</span><span class="o">$</span><span class="n">args</span><span class="p">[[</span><span class="m">2</span><span class="p">]][</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="w">
  </span><span class="n">steps</span><span class="p">[[</span><span class="n">i</span><span class="p">]]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">step</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Finally, I need to add the slider, and tie it to the figure. I do that with the following code. I can label the slider with the “current value” and prefix functionality, and steps can be passed in to modify visibility.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">fig</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">layout</span><span class="p">(</span><span class="n">sliders</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">active</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w">
                             </span><span class="n">currentvalue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">prefix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Date: "</span><span class="p">),</span><span class="w">
                             </span><span class="n">steps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">steps</span><span class="p">)))</span><span class="w">
</span></code></pre></div></div>

<p>From this point, the visualization is done. It can be viewed by simply calling fig in the code or R console. If you intend to save the visualization, save it as an HTML document, as this will keep the interactive component.</p>

<p><img src="assets/globe-plot-viz.gif" alt="globe-plot-viz.gif" /></p>

<h3 id="complete-code">Complete Code</h3>

<p>Running this will generate an interactive globe as intended by the tutorial. There are some memory management function calls that improve memory usage of this script that was not included in the tutorial. They are not strictly necessary, however, they improve the performance of the script on low-powered machines.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">plotly</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">countrycode</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">dplyr</span><span class="p">)</span><span class="w">

</span><span class="c1"># Global Case Count</span><span class="w">
</span><span class="n">global_case_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">read.csv</span><span class="p">(</span><span class="s1">'https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv'</span><span class="p">)</span><span class="w">

</span><span class="c1"># Rename 'US' to 'United States' for consistency</span><span class="w">
</span><span class="n">global_case_data</span><span class="o">$</span><span class="n">Country.Region</span><span class="p">[</span><span class="m">250</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="s2">"United States"</span><span class="w">

</span><span class="c1"># Consolidate data - combining rows that are split over different regions of a country</span><span class="w">
</span><span class="n">gc_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">data.frame</span><span class="p">()</span><span class="w">
</span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">country</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">unique</span><span class="p">(</span><span class="n">global_case_data</span><span class="o">$</span><span class="n">Country.Region</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">subset</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">global_case_data</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">filter</span><span class="p">(</span><span class="n">Country.Region</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">country</span><span class="p">)</span><span class="w">
  </span><span class="n">temp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">subset</span><span class="p">[</span><span class="m">1</span><span class="p">,]</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">nrow</span><span class="p">(</span><span class="n">subset</span><span class="p">)</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">2</span><span class="o">:</span><span class="n">nrow</span><span class="p">(</span><span class="n">subset</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">temp</span><span class="p">[,</span><span class="m">5</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">subset</span><span class="p">)]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">temp</span><span class="p">[,</span><span class="m">5</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">subset</span><span class="p">)]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">subset</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="m">5</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">subset</span><span class="p">)]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="n">gc_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbind</span><span class="p">(</span><span class="n">gc_data</span><span class="p">,</span><span class="w"> </span><span class="n">temp</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Clean up memory after preprocessing</span><span class="w">
</span><span class="n">rm</span><span class="p">(</span><span class="n">temp</span><span class="p">,</span><span class="w"> </span><span class="n">subset</span><span class="p">,</span><span class="w"> </span><span class="n">country</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">)</span><span class="w">
</span><span class="n">gc</span><span class="p">()</span><span class="w">

</span><span class="c1"># Append a column for country codes</span><span class="w">
</span><span class="n">gc_data</span><span class="o">$</span><span class="n">Codes</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">countrycode</span><span class="p">(</span><span class="n">gc_data</span><span class="o">$</span><span class="n">Country.Region</span><span class="p">,</span><span class="w"> </span><span class="n">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'country.name'</span><span class="p">,</span><span class="w">
                             </span><span class="n">destination</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'iso3c'</span><span class="p">)</span><span class="w">
</span><span class="n">gc_data</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbind</span><span class="p">(</span><span class="n">gc_data</span><span class="p">,</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="kc">FALSE</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="p">(</span><span class="n">gc_data</span><span class="p">)))</span><span class="w">

</span><span class="c1"># Identify column headers to query for slider</span><span class="w">
</span><span class="n">dates</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s1">'X3.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X4.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X5.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X6.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X7.1.20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'X8.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X9.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X10.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X11.1.20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X12.1.20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'X1.1.21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X2.1.21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'X3.1.21'</span><span class="p">,</span><span class="s1">'X4.1.21'</span><span class="p">)</span><span class="w">

</span><span class="c1"># Create print-ready labels for figure</span><span class="w">
</span><span class="n">date_str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s1">'3/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'4/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'5/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'6/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'7/1/20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'8/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'9/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'10/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'11/1/20'</span><span class="p">,</span><span class="w"> </span><span class="s1">'12/1/20'</span><span class="p">,</span><span class="w">
          </span><span class="s1">'1/1/21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'2/1/21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'3/1/21'</span><span class="p">,</span><span class="w"> </span><span class="s1">'4/1/21'</span><span class="p">)</span><span class="w">

</span><span class="c1"># Ensure that first frame is visible</span><span class="w">
</span><span class="n">gc_data</span><span class="p">[</span><span class="n">nrow</span><span class="p">(</span><span class="n">gc_data</span><span class="p">),</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="m">1</span><span class="p">]]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="w">

</span><span class="c1"># create steps and plot all traces</span><span class="w">
</span><span class="c1"># Set country boundaries as light grey</span><span class="w">
</span><span class="n">border_settings</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">toRGB</span><span class="p">(</span><span class="s2">"#000000"</span><span class="p">),</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">)</span><span class="w">

</span><span class="c1"># Specify map projection and options</span><span class="w">
</span><span class="n">globe_settings</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
  </span><span class="n">showframe</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">,</span><span class="w">
  </span><span class="n">showcoastlines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">,</span><span class="w">
  </span><span class="n">projection</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'orthographic'</span><span class="p">),</span><span class="w">
  </span><span class="n">resolution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'100'</span><span class="p">,</span><span class="w">
  </span><span class="n">showcountries</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">countrycolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#d1d1d1'</span><span class="p">,</span><span class="w">
  </span><span class="n">showocean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">oceancolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#c9d2e0'</span><span class="p">,</span><span class="w">
  </span><span class="n">showlakes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">lakecolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#99c0db'</span><span class="p">,</span><span class="w">
  </span><span class="n">showrivers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
  </span><span class="n">rivercolor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#99c0db'</span><span class="p">)</span><span class="w">

</span><span class="c1"># Create initial plot object</span><span class="w">
</span><span class="n">fig</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">plot_geo</span><span class="p">(</span><span class="n">gc_data</span><span class="p">)</span><span class="w">

</span><span class="c1"># Iterate across time slices and add individual globe figures</span><span class="w">
</span><span class="c1"># to the figure so that a slider can slide across the date range</span><span class="w">
</span><span class="c1"># also creates steps so that when the slider is used, visibility for</span><span class="w">
</span><span class="c1"># other globes are disabled</span><span class="w">
</span><span class="n">steps</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">()</span><span class="w">
</span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">dates</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="c1"># Specifically, it maps global covid data to the z function (a layer) on the map</span><span class="w">
  </span><span class="n">fig</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">fig</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">add_trace</span><span class="p">(</span><span class="n">z</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">gc_data</span><span class="p">[,</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="n">i</span><span class="p">]],</span><span class="w">
                   </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">gc_data</span><span class="p">[,</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="n">i</span><span class="p">]],</span><span class="w">
                   </span><span class="n">visible</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.logical</span><span class="p">(</span><span class="n">gc_data</span><span class="p">[</span><span class="n">nrow</span><span class="p">(</span><span class="n">gc_data</span><span class="p">),</span><span class="w"> </span><span class="n">dates</span><span class="p">[</span><span class="n">i</span><span class="p">]]),</span><span class="w">
                   </span><span class="n">colors</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">hcl.colors</span><span class="p">(</span><span class="m">10</span><span class="p">,</span><span class="w"> </span><span class="s2">"Sunset"</span><span class="p">,</span><span class="w"> </span><span class="n">rev</span><span class="o">=</span><span class="kc">TRUE</span><span class="p">),</span><span class="w">
                   </span><span class="n">text</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">Country.Region</span><span class="p">,</span><span class="w">
                   </span><span class="n">locations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">Codes</span><span class="p">,</span><span class="w">
                   </span><span class="n">marker</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">line</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">border_settings</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">layout</span><span class="p">(</span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'COVID-19 Cases Over Time'</span><span class="p">,</span><span class="w"> </span><span class="n">geo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">globe_settings</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
          </span><span class="n">colorbar</span><span class="p">(</span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Cases:'</span><span class="p">,</span><span class="w"> </span><span class="n">limits</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">31000000</span><span class="p">),</span><span class="w"> </span><span class="n">len</span><span class="o">=</span><span class="m">.5</span><span class="p">,</span><span class="w"> </span><span class="n">which</span><span class="o">=</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="o">=</span><span class="m">.5</span><span class="p">)</span><span class="w">

  </span><span class="c1"># This step is what determines layer visibility</span><span class="w">
  </span><span class="c1"># as the slider moves through the dates</span><span class="w">
  </span><span class="n">step</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">args</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="s1">'visible'</span><span class="p">,</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="kc">FALSE</span><span class="p">,</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">dates</span><span class="p">))),</span><span class="w">
               </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'restyle'</span><span class="p">,</span><span class="w"> </span><span class="n">label</span><span class="o">=</span><span class="n">date_str</span><span class="p">[</span><span class="n">i</span><span class="p">])</span><span class="w">
  </span><span class="n">step</span><span class="o">$</span><span class="n">args</span><span class="p">[[</span><span class="m">2</span><span class="p">]][</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="w">
  </span><span class="n">steps</span><span class="p">[[</span><span class="n">i</span><span class="p">]]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">step</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Add slider control to plot</span><span class="w">
</span><span class="n">fig</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">fig</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">layout</span><span class="p">(</span><span class="n">sliders</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">active</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w">
                             </span><span class="n">currentvalue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">prefix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Date: "</span><span class="p">),</span><span class="w">
                             </span><span class="n">steps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">steps</span><span class="p">)))</span><span class="w">

</span><span class="c1"># Display Figure</span><span class="w">
</span><span class="n">fig</span><span class="w">

</span><span class="c1"># Remove unwanted variables to ensure memory cleanup</span><span class="w">
</span><span class="n">rm</span><span class="p">(</span><span class="n">border_settings</span><span class="p">,</span><span class="w"> </span><span class="n">fig</span><span class="p">,</span><span class="w"> </span><span class="n">gc_data</span><span class="p">,</span><span class="w"> </span><span class="n">global_case_data</span><span class="p">,</span><span class="w">
   </span><span class="n">date_str</span><span class="p">,</span><span class="w"> </span><span class="n">dates</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">globe_settings</span><span class="p">,</span><span class="w"> </span><span class="n">step</span><span class="p">,</span><span class="w"> </span><span class="n">steps</span><span class="p">)</span><span class="w">
</span><span class="n">gc</span><span class="p">()</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>Carson Woods</name><email>your-email@email.com</email></author><category term="R" /><category term="plotly" /><category term="data visualization" /><summary type="html"><![CDATA[Building interactive visualizations in R is quickly becoming a popular method of visualizing large and complex datasets. In recent months, I have been fascinated by trends in COVID-19 data. Using this as inspiration, this post will be describing how to build an interactive globe in R for visualizing time-series data. In this tutorial, the globe will be both interactive (meaning that it can be moved and panned around on screen, with countries being able to be hovered over for more information), and a slider will be present at the bottom of the screen to move the globe “through time” in the dataset.]]></summary></entry></feed>