avatarAsjad Naqvi

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

18205

Abstract

="hljs-built_in">sort</span> <span class="hljs-built_in">date</span> region</pre></div><div id="3a5d"><pre>levelsof <span class="hljs-built_in">date</span>, <span class="hljs-keyword">local</span>(dates)</pre></div><div id="9069"><pre><span class="hljs-keyword">foreach</span> y of <span class="hljs-keyword">local</span> dates {

<span class="hljs-keyword">summ</span> region</pre></div><div id="bfc5"><pre>*** cases <span class="hljs-built_in">replace</span> stack_cases = new_cases_ma7 <span class="hljs-keyword">if</span> <span class="hljs-built_in">date</span>==<span class="hljs-string">y' &amp; region==</span>r(<span class="hljs-built_in">min</span>)<span class="hljs-string">' </span></pre></div><div id="c4c3"><pre><span class="hljs-built_in">replace</span> stack_cases = new_cases_ma7 + stack_cases[_n<span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> <span class="hljs-built_in">date</span>==<span class="hljs-string">y' &amp; region!=</span>r(<span class="hljs-built_in">min</span>)<span class="hljs-string">' </span></pre></div><div id="9b68"><pre><span class="hljs-bullet">*** </span>deaths </pre></div><div id="ff11"><pre><span class="hljs-built_in">replace</span> stack_deaths = new_deaths_ma7 <span class="hljs-keyword">if</span> <span class="hljs-built_in">date</span>==<span class="hljs-string">y' &amp; region==</span>r(<span class="hljs-built_in">min</span>)<span class="hljs-string">' </span></pre></div><div id="8886"><pre><span class="hljs-built_in">replace</span> stack_deaths = new_deaths_ma7 + stack_deaths[n<span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> <span class="hljs-built_in">date</span>==<span class="hljs-string">y' &amp; region!=</span>r(<span class="hljs-built_in">min</span>)<span class="hljs-string">' </span></pre></div><div id="cd0a"><pre>}</pre></div><p id="c244">Essentially, we are taking the first region observation as it is, and for the subsequent regions iteratively adding up the values. We can also plot the new variables:</p><div id="b839"><pre>twoway <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> stack_cases <span class="hljs-built_in">date</span> <span class="hljs-keyword">if</span> region==<span class="hljs-number">1</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> stack_cases <span class="hljs-built_in">date</span> <span class="hljs-keyword">if</span> region==<span class="hljs-number">2</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> stack_cases <span class="hljs-built_in">date</span> <span class="hljs-keyword">if</span> region==<span class="hljs-number">3</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> stack_cases <span class="hljs-built_in">date</span> <span class="hljs-keyword">if</span> region==<span class="hljs-number">4</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> stack_cases <span class="hljs-built_in">date</span> <span class="hljs-keyword">if</span> region==<span class="hljs-number">5</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> stack_cases <span class="hljs-built_in">date</span> <span class="hljs-keyword">if</span> region==<span class="hljs-number">6</span>), <span class="hljs-comment">///</span> <span class="hljs-built_in">legend</span>(off)</pre></div><p id="d867">where we get this graph:</p><figure id="382a"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*ri_pBxrnhDrJvw8-Sk85ig.png"><figcaption></figcaption></figure><p id="bd0d">Here the difference between the two lines are the daily cases for each region. This figure is also easier to look at relative to the earlier graph.</p><p id="5fa8">If you look at the help of the <code>twoway rarea</code> command:</p><div id="be02"><pre><span class="hljs-built_in">help</span> twoway_rarea</pre></div><p id="1c50">here you will see that the syntax is:</p><div id="131e"><pre>twoway rarea y1var y2var xvar <span class="hljs-selector-attr">[if]</span> <span class="hljs-selector-attr">[in]</span> <span class="hljs-selector-attr">[, options]</span></pre></div><p id="3192">This implies that in Stata, if we need to make area graphs, then each region needs to be its own variable. This essentially means we need to reshape the data and make it wide.</p><p id="0a3d">Before we reshape, we keep the variables we need and rename the new stack* variable for convivence:</p><div id="a587"><pre>keep region date <span class="hljs-keyword">new</span><span class="hljs-type">_cases</span> stack_cases <span class="hljs-keyword">new</span><span class="hljs-type">_deaths</span> stack_deaths</pre></div><div id="d125"><pre><span class="hljs-comment">// rename just to keep life easy </span> <span class="hljs-keyword">ren</span> stack_cases cases <span class="hljs-keyword">ren</span> stack_deaths deaths</pre></div><p id="c5da">As discussed in <a href="https://readmedium.com/covid-19-visualizations-with-stata-part-9-customized-bar-graphs-dde096567837">Guide 9</a>, reshaping losses the information on value labels. We can use this three-step process to (a) preserve the labels in locals before reshaping, (b) reshape the data, and, (c) apply the labels after the reshape:</p><div id="7c6f"><pre><span class="hljs-bullet">*** </span>preserve the labels</pre></div><div id="83fc"><pre><span class="hljs-keyword">levelsof</span> region, <span class="hljs-keyword">local</span>(idlabels) <span class="hljs-comment">// store the id levels</span>

<span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">local</span> idlabels {
<span class="hljs-keyword">local</span> idlab_<span class="hljs-symbol">x'</span> : <span class="hljs-keyword">label</span> region <span class="hljs-symbol">x'</span>
}</pre></div><div id="f5ba"><pre>*** <span class="hljs-built_in">reshape</span> the <span class="hljs-keyword">data</span></pre></div><div id="5191"><pre>reshape wide cases <span class="hljs-keyword">new</span><span class="hljs-type">_cases</span> deaths <span class="hljs-keyword">new</span><span class="hljs-type">deaths</span>, i(date) j(region) order date cases* <span class="hljs-keyword">new</span><span class="hljs-type">cases</span>* deaths*</pre></div><div id="b744"><pre><span class="hljs-comment">*** and apply the labels back</span></pre></div><div id="2283"><pre><span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">local</span> idlabels { </pre></div><div id="59ce"><pre> lab var casesx' <span class="hljs-string">"idlabx''"</span> lab var new_casesx' <span class="hljs-string">"idlab_x''"</span>
lab var deathsx' <span class="hljs-string">"idlab
x''"</span> lab var new_deathsx' <span class="hljs-string">"idlab_x''"</span>
}</pre></div><p id="e8d5">Since there are locals involved, the above code has to run in one go. If the code runs fine, we should get something like this where each variable is given a label based on the corresponding number in the variable name:</p><figure id="430d"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*vH4nv6xcg_Qu8RVdCS4P1g.png"><figcaption></figcaption></figure><p id="d930">We can now redraw the line graph shown earlier but now we just do it using variable names rather than <code>if</code> conditions:</p><div id="65f7"><pre>twoway <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases1 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases2 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases3 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases4 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases5 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases6 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases12 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> , <span class="hljs-built_in">legend</span>(off)</pre></div><p id="6424">Note that here I am using the last variable <i>cases12 </i>just to show the extent of the data:</p><figure id="496f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*srthcbu7nPKq9l1LpOsNcw.png"><figcaption></figcaption></figure><p id="299e">Since we want areas to stack up, we also need to define a dummy 0 variable for the first country:</p><div id="0b81"><pre>gen cases0 <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
gen deaths0 <span class="hljs-operator">=</span> <span class="hljs-number">0</span></pre></div><div id="bb43"><pre>twoway <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases0 <span class="hljs-built_in">date</span>) <span class="hljs-comment">/// </span> (<span class="hljs-keyword">line</span> cases1 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases2 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases3 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases4 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases5 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases6 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases12 <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> , <span class="hljs-built_in">legend</span>(off)</pre></div><p id="c9e5">Which just gives us this figure:</p><figure id="b3af"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*teSaueTmUwQMQOpYPhSzQA.png"><figcaption></figcaption></figure><p id="d01c">The importance of having a zero line will become obvious below.</p><p id="fda2">Now we have all the data in place, we can re-center the graph around zero on the y-axis. For this we need to take the maximum value of each date, divide it by two, and subtract each region’s observation by it.</p><p id="bb24">In order to re-center the graph, we take the highest value, or the value of the last variable and divided it by two:</p><div id="c305"><pre><span class="hljs-keyword">ds</span> cases* <span class="hljs-keyword">local</span> items : word <span class="hljs-keyword">count</span> <span class="hljs-built_in">r</span>(<span class="hljs-keyword">varlist</span>)' <span class="hljs-keyword">local</span> items = <span class="hljs-symbol">items'</span> - 1 <span class="hljs-keyword">display</span> <span class="hljs-symbol">items'</span></pre></div><div id="c038"><pre><span class="hljs-keyword">gen</span> meanval_cases = cases<span class="hljs-symbol">items'</span> / 2 <span class="hljs-keyword">gen</span> meanval_deaths = deaths<span class="hljs-symbol">items'</span> / 2</pre></div><div id="e5c5"><pre><span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">varlist</span> cases* { <span class="hljs-keyword">gen</span> <span class="hljs-symbol">x'</span>_norm = <span class="hljs-symbol">x'</span> - meanval_cases }</pre></div><div id="935b"><pre><span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">varlist</span> deaths* { <span class="hljs-keyword">gen</span> <span class="hljs-symbol">x'</span>_norm = <span class="hljs-symbol">x'</span> - meanval_deaths }</pre></div><div id="4263"><pre><span class="hljs-built_in">drop</span> meanval*</pre></div><p id="71c7">The first three lines automate to process of counting the number of variables in the dataset. Note, that we do <code>local items = items' — 1</code> to account for the additional cases0 variable we generated earlier, so that the total number of variables in our example are 12, and not 13, for the loop to run properly.</p><p id="30a1">The automation helps if regions are added or subtracted, or the graph is looped over multiple regions, for example, generating a stream graph for each continent with all the countries.</p><p id="38f4">We can now plot the normalized variable given with the suffix _norm as follows:</p><div id="a007"><pre>twoway <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases0_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases1_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases2_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases3_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases4_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases5_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases6_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> cases12_norm <span class="hljs-built_in">date</span>) <span class="hljs-comment">///</span> , <span class="hljs-built_in">legend</span>(off)</pre></div><p id="fa00">which gives us the core skeleton structure we need for stream graphs:</p><figure id="c9e8"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*etMXlcnFwTN3CGFPTyCCGA.png"><figcaption></figcaption></figure><p id="4e5c">Here we can see the y=0 line has also been re-centered. We can now convert the above figure into an area graph as follows:</p><div id="23ac"><pre>twoway <span class="hljs-comment">///</span> (rarea cases0_norm cases1_norm date) <span class="hljs-comment">///</span> (rarea cases1_norm cases2_norm date) <span class="hljs-comment">///</span> (rarea cases2_norm cases3_norm date) <span class="hljs-comment">///</span> (rarea cases3_norm cases4_norm date) <span class="hljs-comment">///</span> (rarea cases4_norm cases5_norm date) <span class="hljs-comment">///</span> (rarea cases5_norm cases6_norm date) <span class="hljs-comment">///</span> (rarea cases6_norm cases12_norm date) <span class="hljs-comment">///</span> , <span class="hljs-selector-tag">legend</span>(off)</pre></div><p id="6846">which gives us this figure:</p><figure id="36a3"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*w2XaJ884kqcAJV24_ty59w.png"><figcaption></figcaption></figure><p id="7cf3">Notice also the y-axis which is now showing zero in the center and everything is distributed around it. Also note the pattern for generating the rarea graph where two y-variables have a difference of one in the name.</p><h2 id="b2ca">Labels</h2><p id="01b4">We can also automate the labels in three steps.</p><p id="978a"><b>Step 1</b>: generate the mid points of the last data observation:</p><div id="6abf"><pre>** <span class="hljs-keyword">this</span> <span class="hljs-keyword">part</span> <span class="hljs-keyword">is</span> <span class="hljs-keyword">for</span> the mid points</pre></div><div id="9437"><pre>summ <span class="hljs-built_in">date</span> gen <span class="hljs-keyword">last</span> = <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> <span class="hljs-built_in">date</span>==r(<span class="hljs-built_in">max</span>)</pre></div><div id="4948"><pre><span class="hljs-keyword">ds</span> casesnorm <span class="hljs-keyword">local</span> items : word <span class="hljs-keyword">count</span> <span class="hljs-built_in">r</span>(<span class="hljs-keyword">varlist</span>)' <span class="hljs-keyword">local</span> items = <span class="hljs-symbol">items'</span> - 2 <span class="hljs-keyword">display</span> <span class="hljs-symbol">items'</span></pre></div><div id="de0c"><pre><span class="hljs-keyword">forval</span> i = 0/<span class="hljs-symbol">items'</span> { <span class="hljs-keyword">local</span> i0 = <span class="hljs-symbol">i'</span> <span class="hljs-keyword">local</span> i1 = <span class="hljs-symbol">i'</span> + 1</pre></div><div id="1622"><pre>gen ycases<span class="hljs-built_in">i1</span>' = (cases<span class="hljs-built_in">i0</span>'_norm + cases<span class="hljs-built_in">i1</span>'_norm) / <span class="hljs-number">2</span> <span class="hljs-keyword">if</span> <span class="hljs-built_in">last</span>==<span class="hljs-number">1</span> gen ydeaths<span class="hljs-built_in">i1</span>' = (deaths<span class="hljs-built_in">i0</span>'_norm + deaths<span class="hljs-built_in">i1</span>'_norm) / <span class="hljs-number">2</span> <span class="hljs-keyword">if</span> <span class="hljs-built_in">last</span>==<span class="hljs-number">1</span></pre></div><div id="3e85"><pre>}</pre></div><p id="46c5">Here note the use of the locals and the word count again to automate the whole process. The mid point has to be calculated as starting value + ending value / 2. Since we are counting from zero, we update the <code>items</code> local by reducing it’s value by 2. Within the loop, we define two locals, <code>i0</code> and <code>i1</code>, as indices for variable names, which can then be dynamically used in generating the mid points.</p><p id="e35d"><b>Step 2</b>: Next we generate a variable for the share of cases and deaths for the last observation. This is just to indicate how much each region is contributing to the total of the last data point.</p><p id="8fc1">This is achieved using a fairly straightforward loop:</p><div id="8517"><pre>** <span class="hljs-keyw

Options

ord">this</span> <span class="hljs-keyword">part</span> <span class="hljs-keyword">is</span> <span class="hljs-keyword">for</span> the shares</pre></div><div id="9d0b"><pre>egen lastsum_cases <span class="hljs-operator">=</span> rowtotal(new_cases*) if last<span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-number">1</span> egen lastsum_deaths <span class="hljs-operator">=</span> rowtotal(new_deaths*) if last<span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-number">1</span></pre></div><div id="62a5"><pre><span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">varlist</span> new_cases* { <span class="hljs-keyword">gen</span> <span class="hljs-symbol">x'</span>_share = (<span class="hljs-symbol">x'</span> / lastsum_cases) * 100 }</pre></div><div id="97a6"><pre><span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">varlist</span> new_deaths* { <span class="hljs-keyword">gen</span> <span class="hljs-symbol">x'</span>_share = (<span class="hljs-symbol">x'</span> / lastsum_deaths) * 100 }</pre></div><div id="9e09"><pre><span class="hljs-built_in">drop</span> lastsum*</pre></div><p id="84ea">Note here that I am not using the smoothed variable but the actual data for the accurate value of the share of cases. This is also the reason, we carry this variable forward throughout the collapse and reshaping process.</p><p id="d952"><b>Step 3</b>: generate the variables containing the label for the graphs. Here we again use a mix of several locals to automate the label generation process:</p><div id="0f33"><pre>**** here we <span class="hljs-keyword">generate</span> the labels</pre></div><div id="c8b6"><pre><span class="hljs-keyword">ds</span> cases*norm <span class="hljs-keyword">local</span> items : word <span class="hljs-keyword">count</span> <span class="hljs-built_in">r</span>(<span class="hljs-keyword">varlist</span>)' <span class="hljs-keyword">local</span> items = <span class="hljs-symbol">items'</span> - 1</pre></div><div id="e69b"><pre><span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">numlist</span> 1/<span class="hljs-symbol">`items'</span> {

<span class="hljs-keyword">local</span> t : <span class="hljs-keyword">var</span> <span class="hljs-keyword">lab</span> cases<span class="hljs-symbol">x'</span> </pre></div><div id="cae9"><pre><span class="hljs-comment">*** cases </span> <span class="hljs-keyword">gen</span> <span class="hljs-keyword">label</span><span class="hljs-symbol">x'</span>_cases = <span class="hljs-string">"t'"</span> + <span class="hljs-string">" ("</span> + <span class="hljs-built_in">string</span>( new_cases<span class="hljs-symbol">x'</span>, <span class="hljs-string">"%9.0f"</span>) + <span class="hljs-string">", "</span> + <span class="hljs-built_in">string</span>( new_cases<span class="hljs-symbol">x'</span>_share, <span class="hljs-string">"%9.0fc"</span>) + <span class="hljs-string">"%)"</span> <span class="hljs-keyword">if</span> last==1</pre></div><div id="cb91"><pre><span class="hljs-comment">*** deaths</span> <span class="hljs-keyword">gen</span> <span class="hljs-keyword">label</span><span class="hljs-symbol">x'</span>_deaths = <span class="hljs-string">"t'"</span> + <span class="hljs-string">" ("</span> + <span class="hljs-built_in">string</span>(new_deaths<span class="hljs-symbol">x'</span>, <span class="hljs-string">"%9.0f"</span>) + <span class="hljs-string">", "</span> + <span class="hljs-built_in">string</span>(new_deaths<span class="hljs-symbol">`x'</span>_share, <span class="hljs-string">"%9.0fc"</span>) + <span class="hljs-string">"%)"</span> <span class="hljs-keyword">if</span> last==1

}</pre></div><p id="259a">In the code above, <code>ds</code> and <code>word count</code> and <code>item</code> define the number for the loop. The local <code>t</code> picks the variable label, and the <code>gen</code> command puts all this information together. Since the data is in a wide form after the reshape, a new variable is generated for each region.</p><p id="433c">We can also plot this manually for the same regions shown earlier:</p><div id="1e19"><pre>twoway <span class="hljs-comment">///</span> (rarea cases0_norm cases1_norm date) <span class="hljs-comment">///</span> (rarea cases1_norm cases2_norm date) <span class="hljs-comment">///</span> (rarea cases2_norm cases3_norm date) <span class="hljs-comment">///</span> (rarea cases3_norm cases4_norm date) <span class="hljs-comment">///</span> (rarea cases4_norm cases5_norm date) <span class="hljs-comment">///</span> (rarea cases5_norm cases6_norm date) <span class="hljs-comment">///</span> (rarea cases6_norm cases12_norm date) <span class="hljs-comment">///</span> (scatter ycases1 date if last==<span class="hljs-number">1</span>, ms(smcircle) <span class="hljs-built_in">msize</span>(<span class="hljs-number">0.2</span>) <span class="hljs-built_in">mlabel</span>(label1_cases) <span class="hljs-built_in">mcolor</span>(black%<span class="hljs-number">20</span>) <span class="hljs-built_in">mlabsize</span>(tiny) <span class="hljs-built_in">mlabcolor</span>(black)) <span class="hljs-comment">///</span> (scatter ycases2 date if last==<span class="hljs-number">1</span>, ms(smcircle) <span class="hljs-built_in">msize</span>(<span class="hljs-number">0.2</span>) <span class="hljs-built_in">mlabel</span>(label2_cases) <span class="hljs-built_in">mcolor</span>(black%<span class="hljs-number">20</span>) <span class="hljs-built_in">mlabsize</span>(tiny) <span class="hljs-built_in">mlabcolor</span>(black)) <span class="hljs-comment">///</span> (scatter ycases3 date if last==<span class="hljs-number">1</span>, ms(smcircle) <span class="hljs-built_in">msize</span>(<span class="hljs-number">0.2</span>) <span class="hljs-built_in">mlabel</span>(label3_cases) <span class="hljs-built_in">mcolor</span>(black%<span class="hljs-number">20</span>) <span class="hljs-built_in">mlabsize</span>(tiny) <span class="hljs-built_in">mlabcolor</span>(black)) <span class="hljs-comment">///</span> (scatter ycases4 date if last==<span class="hljs-number">1</span>, ms(smcircle) <span class="hljs-built_in">msize</span>(<span class="hljs-number">0.2</span>) <span class="hljs-built_in">mlabel</span>(label4_cases) <span class="hljs-built_in">mcolor</span>(black%<span class="hljs-number">20</span>) <span class="hljs-built_in">mlabsize</span>(tiny) <span class="hljs-built_in">mlabcolor</span>(black)) <span class="hljs-comment">///</span> (scatter ycases5 date if last==<span class="hljs-number">1</span>, ms(smcircle) <span class="hljs-built_in">msize</span>(<span class="hljs-number">0.2</span>) <span class="hljs-built_in">mlabel</span>(label5_cases) <span class="hljs-built_in">mcolor</span>(black%<span class="hljs-number">20</span>) <span class="hljs-built_in">mlabsize</span>(tiny) <span class="hljs-built_in">mlabcolor</span>(black)) <span class="hljs-comment">///</span> (scatter ycases6 date if last==<span class="hljs-number">1</span>, ms(smcircle) <span class="hljs-built_in">msize</span>(<span class="hljs-number">0.2</span>) <span class="hljs-built_in">mlabel</span>(label6_cases) <span class="hljs-built_in">mcolor</span>(black%<span class="hljs-number">20</span>) <span class="hljs-built_in">mlabsize</span>(tiny) <span class="hljs-built_in">mlabcolor</span>(black)) <span class="hljs-comment">/// </span> , <span class="hljs-selector-tag">legend</span>(off)</pre></div><p id="721f">Which gives us this graph:</p><figure id="9b2c"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*HKcvRXAKLtm-aib5KvSUZw.png"><figcaption></figcaption></figure><p id="2dfe">We have now achieved the core structure required to automate the final figure.</p><h2 id="8cbd">Automate</h2><p id="7c60">In the code below, we use a combination of loops and locals to also define the colors and labels for each region segment of the graph:</p><div id="bdb5"><pre>*** <span class="hljs-built_in">auto</span>mate the areas, colors, labels</pre></div><div id="e2df"><pre><span class="hljs-keyword">ds</span> cases*norm <span class="hljs-keyword">local</span> items : word <span class="hljs-keyword">count</span> <span class="hljs-built_in">r</span>(<span class="hljs-keyword">varlist</span>)' <span class="hljs-keyword">local</span> items = <span class="hljs-symbol">items'</span> - 2 <span class="hljs-keyword">display</span> <span class="hljs-symbol">items'</span></pre></div><div id="6f0b"><pre><span class="hljs-keyword">forval</span> x = 0/<span class="hljs-symbol">items'</span> {

colorpalette <span class="hljs-comment">///</span> <span class="hljs-string">"253 253 150"</span> <span class="hljs-comment">///</span> <span class="hljs-string">"255 197 1"</span> <span class="hljs-comment">///</span> <span class="hljs-string">"255 152 1"</span> <span class="hljs-comment">///</span> <span class="hljs-string">" 3 125 80"</span> <span class="hljs-comment">///</span> <span class="hljs-string">" 2 75 48"</span> <span class="hljs-comment">///</span> , <span class="hljs-keyword">n</span>(13) nograph</pre></div><div id="e33c"><pre><span class="hljs-keyword">local</span> x0 = <span class="hljs-symbol">x'</span> <span class="hljs-keyword">local</span> x1 = <span class="hljs-symbol">x'</span> + 1

<span class="hljs-keyword">local</span> areagraph <span class="hljs-symbol">areagraph'</span> rarea cases<span class="hljs-symbol">x0'</span>_norm cases<span class="hljs-symbol">x1'</span>_norm date, fcolor(<span class="hljs-string">"r(px1')'"</span>) lcolor(black) lwidth(*0.15) || (<span class="hljs-keyword">scatter</span> ycases<span class="hljs-symbol">x1'</span> date <span class="hljs-keyword">if</span> last==1, ms(smcircle) msize(0.2) mlabel(<span class="hljs-keyword">label</span><span class="hljs-symbol">`x1'</span>_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ||

}</pre></div><div id="5f40"><pre>*** <span class="hljs-keyword">get</span> the <span class="hljs-type">date</span> ranges <span class="hljs-keyword">in</span> <span class="hljs-keyword">order</span></pre></div><div id="1676"><pre><span class="hljs-keyword">summ</span> date <span class="hljs-keyword">local</span> x1 = <span class="hljs-built_in">r</span>(min)' <span class="hljs-keyword">local</span> x2 = <span class="hljs-built_in">r</span>(max)' + 50</pre></div><div id="a665"><pre>*** <span class="hljs-keyword">generate</span> the graph</pre></div><div id="2a21"><pre><span class="hljs-keyword">graph</span> <span class="hljs-keyword">twoway</span> <span class="hljs-symbol">areagraph'</span> <span class="hljs-comment">///</span> , <span class="hljs-comment">///</span> legend(off) <span class="hljs-comment">///</span> ytitle(<span class="hljs-string">""</span>, size(small)) <span class="hljs-comment">///</span> ylabel(-300000(100000)300000) <span class="hljs-comment">///</span> yscale(noline) <span class="hljs-comment">///</span> ylabel(, nolabels noticks nogrid) <span class="hljs-comment">///</span> xscale(noline) <span class="hljs-comment">///</span> xtitle(<span class="hljs-string">""</span>) <span class="hljs-comment">///</span> xlabel(<span class="hljs-symbol">x1'</span>(15)<span class="hljs-symbol">`x2'</span>, labsize(*0.6) angle(vertical) glwidth(vvthin) glpattern(solid)) <span class="hljs-comment">///</span> title(<span class="hljs-string">"{fontface Arial Bold: COVID-19 Daily Cases - The World}"</span>) <span class="hljs-comment">///</span> <span class="hljs-keyword">note</span>(<span class="hljs-string">"Data sources: Our World in Data. World Bank 2020 classifications used for country groups."</span>, size(tiny))</pre></div><p id="e831">The code above have a lot of parts that fit together. The total number of values that need to be picked are stored in the local <code>items</code>. A custom color palette that has been discussed in the <a href="https://readmedium.com/stata-graphs-define-your-own-color-schemes-4320b16f7ef7">Color guide</a> and applied in the <a href="https://readmedium.com/covid-19-visualizations-with-stata-part-9-customized-bar-graphs-dde096567837">Guide 9 on bar graphs</a>, is also being used here. This can be replaced with any other color scheme. The core body of the figure is stored in the local <code>areagraph</code> which contains two parts; one part for the area graph and one part for the labels including all the customizations of the lines, colors, widths, sizes etc. The color information is also dynamically applied from the values stored in locals after the <code>colorpalette</code> command.</p><p id="8006">The date range is stored in the two locals <code>x1</code> and <code>x2</code>. The graph command call the <code>areagraph </code>local, and the date locals. Y-axis is turned off completely including the line which show the values centered around zero. The title is also customized using the <code>fontface</code> argument (see the <a href="https://readmedium.com/stata-graphs-get-those-fonts-right-c38d35625142">Font Guide</a> for details).</p><p id="338e">From the code above, we get the following final figure:</p><figure id="f0b1"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*juFGIEfTB1JhjfWM5OlZ3A.png"><figcaption></figcaption></figure><p id="2307">The stream graph above provides a neat representation of how the cases evolve and which region is contributing to the daily cases. It also helps immediately convey the relative scale of the epidemic and how different regions evolve over time. For example, the United States and Europe are major contributors at the time of writing this article. This graph can also be generated per-capita to normalize the regions for more accurate comparisons.</p><h1 id="b206">Exercise</h1><p id="c8ee">Generate the graph for daily deaths shown below:</p><figure id="38fb"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*4aosUCaKFIL6ULW38fy8bw.png"><figcaption></figcaption></figure><p id="d3ce">This graph uses the custom color palette:</p><div id="e112"><pre>colorpalette <span class="hljs-comment">///</span> <span class="hljs-string">"253 253 150"</span> <span class="hljs-comment">///</span> <span class="hljs-string">"255 197 1"</span> <span class="hljs-comment">///</span> <span class="hljs-string">"255 152 1"</span> <span class="hljs-comment">///</span> <span class="hljs-string">" 3 125 80"</span> <span class="hljs-comment">///</span> <span class="hljs-string">" 2 75 48"</span> <span class="hljs-comment">///</span></pre></div><p id="5a15">Don’t forget to clear the locals if you generate the stream graph for daily deaths right after the daily cases graph. Locals can be simply reset as follows:</p><div id="ed42"><pre><span class="hljs-keyword">local</span> areagraph</pre></div><p id="f448">Hope you enjoyed this guide!</p><h1 id="2e65">Other Stata guides</h1><p id="b2a1"><a href="https://readmedium.com/covid-19-data-visualization-with-stata-part-1-an-introduction-to-data-setup-and-customized-6b879a1e8647">Part 1: An introduction to data setup and customized graphs</a></p><p id="979c"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-2-customizing-color-schemes-206af77d00ce">Part 2: Customizing colors schemes</a></p><p id="1b29"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-3-heat-plots-e2ef5ac1160b">Part 3: Heat plots</a></p><p id="cecf"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-4-maps-fbd4fe2642f6">Part 4: Maps</a></p><p id="b0be"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-5-stacked-area-graphs-ed976d025365">Part 5: Stacked area graphs</a></p><p id="5f16"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-6-animations-f9d2b09985c2">Part 6: Animations</a></p><p id="7933"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-7-doubling-time-graphs-1-58b7687cbdc0">Part 7: Doubling time graphs</a></p><p id="b696"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-8-joy-plots-ridge-line-plots-dbe022e7264d">Part 8: Ridge-line plots (Joy plots)</a></p><p id="357f"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-9-customized-bar-graphs-dde096567837">Part 9: Customized bar graphs</a></p><p id="d6f2"><a href="https://readmedium.com/covid-19-visualizations-with-stata-part-10-stream-graphs-9d55db12318a">Part 10: Stream graphs</a></p><p id="1a1c">If you enjoy these guides and find them useful, then please like and follow <a href="https://medium.com/the-stata-guide">The Stata Guide</a>. Also, please share your visualizations if you use these guides!</p><h1 id="3c6a">About the author</h1><p id="6dda">I am an economist by profession and I have been using Stata since 2003. I am currently based in Vienna, Austria where I work at the <a href="https://www.wu.ac.at/en/ecolecon/institute">Vienna University of Economics and Business (WU)</a> and at the <a href="https://iiasa.ac.at/">International Institute for Applied Systems Analysis (IIASA)</a>. You can find my research work on <a href="https://www.researchgate.net/profile/Asjad_Naqvi">ResearchGate</a> and <a href="https://scholar.google.com/citations?user=oWGGVpYAAAAJ&amp;hl=en">Google Scholar</a>, and Stata code repository on <a href="https://github.com/asjadnaqvi">GitHub</a>. You can follow my COVID-19 related Stata visualizations on my <a href="https://twitter.com/AsjadNaqvi">Twitter</a>. I am also featured on the Stata <a href="https://www.stata.com/covid19/">COVID-19 webpage</a> in the visualization and graphics section.</p><p id="4c0f">You can connect with me via <a href="https://medium.com/@asjadnaqvi">Medium</a>, <a href="https://twitter.com/AsjadNaqvi">Twitter</a>, <a href="https://www.linkedin.com/in/asjad-naqvi-phd-9a539512/">LinkedIn </a>or simply via email: [email protected].</p><p id="7eae">My Medium blog for Stata stuff here: <a href="https://medium.com/the-stata-guide">The Stata Guide</a> where new awesome content is released regularly. Clap, and/or follow if you like these guides!</p></article></body>

COVID-19 visualizations with Stata Part 10: Stream graphs

In this Stata guide, learn how to create the following Stream graph using publicly available COVID-19 information on daily cases and deaths from the Our World in Data (OWID) database:

Stream graphs are a follow-up of the Stacked-area graphs guide. It is recommended that you go through the earlier guide before using this one since the essential building blocks are the same, and the earlier guide explains the steps carefully. The additional things we will learn in this guide are preserving and labels before reshaping and applying them after reshaping (also introduced in Guide 9 on Bar graphs), automating area graphs, colors, and labels.

This visualization is inspired by Financial Times’ COVID-19 tracker which currently displays the a stream graph on COVID-19 deaths on their website.

Preamble

Like all previous guides, this guide assumes a basic knowledge of Stata. This guide deals with advanced usage of locals, loops, and code structures that require some experience and familiarity with Stata programming. If you are using this guide for the first time, and are new to Stata, then Guide 1 and Guide 2 are highly recommended, followed by the next set of guides which are in increasing order of difficulty.

This guide uses the following folder structure for the work-flow management:

Within the graphs folder, I also create an additional sub-folder called guide10, to store the figures generated here. For details on how to organize your files, please see Guide 1.

In order to make the graphs exactly as they are shown here, several additional item are required:

ssc install schemepack, replace

and set the scheme to White Tableau:

set scheme white_tableau
ssc install palettes, replace
  • Set default graph font to Arial Narrow (see the Font guide on customizing fonts)
graph set window fontface "Arial Narrow"

This guide has been written in version 16.1 and should work with version 14 and onwards. Earlier versions might need some modification for implementing custom colors.

Get the data in order

We pull the data from the Our World in Data’s COVID-19 webpage as follows:

************************
***  COVID 19 data   ***
************************
insheet using "https://covid.ourworldindata.org/data/owid-covid-data.csv", clear
save ./raw/full_data_raw.dta, replace
gen date2 = date(date, "YMD")
format date2 %tdDD-Mon-yy
drop date
ren date2 date
ren location country
replace country = "Slovak Republic" if country == "Slovakia"
drop if date < 21915  // 1st Jan 2020
save "./master/OWID_data.dta", replace

All observations before 1st Jan 2020 are dropped in the dataset.

Since OWID uses very broad classifications for continents (five continents in total), we will use the World Bank 2020 classifications for country groupings which provides a very large set of regions.

The Excel file can be downloaded from the page linked above. I have already cleaned the file and uploaded it on my GitHub page in Stata format. It can be directly pulled into Stata and saved in the master folder as follows:

**********************************
***  Country classifications   ***
**********************************
  
copy "https://github.com/asjadnaqvi/COVID19-Stata-Tutorials/blob/master/data/country_codes.dta?raw=true" "./master/country_codes.dta", replace

Next we merge the two files together and drop anything that does not match:

use "./master/OWID_data.dta", clear
merge m:1 country using "./master/country_codes.dta"
drop if _m!=3
keep country date new_cases new_deaths group*
summ date
drop if date>=r(max)

We also only keep the variables we need and drop the last date observation to avoid missing values for some countries. This might not be necessary and it all depends on when the data is pulled from the OWID website. In the past, not all countries were updated at the same time.

If everything works well, the dataset should look something like this:

Stata 16.1 interface using the Dark theme

Each group variable corresponds to the classification defined by the World Bank.

Setup for stream graphs

Since we now have World Bank country groupings, we can split the data in as many groups as we want. For now, I am defining the following 12 regions:

gen region = .
replace region = 1 if group29==1 & country=="United States" 
replace region = 2 if group29==1 & country!="United States" 
replace region = 3 if group20==1 & country=="Brazil" 
replace region = 4 if group20==1 & country!="Brazil"
replace region = 5 if group10==1 
replace region = 6 if group8==1 & group10!=1 & country=="United Kingdom" 
replace region = 7 if group8==1 & group10!=1 & country!="United Kingdom" 
replace region = 8 if group26==1 
replace region = 9 if group37==1 
replace region = 10 if group35==1 & country=="India"
replace region = 11 if group35==1 & country!="India" 
replace region = 12 if group6==1

This of course can be increased or decreased based on the level of detail required. We can also label the values of this variable:

lab de region  1 "United States" 2 "Rest of North America" 3 "Brazil" 4 "Rest of Latin America" 5 "European Union" 6 "United Kingdom" 7 "Rest of Europe" 8 "Middle East and North Africa" 9 "Sub-Saharan Africa" 10 "India" 11 "Rest of South Asia" 12 "East Asia and Pacific"
lab val region region

In the next step, we collapse the data and sum up the daily cases and deaths by date and region combination.

collapse (sum) new_cases new_deaths, by(date region)
format date %tdDD-Mon-yy
format new_cases %9.0fc
*** minor cleaning of negative cases
replace new_cases = 0 if new_cases < 0
replace new_deaths = 0 if new_deaths < 0

Note that all variables that are not defined in the collapse command are automatically dropped from the dataset. We also clean up date format and the variables.

Now we declare the data to be a panel dataset using the xtset command. We can use the panel structure to generate a 7-day moving average to smooth out the series:

xtset region date
tssmooth ma new_cases_ma7  = new_cases , w(6 1 0) 
tssmooth ma new_deaths_ma7  = new_deaths , w(6 1 0) 

and we can plot the series for the first few regions to see what it looks like:

twoway ///
 (line new_cases_ma7 date if region==1) ///
 (line new_cases_ma7 date if region==2) ///
 (line new_cases_ma7 date if region==3) ///
 (line new_cases_ma7 date if region==4) ///
 (line new_cases_ma7 date if region==5) ///
 (line new_cases_ma7 date if region==6), ///
  legend(off)

which gives us this graph:

Next we use the logic introduced in Guide 5 on Stacked area graphs and generate a new set of variables which provides cumulative graphs by stacking values on top of each other:

******** new we stack these up
gen stack_cases  = .
gen stack_deaths = .
sort date region
levelsof date, local(dates)
foreach y of local dates {
  
  summ region
*** cases
replace stack_cases  = new_cases_ma7 if date==`y' &  region==`r(min)' 
replace stack_cases  = new_cases_ma7  + stack_cases[_n-1]  if  date==`y' & region!=`r(min)'  
*** deaths  
replace stack_deaths = new_deaths_ma7 if  date==`y' & region==`r(min)' 
replace stack_deaths = new_deaths_ma7 + stack_deaths[_n-1]  if  date==`y' & region!=`r(min)' 
}

Essentially, we are taking the first region observation as it is, and for the subsequent regions iteratively adding up the values. We can also plot the new variables:

twoway ///
 (line stack_cases date if region==1) ///
 (line stack_cases date if region==2) ///
 (line stack_cases date if region==3) ///
 (line stack_cases date if region==4) ///
 (line stack_cases date if region==5) ///
 (line stack_cases date if region==6), ///
  legend(off)

where we get this graph:

Here the difference between the two lines are the daily cases for each region. This figure is also easier to look at relative to the earlier graph.

If you look at the help of the twoway rarea command:

help twoway_rarea

here you will see that the syntax is:

twoway rarea y1var y2var xvar [if] [in] [, options]

This implies that in Stata, if we need to make area graphs, then each region needs to be its own variable. This essentially means we need to reshape the data and make it wide.

Before we reshape, we keep the variables we need and rename the new stack_* variable for convivence:

keep region date new_cases stack_cases new_deaths stack_deaths
// rename just to keep life easy  
ren stack_cases   cases
ren stack_deaths  deaths

As discussed in Guide 9, reshaping losses the information on value labels. We can use this three-step process to (a) preserve the labels in locals before reshaping, (b) reshape the data, and, (c) apply the labels after the reshape:

*** preserve the labels
levelsof region, local(idlabels)      // store the id levels
 
foreach x of local idlabels {       
  local idlab_`x' : label region `x'  
 }
*** reshape the data
reshape wide cases new_cases deaths new_deaths, i(date) j(region) 
order date cases* new_cases* deaths*
*** and apply the labels back
foreach x of local idlabels { 
 lab var     cases`x'  "`idlab_`x''" 
 lab var new_cases`x'  "`idlab_`x''"   
 lab var     deaths`x' "`idlab_`x''"    
 lab var new_deaths`x' "`idlab_`x''"    
 }

Since there are locals involved, the above code has to run in one go. If the code runs fine, we should get something like this where each variable is given a label based on the corresponding number in the variable name:

We can now redraw the line graph shown earlier but now we just do it using variable names rather than if conditions:

twoway ///
 (line cases1 date) ///
 (line cases2 date) ///
 (line cases3 date) ///
 (line cases4 date) ///
 (line cases5 date) ///
 (line cases6 date) ///
 (line cases12 date) ///
  , legend(off)

Note that here I am using the last variable cases12 just to show the extent of the data:

Since we want areas to stack up, we also need to define a dummy 0 variable for the first country:

gen cases0 = 0  
gen deaths0 = 0
twoway ///
 (line cases0 date) /// 
 (line cases1 date) ///
 (line cases2 date) ///
 (line cases3 date) ///
 (line cases4 date) ///
 (line cases5 date) ///
 (line cases6 date) ///
 (line cases12 date) ///
  , legend(off)

Which just gives us this figure:

The importance of having a zero line will become obvious below.

Now we have all the data in place, we can re-center the graph around zero on the y-axis. For this we need to take the maximum value of each date, divide it by two, and subtract each region’s observation by it.

In order to re-center the graph, we take the highest value, or the value of the last variable and divided it by two:

ds cases*
local items : word count `r(varlist)'
local items = `items' - 1
display `items'
gen meanval_cases  =  cases`items' / 2
gen meanval_deaths = deaths`items' / 2
foreach x of varlist cases* {
 gen `x'_norm  = `x' - meanval_cases
}
foreach x of varlist deaths* {
 gen `x'_norm  = `x' - meanval_deaths
}
drop meanval*

The first three lines automate to process of counting the number of variables in the dataset. Note, that we do local items = `items' — 1 to account for the additional cases0 variable we generated earlier, so that the total number of variables in our example are 12, and not 13, for the loop to run properly.

The automation helps if regions are added or subtracted, or the graph is looped over multiple regions, for example, generating a stream graph for each continent with all the countries.

We can now plot the normalized variable given with the suffix *_norm as follows:

twoway ///
 (line cases0_norm date) ///
 (line cases1_norm date) ///
 (line cases2_norm date) ///
 (line cases3_norm date) ///
 (line cases4_norm date) ///
 (line cases5_norm date) ///
 (line cases6_norm date) ///
 (line cases12_norm date) ///
  , legend(off)

which gives us the core skeleton structure we need for stream graphs:

Here we can see the y=0 line has also been re-centered. We can now convert the above figure into an area graph as follows:

twoway ///
 (rarea cases0_norm  cases1_norm date) ///
 (rarea cases1_norm  cases2_norm date) ///
 (rarea cases2_norm  cases3_norm date) ///
 (rarea cases3_norm  cases4_norm date) ///
 (rarea cases4_norm  cases5_norm date) ///
 (rarea cases5_norm  cases6_norm date) ///
 (rarea cases6_norm cases12_norm date) ///
  , legend(off)

which gives us this figure:

Notice also the y-axis which is now showing zero in the center and everything is distributed around it. Also note the pattern for generating the rarea graph where two y-variables have a difference of one in the name.

Labels

We can also automate the labels in three steps.

Step 1: generate the mid points of the last data observation:

*** this part is for the mid points
summ date
gen last = 1 if date==r(max)
ds cases*norm
local items : word count `r(varlist)'
local items = `items' - 2
display `items'
forval i = 0/`items' {
local i0 = `i'
local i1 = `i' + 1
gen ycases`i1'  = (cases`i0'_norm + cases`i1'_norm) / 2 if last==1
gen ydeaths`i1' = (deaths`i0'_norm + deaths`i1'_norm) / 2 if last==1
}

Here note the use of the locals and the word count again to automate the whole process. The mid point has to be calculated as starting value + ending value / 2. Since we are counting from zero, we update the items local by reducing it’s value by 2. Within the loop, we define two locals, i0 and i1, as indices for variable names, which can then be dynamically used in generating the mid points.

Step 2: Next we generate a variable for the share of cases and deaths for the last observation. This is just to indicate how much each region is contributing to the total of the last data point.

This is achieved using a fairly straightforward loop:

*** this part is for the shares
egen lastsum_cases  = rowtotal(new_cases*)  if last==1
egen lastsum_deaths = rowtotal(new_deaths*) if last==1
foreach x of varlist new_cases* {
 gen `x'_share = (`x' / lastsum_cases) * 100
 }
foreach x of varlist new_deaths* {
 gen `x'_share = (`x' / lastsum_deaths) * 100
 }
drop lastsum*

Note here that I am not using the smoothed variable but the actual data for the accurate value of the share of cases. This is also the reason, we carry this variable forward throughout the collapse and reshaping process.

Step 3: generate the variables containing the label for the graphs. Here we again use a mix of several locals to automate the label generation process:

**** here we generate the labels
ds cases*norm
local items : word count `r(varlist)'
local items = `items' - 1
foreach x of numlist 1/`items' {
    
 local t : var lab cases`x'
  
*** cases 
gen label`x'_cases  = "`t'" + " (" + string( new_cases`x', "%9.0f") + ", " + string( new_cases`x'_share, "%9.0fc") + "%)" if last==1
*** deaths
gen label`x'_deaths = "`t'" + " (" + string(new_deaths`x', "%9.0f") + ", " + string(new_deaths`x'_share, "%9.0fc") + "%)" if last==1
  
}

In the code above, ds and word count and item define the number for the loop. The local t picks the variable label, and the gen command puts all this information together. Since the data is in a wide form after the reshape, a new variable is generated for each region.

We can also plot this manually for the same regions shown earlier:

twoway ///
 (rarea cases0_norm  cases1_norm date) ///
 (rarea cases1_norm  cases2_norm date) ///
 (rarea cases2_norm  cases3_norm date) ///
 (rarea cases3_norm  cases4_norm date) ///
 (rarea cases4_norm  cases5_norm date) ///
 (rarea cases5_norm  cases6_norm date) ///
 (rarea cases6_norm cases12_norm date) ///
  (scatter ycases1 date if last==1, ms(smcircle) msize(0.2) mlabel(label1_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ///
  (scatter ycases2 date if last==1, ms(smcircle) msize(0.2) mlabel(label2_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ///
  (scatter ycases3 date if last==1, ms(smcircle) msize(0.2) mlabel(label3_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ///
  (scatter ycases4 date if last==1, ms(smcircle) msize(0.2) mlabel(label4_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ///
  (scatter ycases5 date if last==1, ms(smcircle) msize(0.2) mlabel(label5_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ///
  (scatter ycases6 date if last==1, ms(smcircle) msize(0.2) mlabel(label6_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ///  
  , legend(off)

Which gives us this graph:

We have now achieved the core structure required to automate the final figure.

Automate

In the code below, we use a combination of loops and locals to also define the colors and labels for each region segment of the graph:

*** automate the areas, colors, labels
ds cases*norm
local items : word count `r(varlist)'
local items = `items' - 2
display `items'
forval x = 0/`items' {  

colorpalette    ///
 "253 253 150"  ///
 "255 197   1"  ///
 "255 152   1"  ///
 "  3 125  80"  ///
 "  2  75  48"  ///
 , n(13) nograph
local x0 =  `x'
local x1 =  `x' + 1
 
local areagraph `areagraph' rarea cases`x0'_norm cases`x1'_norm date, fcolor("`r(p`x1')'") lcolor(black) lwidth(*0.15) || (scatter ycases`x1' date if last==1, ms(smcircle) msize(0.2) mlabel(label`x1'_cases) mcolor(black%20) mlabsize(tiny) mlabcolor(black)) ||
 
 }
*** get the date ranges in order
summ date 
local x1 = `r(min)'
local x2 = `r(max)' + 50
*** generate the graph
graph twoway `areagraph' ///
 , ///
  legend(off) ///
  ytitle("", size(small)) ///
  ylabel(-300000(100000)300000) ///
  yscale(noline) ///
  ylabel(, nolabels noticks nogrid) ///
  xscale(noline) ///
  xtitle("") ///
  xlabel(`x1'(15)`x2', labsize(*0.6) angle(vertical) glwidth(vvthin) glpattern(solid)) ///
  title("{fontface Arial Bold: COVID-19 Daily Cases - The World}") ///
  note("Data sources: Our World in Data. World Bank 2020 classifications used for country groups.", size(tiny))

The code above have a lot of parts that fit together. The total number of values that need to be picked are stored in the local items. A custom color palette that has been discussed in the Color guide and applied in the Guide 9 on bar graphs, is also being used here. This can be replaced with any other color scheme. The core body of the figure is stored in the local areagraph which contains two parts; one part for the area graph and one part for the labels including all the customizations of the lines, colors, widths, sizes etc. The color information is also dynamically applied from the values stored in locals after the colorpalette command.

The date range is stored in the two locals x1 and x2. The graph command call the areagraph local, and the date locals. Y-axis is turned off completely including the line which show the values centered around zero. The title is also customized using the fontface argument (see the Font Guide for details).

From the code above, we get the following final figure:

The stream graph above provides a neat representation of how the cases evolve and which region is contributing to the daily cases. It also helps immediately convey the relative scale of the epidemic and how different regions evolve over time. For example, the United States and Europe are major contributors at the time of writing this article. This graph can also be generated per-capita to normalize the regions for more accurate comparisons.

Exercise

Generate the graph for daily deaths shown below:

This graph uses the custom color palette:

colorpalette ///
 "253 253 150" ///
 "255 197   1" ///
 "255 152   1" ///
 "  3 125  80" ///
 "  2  75  48" ///

Don’t forget to clear the locals if you generate the stream graph for daily deaths right after the daily cases graph. Locals can be simply reset as follows:

local areagraph

Hope you enjoyed this guide!

Other Stata guides

Part 1: An introduction to data setup and customized graphs

Part 2: Customizing colors schemes

Part 3: Heat plots

Part 4: Maps

Part 5: Stacked area graphs

Part 6: Animations

Part 7: Doubling time graphs

Part 8: Ridge-line plots (Joy plots)

Part 9: Customized bar graphs

Part 10: Stream graphs

If you enjoy these guides and find them useful, then please like and follow The Stata Guide. Also, please share your visualizations if you use these guides!

About the author

I am an economist by profession and I have been using Stata since 2003. I am currently based in Vienna, Austria where I work at the Vienna University of Economics and Business (WU) and at the International Institute for Applied Systems Analysis (IIASA). You can find my research work on ResearchGate and Google Scholar, and Stata code repository on GitHub. You can follow my COVID-19 related Stata visualizations on my Twitter. I am also featured on the Stata COVID-19 webpage in the visualization and graphics section.

You can connect with me via Medium, Twitter, LinkedIn or simply via email: [email protected].

My Medium blog for Stata stuff here: The Stata Guide where new awesome content is released regularly. Clap, and/or follow if you like these guides!

Stata
Covid-19
Stream Graph
Visualization
Automation
Recommended from ReadMedium