avatarAsjad Naqvi

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

23280

Abstract

the <i>sin theta = tan theta * cos theta </i>formula. Markers are labeled by the number of observations in the last step.</p><p id="afb8">Why do we use <i>double </i>when generating variables? This is to ensure precision is maintained while calculating the values. Check out the <a href="https://readmedium.com/the-power-of-precision-f66a68c99bfc">Power of Precision</a> guide for more information and why it really matters for your calculations.</p><p id="dd59">We can plot the markers as follows:</p><div id="59fb"><pre>twoway <span class="hljs-comment">///</span> (scatter py1 px1, mc(none) <span class="hljs-built_in">ms</span>(point) <span class="hljs-built_in">mlab</span>(marker1) <span class="hljs-built_in">mlabpos</span>(<span class="hljs-number">0</span>)) <span class="hljs-comment">///</span> (scatter py2 px2, mc(none) <span class="hljs-built_in">ms</span>(point) <span class="hljs-built_in">mlab</span>(marker2) <span class="hljs-built_in">mlabpos</span>(<span class="hljs-number">0</span>))</pre></div><p id="1b22">to get this circle of numbers:</p><figure id="05e6"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*fdfbP-3jCiuceCqFDLXiDw.png"><figcaption></figcaption></figure><p id="136d">Note the pattern here. The values 1–10 repeat themselves twice. Since the polar plot starts from the value of 1 give in red and goes clockwise, we need to change the values after the blue 10. Here we just add 10 to the markers that need to be modified and adjust for a small threshold to make sure all value are covered:</p><div id="d73a"><pre><span class="hljs-attribute">replace</span> marker1 = marker1 + <span class="hljs-number">10</span> if py1 > -<span class="hljs-number">3</span>e-<span class="hljs-number">06</span> <span class="hljs-attribute">replace</span> marker2 = marker2 + <span class="hljs-number">10</span> if py2 > <span class="hljs-number">3</span>e-<span class="hljs-number">06</span></pre></div><p id="2aec">This might need slight modification if the radius and number of spikes increase. We can plot the values again:</p><div id="45e6"><pre>twoway <span class="hljs-comment">///</span> (scatter py1 px1, mc(none) <span class="hljs-built_in">ms</span>(point) <span class="hljs-built_in">mlab</span>(marker1) <span class="hljs-built_in">mlabpos</span>(<span class="hljs-number">0</span>)) <span class="hljs-comment">///</span> (scatter py2 px2, mc(none) <span class="hljs-built_in">ms</span>(point) <span class="hljs-built_in">mlab</span>(marker2) <span class="hljs-built_in">mlabpos</span>(<span class="hljs-number">0</span>))</pre></div><figure id="66e3"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*1omYC1-6adJVUbGc-HP1SA.png"><figcaption></figcaption></figure><p id="2897">and we get the correct numbers around the wheel. The option <code><b>mlabpos(0)</b></code><b> </b>centers the label directly on top of the scatter points.</p><p id="e0fe">The spike labels are easier. We pick an axis on which to plot the values, keep it constant, and label the other axis by the number of circles which cut the spikes. In the code below, we freeze the y-axis at 0 and generate x-axis values as simply 1 to 5 for the circles we drew earlier:</p><div id="25c1"><pre><span class="hljs-attribute">gen xvar</span> = . <span class="hljs-attribute">gen yvar</span> = .</pre></div><div id="fdbc"><pre><span class="hljs-keyword">forval</span> x = 1/5 { <span class="hljs-keyword">replace</span> xvar = <span class="hljs-symbol">x'</span> <span class="hljs-keyword">in</span> <span class="hljs-symbol">x'</span> <span class="hljs-keyword">replace</span> yvar = 0 <span class="hljs-keyword">in</span> <span class="hljs-symbol">x'</span> }</pre></div><div id="6324"><pre>twoway <span class="hljs-comment">///</span> (scatter yvar xvar, mc(none) <span class="hljs-built_in">ms</span>(point) <span class="hljs-built_in">mlab</span>(xvar) <span class="hljs-built_in">mlabpos</span>(<span class="hljs-number">3</span>))</pre></div><p id="9dd7">which gives us this simple scatter:</p><figure id="5947"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*YUoGLVC3fC6D_w5LWLqwtg.png"><figcaption></figcaption></figure><p id="aa43">Now let’s put everything together, the circles, the spikes, and their corresponding labels:</p><div id="1672"><pre><span class="hljs-keyword">local</span> <span class="hljs-type">circle</span> // <span class="hljs-keyword">reset</span> the locals <span class="hljs-keyword">local</span> spike</pre></div><div id="93de"><pre>colorpalette <span class="hljs-symbol">gs6</span> <span class="hljs-symbol">gs14</span>, <span class="hljs-built_in">n</span>(<span class="hljs-comment">5</span>) reverse nograph </pre></div><div id="8238"><pre><span class="hljs-keyword">forval</span> x = 1/5 { <span class="hljs-keyword">local</span> width = <span class="hljs-symbol">x'</span> * 0.1 <span class="hljs-keyword">local</span> circle <span class="hljs-symbol">circle'</span> (function <span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(px')'%80"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">x'</span>)) || (function -<span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(px')'%80"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">x'</span>)) || }</pre></div><div id="30c8"><pre>forval x = <span class="hljs-number">1</span>/<span class="hljs-number">10</span> { local theta = (x') * _pi / <span class="hljs-number">10</span>
local liner = <span class="hljs-built_in">abs</span>((<span class="hljs-number">5</span> + <span class="hljs-number">0.2</span>) * <span class="hljs-built_in">cos</span>(`theta'))

local spike spike' (<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(tan<span class="hljs-params">(theta')</span>)</span>*<span class="hljs-title">x</span>, <span class="hljs-title">n</span><span class="hljs-params">(<span class="hljs-number">2</span>)</span> <span class="hljs-title">range</span><span class="hljs-params">(-liner' liner')</span> <span class="hljs-title">lw</span><span class="hljs-params">(vthin)</span> <span class="hljs-title">lc</span><span class="hljs-params">(gs12)</span> <span class="hljs-title">lp</span><span class="hljs-params">(solid)</span>) ||

}

<span class="hljs-title">twoway</span> /// <span class="hljs-title">circle</span>' /// <span class="hljs-title">spike</span>' /// <span class="hljs-params">(scatter py1 px1, mc<span class="hljs-params">(none)</span> ms<span class="hljs-params">(point)</span> mlab<span class="hljs-params">(marker1)</span> mlabpos<span class="hljs-params">(<span class="hljs-number">0</span>)</span> mlabc<span class="hljs-params">(black)</span> mlabsize<span class="hljs-params">(vsmall)</span>)</span> /// <span class="hljs-params">(scatter py2 px2, mc<span class="hljs-params">(none)</span> ms<span class="hljs-params">(point)</span> mlab<span class="hljs-params">(marker2)</span> mlabpos<span class="hljs-params">(<span class="hljs-number">0</span>)</span> mlabc<span class="hljs-params">(black)</span> mlabsize<span class="hljs-params">(vsmall)</span>)</span> /// <span class="hljs-params">(scatter yvar xvar, mc<span class="hljs-params">(none)</span> ms<span class="hljs-params">(point)</span> mlab<span class="hljs-params">(xvar)</span> mlabpos<span class="hljs-params">(<span class="hljs-number">10</span>)</span> mlabc<span class="hljs-params">(black)</span> mlabsize<span class="hljs-params">(tiny)</span>)</span> /// , /// <span class="hljs-title">aspect</span><span class="hljs-params">(<span class="hljs-number">1</span>)</span> <span class="hljs-title">legend</span><span class="hljs-params">(off)</span> /// <span class="hljs-title">xscale</span><span class="hljs-params">(off)</span> <span class="hljs-title">yscale</span><span class="hljs-params">(off)</span> /// <span class="hljs-title">xlabel</span><span class="hljs-params">(, nogrid)</span> <span class="hljs-title">ylabel</span><span class="hljs-params">(, nogrid)</span></span></pre></div><p id="89e0">Three additional elements are added here. The color and the width of circles are now controlled via locals, and the circle labels are positioned at 10'o clock. These options give the graph a clean look with larger circles being more prominent and thicker than the inner circles:</p><figure id="ddf2"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*u5q2HYH90DwWmfPz1fnznA.png"><figcaption></figcaption></figure><p id="40ca">So here we get the underlying blueprint for polar plots.</p><h1 id="0ab3">Part III: Testing with random data</h1><p id="839c">Now let’s just generate random data. Since we are plotting weekly deaths, we just generate 52 random observations for the number of weeks:</p><div id="6195"><pre>clear <span class="hljs-keyword">set</span> obs <span class="hljs-comment">52</span> gen <span class="hljs-comment">obs = _n</span></pre></div><div id="076e"><pre><span class="hljs-attribute">gen</span> double y = runiform(<span class="hljs-number">2</span>, <span class="hljs-number">4</span>)</pre></div><p id="da6a">Note that any number of observations can be used. If there are 52 segments, then observations higher than 52 will just continue on the wheel (that is also the beauty of trigonometry and circles). Here we limit observations to 52 just for clarity in how the plotted data looks like.</p><p id="8bcb">The next part is fairly straightforward, where we generate the angle of each observation on the wheel:</p><div id="8a78"><pre><span class="hljs-attribute">gen</span> double angle = obs * <span class="hljs-number">2</span> * _pi / <span class="hljs-number">52</span></pre></div><p id="a929">Note that the angle is independent of the actual data. It is determined by the full circle or 360 degrees = <code>2 pi</code> divided by the number of spikes. Once the angle is calculated, the polar coordinates of the actual data we want to plot can be generated as follows:</p><div id="1d4f"><pre>gen double obsx = y * <span class="hljs-built_in">cos</span>(<span class="hljs-built_in">angle</span>) gen double obsy = y * <span class="hljs-built_in">sin</span>(<span class="hljs-built_in">angle</span>)</pre></div><p id="757e">where the data value <code>y</code> plays the role of the radius and the angle determines the position on the circle (see reference figure above for formulas).</p><p id="4b4b">We can now apply what we learned above and plot the values as follows:</p><div id="920d"><pre><span class="hljs-keyword">local</span> <span class="hljs-type">circle</span> // <span class="hljs-keyword">reset</span> the locals <span class="hljs-keyword">local</span> spike</pre></div><div id="dded"><pre><span class="hljs-bullet">*** </span>circles</pre></div><div id="f0f9"><pre>colorpalette <span class="hljs-symbol">gs6</span> <span class="hljs-symbol">gs14</span>, <span class="hljs-built_in">n</span>(<span class="hljs-comment">5</span>) reverse nograph</pre></div><div id="ba9b"><pre><span class="hljs-keyword">forval</span> x = 1/5 { <span class="hljs-keyword">local</span> width = <span class="hljs-symbol">x'</span> * 0.05 <span class="hljs-keyword">local</span> circle <span class="hljs-symbol">circle'</span> (function <span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(px')'%80"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">x'</span>)) || (function -<span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(px')'%80"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">x'</span>)) || }</pre></div><div id="5c72"><pre><span class="hljs-bullet">*** </span>spikes</pre></div><div id="29ec"><pre>forval x = <span class="hljs-number">1</span>/<span class="hljs-number">26</span> { <span class="hljs-keyword">local</span> theta = (x<span class="hljs-string">') * _pi / 26 local liner = abs((5 + 0.2) * cos(theta'</span>))

<span class="hljs-keyword">local</span> spike spik<span class="hljs-string">e' (function (tan(theta'</span>))x, n(<span class="hljs-number">2</span>) range(-liner<span class="hljs-string">' liner'</span>) lw(<span class="hljs-number">0.2</span>) lc(gs13) lp(solid)) ||

}</pre></div><div id="2fe5"><pre><span class="hljs-bullet">*** </span>figure </pre></div><div id="166e"><pre><span class="hljs-keyword">twoway</span> <span class="hljs-comment">///</span> (connected obsy obsx, lc(red)) <span class="hljs-comment">///</span> <span class="hljs-symbol">circle'</span> <span class="hljs-comment">///</span> <span class="hljs-symbol">spike'</span> <span class="hljs-comment">/// </span> , <span class="hljs-comment">///</span> aspect(1) legend(off) <span class="hljs-comment">///</span> xscale(off) yscale(off) <span class="hljs-comment">///</span> xlabel(, nogrid) ylabel(, nogrid)</pre></div><p id="b741">which gives us this figure:</p><figure id="98d0"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*anbmxHEgpq20uFnRCWzfig.png"><figcaption></figcaption></figure><p id="2223">Notice also how the locals are modified based on the number of spikes. Additionally since the data is random, the red line will look different every time one runs the script, but it should start and end on the same spikes. Also note that the individual points align perfectly with each spike. This means that the angles are calculated correctly.</p><p id="e88a">We can now go ahead and add the other styling elements including labels:</p><div id="3c1e"><pre>*** <span class="hljs-keyword">add</span> <span class="hljs-type">circle</span> markers</pre></div><div id="6d2f"><pre><span class="hljs-attribute">gen xvar</span> = . <span class="hljs-attribute">gen yvar</span> = .</pre></div><div id="c379"><pre><span class="hljs-keyword">forval</span> x = 1/5 { <span class="hljs-keyword">replace</span> xvar = <span class="hljs-symbol">x'</span> <span class="hljs-keyword">in</span> <span class="hljs-symbol">x'</span> <span class="hljs-keyword">replace</span> yvar = 0 <span class="hljs-keyword">in</span> <span class="hljs-symbol">x'</span> }</pre></div><div id="28f6"><pre>*** <span class="hljs-built_in">add</span> spike markers</pre></div><div id="a8fe"><pre><span class="hljs-attribute">gen</span> spikes = _n in <span class="hljs-number">1</span>/<span class="hljs-number">26</span> <span class="hljs-attribute">gen</span> double theta = - spikes * _pi / <span class="hljs-number">26</span></pre></div><div id="1577"><pre><span class="hljs-attribute">gen</span> double px1 = abs((<span class="hljs-number">5</span> + <span class="hljs-number">0</span>.<span class="hljs-number">2</span>) * cos(theta)) <span class="hljs-attribute">gen</span> double px2 = -abs((<span class="hljs-number">5</span> + <span class="hljs-number">0</span>.<span class="hljs-number">2</span>) * cos(theta))</pre></div><div id="48db"><pre>gen <span class="hljs-type">double</span> py1 = <span class="hljs-built_in">tan</span>(theta)*px1 gen <span class="hljs-type">double</span> py2 = <span class="hljs-built_in">tan</span>(theta)*px2</pre></div><div id="5b10"><pre>gen marker1 <span class="hljs-operator">=</span> obs gen marker2 <span class="hljs-operator">=</span> obs</pre></div><div id="482c"><pre><span class="hljs-attribute">replace</span> marker1 = marker1 + <span class="hljs-number">26</span> if py1 &gt; -<span class="hljs-number">3</span>e-<span class="hljs-number">06</span> <span class="hljs-attribute">replace</span> marker2 = marker2 + <span class="hljs-number">26</span> if py2 &gt; <span class="hljs-number">3</span>e-<span class="hljs-number">06</span></pre></div><div id="3552"><pre><span class="hljs-keyword">local</span> <span class="hljs-type">circle</span> // <span class="hljs-keyword">reset</span> the locals <span class="hljs-keyword">local</span> spike</pre></div><div id="5c6c"><pre>*** <span class="hljs-keyword">generate</span> the circles</pre></div><div id="caa0"><pre>colorpalette gs10 gs14, n(<span class="hljs-number">5</span>) <span class="hljs-keyword">reverse</span> nograph <span class="hljs-keyword">return</span> list</pre></div><div id="aa8c"><pre><span class="hljs-keyword">forval</span> x = 1/5 { <span class="hljs-keyword">local</span> width = <span class="hljs-symbol">x'</span> * 0.05 <span class="hljs-keyword">local</span> circle <span class="hljs-symbol">circle'</span> (function <span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(px')'%80"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">x'</span>)) || (function -<span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(px')'%80"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">x'</span>)) || }</pre></div><div id="d83b"><pre>*** <span class="hljs-keyword">generate</span> the spikes</pre></div><div id="cfc9"><pre>forval x = <span class="hljs-number">1</span>/<span class="hljs-number">26</span> { <span class="hljs-keyword">local</span> theta = (x<span class="hljs-string">') * _pi / 26
local liner = abs((5 + 0.2) * cos(`theta'</span>))

<span class="hljs-keyword">local</span> spike spik<span class="hljs-string">e' (function (tan(theta'</span>))x, n(<span class="hljs-number">2</span>) range(-liner<span class="hljs-string">' liner'</span>) lw(<span class="hljs-number">0.2</span>) lc(gs13) lp(solid)) ||

}</pre></div><div id="b3e5"><pre>*** <span class="hljs-built_in">plot</span> the <span class="hljs-built_in">figure</span></pre></div><div id="27a3"><pre><span class="hljs-keyword">twoway</span> <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> obsy obsx, lc(red)) <span class="hljs-comment">///</span> <span class="hljs-symbol">circle'</span> <span class="hljs-comment">///</span> <span class="hljs-symbol">spike'</span> <span class="hljs-comment">/// </span> (<span class="hljs-keyword">scatter</span> py1 px1, mc(none) ms(point) mlab(marker1) mlabpos(0) mlabc(black) mlabsize(tiny)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">scatter</span> py2 px2, mc(none) ms(point) mlab(marker2) mlabpos(0) mlabc(black) mlabsize(tiny)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">scatter</span> yvar xvar, mc(none) ms(point) mlab(xvar) mlabpos(10) mlabc(gs8) mlabsize(tiny)) <span class="hljs-comment">///</span> , <span class="hljs-comment">///</span> aspect(1) legend(off) <span class="hljs-comment">///</span> xlabel(-5(1)5) ylabel(-5(1)5) <span class="hljs-comment">///</span> xscale(off) yscale(off) <span class="hljs-comment">///</span> xsize(1) ysize(1) <span class="hljs-comment">///</span> xlabel(, nogrid) ylabel(, nogrid)</pre></div><p id="b6c8">and this gives us:</p><figure id="c6b2"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*vB2fWEWbNGlqpOBJ0OpZGQ.png"><figcaption></figcaption></figure><p id="8be1">Here we add <code>xsize</code> and <code>ysize</code> to get rid of the white space on the sides of the circle. This makes the image a square and therefore makes the polar plot stand out more. You can click on this figure and the earlier figure to see the difference. This example also illustrates how <code>aspect</code> controls the plot dimensions and <code>xsize </code>and <code>ysize</code> control the overall figure dimensions. Both elements can be modified depending on the purpose of the figure.</p><h1 id="ffb6">Part IV: Excess death polar plot</h1><p id="2122">In this part, we will learn how to graph actual data on a polar plot. For this, we will make use of the weekly deaths dataset available from <a href="https://ec.europa.eu/eurostat/data/database">Eurostat</a> in the file <a href="https://appsso.eurostat.ec.europa.eu/nui/show.do?dataset=demo_r_mweek3&amp;lang=en">demo_r_mweek3</a>. The dataset is very extensive, providing information up to NUTS-3 level (the lowest homogenous administrative sub-division for the EU), for all year and week combinations starting from the year 2000. It also provides breakdown by gender and age groups. This file is also the key dataset used for producing excess death figures for Europe.</p><p id="d4e3">Here, I initially had the idea to also introduce the script that processes the raw data based on the <a href="https://readmedium.com/automating-eurostat-in-stata-part-1-a047941b2b4f">Eurostat guide</a> I posted earlier. But the dataset is simply too large and needs a detailed guide on its own. For example, for year 2015 and onwards, there are more than 26 million data points, and the Stata file, which is very efficient in storing information, is almost one giga byte (1 GB) in size. This can be challenging if the processing power is not there, and also distracts from the radial plots we want to build in this guide.</p><p id="b23c">Instead, I have posted a reduced form of the dataset on <a href="https://github.com/asjadnaqvi/The-Stata-Guide">the Stata Guide GitHub</a>, which was processed at the time of writing this guide. The file has information at the country (NUTS 0) level. It can be stored in your working directory using the following command:</p><div id="0b1e"><pre><span class="hljs-keyword">use</span> <span class="hljs-string">"https://github.com/asjadnaqvi/The-Stata-Guide/blob/master/data/demo_r_mweek3_medium.dta?raw=true"</span>, <span class="hljs-keyword">clear</span></pre></div><p id="be07">Note, it is always good practice to set directories and paths at the beginning of the dofiles. See <a href="https://readmedium.com/covid-19-data-visualization-with-stata-part-1-an-introduction-to-data-setup-and-customized-6b879a1e8647">Guide 1 here</a> on an introduction to workflow management.</p><p id="e540">For the tutorial, I will use Austria as the case example. Other countries can be generated using the procedures defined below or the whole script can be looped over all the countries as well.</p><p id="2b73">Basic cleaning is done as follows:</p><div id="07ef"><pre>keep if geo<span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-string">"AT"</span> drop unit</pre></div><div id="2c77"><pre><span class="hljs-built_in">split</span> <span class="hljs-built_in">year</span>, p(W) destring <span class="hljs-comment">// extract

Options

year and weeks</span> drop <span class="hljs-built_in">year</span> ren year1 <span class="hljs-built_in">year</span> ren year2 <span class="hljs-built_in">week</span></pre></div><div id="0bdf"><pre><span class="hljs-keyword">drop</span> <span class="hljs-keyword">if</span> year><span class="hljs-number">2020</span> // <span class="hljs-keyword">in</span> <span class="hljs-keyword">case</span> the data <span class="hljs-keyword">is</span> updated <span class="hljs-keyword">to</span> <span class="hljs-keyword">include</span> <span class="hljs-number">2021</span></pre></div><div id="e097"><pre>gen <span class="hljs-built_in">date</span> = yw(<span class="hljs-built_in">year</span>,<span class="hljs-built_in">week</span>) <span class="hljs-comment">// not needed but given as date example</span> format <span class="hljs-built_in">date</span> %tw</pre></div><div id="5b26"><pre><span class="hljs-keyword">order</span> <span class="hljs-title">geo</span> <span class="hljs-keyword">date</span> age sort geo <span class="hljs-keyword">date</span> age</pre></div><div id="97b1"><pre><span class="hljs-comment">*** keep the main age brackets</span> keep <span class="hljs-keyword">if</span> age == <span class="hljs-string">"Y_LT5"</span> | <span class="hljs-comment">///</span> age == <span class="hljs-string">"Y5-9"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y10-14"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y15-19"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y20-24"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y25-29"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y30-34"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y35-39"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y40-44"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y45-49"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y50-54"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y55-59"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y60-64"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y65-69"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y70-74"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y75-79"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"Y80-84"</span> | <span class="hljs-comment">///</span> age == <span class="hljs-string">"Y85-89"</span> | <span class="hljs-comment">///</span> age == <span class="hljs-string">"Y_GE90"</span> | <span class="hljs-comment">/// </span> age == <span class="hljs-string">"TOTAL"</span></pre></div><div id="50a4"><pre>*** <span class="hljs-keyword">keep</span> information <span class="hljs-keyword">for</span> total population <span class="hljs-keyword">keep</span> if sex==<span class="hljs-string">"T"</span>
<span class="hljs-keyword">drop</span> sex</pre></div><div id="7305"><pre>*** <span class="hljs-built_in">replace</span> hyphens <span class="hljs-built_in">with</span> underscores <span class="hljs-built_in">replace</span> age = subinstr(age, <span class="hljs-string">"-"</span>, <span class="hljs-string">""</span>, .) </pre></div><p id="1b42">We basically reduce the information in the file and clean up the variables. Next step, we reshape the data and clean it some more so we have the information in the shape we need:</p><div id="d37b"><pre><span class="hljs-keyword">ren</span> y tot <span class="hljs-keyword">reshape</span> wide tot_, <span class="hljs-built_in">i</span>(geo date year week) <span class="hljs-built_in">j</span>(age) string</pre></div><div id="9cc0"><pre><span class="hljs-built_in">sort</span> geo <span class="hljs-built_in">date</span></pre></div><div id="1572"><pre><span class="hljs-built_in">ren</span> tot_* </pre></div><div id="b2ef"><pre>recode Y (.<span class="hljs-operator">=</span><span class="hljs-number">0</span>) drop if date<span class="hljs-operator">=</span><span class="hljs-operator">=</span>.</pre></div><div id="5fa3"><pre>encode geo, gen(geo2) <span class="hljs-keyword">order</span> <span class="hljs-title">geo2</span> <span class="hljs-keyword">date</span></pre></div><div id="0202"><pre><span class="hljs-keyword">drop</span> <span class="hljs-keyword">TOTAL</span></pre></div><div id="611d"><pre><span class="hljs-comment">*** clean up names</span> <span class="hljs-keyword">ren</span> Y5_9 Y05_09 <span class="hljs-keyword">ren</span> Y_LT5 Y00_04 <span class="hljs-keyword">ren</span> Y_GE90 Y90_100</pre></div><div id="6e10"><pre><span class="hljs-keyword">order</span> <span class="hljs-title">Y</span>, alpha last // notice the <span class="hljs-keyword">order</span> <span class="hljs-title">options</span> here</pre></div><div id="2327"><pre>** <span class="hljs-variable">aggregate</span> <span class="hljs-variable">the</span> <span class="hljs-variable">data</span> <span class="hljs-variable">egen</span> <span class="hljs-variable">Y00_64</span> = <span class="hljs-function"><span class="hljs-title">rowtotal</span>(<span class="hljs-variable">Y00_04</span> - <span class="hljs-variable">Y60_64</span>)</span> <span class="hljs-variable">egen</span> <span class="hljs-variable">Y65_99</span> = <span class="hljs-function"><span class="hljs-title">rowtotal</span>(<span class="hljs-variable">Y65_69</span> - <span class="hljs-variable">Y90_100</span>)</span></pre></div><div id="88ab"><pre>drop Y00_04 - Y90_100 <span class="hljs-keyword">order</span> <span class="hljs-title">geo</span> geo2 <span class="hljs-keyword">date</span> year week</pre></div><p id="d83c">In the end, the data should look like this:</p><figure id="f2dc"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*OcqG6nS9i3q5RnCRBcV6kA.png"><figcaption></figcaption></figure><p id="c882">Next start with generating the angles and the polar coordinates of the data:</p><div id="fb38"><pre><span class="hljs-comment">// fix the angles of data points</span></pre></div><div id="1c96"><pre><span class="hljs-built_in">sort</span> <span class="hljs-built_in">year</span> <span class="hljs-built_in">week</span></pre></div><div id="da8f"><pre><span class="hljs-keyword">levelsof</span> year, <span class="hljs-keyword">local</span>(lvls) <span class="hljs-keyword">foreach</span> x of <span class="hljs-keyword">local</span> lvls {

<span class="hljs-keyword">gen</span> obs_<span class="hljs-symbol">x'</span> = _n <span class="hljs-keyword">if</span> year==<span class="hljs-symbol">x'</span>

<span class="hljs-keyword">local</span> year1 = <span class="hljs-symbol">x'</span> - 1 <span class="hljs-keyword">cap</span> <span class="hljs-keyword">replace</span> obs_<span class="hljs-symbol">x'</span> = n <span class="hljs-keyword">if</span> year==<span class="hljs-symbol">year1'</span> &amp; week==52</pre></div><div id="c31e"><pre><span class="hljs-keyword">gen</span> double angle_<span class="hljs-symbol">x'</span> = obs<span class="hljs-symbol">`x'</span> * -2 * _pi / 52

<span class="hljs-keyword">gen</span> double x65_<span class="hljs-symbol">x'</span> = Y65_99 * <span class="hljs-built_in">cos</span>(angle_<span class="hljs-symbol">x'</span>) <span class="hljs-keyword">gen</span> double y65_<span class="hljs-symbol">x'</span> = Y65_99 * <span class="hljs-built_in">sin</span>(angle_<span class="hljs-symbol">x'</span>) }</pre></div><div id="3389"><pre>drop obs* <span class="hljs-built_in">angle</span></pre></div><p id="cc69">What this piece of code is doing is taking the value of age 65 plus from the <i>Y65_99</i> variable and converting them in polar coordinates. Each year also has its own data column where the last entry of a year is padded on to the next year as the starting value. These steps, are done to generate colored lines for each year, and the padding ensures that the new year starts from the last point of the previous year and ensures continuity. The screen shot of the data is as follows:</p><figure id="d882"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*0VQiiQIbOCbMm894pyBrbQ.png"><figcaption></figcaption></figure><p id="19f5">Next we automate the spike markers using the min/max of the key variable <i>Y65_99:</i></p><div id="85fd"><pre><span class="hljs-bullet">***** </span>spike markers here</pre></div><div id="001e"><pre><span class="hljs-attribute">gen</span> double obs = _n in <span class="hljs-number">1</span>/<span class="hljs-number">26</span> <span class="hljs-attribute">gen</span> double theta = -obs * _pi / <span class="hljs-number">26</span></pre></div><div id="4010"><pre><span class="hljs-keyword">summ</span> Y65_99 <span class="hljs-keyword">local</span> cmin = <span class="hljs-built_in">max</span>(0, <span class="hljs-built_in">round</span>(<span class="hljs-built_in">r</span>(min)', 20) - 50) <span class="hljs-keyword">local</span> cmax = <span class="hljs-built_in">round</span>(<span class="hljs-built_in">r</span>(max)', 20) + 50</pre></div><div id="cf8d"><pre><span class="hljs-keyword">display</span> <span class="hljs-symbol">cmin'</span> <span class="hljs-keyword">display</span> <span class="hljs-symbol">cmax'</span></pre></div><div id="2a17"><pre><span class="hljs-keyword">local</span> diff = <span class="hljs-built_in">round</span>((<span class="hljs-symbol">cmax'</span> - <span class="hljs-symbol">cmin'</span>) / 6) <span class="hljs-comment">// divisions</span></pre></div><div id="d906"><pre><span class="hljs-keyword">gen</span> px1 = <span class="hljs-built_in">abs</span>((<span class="hljs-symbol">cmax'</span> * 1.05) * <span class="hljs-built_in">cos</span>(theta)) <span class="hljs-keyword">gen</span> px2 = -<span class="hljs-built_in">abs</span>((<span class="hljs-symbol">cmax'</span> * 1.05) * <span class="hljs-built_in">cos</span>(theta))</pre></div><div id="6928"><pre><span class="hljs-keyword">gen</span> py1 = <span class="hljs-built_in">tan</span>(theta)*px1 <span class="hljs-keyword">gen</span> py2 = <span class="hljs-built_in">tan</span>(theta)*px2</pre></div><div id="a205"><pre>gen marker1 <span class="hljs-operator">=</span> obs gen marker2 <span class="hljs-operator">=</span> obs</pre></div><div id="2a01"><pre><span class="hljs-attribute">replace</span> marker1 = marker1 + <span class="hljs-number">26</span> if py1 > -<span class="hljs-number">1</span>e-<span class="hljs-number">02</span> <span class="hljs-attribute">replace</span> marker2 = marker2 + <span class="hljs-number">26</span> if py2 > <span class="hljs-number">1</span>e-<span class="hljs-number">02</span></pre></div><p id="e70a">A buffer of 50 is added to the minimum and maximum values and the number of divisions of circles is set to 6. These are control variables that can be modified as well. Markers are pushed out by a factor of 5% (<code>cmax' * <b>1.05</b></code>), which also ensures that labels are scaled according to the maximum circle value. Label markers are corrected in the last step.</p><p id="5e27">In the next step, circle markers are generated as follows:</p><div id="0873"><pre><span class="hljs-bullet">****** </span>spike markers here</pre></div><div id="6405"><pre><span class="hljs-keyword">summ</span> Y65_99 <span class="hljs-keyword">local</span> cmin = <span class="hljs-built_in">max</span>(0, <span class="hljs-built_in">round</span>(<span class="hljs-built_in">r</span>(min)', 20) - 50) <span class="hljs-keyword">local</span> cmax = <span class="hljs-built_in">round</span>(<span class="hljs-built_in">r</span>(max)', 20) + 50</pre></div><div id="21c1"><pre><span class="hljs-variable">local</span> <span class="hljs-variable">diff</span> = <span class="hljs-function"><span class="hljs-title"><span class="hljs-built_in">round</span></span>((<span class="hljs-variable">cmax</span><span class="hljs-string">' - cmin'</span>) / <span class="hljs-number">6</span>)</span></pre></div><div id="188d"><pre><span class="hljs-attribute">cap gen xvar</span> = . <span class="hljs-attribute">cap gen yvar</span> = .</pre></div><div id="55a9"><pre>local i <span class="hljs-operator">=</span> <span class="hljs-number">1</span></pre></div><div id="ba00"><pre><span class="hljs-keyword">forval</span> x = <span class="hljs-symbol">cmin'</span>(<span class="hljs-symbol">diff'</span>)<span class="hljs-symbol">cmax'</span> {

<span class="hljs-keyword">replace</span> xvar = <span class="hljs-symbol">x'</span> <span class="hljs-keyword">in</span> <span class="hljs-symbol">i'</span> <span class="hljs-keyword">replace</span> yvar = 0 <span class="hljs-keyword">in</span> <span class="hljs-symbol">i'</span> <span class="hljs-keyword">local</span> i = <span class="hljs-symbol">i'</span> + 1 }</pre></div><p id="6c49">Here I am using the <i>cmin,</i> <i>cmax, diff </i>locals again for the sake of completion. If the script is executing in one go, these locals just need to be defined once.</p><p id="81c4">Once the labels are sorted out, the next piece of code generates the final graph we need:</p><div id="b159"><pre><span class="hljs-keyword">local</span> <span class="hljs-type">circle</span> // <span class="hljs-keyword">reset</span> the <span class="hljs-keyword">local</span></pre></div><div id="8c79"><pre><span class="hljs-keyword">summ</span> Y65_99 <span class="hljs-keyword">local</span> cmin = <span class="hljs-built_in">max</span>(0, <span class="hljs-built_in">round</span>(<span class="hljs-built_in">r</span>(min)', 20) - 100) <span class="hljs-keyword">local</span> cmax = <span class="hljs-built_in">round</span>(<span class="hljs-built_in">r</span>(max)', 20) + 100</pre></div><div id="2608"><pre><span class="hljs-variable">local</span> <span class="hljs-variable">diff</span> = <span class="hljs-function"><span class="hljs-title"><span class="hljs-built_in">round</span></span>((<span class="hljs-variable">cmax</span><span class="hljs-string">' - cmin'</span>) / <span class="hljs-number">6</span>)</span></pre></div><div id="2808"><pre><span class="hljs-bullet">*** </span>circles</pre></div><div id="26fa"><pre>colorpalette <span class="hljs-symbol">gs12</span> <span class="hljs-symbol">gs14</span>, <span class="hljs-built_in">n</span>(<span class="hljs-comment">12</span>) reverse nograph</pre></div><div id="f2a6"><pre>local i <span class="hljs-operator">=</span> <span class="hljs-number">1</span></pre></div><div id="469b"><pre><span class="hljs-keyword">forval</span> x = <span class="hljs-symbol">cmin'</span>(<span class="hljs-symbol">diff'</span>)<span class="hljs-symbol">cmax'</span> {</pre></div><div id="e98c"><pre> <span class="hljs-keyword">local</span> width = <span class="hljs-symbol">i'</span> * 0.05

<span class="hljs-keyword">local</span> circle <span class="hljs-symbol">circle'</span> (function <span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(pi')'%70"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">x'</span>)) || (function -<span class="hljs-built_in">sqrt</span>(<span class="hljs-symbol">x'</span>^2 - x^2), lc(<span class="hljs-string">"r(pi')'%70"</span>) lw(<span class="hljs-symbol">width'</span>) lp(solid) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">x'</span> <span class="hljs-symbol">`x'</span>)) ||

<span class="hljs-keyword">local</span> i = <span class="hljs-symbol">i'</span> + 1 }</pre></div><div id="83b6"><pre><span class="hljs-bullet">*** </span>spikes</pre></div><div id="a770"><pre><span class="hljs-keyword">local</span> spike</pre></div><div id="919f"><pre><span class="hljs-keyword">forval</span> x = 1/26 { <span class="hljs-keyword">local</span> theta = (<span class="hljs-symbol">x'</span>) * _pi / 26
<span class="hljs-keyword">local</span> liner = <span class="hljs-built_in">abs</span>((<span class="hljs-symbol">cmax'</span> + 40) * <span class="hljs-built_in">cos</span>(<span class="hljs-symbol">theta'</span>))
<span class="hljs-keyword">local</span> spike <span class="hljs-symbol">spike'</span> (function (<span class="hljs-built_in">tan</span>(<span class="hljs-symbol">theta'</span>))x, <span class="hljs-keyword">n</span>(2) <span class="hljs-keyword">range</span>(-<span class="hljs-symbol">liner'</span> <span class="hljs-symbol">liner'</span>) lw(vvthin) lc(gs8) lp(solid)) || }</pre></div><div id="5f08"><pre>**** <span class="hljs-keyword">final</span> graph <span class="hljs-keyword">here</span></pre></div><div id="3ae2"><pre>colorpalette inferno, <span class="hljs-built_in">n</span>(<span class="hljs-comment">15</span>) reverse nograph</pre></div><div id="5598"><pre><span class="hljs-keyword">twoway</span> <span class="hljs-comment">///</span> <span class="hljs-symbol">circle'</span> <span class="hljs-comment">///</span> <span class="hljs-symbol">spike'</span> <span class="hljs-comment">///</span> (<span class="hljs-keyword">scatter</span> py1 px1, mc(none) ms(point) mlab(marker1) mlabpos(0) mlabc(gs6) mlabsize(*0.5)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">scatter</span> py2 px2, mc(none) ms(point) mlab(marker2) mlabpos(0) mlabc(gs6) mlabsize(*0.5)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">scatter</span> yvar xvar, mc(none) ms(point) mlab(xvar) mlabpos(9) mlabc(gs6) mlabangle(vertical) mlabsize(*0.4)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> y65_2015 x65_2015, lc(<span class="hljs-string">"r(p3)'"</span>) lp(solid) lw(vthin)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> y65_2016 x65_2016, lc(<span class="hljs-string">"r(p4)'"</span>) lp(solid) lw(vthin)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> y65_2017 x65_2017, lc(<span class="hljs-string">"r(p5)'"</span>) lp(solid) lw(vthin)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> y65_2018 x65_2018, lc(<span class="hljs-string">"r(p6)'"</span>) lp(solid) lw(vthin)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> y65_2019 x65_2019, lc(<span class="hljs-string">"r(p7)'"</span>) lp(solid) lw(vthin)) <span class="hljs-comment">///</span> (<span class="hljs-keyword">line</span> y65_2020 x65_2020, lc(<span class="hljs-string">"r(p15)'"</span>) lp(solid) lw(thin)) <span class="hljs-comment">///</span> , <span class="hljs-comment">///</span> aspect(1) legend(off) <span class="hljs-comment">///</span> xscale(off) yscale(off) <span class="hljs-comment">///</span> xsize(1) ysize(1) <span class="hljs-comment">///</span> xlabel(, nogrid) ylabel(, nogrid) <span class="hljs-comment">///</span> title(<span class="hljs-string">"{fontface Arial Bold: Excess deaths in Austria}"</span>) <span class="hljs-comment">///</span> subtitle(<span class="hljs-string">"65 years and older"</span>, size(small)) <span class="hljs-comment">///</span> <span class="hljs-keyword">note</span>(<span class="hljs-string">"Source: Eurostat table demo_r_mweek3. Week spokes are labeled on the outer edge in a clockwise direction. The ring values, given as vertical numbers,"</span> <span class="hljs-string">"show the number of deaths. 2020 values plotted in bold red color, and 2015-2019 values plotted in light grey-red shades."</span>, size(tiny))</pre></div><p id="5b13">The code above does a lot of fine tuning to minor elements to gives us this clean-looking polar plot:</p><figure id="0fd7"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*lgeEF46p_i70nysqIjGJLA.png"><figcaption></figcaption></figure><p id="c679">Since the data is only available till week 50, at the time of writing this guide, we can see that there have been excess deaths compared to the previous five years after week 43 for age groups 65 and over. In week 49 there were almost a 1000 extra deaths. One previous year also shows extra deaths mostly due to the combination of flu and cold (see official <a href="https://twitter.com/STATISTIK_AT/status/1349629885581582336/photo/1">Statistik Austria explanations here</a>). Excess deaths can also be observed in other weeks especially around the first wave in weeks 14–16 but these could also be wrongly associated with COVID-19 (Type I error). There is also a possibility that the measures put in place to prevent COVID-19 also reduce other types of mortalities such that one sees a zero net impact of the virus. This of course, is a deeper look at the data and subject to debate as well.</p><h1 id="5974">Exercise</h1><p id="638e">Try and replicating the graph for ages below 65 years, which also shows excess deaths in the last weeks of 2020:</p><figure id="375d"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*Mu26zNbvRcEEPjNVNx_mZw.png"><figcaption></figcaption></figure><p id="9546">Also try and generate polar plots for other countries. For example, see Spain and France below which show very different patterns of weekly deaths.</p><figure id="3a01"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*sOswLtP-2IhY4o1IkYSOwg.png"><figcaption></figcaption></figure><figure id="62d9"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*Zl1IDBnL6le3CeAY1-E5PA.png"><figcaption></figcaption></figure><p id="e233">Note that the scale of excess deaths in these countries is in 7,000–10,000 range in some weeks, so the cumulative impact is very high.</p><p id="2435">Hope you found this guide useful! Please share your visualizations, errors, bugs, suggestions, comments etc. For more polar plot guides, check out the <a href="https://medium.com/the-stata-guide/tagged/polar">Polar section</a> on the Stata guide.</p><h1 id="7c46">About the author</h1><p id="1224">I am an economist by profession and I have been using Stata since 2003. I am currently based in Vienna, Austria. You can see my profile, research, and projects on <a href="https://github.com/asjadnaqvi">GitHub</a> or my <a href="https://asjadnaqvi.github.io/">website</a>. 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: <i>[email protected]</i>. If you have questions regarding the Guide or Stata in general post them on <a href="https://discord.gg/qpHZtX6Xkk"><b>The Code Block</b></a> Discord server.</p><p id="628a"><a href="https://medium.com/the-stata-guide">The Stata Guide</a> releases awesome new content regularly. <a href="https://asjadnaqvi.medium.com/about">Subscribe</a>, Clap, and/or Follow the guide if you like the content!</p></article></body>

Stata graphs: Polar (Radial) Plots

In this guide, learn how to make polar plots from scratch in Stata. The polar plot script is also applied to the Eurostat’s weekly deaths dataset to generate excess deaths figures shown below:

The above visualization was inspired by this Reddit post where weekly deaths are animated in a wheel. This visualization makes a lot of sense, especially when exploring seasonal indicators that continue from one year to the next. For example, deaths go up in winter starting around November and this wave continues till February. The standard way of graphing this information it to plot different lines for each year (see graph above). Even though this also work, the continuity of seasonal patterns is broken.

This guide is slightly more advanced than previous guides since the visualization is programmed from scratch. It also involves a basic knowledge of trigonometry, transformation of cartesian to polar coordinates, and knowledge of programming in Stata. These are discussed where necessary.

Preamble

Like other guides, a basic knowledge of Stata is assumed. 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.

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 Stata version 16.1. Earlier versions might need some modifications.

Additionally, keep this figure as a reference point for formulas used below:

Own illustration

This figure was introduced in a previous guide where we learned how to add arrows to line graphs which also involved calculating the correct angle of the arrows.

Part I: Functions in Stata

An under-explored, yet an extremely powerful feature of Stata is the ability to plot functions. In fact, I have hardly seen it being used or discussed except by Stata programmers. The functions feature can be combined with any other twoway graph option to generate a range of exciting visualizations.

The Stata GUI to access the Functions option is given in Graphics>Twoway graphs>Create>Advanced plots>Functions. A screenshot is shown below:

Stata 16.1 dark mode

Essentially here we need to define a function in the form of y=f(x) , where the f(x) is plotted for the range (-n n). The y-values, or the domain, is calculated from the function. Stata, by default uses 300 points for evaluating a function. This is sufficient, but this can be increased or decreased from the advanced menu in case one needs a very high resolution image or not enough processing power is available to evaluate multiple functions. This 2004 Stata Journal article by the prolific Nick Cox is a good introduction to plotting functions.

Circles

The formula for a circle centered at (0,0) is x^2 - y^2 = r^2 where r is the radius. Thus for Stata, we need to make y the subject. This is simply derived as: y = +/- sqrt(r^2 — x^2), where the positive and negative domains need to be evaluated to since a square root is involved. In Stata, a basic circle can be drawn as follows:

twoway (function   sqrt(1 - (x)^2), lc(black) range(-1 1)) ||  ///
       (function  -sqrt(1 - (x)^2), lc(gs12)  range(-1 1)),    ///
     legend(off)

Here we essentially draw two semi-circles of radius 1. The black line shows the positive domain, while the gray line shows the negative domain. We can modify the code as follows to make it also round:

twoway (function   sqrt(1 - (x)^2), lc(gs8) range(-1 1)) ||  ///
       (function  -sqrt(1 - (x)^2), lc(gs8) range(-1 1)),    ///
          aspect(1) legend(off)

where aspect(x) defines the aspect of y-axis to x-axis. Note this does not change the dimensions of the graph which is controlled by xsize() and ysize(). This will be discussed later in this guide.

We can also draw a circle of any radius. For example, the code below shows a circle with a radius of 4:

twoway (function   sqrt(4 - (x)^2), lc(gs8) range(-2 2)) ||  ///
       (function  -sqrt(4 - (x)^2), lc(gs8) range(-2 2)),    ///
         aspect(1) legend(off) xscale(off) yscale(off)

Since the logic is fairly simple, several circles can be drawn using a combination of loops and locals:

forval x = 1/5 {
local circle `circle' (function sqrt(`x'^2 - x^2), lc(gs12)   lw(thin) lp(solid) range(-`x' `x')) || (function -sqrt(`x'^2 - x^2), lc(gs12) lw(thin) lp(solid) range(-`x' `x')) ||
}
twoway ///
   `circle' ///  
       ,    ///
     aspect(1) legend(off)    ///
     xlabel(-5(1)5) ylabel(-5(1)5)

which gives us circles from radius 1 to 5 in steps of 1.

The above formulation provides a neat way of generate any number of circles. For details on this type of coding, see Guide 2 or subsequent guides for a step-by-step introduction.

Spikes

The next thing we need to draw are spikes that split the circle in even intervals. Here we need to make use of a couple of trigonometry formulas shown in the illustration above.

Let’s say we want to create 20 spikes. We can calculate half of them and extend the range in which they are evaluated. Therefore we only need to deal with a semi-circle, which is represented by 180 degrees or pi radians. If we to draw 10 spikes in a semi-circle then this is simply pi/10. The angle of each spike x is theta = x * pi/10. Once the angle is known, the line extends from +/- radius * cos(theta) on the x-axis and +/- radius * sin(theta) on the y-axis. Again see the the illustration above for the formulas.

For example, if we want to plot the third spike, this can be operationalized as follows:

local theta = 3 * _pi / 10               // 3rd of 10 segments
local liner = abs(5 * cos(`theta'))      // 5 is the radius
twoway ///
   (function (tan(`theta'))*x, n(2) range(-`liner' `liner') lw(vthin) lc(gs10) lp(solid))  /// 
     ,    ///
     aspect(1) legend(off)    ///
     xlabel(-5(1)5) ylabel(-5(1)5)

where we make use of the identity sin theta = tan theta * cos theta for the y-axis values. This allows us to define the line radius local liner only once and we can then use it both to define the length of the lines, and calculate the y values. Note that since this is a straight line, we can also just evaluate it using two points using the n(2) option. From the above script, we get a plot of the 3rd segment of the spike:

The 3 in the above formula can be replaced to generate other segments as well. Or we can just loop over all the segments and generate them in a local spike:

forval x = 1/10 {
 local theta = (`x') * _pi / 10   
 local liner = abs(5 * cos(`theta'))    
 
 local spike `spike' (function (tan(`theta'))*x, n(2) range(-`liner' `liner') lw(vthin) lc(gs10) lp(solid)) ||
 
 }
twoway ///
   `spike'  ///   
     ,    ///
     aspect(1) legend(off)    ///
     xlabel(-5(1)5) ylabel(-5(1)5)

which gives us this star burst figure with 20 spikes:

Note that the code above automates the process of generating the spikes. By modifying the value of the number of segments and the length of the segment, any type of spikes can be generated.

Circles and spikes

Here we combine the circles and the spikes:

local circle  // reset the locals
local spike
forval x = 1/5 {
 local circle `circle' (function sqrt(`x'^2 - x^2), lc(black) lw(thin) lp(solid) range(-`x' `x')) || (function -sqrt(`x'^2 - x^2), lc(black) lw(thin) lp(solid) range(-`x' `x')) ||
}
forval x = 1/10 {
 local theta = (`x') * _pi / 10   
 local liner = abs(5 * cos(`theta'))    
 
 local spike `spike' (function (tan(`theta'))*x, n(2) range(-`liner' `liner') lw(vthin) lc(gs10) lp(solid)) ||
 
 }
twoway ///
   `circle' ///
   `spike'  ///   
     ,    ///
     aspect(1) legend(off)    ///
     xlabel(-5(1)5) ylabel(-5(1)5)

to get the baseline frame of a polar plot:

Part II: Labels and fine tuning

The next challenge is to add labels to the wheels and the spikes. Let’s start by generating a set of observations which equal the number of spikes divided by two:

clear
set obs 10
gen obs = _n

Here we need to generate the labels for the positive and the negative values of the circle. For this, we also need some maths! The angle for each spike is calculated as spike number times pi divided by number of spikes / 2:

** calculate the angle
gen double theta = - obs * _pi / 10
** generate the x values 
gen double px1 =  abs((5 + 0.5) * cos(theta))  
gen double px2 = -abs((5 + 0.5) * cos(theta))
** generate the y values
gen double py1 = tan(theta)*px1
gen double py2 = tan(theta)*px2
** generate the marker labels
gen marker1 = obs
gen marker2 = obs

The x values are calculated for the negative and the positive ranges. The values are also extended by a small number to move them away from the edge of the circle. In the above example, we adjust the markers by 0.5. Similarly, the y values are calculated using the sin theta = tan theta * cos theta formula. Markers are labeled by the number of observations in the last step.

Why do we use double when generating variables? This is to ensure precision is maintained while calculating the values. Check out the Power of Precision guide for more information and why it really matters for your calculations.

We can plot the markers as follows:

twoway ///
 (scatter py1 px1, mc(none) ms(point) mlab(marker1) mlabpos(0)) ///
 (scatter py2 px2, mc(none) ms(point) mlab(marker2) mlabpos(0))

to get this circle of numbers:

Note the pattern here. The values 1–10 repeat themselves twice. Since the polar plot starts from the value of 1 give in red and goes clockwise, we need to change the values after the blue 10. Here we just add 10 to the markers that need to be modified and adjust for a small threshold to make sure all value are covered:

replace marker1 = marker1 + 10 if py1 > -3e-06
replace marker2 = marker2 + 10 if py2 >  3e-06

This might need slight modification if the radius and number of spikes increase. We can plot the values again:

twoway ///
 (scatter py1 px1, mc(none) ms(point) mlab(marker1) mlabpos(0)) ///
 (scatter py2 px2, mc(none) ms(point) mlab(marker2) mlabpos(0))

and we get the correct numbers around the wheel. The option mlabpos(0) centers the label directly on top of the scatter points.

The spike labels are easier. We pick an axis on which to plot the values, keep it constant, and label the other axis by the number of circles which cut the spikes. In the code below, we freeze the y-axis at 0 and generate x-axis values as simply 1 to 5 for the circles we drew earlier:

gen xvar = .
gen yvar = .
forval x = 1/5 {
   replace xvar = `x' in `x' 
   replace yvar = 0 in `x' 
}
twoway ///
 (scatter yvar xvar, mc(none) ms(point) mlab(xvar) mlabpos(3))

which gives us this simple scatter:

Now let’s put everything together, the circles, the spikes, and their corresponding labels:

local circle  // reset the locals
local spike
colorpalette gs6 gs14, n(5) reverse nograph
forval x = 1/5 {
 local width = `x' * 0.1
 local circle `circle' (function sqrt(`x'^2 - x^2), lc("`r(p`x')'%80") lw(`width') lp(solid) range(-`x' `x')) || (function -sqrt(`x'^2 - x^2), lc("`r(p`x')'%80") lw(`width') lp(solid) range(-`x' `x')) ||
}
forval x = 1/10 {
 local theta = (`x') * _pi / 10   
 local liner = abs((5 + 0.2) * cos(`theta'))    
 
 local spike `spike' (function (tan(`theta'))*x, n(2) range(-`liner' `liner') lw(vthin) lc(gs12) lp(solid)) ||
 
 } 
 
 
twoway ///
   `circle' ///
   `spike'  ///
    (scatter py1 px1, mc(none) ms(point) mlab(marker1) mlabpos(0) mlabc(black) mlabsize(vsmall)) ///
    (scatter py2 px2, mc(none) ms(point) mlab(marker2) mlabpos(0) mlabc(black) mlabsize(vsmall)) ///
    (scatter yvar xvar, mc(none) ms(point) mlab(xvar) mlabpos(10) mlabc(black) mlabsize(tiny))  ///
     ,    ///
     aspect(1) legend(off)    ///
     xscale(off) yscale(off)    ///
      xlabel(, nogrid) ylabel(, nogrid)

Three additional elements are added here. The color and the width of circles are now controlled via locals, and the circle labels are positioned at 10'o clock. These options give the graph a clean look with larger circles being more prominent and thicker than the inner circles:

So here we get the underlying blueprint for polar plots.

Part III: Testing with random data

Now let’s just generate random data. Since we are plotting weekly deaths, we just generate 52 random observations for the number of weeks:

clear 
set obs 52
gen obs = _n
gen double y = runiform(2, 4)

Note that any number of observations can be used. If there are 52 segments, then observations higher than 52 will just continue on the wheel (that is also the beauty of trigonometry and circles). Here we limit observations to 52 just for clarity in how the plotted data looks like.

The next part is fairly straightforward, where we generate the angle of each observation on the wheel:

gen double angle = obs * 2 * _pi / 52

Note that the angle is independent of the actual data. It is determined by the full circle or 360 degrees = 2 pi divided by the number of spikes. Once the angle is calculated, the polar coordinates of the actual data we want to plot can be generated as follows:

gen double obsx = y * cos(angle)
gen double obsy = y * sin(angle)

where the data value y plays the role of the radius and the angle determines the position on the circle (see reference figure above for formulas).

We can now apply what we learned above and plot the values as follows:

local circle  // reset the locals
local spike
*** circles
colorpalette gs6 gs14, n(5) reverse nograph
forval x = 1/5 {
 local width = `x' * 0.05
 local circle `circle' (function sqrt(`x'^2 - x^2), lc("`r(p`x')'%80") lw(`width') lp(solid) range(-`x' `x')) || (function -sqrt(`x'^2 - x^2), lc("`r(p`x')'%80") lw(`width') lp(solid) range(-`x' `x')) ||
}
*** spikes
forval x = 1/26 {
 local theta = (`x') * _pi / 26   
 local liner = abs((5 + 0.2) * cos(`theta'))    
 
 local spike `spike' (function (tan(`theta'))*x, n(2) range(-`liner' `liner') lw(*0.2) lc(gs13) lp(solid)) ||
 
 }
*** figure 
twoway ///
   (connected obsy obsx, lc(red)) ///
   `circle' ///
   `spike'  ///   
     ,    ///
     aspect(1) legend(off)    ///
     xscale(off) yscale(off)    ///
      xlabel(, nogrid) ylabel(, nogrid)

which gives us this figure:

Notice also how the locals are modified based on the number of spikes. Additionally since the data is random, the red line will look different every time one runs the script, but it should start and end on the same spikes. Also note that the individual points align perfectly with each spike. This means that the angles are calculated correctly.

We can now go ahead and add the other styling elements including labels:

*** add circle markers
gen xvar = .
gen yvar = .
forval x = 1/5 {
   replace xvar = `x' in `x' 
   replace yvar = 0 in `x' 
}
*** add spike markers
gen spikes = _n in 1/26
gen double theta = - spikes * _pi / 26
gen double px1 =  abs((5 + 0.2) * cos(theta))  
gen double px2 = -abs((5 + 0.2) * cos(theta))
gen double py1 = tan(theta)*px1
gen double py2 = tan(theta)*px2
gen marker1 = obs
gen marker2 = obs
replace marker1 = marker1 + 26 if py1 > -3e-06
replace marker2 = marker2 + 26 if py2 >  3e-06
local circle  // reset the locals
local spike
*** generate the circles
colorpalette gs10 gs14, n(5) reverse nograph
return list
forval x = 1/5 {
 local width = `x' * 0.05
 local circle `circle' (function sqrt(`x'^2 - x^2), lc("`r(p`x')'%80") lw(`width') lp(solid) range(-`x' `x')) || (function -sqrt(`x'^2 - x^2), lc("`r(p`x')'%80") lw(`width') lp(solid) range(-`x' `x')) ||
}
*** generate the spikes
forval x = 1/26 {
 local theta = (`x') * _pi / 26   
 local liner = abs((5 + 0.2) * cos(`theta'))    
 
 local spike `spike' (function (tan(`theta'))*x, n(2) range(-`liner' `liner') lw(*0.2) lc(gs13) lp(solid)) ||
 
 }
*** plot the figure
twoway ///
   (line obsy obsx, lc(red)) ///
   `circle' ///
   `spike'  /// 
    (scatter py1 px1, mc(none) ms(point) mlab(marker1) mlabpos(0) mlabc(black) mlabsize(tiny)) ///
    (scatter py2 px2, mc(none) ms(point) mlab(marker2) mlabpos(0) mlabc(black) mlabsize(tiny)) ///
    (scatter yvar xvar, mc(none) ms(point) mlab(xvar) mlabpos(10) mlabc(gs8) mlabsize(tiny))  ///
     ,    ///
     aspect(1) legend(off)    ///
     xlabel(-5(1)5) ylabel(-5(1)5) ///
      xscale(off) yscale(off) ///
      xsize(1) ysize(1) ///
      xlabel(, nogrid) ylabel(, nogrid)

and this gives us:

Here we add xsize and ysize to get rid of the white space on the sides of the circle. This makes the image a square and therefore makes the polar plot stand out more. You can click on this figure and the earlier figure to see the difference. This example also illustrates how aspect controls the plot dimensions and xsize and ysize control the overall figure dimensions. Both elements can be modified depending on the purpose of the figure.

Part IV: Excess death polar plot

In this part, we will learn how to graph actual data on a polar plot. For this, we will make use of the weekly deaths dataset available from Eurostat in the file demo_r_mweek3. The dataset is very extensive, providing information up to NUTS-3 level (the lowest homogenous administrative sub-division for the EU), for all year and week combinations starting from the year 2000. It also provides breakdown by gender and age groups. This file is also the key dataset used for producing excess death figures for Europe.

Here, I initially had the idea to also introduce the script that processes the raw data based on the Eurostat guide I posted earlier. But the dataset is simply too large and needs a detailed guide on its own. For example, for year 2015 and onwards, there are more than 26 million data points, and the Stata file, which is very efficient in storing information, is almost one giga byte (1 GB) in size. This can be challenging if the processing power is not there, and also distracts from the radial plots we want to build in this guide.

Instead, I have posted a reduced form of the dataset on the Stata Guide GitHub, which was processed at the time of writing this guide. The file has information at the country (NUTS 0) level. It can be stored in your working directory using the following command:

use "https://github.com/asjadnaqvi/The-Stata-Guide/blob/master/data/demo_r_mweek3_medium.dta?raw=true", clear

Note, it is always good practice to set directories and paths at the beginning of the dofiles. See Guide 1 here on an introduction to workflow management.

For the tutorial, I will use Austria as the case example. Other countries can be generated using the procedures defined below or the whole script can be looped over all the countries as well.

Basic cleaning is done as follows:

keep if geo=="AT"
drop unit
split year, p(W) destring   // extract year and weeks
drop year
ren year1 year
ren year2 week
drop if year>2020  // in case the data is updated to include 2021
gen date = yw(year,week) // not needed but given as date example
format date %tw
order geo date age
sort geo date age
*** keep the main age brackets
keep if  age == "Y_LT5"  | ///
   age == "Y5-9"  | /// 
   age == "Y10-14" | /// 
   age == "Y15-19" | /// 
   age == "Y20-24" | /// 
   age == "Y25-29" | /// 
   age == "Y30-34" | /// 
   age == "Y35-39" | /// 
   age == "Y40-44" | /// 
   age == "Y45-49" | /// 
   age == "Y50-54" | ///  
   age == "Y55-59" | ///  
   age == "Y60-64" | /// 
   age == "Y65-69" | /// 
   age == "Y70-74" | /// 
   age == "Y75-79" | /// 
   age == "Y80-84" | ///
   age == "Y85-89" | ///
   age == "Y_GE90" | /// 
   age == "TOTAL"
*** keep information for total population
keep if sex=="T"  
drop sex
*** replace hyphens with underscores
replace age = subinstr(age, "-", "_", .) 

We basically reduce the information in the file and clean up the variables. Next step, we reshape the data and clean it some more so we have the information in the shape we need:

ren y tot_
reshape wide tot_, i(geo date year week) j(age) string
sort geo date
ren tot_* *
recode Y* (.=0)
drop if date==.
encode geo, gen(geo2)
order geo2 date
drop TOTAL
*** clean up names
ren Y5_9   Y05_09
ren Y_LT5  Y00_04 
ren Y_GE90 Y90_100
order Y*, alpha last    // notice the order options here
*** aggregate the data
egen Y00_64 = rowtotal(Y00_04 - Y60_64)
egen Y65_99 = rowtotal(Y65_69 - Y90_100)
drop Y00_04 - Y90_100
order geo geo2 date year week

In the end, the data should look like this:

Next start with generating the angles and the polar coordinates of the data:

// fix the angles of data points
sort year week
levelsof year, local(lvls)
foreach x of local lvls {
 
 gen obs_`x' = _n if year==`x'
 
 local year1 = `x' - 1
 cap replace obs_`x' = _n if year==`year1' & week==52
gen double angle_`x' = obs_`x' * -2 * _pi / 52
 
 gen double x65_`x' = Y65_99 * cos(angle_`x')
 gen double y65_`x' = Y65_99 * sin(angle_`x')
 }
drop obs* angle*

What this piece of code is doing is taking the value of age 65 plus from the Y65_99 variable and converting them in polar coordinates. Each year also has its own data column where the last entry of a year is padded on to the next year as the starting value. These steps, are done to generate colored lines for each year, and the padding ensures that the new year starts from the last point of the previous year and ensures continuity. The screen shot of the data is as follows:

Next we automate the spike markers using the min/max of the key variable Y65_99:

****** spike markers here
gen double obs = _n in 1/26
gen double theta = -obs * _pi / 26
summ Y65_99
   local cmin = max(0, round(`r(min)', 20) - 50)
   local cmax = round(`r(max)', 20) + 50
display `cmin'
display `cmax'
local diff = round((`cmax' -  `cmin') / 6) // divisions
gen px1 =  abs((`cmax' * 1.05) * cos(theta))   
gen px2 = -abs((`cmax' * 1.05) * cos(theta))
gen py1 = tan(theta)*px1
gen py2 = tan(theta)*px2
gen marker1 = obs
gen marker2 = obs
replace marker1 = marker1 + 26 if py1 > -1e-02
replace marker2 = marker2 + 26 if py2 >  1e-02

A buffer of 50 is added to the minimum and maximum values and the number of divisions of circles is set to 6. These are control variables that can be modified as well. Markers are pushed out by a factor of 5% (`cmax' * 1.05), which also ensures that labels are scaled according to the maximum circle value. Label markers are corrected in the last step.

In the next step, circle markers are generated as follows:

****** spike markers here
summ Y65_99
   local cmin = max(0, round(`r(min)', 20) - 50)
   local cmax = round(`r(max)', 20) + 50
local diff = round((`cmax' -  `cmin') / 6)
cap gen xvar = .
cap gen yvar = .
local i = 1
forval x = `cmin'(`diff')`cmax' {
   
   replace xvar = `x' in `i' 
   replace yvar = 0 in `i'
   local i = `i' + 1
}

Here I am using the cmin, cmax, diff locals again for the sake of completion. If the script is executing in one go, these locals just need to be defined once.

Once the labels are sorted out, the next piece of code generates the final graph we need:

local circle  // reset the local
summ Y65_99
   local cmin = max(0, round(`r(min)', 20) - 100)
   local cmax = round(`r(max)', 20) + 100
local diff = round((`cmax' -  `cmin') / 6)
*** circles
colorpalette gs12 gs14, n(12) reverse nograph
local i = 1
forval x = `cmin'(`diff')`cmax' {
   local width = `i' * 0.05
 
   local circle `circle' (function sqrt(`x'^2 - x^2),    lc("`r(p`i')'%70") lw(`width') lp(solid) range(-`x' `x')) || (function -sqrt(`x'^2 - x^2), lc("`r(p`i')'%70") lw(`width') lp(solid) range(-`x' `x')) ||
 
   local i = `i' + 1
}
*** spikes
local spike
forval x = 1/26 {
 local theta = (`x') * _pi / 26    
 local liner = abs((`cmax' + 40) * cos(`theta'))    
  local spike `spike' (function (tan(`theta'))*x, n(2) range(-`liner' `liner') lw(vvthin) lc(gs8) lp(solid)) ||
}
***** final graph here
colorpalette inferno, n(15) reverse nograph
twoway ///
   `circle' ///
   `spike'  ///
    (scatter py1 px1, mc(none) ms(point) mlab(marker1) mlabpos(0) mlabc(gs6) mlabsize(*0.5)) ///
    (scatter py2 px2, mc(none) ms(point) mlab(marker2) mlabpos(0) mlabc(gs6) mlabsize(*0.5)) ///
    (scatter yvar xvar, mc(none) ms(point) mlab(xvar) mlabpos(9) mlabc(gs6) mlabangle(vertical) mlabsize(*0.4))  ///
     (line y65_2015 x65_2015, lc("`r(p3)'")  lp(solid) lw(vthin)) ///
     (line y65_2016 x65_2016, lc("`r(p4)'")  lp(solid) lw(vthin)) ///
     (line y65_2017 x65_2017, lc("`r(p5)'")  lp(solid) lw(vthin)) ///
     (line y65_2018 x65_2018, lc("`r(p6)'")  lp(solid) lw(vthin)) ///
     (line y65_2019 x65_2019, lc("`r(p7)'")  lp(solid) lw(vthin)) ///
     (line y65_2020 x65_2020, lc("`r(p15)'") lp(solid) lw(thin)) ///
     ,    ///
     aspect(1) legend(off)   ///
      xscale(off) yscale(off) ///
      xsize(1) ysize(1) ///
      xlabel(, nogrid) ylabel(, nogrid) ///
      title("{fontface Arial Bold: Excess deaths in Austria}") ///
      subtitle("65 years and older", size(small)) ///
      note("Source: Eurostat table demo_r_mweek3. Week spokes are labeled on the outer edge in a clockwise direction. The ring values, given as vertical numbers," "show the number of deaths. 2020 values plotted in bold red color, and 2015-2019 values plotted in light grey-red shades.", size(tiny))

The code above does a lot of fine tuning to minor elements to gives us this clean-looking polar plot:

Since the data is only available till week 50, at the time of writing this guide, we can see that there have been excess deaths compared to the previous five years after week 43 for age groups 65 and over. In week 49 there were almost a 1000 extra deaths. One previous year also shows extra deaths mostly due to the combination of flu and cold (see official Statistik Austria explanations here). Excess deaths can also be observed in other weeks especially around the first wave in weeks 14–16 but these could also be wrongly associated with COVID-19 (Type I error). There is also a possibility that the measures put in place to prevent COVID-19 also reduce other types of mortalities such that one sees a zero net impact of the virus. This of course, is a deeper look at the data and subject to debate as well.

Exercise

Try and replicating the graph for ages below 65 years, which also shows excess deaths in the last weeks of 2020:

Also try and generate polar plots for other countries. For example, see Spain and France below which show very different patterns of weekly deaths.

Note that the scale of excess deaths in these countries is in 7,000–10,000 range in some weeks, so the cumulative impact is very high.

Hope you found this guide useful! Please share your visualizations, errors, bugs, suggestions, comments etc. For more polar plot guides, check out the Polar section on the Stata guide.

About the author

I am an economist by profession and I have been using Stata since 2003. I am currently based in Vienna, Austria. You can see my profile, research, and projects on GitHub or my website. You can connect with me via Medium, Twitter, LinkedIn, or simply via email: [email protected]. If you have questions regarding the Guide or Stata in general post them on The Code Block Discord server.

The Stata Guide releases awesome new content regularly. Subscribe, Clap, and/or Follow the guide if you like the content!

Stata
Radial Plot
Excess Death
Eurostat
Polar
Recommended from ReadMedium