<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://demirev.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://demirev.github.io/" rel="alternate" type="text/html" /><updated>2026-11-03T15:45:00+00:00</updated><id>https://demirev.github.io/feed.xml</id><title type="html">Georgi Demirev</title><subtitle>does things with data sometimes</subtitle><author><name>Georgi Demirev</name><email>georgi_demirev@proton.me</email></author><entry><title type="html">Heat Waves</title><link href="https://demirev.github.io/posts/heat-waves/" rel="alternate" type="text/html" title="Heat Waves" /><published>2025-07-30T00:00:00+00:00</published><updated>2025-07-30T00:00:00+00:00</updated><id>https://demirev.github.io/posts/heat-deaths</id><content type="html" xml:base="https://demirev.github.io/posts/heat-waves/"><![CDATA[<p>The recent days have seen quite a heat wave hit Europe, and especially <a href="https://dariknews.bg/regioni/montana/43-na-sianka-izmeriha-naj-visokata-temperatura-u-nas-ot-nachaloto-na-godinata-2426345">Bulgaria</a>. At the same time, I cam upon <a href="https://www.ft.com/content/50f69324-8dc8-4ef1-b471-d78e260adae0">this article</a> in the Financial Times, arguing that lack of air conditioning in Europe vis-a-vis the United States up to twice higher death rates on days with temperatures exceeding 28 degrees Celsium.</p>

<p><img src="/images/posts/heat-ft.png" alt="FT Heat Deaths Chart" /></p>

<p>I have never seen an excess heat deaths graph for Bulgaria, so naturally, I set up to make one. The time budget I gave myself was <strong>one afternoon</strong>, so let’s see how far we can get.</p>

<p>The academic reference for the chart above is <a href="https://www.nature.com/articles/s41467-024-45901-z">Chen et al. 2024</a>, and while I won’t follow their approach 1:1 it is mostly the same idea. We are going to try to fit a (non-linear) mortality model with a spline temperature predictor along with some demographic controls and see what we get as a result.</p>

<h2 id="input-data">Input Data</h2>

<p>We need three sources of data: deaths, population (so we can estimate death rates), and temperature. The lower the granularity, the better.</p>

<p>Starting with deaths, NSI publishes <a href="https://infostat.nsi.bg/infostat/pages/reports/query.jsf?x_2=1818">weekly totals</a>, broken down by region, gender, and age group. Population estimates are also available at a similar granularity, but only on an annual basis. Still, for the current purposes it will do.</p>

<p><a href="https://surfobs.climate.copernicus.eu/dataaccess/access_eobs.php#datafiles">The European Climate Assessment project</a> provides daily temperature data on a 10km grid across Europe. Specifically, I am going to use the <code class="language-plaintext highlighter-rouge">tx_ens_mean_0.1deg_reg_v31.0e.nc</code> dataset, which contains data on maximum daily temperatures.</p>

<h2 id="data-cleaning">Data Cleaning</h2>

<p>The temperature data was in the <code class="language-plaintext highlighter-rouge">.ns</code> format, which I haven’t encountered before. Since the file is quite large, we can filter based on geographical coordinates and dates and read only the part that we need. After some formatting efforts, we get it into a tidy format:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">read_temperature</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="w">
  </span><span class="n">file_path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"data/tg_ens_mean_0.1deg_reg_v31.0e.nc"</span><span class="p">,</span><span class="w">
  </span><span class="n">from_date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">as.Date</span><span class="p">(</span><span class="s2">"2015-01-01"</span><span class="p">),</span><span class="w">
  </span><span class="n">measure</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"tx"</span><span class="w">
</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">temperature_file</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">nc_open</span><span class="p">(</span><span class="n">file_path</span><span class="p">)</span><span class="w">
  
  </span><span class="n">print</span><span class="p">(</span><span class="n">temperature_file</span><span class="p">)</span><span class="w">
  
  </span><span class="n">longitude</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ncvar_get</span><span class="p">(</span><span class="n">temperature_file</span><span class="p">,</span><span class="w"> </span><span class="s2">"longitude"</span><span class="p">)</span><span class="w">
  </span><span class="n">latitude</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ncvar_get</span><span class="p">(</span><span class="n">temperature_file</span><span class="p">,</span><span class="w"> </span><span class="s2">"latitude"</span><span class="p">)</span><span class="w">
  </span><span class="n">time_vals</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ncvar_get</span><span class="p">(</span><span class="n">temperature_file</span><span class="p">,</span><span class="w"> </span><span class="s2">"time"</span><span class="p">)</span><span class="w">
  </span><span class="n">time_dates</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.Date</span><span class="p">(</span><span class="n">time_vals</span><span class="p">,</span><span class="w"> </span><span class="n">origin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"1950-01-01"</span><span class="p">)</span><span class="w"> </span><span class="c1"># "days since 1950-01-01 00:00"</span><span class="w">
  
  </span><span class="n">bulgaria_coords</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
    </span><span class="n">lat_min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">41</span><span class="p">,</span><span class="w"> </span><span class="n">lat_max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">44.5</span><span class="p">,</span><span class="w"> </span><span class="n">lon_min</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">22</span><span class="p">,</span><span class="w"> </span><span class="n">lon_max</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">29</span><span class="w">
  </span><span class="p">)</span><span class="w">
  
  </span><span class="c1"># Find indices for Bulgaria's spatial extent</span><span class="w">
  </span><span class="n">lon_indices</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">which</span><span class="p">(</span><span class="w">
    </span><span class="p">(</span><span class="n">longitude</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">bulgaria_coords</span><span class="o">$</span><span class="n">lon_min</span><span class="p">)</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="p">(</span><span class="n">longitude</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">bulgaria_coords</span><span class="o">$</span><span class="n">lon_max</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w">
  </span><span class="n">lat_indices</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">which</span><span class="p">(</span><span class="w">
    </span><span class="n">latitude</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">bulgaria_coords</span><span class="o">$</span><span class="n">lat_min</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">latitude</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">bulgaria_coords</span><span class="o">$</span><span class="n">lat_max</span><span class="w">
  </span><span class="p">)</span><span class="w">
  </span><span class="c1"># Find temporal indices for 2015 onwards</span><span class="w">
  </span><span class="n">time_indices</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">which</span><span class="p">(</span><span class="n">time_dates</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">from_date</span><span class="p">)</span><span class="w">
  
  </span><span class="c1"># Extract data for Bulgaria from 2015 onwards</span><span class="w">
  </span><span class="n">bulgaria_temp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ncvar_get</span><span class="p">(</span><span class="w">
    </span><span class="n">temperature_file</span><span class="p">,</span><span class="w"> </span><span class="n">measure</span><span class="p">,</span><span class="w">
    </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="nf">min</span><span class="p">(</span><span class="n">lon_indices</span><span class="p">),</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">lat_indices</span><span class="p">),</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">time_indices</span><span class="p">)),</span><span class="w">
    </span><span class="n">count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="n">lon_indices</span><span class="p">),</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">lat_indices</span><span class="p">),</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">time_indices</span><span class="p">))</span><span class="w">
  </span><span class="p">)</span><span class="w">
  
  </span><span class="n">bulgaria_lon</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">longitude</span><span class="p">[</span><span class="n">lon_indices</span><span class="p">]</span><span class="w">
  </span><span class="n">bulgaria_lat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">latitude</span><span class="p">[</span><span class="n">lat_indices</span><span class="p">]</span><span class="w">
  </span><span class="n">bulgaria_dates</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">time_dates</span><span class="p">[</span><span class="n">time_indices</span><span class="p">]</span><span class="w">
  
  </span><span class="n">temperature</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">crossing</span><span class="p">(</span><span class="w">
    </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">seq_along</span><span class="p">(</span><span class="n">bulgaria_lon</span><span class="p">),</span><span class="w">
    </span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">seq_along</span><span class="p">(</span><span class="n">bulgaria_lat</span><span class="p">),</span><span class="w">
    </span><span class="n">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">seq_along</span><span class="p">(</span><span class="n">bulgaria_dates</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">transmute</span><span class="p">(</span><span class="w">
      </span><span class="n">lon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bulgaria_lon</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w">
      </span><span class="n">lat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bulgaria_lat</span><span class="p">[</span><span class="n">j</span><span class="p">],</span><span class="w">
      </span><span class="n">date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bulgaria_dates</span><span class="p">[</span><span class="n">k</span><span class="p">],</span><span class="w">
      </span><span class="n">t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">bulgaria_temp</span><span class="p">[</span><span class="n">cbind</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">,</span><span class="w"> </span><span class="n">k</span><span class="p">)]</span><span class="w">
    </span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">temperature</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">read_temperature</span><span class="p">(</span><span class="s2">"data/tx_ens_mean_0.1deg_reg_v31.0e.nc"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Since we have weekly deaths data, we will also have to aggregate the temperature to a weekly average (of daily max values). Also, since the temperature data is on a grid, we will have to decide exactly which values to use for every region in the NSI data.</p>

<p>One approach would be to average all observations within a given region. That wouldn’t be ideal though, as the population is not uniformly spread over the territory of the given region. For example, for Blagoevgrad we would be taking some measurements in the high mountains, where basically nobody lives.</p>

<p>Instead, I decided to just take the grid square closest to the biggest population center in each region and use that as the measurement for the entire region. Not ideal, but it should be widely representative of the temperatures the majority of the region’s populations experiences.</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">regional_cities</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bind_rows</span><span class="p">(</span><span class="n">read_json</span><span class="p">(</span><span class="s2">"data/bulgaria_regional_cities.json"</span><span class="p">))</span><span class="w">

</span><span class="n">grid_points</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">temperature</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">distinct</span><span class="p">(</span><span class="n">lat</span><span class="p">,</span><span class="w"> </span><span class="n">lon</span><span class="p">)</span><span class="w">

</span><span class="n">closest_grid_point</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">map</span><span class="p">(</span><span class="n">regional_cities</span><span class="o">$</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">city</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">city_lat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regional_cities</span><span class="o">$</span><span class="n">lat</span><span class="p">[</span><span class="n">regional_cities</span><span class="o">$</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">city</span><span class="p">]</span><span class="w">
  </span><span class="n">city_lon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">regional_cities</span><span class="o">$</span><span class="n">lon</span><span class="p">[</span><span class="n">regional_cities</span><span class="o">$</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">city</span><span class="p">]</span><span class="w">
  </span><span class="n">grid_points</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="w">
      </span><span class="n">dist</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">haversine_dist</span><span class="p">(</span><span class="w">
        </span><span class="n">lat</span><span class="p">,</span><span class="w"> </span><span class="n">lon</span><span class="p">,</span><span class="w">
        </span><span class="n">city_lat</span><span class="p">,</span><span class="w"> </span><span class="n">city_lon</span><span class="w">
      </span><span class="p">)</span><span class="w">
    </span><span class="p">)</span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">arrange</span><span class="p">(</span><span class="n">dist</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">slice</span><span class="p">(</span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">select</span><span class="p">(</span><span class="n">lat</span><span class="p">,</span><span class="w"> </span><span class="n">lon</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="n">region</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">city</span><span class="p">)</span><span class="w">
</span><span class="p">})</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
    </span><span class="n">bind_rows</span><span class="p">()</span><span class="w">
</span></code></pre></div></div>

<p>With that out the way, we can go ahead with joining all data together. Only thing left to do is accounting for the slightly different age groupings in the population and mortality data from NSI:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">population</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">read_csv</span><span class="p">(</span><span class="s2">"data/population.csv"</span><span class="p">)</span><span class="w">
</span><span class="n">deaths</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">read_csv</span><span class="p">(</span><span class="s2">"data/mortality.csv"</span><span class="p">)</span><span class="w">

</span><span class="n">mortality</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">deaths</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">pivot_longer</span><span class="p">(</span><span class="w">
    </span><span class="n">cols</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">starts_with</span><span class="p">(</span><span class="s2">"y"</span><span class="p">),</span><span class="w">
    </span><span class="n">names_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"date_"</span><span class="p">,</span><span class="w">
    </span><span class="n">values_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"deaths"</span><span class="w">
  </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">mutate</span><span class="p">(</span><span class="w">
    </span><span class="n">year</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">str_extract</span><span class="p">(</span><span class="n">date_</span><span class="p">,</span><span class="w"> </span><span class="s2">"(?&lt;=y)\\d{4}"</span><span class="p">)),</span><span class="w">
    </span><span class="n">week</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">str_extract</span><span class="p">(</span><span class="n">date_</span><span class="p">,</span><span class="w"> </span><span class="s2">"(?&lt;=_w)(\\d+)"</span><span class="p">)),</span><span class="w">
    </span><span class="n">gender</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">str_detect</span><span class="p">(</span><span class="n">date_</span><span class="p">,</span><span class="w"> </span><span class="s2">"female"</span><span class="p">),</span><span class="w"> </span><span class="s2">"female"</span><span class="p">,</span><span class="w"> </span><span class="s2">"male"</span><span class="p">),</span><span class="w">
    </span><span class="n">date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">make_date</span><span class="p">(</span><span class="n">year</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">weeks</span><span class="p">(</span><span class="n">week</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">arrange</span><span class="p">(</span><span class="w">
    </span><span class="n">region</span><span class="p">,</span><span class="w"> </span><span class="n">gender</span><span class="p">,</span><span class="w"> </span><span class="n">age_group</span><span class="p">,</span><span class="w"> </span><span class="n">date</span><span class="w">
  </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">inner_join</span><span class="p">(</span><span class="w">
    </span><span class="n">population</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">mutate</span><span class="p">(</span><span class="w">
        </span><span class="n">age_group</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">age_group</span><span class="w"> </span><span class="o">%in%</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"0"</span><span class="p">,</span><span class="w"> </span><span class="s2">"1-4"</span><span class="p">),</span><span class="w"> </span><span class="s2">"0-4"</span><span class="p">,</span><span class="w"> </span><span class="n">age_group</span><span class="p">),</span><span class="w">
        </span><span class="n">age_group</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ifelse</span><span class="p">(</span><span class="n">age_group</span><span class="w"> </span><span class="o">%in%</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"90-94"</span><span class="p">,</span><span class="w"> </span><span class="s2">"95-99"</span><span class="p">,</span><span class="w"> </span><span class="s2">"100+"</span><span class="p">),</span><span class="w"> </span><span class="s2">"90 +"</span><span class="p">,</span><span class="w"> </span><span class="n">age_group</span><span class="p">)</span><span class="w">
      </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">group_by</span><span class="p">(</span><span class="n">year</span><span class="p">,</span><span class="w"> </span><span class="n">region</span><span class="p">,</span><span class="w"> </span><span class="n">age_group</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">summarise</span><span class="p">(</span><span class="n">male</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">male</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">T</span><span class="p">),</span><span class="w"> </span><span class="n">female</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">female</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">T</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">pivot_longer</span><span class="p">(</span><span class="w">
        </span><span class="n">cols</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"male"</span><span class="p">,</span><span class="w"> </span><span class="s2">"female"</span><span class="p">),</span><span class="w">
        </span><span class="n">names_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"gender"</span><span class="p">,</span><span class="w">
        </span><span class="n">values_to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"population"</span><span class="w">
      </span><span class="p">),</span><span class="w">
    </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"year"</span><span class="p">,</span><span class="w"> </span><span class="s2">"region"</span><span class="p">,</span><span class="w"> </span><span class="s2">"age_group"</span><span class="p">,</span><span class="w"> </span><span class="s2">"gender"</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
  </span><span class="n">select</span><span class="p">(</span><span class="w">
    </span><span class="n">region</span><span class="p">,</span><span class="w"> </span><span class="n">date</span><span class="p">,</span><span class="w"> </span><span class="n">age_group</span><span class="p">,</span><span class="w"> </span><span class="n">gender</span><span class="p">,</span><span class="w"> </span><span class="n">population</span><span class="p">,</span><span class="w"> </span><span class="n">deaths</span><span class="w">
  </span><span class="p">)</span><span class="w"> 


</span><span class="c1"># join mortality and temperature figures ----------------------------------</span><span class="w">
</span><span class="n">mortality</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mortality</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">left_join</span><span class="p">(</span><span class="w">
    </span><span class="n">closest_grid_point</span><span class="p">,</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"region"</span><span class="w">
  </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
  </span><span class="n">left_join</span><span class="p">(</span><span class="w">
    </span><span class="n">temperature</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">mutate</span><span class="p">(</span><span class="w">
        </span><span class="n">year</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">year</span><span class="p">(</span><span class="n">date</span><span class="p">),</span><span class="w">
        </span><span class="n">week</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">week</span><span class="p">(</span><span class="n">date</span><span class="p">)</span><span class="w">
      </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">group_by</span><span class="p">(</span><span class="n">lon</span><span class="p">,</span><span class="w"> </span><span class="n">lat</span><span class="p">,</span><span class="w"> </span><span class="n">year</span><span class="p">,</span><span class="w"> </span><span class="n">week</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">summarise</span><span class="p">(</span><span class="w">
        </span><span class="n">t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="n">na.rm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">T</span><span class="p">)</span><span class="w">
      </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">mutate</span><span class="p">(</span><span class="w">
        </span><span class="n">date</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">make_date</span><span class="p">(</span><span class="n">year</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">weeks</span><span class="p">(</span><span class="n">week</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
      </span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">mutate</span><span class="p">(</span><span class="w">
    </span><span class="n">death_rate_1w</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">deaths</span><span class="o">/</span><span class="n">population</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">1000</span><span class="w"> </span><span class="c1"># per 1000 people</span><span class="w">
  </span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Finally, since heat deaths are likely to disproportionally affect older people, we can focus only on the subset of the population in retirement age:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">old_ages</span><span class="w">  </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="w">
  </span><span class="s2">"90 +"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"85-89"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"80-84"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"75-79"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"70-74"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"65-69"</span><span class="w">
</span><span class="p">)</span><span class="w">

</span></code></pre></div></div>

<h2 id="model">Model</h2>

<p>For the modelling part we will use a Poisson generalized additive model. Namely:</p>

<p>\begin{equation}
Y_i = Poisson(\mu_i, \phi)
\end{equation}</p>

<p>where $Y_i$ is deaths for a given week-gender-age-region group. $\mu_i$ in turn is:</p>

<p>\begin{equation}
log{\mu_i}=log{N_i} + f(t_i) + controls
\end{equation}</p>

<p>$<code class="language-plaintext highlighter-rouge">N_i</code>$ is the population, and $f()$ is a spline function. The death rate is thus:</p>

<p>\begin{equation}
\lambda_i = \frac{\mu_i}{N_i} = exp\left[f(t_i)+controls\right]
\end{equation}</p>

<p>Therefore the exponent of any coefficient (or a given value of the spline function $f()$) from the model below should be interpreted as a <strong>relative multiplicative</strong> change in death rates, holding all controls fixed.</p>

<h2 id="estimation">Estimation</h2>

<p>Fitting the model with the <code class="language-plaintext highlighter-rouge">bam</code> package, we have:</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model_df</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mortality</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
  </span><span class="n">rename</span><span class="p">(</span><span class="n">temp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">mutate</span><span class="p">(</span><span class="w">
    </span><span class="n">date_num</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">date</span><span class="p">),</span><span class="w"> </span><span class="c1"># for long term trend</span><span class="w">
    </span><span class="n">doy</span><span class="w">      </span><span class="o">=</span><span class="w"> </span><span class="n">yday</span><span class="p">(</span><span class="n">date</span><span class="p">),</span><span class="w"> </span><span class="c1"># seasonal trend</span><span class="w">
    </span><span class="n">age_group</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="n">age_group</span><span class="p">),</span><span class="w">
    </span><span class="n">gender</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="n">gender</span><span class="p">),</span><span class="w">
    </span><span class="n">region</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="n">region</span><span class="p">),</span><span class="w">
    </span><span class="n">year</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="n">year</span><span class="p">(</span><span class="n">date</span><span class="p">)),</span><span class="w">
    </span><span class="n">month</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">as.factor</span><span class="p">(</span><span class="n">month</span><span class="p">(</span><span class="n">date</span><span class="p">))</span><span class="w">
  </span><span class="p">)</span><span class="w">

</span><span class="n">set.seed</span><span class="p">(</span><span class="m">20250730</span><span class="p">)</span><span class="w">

</span><span class="n">g_simple</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bam</span><span class="p">(</span><span class="w">
  </span><span class="n">deaths</span><span class="w"> </span><span class="o">~</span><span class="w">
    </span><span class="n">s</span><span class="p">(</span><span class="n">temp</span><span class="p">,</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">9</span><span class="p">,</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"cs"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># non‑linear temperature-response</span><span class="w">
    </span><span class="n">year</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># yearly average</span><span class="w">
    </span><span class="n">month</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># seasonal pattern</span><span class="w">
    </span><span class="n">age_group</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># control for age</span><span class="w">
    </span><span class="n">gender</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># control for gender</span><span class="w">
    </span><span class="n">region</span><span class="p">,</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># regional dummies</span><span class="w">
    </span><span class="n">offset</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">population</span><span class="p">)),</span><span class="w"> </span><span class="c1"># we are modelling raw counts</span><span class="w">
  </span><span class="n">family</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">quasipoisson</span><span class="p">(</span><span class="n">link</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"log"</span><span class="p">),</span><span class="w">
  </span><span class="n">data</span><span class="w">   </span><span class="o">=</span><span class="w"> </span><span class="n">model_df</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">filter</span><span class="p">(</span><span class="n">age_group</span><span class="w"> </span><span class="o">%in%</span><span class="w"> </span><span class="n">old_ages</span><span class="p">),</span><span class="w">
  </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"fREML"</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="n">g_death</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bam</span><span class="p">(</span><span class="w">
  </span><span class="n">deaths</span><span class="w"> </span><span class="o">~</span><span class="w">
    </span><span class="n">s</span><span class="p">(</span><span class="n">temp</span><span class="p">,</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">9</span><span class="p">,</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"cs"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># non‑linear temperature-response</span><span class="w">
    </span><span class="n">s</span><span class="p">(</span><span class="n">date_num</span><span class="p">,</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">15</span><span class="p">,</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"cs"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># smooth long‑term trend (multi‑year)</span><span class="w">
    </span><span class="n">s</span><span class="p">(</span><span class="n">doy</span><span class="p">,</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"cc"</span><span class="p">,</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">12</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># cyclic seasonal pattern inside each year</span><span class="w">
    </span><span class="n">age_group</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># control for age</span><span class="w">
    </span><span class="n">gender</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># control for gender</span><span class="w">
    </span><span class="n">s</span><span class="p">(</span><span class="n">region</span><span class="p">,</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"re"</span><span class="p">),</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="c1"># regional random effects for shrinking</span><span class="w">
    </span><span class="n">offset</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">population</span><span class="p">)),</span><span class="w"> </span><span class="c1"># we are modelling raw counts</span><span class="w">
  </span><span class="n">family</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">quasipoisson</span><span class="p">(</span><span class="n">link</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"log"</span><span class="p">),</span><span class="w">
  </span><span class="n">data</span><span class="w">   </span><span class="o">=</span><span class="w"> </span><span class="n">model_df</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">filter</span><span class="p">(</span><span class="n">age_group</span><span class="w"> </span><span class="o">%in%</span><span class="w"> </span><span class="n">old_ages</span><span class="p">),</span><span class="w">
  </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"fREML"</span><span class="w">
</span><span class="p">)</span><span class="w">

</span></code></pre></div></div>

<p>We control for overall long-term trends, seasonal pattern, age, gender, and region. I have included two versions of the model. The simpler one just includes fixed effects dummies for all controls, while the more “complicated” one incorporates more spline funcitons and random effects. The results are not meaningfully different.</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">summary</span><span class="p">(</span><span class="n">g_death</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Family: quasipoisson 
Link function: log 

Formula:
deaths ~ s(temp, k = 9, bs = "cs") + s(date_num, k = 15, bs = "cs") + 
    s(doy, bs = "cc", k = 12) + age_group + gender + s(region, 
    bs = "re")

Parametric coefficients:
                Estimate Std. Error t value Pr(&gt;|t|)    
(Intercept)     1.242184   0.097891  12.690  &lt; 2e-16 ***
age_group70-74  0.233220   0.004274  54.568  &lt; 2e-16 ***
age_group75-79  0.352411   0.004198  83.942  &lt; 2e-16 ***
age_group80-84  0.500775   0.004114 121.731  &lt; 2e-16 ***
age_group85-89  0.405932   0.004271  95.038  &lt; 2e-16 ***
age_group90 +  -0.039554   0.004987  -7.931 2.19e-15 ***
gendermale     -0.058248   0.002407 -24.197  &lt; 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
               edf Ref.df       F p-value    
s(temp)      7.140      8  4839.8  &lt;2e-16 ***
s(date_num) 13.908     14  1036.5  &lt;2e-16 ***
s(doy)       9.631     10   295.8  &lt;2e-16 ***
s(region)   26.994     27 11351.3  &lt;2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

R-sq.(adj) =  0.687   Deviance explained = 63.6%
fREML = 4.5825e+05  Scale est. = 10.858    n = 175392
</code></pre></div></div>

<h2 id="heat-and-mortality">Heat and Mortality</h2>

<p>We can finally answer the original question and examine the relationship between heat and mortality:</p>

<p><img src="/images/posts/heat-mortality.png" alt="Relative Hazard and Max Temperature" /></p>

<p>We can see that according to the model both high and low temperatures are associated with elevated mortality, while the 20-30 degree range has the lowest relative death rates (remember, we are taking into account seasonal effects separately).</p>

<p>The way to read this is that a week with an average daily high of 40 degrees is associated with about 23% more deaths than a week with an average weekly high of 30 degrees ($\frac{exp(t(40))}{exp(t(30))}=\frac{exp(0.14)}{exp(-0.07)}$). Since the average weekly death rate for this age group in the data is $1.157$ and there are just over 1.5 million pensioners in Bulgaria, this all adds up to about $400$ extra weekly deaths when we have averages of above 40-degrees.</p>

<p>While the relationship is clearly there and is significant, the magnitude seems somewhat weaker than the ones from Chen et al. But since it’s 17:20, it means my time budget is spent, so we’ll leave it at that for now.</p>]]></content><author><name>Georgi Demirev</name><email>georgi_demirev@proton.me</email></author><category term="models" /><summary type="html"><![CDATA[The recent days have seen quite a heat wave hit Europe, and especially Bulgaria. At the same time, I cam upon this article in the Financial Times, arguing that lack of air conditioning in Europe vis-a-vis the United States up to twice higher death rates on days with temperatures exceeding 28 degrees Celsium.]]></summary></entry><entry><title type="html">Социалните мрежи и политическата поляризация</title><link href="https://demirev.github.io/posts/polarization/" rel="alternate" type="text/html" title="Социалните мрежи и политическата поляризация" /><published>2021-01-12T00:00:00+00:00</published><updated>2021-01-12T00:00:00+00:00</updated><id>https://demirev.github.io/posts/social-media-and-polarization</id><content type="html" xml:base="https://demirev.github.io/posts/polarization/"><![CDATA[<p>Гледайки събитията от САЩ, си мисля доста за влиянието на социалните мрежи върху гражданското общество и политическия диалог. Като цяло заключенията ми са доста мрачни.</p>

<p>TLDR: Начинът, по който говорим за политика във фейсбук и туитър ни тласка към все повече поляризация и конфликти.
Причините са няколко и са директен резултат от дизайна и фичърите на платформи от този тип:</p>

<ul>
  <li>
    <p><strong>Трибуна за най-противоречивите мнения.</strong> Знаем, че публикациите, които будят силни емоции и най-вече гняв, са тези, които се споделят най-много и събират най-много реакции. Тези публикации в последствие провокират ядосани реакции от несъгласните с тях, от които пък най-голям отзвук отново получават най-противоречивите, които пък на свой ред провокират контра реацкии… безкраен цикъл на хейт и контра-хейт.</p>
  </li>
  <li>
    <p><strong>Емоции преди факти.</strong> Когато сме ядосани или провокирани от нещо, често чувствата ни изпреварват мисълта. Затова не рядко иначе разумни хора споделят фалшиви новини или разпространяват откровени измислици. При дефицита на внимание, от който страдаме всички онлайн, малцина се занимават с проверяване на източници или търсене на допълнителен контекст. В пъти по-лесно да изречеш невярно твърдение, отколкото да опровергаеш такова. Или както казва старата поговорка - лъжата обикаля интернета три пъти преди истината да си върже обувките.</p>
  </li>
  <li>
    <p><strong>Преувеличаване като за последно.</strong> В среда на постоянно изобилие от информация е трудно да изпъкнеш. Ако искаш някой изобщо да обърне внимание на мнението ти, се налага да предозираш. Всеки проблем се представя като потенциален апокалипсис. Всяко действие, с което не сме съгласни, е ‘краят на демокрацията’, ‘смъртта на цивилизования свят’, ‘по-лошо от нацистите’ и т.н. Резултатът е, че всякакъв нюанс бива премахнат, оставяйки единствено кресливи лозунги.</p>
  </li>
  <li>
    <p><strong>Разделяне на агитки.</strong> Полу-анонимността в социалните мрежи прави много лесно смесването на всички с горе-долу сходни политически идеи в един единствен колективен образ. Просто е по-трудно да възприемаме това, което четем, като мнение на отделни личности, когато срещу нас не седи човек в реалния свят, а само име и профилна снимка. В паметта ни всички мнения се сливат в малък на брой архетипи - консерватора, либерала, социалиста и т.н. И тъй като почти всичко, което четем онлайн и вбесяващо и крайно (точка 1), то колективните образи на политическите ни опоненти се превръщат в карикатури (или поне в мемове).</p>
  </li>
  <li>
    <p><strong>Ехо камерите.</strong> Всички обичаме да се чувстваме част от някаква общност и да получаваме някакъв вид социално одобрение. Затова се събираме в ехо камери - групи от хора, мислещи като нас самите, нашата си ‘агитка’. В тези ехо камери всичко негативно за другия лагер и всичко позитивно за своя се повтаря и усилва стократно, превръщайки карикатурата в гротеска. Всяко ‘външно’ мнение придобива статута на богохулство.</p>
  </li>
  <li>
    <p><strong>Спор за пред публика.</strong> Веднъж приели политически си възгледи като част от своята идентичност и билет за принадлежност към социалната си група, то нищо вече не може да ни накара да си променим мнението и всеки спор става излишен. И въпреки това социалните мрежи са пълни със спорове между членовете на различните ‘агитки’. Те не спорят за да открият истината или за да убедят противника в нещо, а за да намерят одобрението на собствената си група. Затова много бързо спора се свежда до това кой ще измисли по-остроумна обида или кой пръв ще постне сойджак меме с лицето на отсрещната страна.</p>
  </li>
</ul>

<p>Веднъж стигнали до такова делене на ‘племена’, до насилието в истинския живот остава съвсем малко. Еволюционните инстинкти, които сме придобили през хилядолетията в саваната, са ни научили да бъдем просоциални с нашата си група и безжалостни към всеки външен. Има твърде много примери в човешката история за жестока, нерационална и непримирима омраза, завършили с кръвопролития - хугеноти и католици във Франция, Туци и Хуту в Руанда, ‘Зелени’ и ‘Сини’ във Визанитя… За жалост това е процес с позитивна обратна връзка - имаме склонност да намираме оправдания за злосторниците от собствения си лагер и в същото време да искаме многократна разплата за простъпките на противниковия. Омагьосан кръг…</p>

<p>Може би преувеличавам (точка 3). Надявам се да е така. Но всичко, което виждам ме кара да смятам, че социалните мрежи унищожават капацитета ни за смислен граждански диалог и ни водят по дългия и тъжен път на конфликтите. Според мен това, което виждаме днес в САЩ, е само началото на процес по разединение, който ще стигне и до нас. Лагерите и темите ще са различни, но първопричината ще е същата - унищожителния ефект на стила общуване в социалните мрежи.</p>

<p><img src="/images/posts/smp-featured-image.png" alt="" /></p>]]></content><author><name>Georgi Demirev</name><email>georgi_demirev@proton.me</email></author><category term="thoughts" /><summary type="html"><![CDATA[Гледайки събитията от САЩ, си мисля доста за влиянието на социалните мрежи върху гражданското общество и политическия диалог. Като цяло заключенията ми са доста мрачни.]]></summary></entry><entry><title type="html">Playing Around with Epi Models - Part 2</title><link href="https://demirev.github.io/posts/epi-models-2/" rel="alternate" type="text/html" title="Playing Around with Epi Models - Part 2" /><published>2020-05-10T00:00:00+00:00</published><updated>2020-05-10T00:00:00+00:00</updated><id>https://demirev.github.io/posts/agent-based-pandemic-model</id><content type="html" xml:base="https://demirev.github.io/posts/epi-models-2/"><![CDATA[<p>As a follow up to my last couple of posts, this is a detailed description of the agent based pandemic model I built on top of <a href="https://www.thelancet.com/journals/laninf/article/PIIS1473-3099(20)30457-6/fulltext">work</a> by Adam Kucharski et al. as a self-learning exercise.</p>

<p>Code for this project can be found at <a href="https://github.com/demirev/2020-cov-abm">https://github.com/demirev/2020-cov-abm</a></p>

<h2 id="model-description---agent-based-pandemic-model">Model Description - Agent-Based Pandemic Model</h2>

<p>The model simulates a population of agents divided in households and workplaces who interact (and expose each other to the disease) on a daily basis.</p>

<p><img src="/images/posts/epi2-population.png" alt="Agents in the model" /></p>

<p>There are four types of agents based on their age group:</p>
<ul>
  <li>children</li>
  <li>adults</li>
  <li>middle-aged</li>
  <li>pensioners</li>
</ul>

<p>Every individual belongs to a <strong>household</strong>. The distribution of household in terms of age makeup and size is determined by the user.</p>

<p>Adults and Middle-aged agents are also assigned to <strong>workplaces</strong>. Children are assigned to ‘classrooms’ with other children. Pensioners are not assigned to a workplace.</p>

<p>The simulation progresses in ‘days’. It begins with a small number of infected individuals defined by the user. Each day the infected agents interact with others who become exposed to infection:</p>

<p><img src="/images/posts/epi2-interactions.png" alt="Agent interaction" /></p>

<p>I assume that the infected agent meets a number randomly drawn agents from their workplace as well as a number of randomly drawn agents from the population at large. The number of meetings is determined for each infected agent by drawing an observation from the interaction data set from the Pandemic study by <a href="https://www.medrxiv.org/content/10.1101/2020.02.16.20023754v2">Krepac et al</a>. Exposed agents face a certain user-defined probability of becoming infected (see section on default parameters).</p>

<p>I assume that infected agents also expose <em>everyone</em> from their own household (at least in the baseline scenario with no interventions). Exposed household members also face a user-defined probability of becoming infectious. For convenience this exposure is done on day 1 of the infection, so this probability should be thought of as the total probability that an exposed household member gets infected throughout the infectious period.</p>

<p>The next day the newly infected agents are added to the list of disease-spreaders and the simulation is carried over again. A simple *SIR status is kept for each agent:</p>

<p><img src="/images/posts/epi2-status.png" alt="Agent status" /></p>

<p>This means that as the disease progresses, the number of susceptible individuals in the population, but also in individual households and workplaces will decrease, and the disease will naturally slow down at some point (note: this will tend to happen earlier because of the compartmentalization of agents into households and workplaces compared to a simulation without this complication).</p>

<p>Throughout the simulation a record is kept of who infected who when. This allows us to compute the <strong>effective average reproduction number</strong> at each time point.</p>

<p>The basic interaction rules described above can be altered by specifying a disease-preventing non-pharmaceutical intervention, which I will cal ‘policies’ for short.</p>

<p>The list of available policies is given below. It is the same set of interventions as in <a href="https://www.medrxiv.org/content/10.1101/2020.04.23.20077024v1">Kucharski et al</a>.</p>

<ul>
  <li><strong>no_measures</strong> - the baseline scenario.</li>
  <li><strong>isolation_only</strong> - individuals who test positive are isolated and can no longer infect. Only a fraction of infectious agents will get tested each day.</li>
  <li><strong>hh_quarantine_only</strong> - in addition to the above, individuals in the household of the ones who tested positive are traced, tested and isolated if needed (i.e. they don’t contribute to spreading the infection further).</li>
  <li><strong>hh_work_only</strong> - as above plus same treatment of coworkers.</li>
  <li><strong>isolation_manual_tracing_met_only</strong> - all contacts are traced (household, work and other) as long as they were contact known to the infected individual beforehand (this is an adjustable parameter). The idea is that in manual tracing you cannot trace stranger.</li>
  <li><strong>isolation_manual_tracing_met_limit</strong> - same as above but also imposing a hard limit on number of social interaction outside the household or work (akin to social distancing measures)</li>
  <li><strong>isolation_manual_tracing</strong> - assume every contact can be traced (including those that were strangers to the infected individual)</li>
  <li><strong>cell_phone</strong> - tracing using smartphone app. Same as isolation_manual_tracing (phone apps allow to trace strangers) but limited by number of people owning a phone.</li>
  <li><strong>cell_phone_met_limit</strong> - same as above plus a hard limit on outside of work and home interactions.</li>
  <li><strong>pop_testing</strong> - random testing on a certain fraction of the population each day and isolation of those who tested positive only (a la Romer)</li>
  <li><strong>pt_extra</strong> - as far as I understand an umbrella for any intervention that reduces the chance of infection given exposure (e.g. mandatory mask wearing). The degree of reduction is a parameter in the model.</li>
</ul>

<h2 id="possible-future-extensions">Possible Future Extensions</h2>

<p>Obviously some aspects of disease spread are simplified in this model. However some complications can be introduced relatively easily. Namely the duration of the disease is currently fixed but can be made variable and dependent on age. SIR groups can be augmented with additional status groups to model hospital admission, ICU care, death or lose of immunity (and the transition between groups can be made dependent on age).</p>

<h2 id="example">Example</h2>
<p>I will give a short example of how to use the model and the code. It is the same one that can be found in the file <code class="language-plaintext highlighter-rouge">scripts/contact_abm_model.R</code></p>

<h3 id="defining-social-behavior-of-each-age-group">Defining social behavior of each age group</h3>

<p>Each age group in the model meets with other people based on interaction data such as the one provided by Fry et al. The first step is to define this interaction distribution:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># load contacts data</span><span class="w">
</span><span class="n">contacts</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bind_rows</span><span class="p">(</span><span class="w">
  </span><span class="n">read_csv</span><span class="p">(</span><span class="s2">"data/contact_distributions_u18.csv"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"student"</span><span class="p">),</span><span class="w">
  </span><span class="n">read_csv</span><span class="p">(</span><span class="s2">"data/contact_distributions_o18.csv"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"adult"</span><span class="p">),</span><span class="w">
  </span><span class="n">read_csv</span><span class="p">(</span><span class="s2">"data/contact_distributions_o18.csv"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"middle_age"</span><span class="p">),</span><span class="w">
  </span><span class="n">read_csv</span><span class="p">(</span><span class="s2">"data/contact_distributions_o18.csv"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"pensioner"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
    </span><span class="n">mutate</span><span class="p">(</span><span class="n">e_work</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w"> </span><span class="c1"># pensioners don't work</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>This is a little coarse since I only have data for over/under 18 rather than for more detailed age brackets, but this can be remedied in the future</p>

<h3 id="creating-a-population-of-agents">Creating a population of agents</h3>

<p>The next step is to create a population of agents for the model. This is done by listing household types and the number of such households to include in the simulation.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># define initial population</span><span class="w">
</span><span class="n">init_pop</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">generate_population</span><span class="p">(</span><span class="w">
  </span><span class="n">household_distribution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
    </span><span class="n">tibble</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100000</span><span class="p">,</span><span class="w"> </span><span class="n">student</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">adult</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">middle_age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">pensioner</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">),</span><span class="w">
    </span><span class="n">tibble</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100000</span><span class="p">,</span><span class="w"> </span><span class="n">student</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">adult</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">middle_age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">pensioner</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">),</span><span class="w">
    </span><span class="n">tibble</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10000</span><span class="p">,</span><span class="w"> </span><span class="n">student</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">adult</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">middle_age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">pensioner</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">),</span><span class="w">
    </span><span class="n">tibble</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">50000</span><span class="p">,</span><span class="w"> </span><span class="n">student</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">adult</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">middle_age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">pensioner</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
  </span><span class="p">),</span><span class="w">
  </span><span class="n">average_workplace_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">40</span><span class="p">,</span><span class="w">
  </span><span class="n">average_classroom_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">25</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>In principle this should be available from census data.</p>

<p>We also need to define the number of initial infected individuals in each age group:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># define initial infected individuals</span><span class="w">
</span><span class="n">init_inf</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">generate_initial_infected</span><span class="p">(</span><span class="w">
  </span><span class="n">init_pop</span><span class="p">,</span><span class="w">
  </span><span class="n">n_initial_infections</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="w">
    </span><span class="s2">"student"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w">
    </span><span class="s2">"adult"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w">
    </span><span class="s2">"middle_age"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w">
    </span><span class="s2">"pensioner"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="w">    
  </span><span class="p">),</span><span class="w">
  </span><span class="n">contact_distribution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contacts</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h3 id="simulating-outbreaks">Simulating outbreaks</h3>

<p>The main workhorse function of the model is <code class="language-plaintext highlighter-rouge">simulate_pandemic_policy_sequence</code>. It takes the initial population defined above and simulates a sequence of different intervention policies for a specified number of days.</p>

<p>Below I simulate the baseline policy for $120$ days. Note the format of the <code class="language-plaintext highlighter-rouge">policy_sequence</code> parameter.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">simulation_baseline</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">simulate_pandemic_policy_sequence</span><span class="p">(</span><span class="w">
  </span><span class="n">initial_population</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init_pop</span><span class="p">,</span><span class="w">
  </span><span class="n">initial_infected</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init_inf</span><span class="p">,</span><span class="w">
  </span><span class="n">initial_recovered</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">tibble</span><span class="p">(</span><span class="n">individual_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">)[</span><span class="m">0</span><span class="p">,],</span><span class="w">
  </span><span class="n">policy_sequence</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"no_measures"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">120</span><span class="p">)),</span><span class="w">
  </span><span class="n">contact_distribution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contacts</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>
<p>The code is somewhat time consuming, with this simulation loop taking around 11-12 minutes on my personal laptop.</p>

<p>The resulting data allows us to plot the different <strong>SIR</strong> groups through time:</p>

<p><img src="/images/posts/epi2-sir_baseline.png" alt="SIR baseline" /></p>

<p>Or just the number of infected individuals at each day:</p>

<p><img src="/images/posts/epi2-i_baseline.png" alt="Infected baseline" /></p>

<p>Finally we can plot the effective reproduction number at each day (the plot is noisy in the beginning and end of the simulation due to having only a few infected individuals at those times):</p>

<p><img src="/images/posts/epi2-rt_baseline.png" alt="R by day" /></p>

<h3 id="simulating-the-effect-of-different-interventions">Simulating the effect of different interventions</h3>

<p>To compare different interventions, I will run one simulation for each intervention scenario, where first the disease spreads unchecked for $20$ days and then the intervention is put in place for $100$ days:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">simulation_all_scenarios</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="w">
  </span><span class="s2">"isolation_only"</span><span class="p">,</span><span class="s2">"hh_quaratine_only"</span><span class="p">,</span><span class="s2">"hh_work_only"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"isolation_manual_tracing_met_only"</span><span class="p">,</span><span class="s2">"isolation_manual_tracing_met_limit"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"isolation_manual_tracing"</span><span class="p">,</span><span class="s2">"cell_phone"</span><span class="p">,</span><span class="s2">"cell_phone_met_limit"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"pop_testing"</span><span class="p">,</span><span class="s2">"pt_extra"</span><span class="w">
</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">future_map</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="n">scn</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">simulate_pandemic_policy_sequence</span><span class="p">(</span><span class="w">
      </span><span class="n">initial_population</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init_pop</span><span class="p">,</span><span class="w">
      </span><span class="n">initial_infected</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init_inf</span><span class="p">,</span><span class="w">
      </span><span class="n">initial_recovered</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">tibble</span><span class="p">(</span><span class="n">individual_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">)[</span><span class="m">0</span><span class="p">,],</span><span class="w">
      </span><span class="n">policy_sequence</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
        </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"no_measures"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">20</span><span class="p">),</span><span class="w"> </span><span class="c1"># assume 20 days before measures are taken</span><span class="w">
        </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">scn</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">100</span><span class="p">)</span><span class="w"> </span><span class="c1"># let simulation run for total of 120 days</span><span class="w">
      </span><span class="p">),</span><span class="w">
      </span><span class="n">contact_distribution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contacts</span><span class="w">
    </span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">
</span></code></pre></div></div>

<p>We can now compare the total number of infected (with possible implications for the burden to the healthcare system) for each scenario:</p>

<p><img src="/images/posts/epi2-i_all_scenarios.png" alt="Infected all scenarios" /></p>

<p>The plot clearly shows that some policies are much more effective than others and some can even lead to disease eradication in these simulations. This is furthers confirmed by looking at $R_t$ for each simulation and noting that it falls well below $1$ for some of them (the plot is noisy at times because of the low number of infected agents at the beginning and end of disease spread):</p>

<p><img src="/images/posts/epi2-rt_all_scenarios.png" alt="Disease reproduction all scnearios" /></p>

<h3 id="multiple-simulations-and-non-trivial-policy-schedules">Multiple simulations and non-trivial policy schedules</h3>

<p>The above plots show the results of running the simulation only once per scenario. Since there is inherent randomness in such models, it is better to conduct several simulations and average the results. This can be done through the <code class="language-plaintext highlighter-rouge">simulate_pandemic_policy_sequence_ntimes</code> function, which as the name suggests is just a wrapper around <code class="language-plaintext highlighter-rouge">simulate_pandemic_policy_sequence</code> that repeats the simulation for a specified number of times.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">simulation_specific_policy</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">simulate_pandemic_policy_sequence_ntimes</span><span class="p">(</span><span class="w">
  </span><span class="n">initial_population</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init_pop</span><span class="p">,</span><span class="w">
  </span><span class="n">initial_infected</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init_inf</span><span class="p">,</span><span class="w">
  </span><span class="n">initial_recovered</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">tibble</span><span class="p">(</span><span class="n">individual_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">)[</span><span class="m">0</span><span class="p">,],</span><span class="w">
  </span><span class="n">policy_sequence</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">policy</span><span class="p">,</span><span class="w">
  </span><span class="n">n_times</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">8</span><span class="p">,</span><span class="w"> </span><span class="c1"># run 8 simulations of the same policy</span><span class="w">
  </span><span class="n">contact_distribution</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contacts</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>One can also use this model to simulate an arbitrary sequence of intervention policies (e.g. a regime of on-and-off social distancing or a policy of initial manual tracing later replaced by cell phone tracing).</p>

<p>To do this just define a <code class="language-plaintext highlighter-rouge">policy</code> list as the one below (specifying the duration in days of each policy):</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">policy</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"no_measures"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="p">),</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"hh_quaratine_only"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">20</span><span class="p">),</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"hh_work_only"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">20</span><span class="p">),</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"isolation_manual_tracing_met_only"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">20</span><span class="p">),</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"cell_phone"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">20</span><span class="p">),</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">scenario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"pop_testing"</span><span class="p">,</span><span class="w"> </span><span class="n">n_days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">30</span><span class="p">)</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Plotting the outputs of <code class="language-plaintext highlighter-rouge">simulate_pandemic_policy_sequence_ntimes</code> will show the result of each simulation as well as the average across simulations:</p>

<p><img src="i_complex_policy.png" alt="Plot of multiple simulations" /></p>

<h2 id="default-simulation-parameters">Default simulation parameters</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">inf_period</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="c1"># Infectious period</span><span class="w">
</span><span class="n">max_low_fix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="c1"># Social distancing limit in scenarios with hard limit</span><span class="w">
</span><span class="n">wfh_prob</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="c1"># Probability people are working from home</span><span class="w">
</span><span class="n">trace_prop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.95</span><span class="p">,</span><span class="w"> </span><span class="c1"># Proportion of contacts traced </span><span class="w">
</span><span class="n">app_cov</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.53</span><span class="p">,</span><span class="w"> </span><span class="c1"># App coverage</span><span class="w">
</span><span class="n">prob_symp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.6</span><span class="p">,</span><span class="w"> </span><span class="c1"># Proportion symptomatic</span><span class="w">
</span><span class="n">prob_t_asymp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="c1"># Transmission probability if asymptotic</span><span class="w">
</span><span class="n">isolate_distn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0.25</span><span class="p">,</span><span class="m">0.25</span><span class="p">,</span><span class="m">0.2</span><span class="p">,</span><span class="m">0.3</span><span class="p">,</span><span class="m">0</span><span class="p">),</span><span class="w"> </span><span class="c1"># distribution of time to isolate in the scenarios with isolation (1st day presymp)</span><span class="w">
</span><span class="n">pt_extra</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="c1"># Optional extra transmission intervention probability</span><span class="w">
</span><span class="n">pt_extra_reduce</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="c1"># Reduction from extra intervention</span><span class="w">
</span><span class="n">hh_risk</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="p">,</span><span class="w"> </span><span class="c1"># HH risk (total)</span><span class="w">
</span><span class="n">cc_risk</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.06</span><span class="p">,</span><span class="w"> </span><span class="c1"># Outside HH contact risk (per daily exposure)</span><span class="w">
</span><span class="n">trace_adherence</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.9</span><span class="p">,</span><span class="w"> 
</span><span class="n">p_pop_test</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.05</span><span class="p">,</span><span class="w"> </span><span class="c1"># Proportion mass tested (5% per week)</span><span class="w">
</span><span class="n">trace_hh</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="c1"># Proportion of household members traced in scenarios involving tracing.</span><span class="w">
</span><span class="n">met_before_w</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.79</span><span class="p">,</span><span class="w"> </span><span class="c1"># Proprotion of meetings which are with familiar people at work. At school = 90%</span><span class="w">
</span><span class="n">met_before_h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="c1"># Within HH</span><span class="w">
</span><span class="n">met_before_o</span><span class="w"> </span><span class="o">=</span><span class="w">  </span><span class="m">0.52</span><span class="w"> </span><span class="c1"># In other settings</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>Georgi Demirev</name><email>georgi_demirev@proton.me</email></author><category term="models" /><summary type="html"><![CDATA[As a follow up to my last couple of posts, this is a detailed description of the agent based pandemic model I built on top of work by Adam Kucharski et al. as a self-learning exercise.]]></summary></entry><entry><title type="html">Playing Around with Epi Models - Part 0</title><link href="https://demirev.github.io/posts/epi-models-0/" rel="alternate" type="text/html" title="Playing Around with Epi Models - Part 0" /><published>2020-04-12T00:00:00+00:00</published><updated>2020-04-12T00:00:00+00:00</updated><id>https://demirev.github.io/posts/playing-around-with-epi-models</id><content type="html" xml:base="https://demirev.github.io/posts/epi-models-0/"><![CDATA[<h2 id="learning-about-epidemiology">Learning about epidemiology</h2>

<p>Like most of you I knew next to nothing about epidemiology until a month ago. And also like most of you I am suddenly stuck at home with a lot of free time at my hands. And since it seems that in the next few months epidemiology will play a huge role in informing decision that can impact many thousands of lives, I decided that it may be a good idea to study the subject a bit in order to be able to have better informed opinions.</p>

<p>My plan was two-fold: first to go through a book on the topic, and then to find one or two mini-projects to practice what I’ve read. I got a hold of <a href="https://www.springer.com/gp/book/9783319974866">this</a> book, which contained some useful code snippets. As for projects, I wanted to find some models that were a) recently created by respected epidemiologists so that I was dealing with the real stuff instead of toy examples, and b) came with open-source code so I could tweak or modify it.</p>

<h2 id="age-specific-seir-model">Age-specific SEIR model</h2>

<p>The first model I was able to find that suited my goal was <a href="https://alhill.shinyapps.io/COVID19seir/">this</a> useful and accessible dashboard by Alison Hill at Harvard’s Program for Evolutionary Dynamics. It is a <a href="https://en.wikipedia.org/wiki/Compartmental_models_in_epidemiology">SEIR</a>-type model that splits the population into susceptible, exposed but not infectious, exposed and infectious, asymptomatic carriers, three different severity levels of cases (mild, severe or hospitalized, and critical or ICU-bound), dead and recovered groups.</p>

<p>The model already had a good deal of complexity, and a lot of effort had gone into choosing sensible parameter values based on available clinical data. One thing that was not included in it however was a split-by age of the population. 
Since COVID seems to be impacting people of various ages quite differently, it seems that incorporating an age structure into such a model would be a useful thing to do. That is what I set out to do.</p>

<p>The end result can be found <a href="https://demirev.shinyapps.io/SIRinterventions/">here</a>. At this point the interface was getting a bit clunky, but it is still fully intractable and all parameters can be adjusted. The main difference from the original is that in my version the key parameters are vectors (one entry per age group) instead of scalars.</p>

<p>You can go ahead and check out the app or read the detailed description of the model in <a href="">this</a> post.</p>

<h2 id="agent-based-model-of-the-pandemic">Agent Based Model of the Pandemic</h2>

<p>The next model that I stumbled on is <a href="https://www.thelancet.com/journals/laninf/article/PIIS1473-3099(20)30457-6/fulltext">this</a> paper by Kucharski et al. It simulates individual-level transmission in various scenarios (at home, at work, at school, or other settings) while imposing various non-pharmaceutical interventions (such as mobile app tracking, household quarantine, manual tracing etc).</p>

<p>Instead of focusing on a single individual as in the original paper, I extended this model to simulate tens of thousands of individuals split into different households, workplaces, and class-rooms (depending on their age). The agents then interact with each other driven by specified encounter probabilities within their household and work/school.</p>

<p>The effect is that this model creates a sort of local pool of susceptibles (the household or the workplace/schoolroom). This result is somewhat smaller reproductive number estimates compared to a random mixing model.</p>

<p>You can read more about this model on <a href="https://github.com/demirev/2020-cov-abm">github</a> or the detailed write-up in <a href="">this</a> blog post.</p>]]></content><author><name>Georgi Demirev</name><email>georgi_demirev@proton.me</email></author><category term="models" /><summary type="html"><![CDATA[Learning about epidemiology]]></summary></entry><entry><title type="html">Playing Around with Epi Models - Part 1</title><link href="https://demirev.github.io/posts/epi-models-1/" rel="alternate" type="text/html" title="Playing Around with Epi Models - Part 1" /><published>2020-04-12T00:00:00+00:00</published><updated>2020-04-12T00:00:00+00:00</updated><id>https://demirev.github.io/posts/age-specific-seir-model</id><content type="html" xml:base="https://demirev.github.io/posts/epi-models-1/"><![CDATA[<p>As a follow up to my last post, this is a detailed description of the SEIR model I built on top of <a href="https://alhill.shinyapps.io/COVID19seir/">work</a> by Alsion Hill as a self-learning exercise.</p>

<p><img src="/images/posts/epi1-featured-image.png" alt="" /></p>

<p>Code for this project can be found <a href="https://github.com/demirev/SEIR_COVID19">here</a>, while an interactive dashboard is available <a href="https://demirev.shinyapps.io/SIRinterventions/">here</a>.</p>

<h2 id="model-description---age-specific-seir-model">Model Description - Age-specific SEIR Model</h2>

<p>Individuals in this model are split up into $A$ age groups and 9 classes. All individuals start in the susceptible ($S$) class. Then they pass through two exposed class $E0$ and $E1$, where once in the second class they become infectious. Infected indivduals can be either asymptomatic ($I0$), mild ($I1$), severe ($I2$) or critical ($I3$). Alls of them can transmit the disease and all of them can either pass to the next stage or heal. Critical cases face a probability of dying and transitioning to the $D$ class. Recovered individuals transit to the $R$ class where they are immune. Optionally the user can specify a probability of losing immunity and transitioning back to the $S$ class. Thet transition dynamics are presented below:</p>

<p><img src="/images/posts/epi1-flow1.png" alt="transition diagram" /></p>

<p>Individuals age at a rate ensuring they spend the correct time in their age bracket. The aging rate for age bracket $i$ is defined as $1$ over the length of the bracket (and $0$ for the oldest bracket). In addition the user can specify a background non-epidemic death rate. In that case I also introduce a birth rate to keep the population constant. The death rate is the same across age groups in the dashboard (although in the source code it need not be) so it only serves to introduce a new flow of susceptibles and a possible endemic equilibrium.</p>

<p><img src="/images/posts/epi1-flow2.png" alt="aging diagram" /></p>

<p>The differential equations that governs the system are given below:</p>

<h2 id="ode-system">ODE System</h2>

<h3 id="equations">Equations</h3>
<ul>
  <li>
    <p>$\dot{S^{i}} = -\beta_{e}\sum_{j \in 1:A}w^{i,j}<em>e E_1^j \frac{S^i}{n^i} \sigma(t) -\beta</em>{0}\sum_{j \in 1:A}w^{i,j}<em>0 I_0^j \frac{S^i}{n^i} \sigma(t)-\beta</em>{1}\sum_{j \in 1:A}w^{i,j}<em>1 I_1^j \frac{S^i}{n^i} \sigma(t)-\beta</em>{2}\sum_{j \in 1:A}w^{i,j}<em>2 I_2^j \frac{S^i}{n^i} \sigma(t)-\beta</em>{3}\sum_{j \in 1:A}w^{i,j}<em>3 I_3^j \frac{S^i}{n^i} \sigma(t) +\alpha^{i-1} S^{i-1} -\alpha^i S^i - \eta</em>{-}^{i} S + \eta_{+}^{i} \sum_{j \in 1:A}n^i +\phi R^i$</p>
  </li>
  <li>
    <p>$\dot{E_0^i} = \beta_{e}\sum_{j \in 1:A}w^{i,j}<em>e E_1^i \frac{S^i}{n^i} \sigma(t) +\beta</em>{0}\sum_{j \in 1:A}w^{i,j}<em>0 I_0^i \frac{S^i}{n^i} \sigma(t) + \beta</em>{1}\sum_{j \in 1:A}w^{i,j}<em>1 I_1^i \frac{S^i}{n^i} \sigma(t) + \beta</em>{2}\sum_{j \in 1:A}w^{i,j}<em>2 I_2^i \frac{S^i}{n^i} \sigma(t) + \beta</em>{3}\sum_{j \in 1:A}w^{i,j}<em>3 I_3^i \frac{S^i}{n^i} -\epsilon_0 E_0^i \sigma(t) +\alpha^{i-1} E_0^{i-1} -\alpha^i E_0^i - \eta</em>{-}^{i} E_0^i$</p>
  </li>
  <li>
    <p>$\dot{E_1^i} = \epsilon_0 E_0^i - \epsilon_1 E_1^i + \alpha^{i-1} E_1^{i-1} - \alpha^i E_1^i -\eta_{-}^{i}E_1^i$</p>
  </li>
  <li>
    <p>$\dot{I_0^i} = f\epsilon_1 E_1^i - \gamma_0 I_0^i + \alpha^{i-1} I_0^{i-1} - \alpha^i I_0^i -\eta_{-}^i I_0^i$</p>
  </li>
  <li>
    <p>$\dot{I_1^i} = (1-f)\epsilon_1 E_1^i - \gamma_1 I_1^i - \theta_1 I_1^i + \alpha^{i-1} I_1^{i-1} - \alpha^i I_1^i -\eta_{-}^i I_1^i$</p>
  </li>
  <li>
    <p>$\dot{I_2^i} = \theta_1 I_1^i - \gamma_2 I_2^i - \theta_2 I_2^i + \alpha^{i-1} I_2^{i-1} - \alpha^i I_2^i -\eta_{-}^i I_2^i$</p>
  </li>
  <li>
    <p>$\dot{I_3^i} = \theta_2 I_2^i - \gamma_3 I_3^i - \mu I_3^i + \alpha^{i-1} I_3^{i-1} - \alpha^i I_3^i -\eta_{-}^i I_3^i$</p>
  </li>
  <li>
    <p>$\dot{R^i} = \gamma_1 I_1^i + \gamma_2 I_2^i + \gamma_3 I_3^i - \phi R^i + \alpha^{i-1} R^{i-1} - \alpha^i R^i -\eta_{-}^i R^i$</p>
  </li>
  <li>
    <p>$\dot{D^i}  = \mu I^i_3$</p>
  </li>
</ul>

<p>Where $i$ takes values in $1:A$ (i.e. one for each age group). Dotted values denote time derivatives.</p>

<h3 id="variables">Variables</h3>
<p>All variables below are vectors of length $A$, where the $i$-th element represents the number of that class in the $i$-th age group.</p>
<ul>
  <li>$S$: Susceptible individuals</li>
  <li>$E_0$: Exposed to the virus but not yet infectious or symptomatic</li>
  <li>$E_1$: Exposed to the virust, already infectious but not yet symptomatic</li>
  <li>$I_0$: Infected individuals with no symptoms. These individuals can pass the virus but they do not pass to the more critical categories and will always heal completely at some point.</li>
  <li>$I_i$: Infected individuals in severity class $i$. Severity increases with $i$ and we assume individuals must pass through all previous classes
    <ul>
      <li>$I_1$: Mild infection</li>
      <li>$I_2$: Severe infection</li>
      <li>$I_3$: Critical infection</li>
    </ul>
  </li>
  <li>$R$: individuals who have recovered from disease and are now immune</li>
  <li>$D$: Dead individuals</li>
</ul>

<h3 id="parameters">Parameters</h3>
<ul>
  <li>$\beta_e$ probability of infection given contact between individual of class $E_1$ and a susceptible individual.</li>
  <li>$\beta_i$ probability of infection given contact between individual of class $I_i$ and a susceptible individual.</li>
  <li>$w_{e}^{i,j}$ daily average number of contacts between an exposed individual of age group $i$ and age group $j$. The defintion for $w_0^{i,j}$, $w_1^{i,j}$, $w_2^{i,j}$, $w_3^{i,j}$. All these variables can be organised into matrices $W_e$, $W_0$, $W_1$, $W_2$, and $W_3$.</li>
  <li>$\eta_{-}$ vector of per age outside probability of unrelated death (in this dashboard all elements are the same)</li>
  <li>$\eta_{+}$ vector of birth rates. Everything except the first element is $0$ since all individuals are born in the first group</li>
  <li>$\alpha$ vector of aging rates. We define $\alpha^0$ to be $0$, and $X^0$ to be $0$ for all vectors $X</li>
  <li>$\epsilon_0$ probability of progression from class $E_0$ to $E_1$</li>
  <li>$\epsilon_i$ probability of progression from $E_1$ to $I_0$ or $I_1$</li>
  <li>$f$ fraction of progressed cases from $E1$ to $I_0$ (the complementary fraction $1-f$ progresses to $I_1$)</li>
  <li>$\gamma_i$ rate at which infected individuals in class $I_i$ recover from disease and become immune</li>
  <li>$\theta_i$ rate at which infected individuals in class $I_i$ progress to class $I_{i+1}$</li>
  <li>$\mu$ death rate for individuals in the most severe stage of disease</li>
  <li>$\sigma(t)$: Seasonal multiplier at time $t$.</li>
</ul>

<p>All probabilities and rates are per day.</p>

<h2 id="linking-the-model-parameters-to-real-world-disease-specifics">Linking the model parameters to real-world disease specifics</h2>

<h3 id="clinical-stages">Clinical stages</h3>

<ul>
  <li>Mild infection - Hospitalization is not required (though in many countries such individuals are also hospitalized)</li>
  <li>Severe infection - Hospitalization and supplemental oxygen are generally required.</li>
  <li>Critical infection - Treatment in an ICU, often with mechanical ventilation, is required.</li>
</ul>

<h3 id="relating-clinical-observations-to-model-parameters">Relating clinical observations to model parameters</h3>

<p>To determine the model parameters consistent with current clinical data, we collect the following values from the slider values chosen by the user, and then use the formulas below to relate them to the rate parameters in the model. Note that the slider inputs for time intervals are average durations.</p>

<ul>
  <li>IncubPeriod:  Average incubation period, days</li>
  <li>DurMildInf: Average duration of mild infections, days</li>
  <li>FracMild: Average fraction of (symptomatic) infections that are mild</li>
  <li>FracSevere: Average fraction of (symptomatic) infections that are severe</li>
  <li>FracCritical: Average fraction of (symptomatic) infections that are critical</li>
  <li>CFR: Case fatality rate (fraction of infections that eventually result in death)</li>
  <li>DurHosp: Average duration of hospitalization (time to recovery) for individuals with severe infection, days</li>
  <li>TimeICUDeath: Average duration of ICU admission (until death or recovery), days</li>
</ul>

<p>(Note g=$\gamma$)</p>
<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span><span class="o">=</span><span class="m">1</span><span class="o">/</span><span class="n">IncubPeriod</span><span class="w">
</span><span class="n">g1</span><span class="o">=</span><span class="p">(</span><span class="m">1</span><span class="o">/</span><span class="n">DurMildInf</span><span class="p">)</span><span class="o">*</span><span class="n">FracMild</span><span class="w">
</span><span class="n">theta1</span><span class="o">=</span><span class="p">(</span><span class="m">1</span><span class="o">/</span><span class="n">DurMildInf</span><span class="p">)</span><span class="o">-</span><span class="n">g1</span><span class="w">
</span><span class="n">g2</span><span class="o">=</span><span class="p">(</span><span class="m">1</span><span class="o">/</span><span class="n">DurHosp</span><span class="p">)</span><span class="o">-</span><span class="n">theta2</span><span class="w">
</span><span class="n">theta2</span><span class="o">=</span><span class="p">(</span><span class="m">1</span><span class="o">/</span><span class="n">DurHosp</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">FracCritical</span><span class="o">/</span><span class="p">(</span><span class="n">FracSevere</span><span class="o">+</span><span class="n">FracCritical</span><span class="p">))</span><span class="w">
</span><span class="n">mu</span><span class="o">=</span><span class="p">(</span><span class="m">1</span><span class="o">/</span><span class="n">TimeICUDeath</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">CFR</span><span class="o">/</span><span class="n">FracCritical</span><span class="p">)</span><span class="w">
</span><span class="n">g3</span><span class="o">=</span><span class="p">(</span><span class="m">1</span><span class="o">/</span><span class="n">TimeICUDeath</span><span class="p">)</span><span class="o">-</span><span class="n">mu</span><span class="w">
</span><span class="n">eta1</span><span class="o">=</span><span class="m">1</span><span class="o">/</span><span class="n">PresymPeriod</span><span class="w">
</span><span class="n">eta0</span><span class="o">=</span><span class="p">(</span><span class="n">IncubPeriod</span><span class="o">-</span><span class="n">PresymPeriod</span><span class="p">)</span><span class="o">^</span><span class="p">(</span><span class="m">-1</span><span class="p">)</span><span class="w">
</span><span class="n">f</span><span class="o">=</span><span class="n">FracAsym</span><span class="w">
</span><span class="n">g0</span><span class="o">=</span><span class="m">1</span><span class="o">/</span><span class="n">DurAsym</span><span class="w">
</span><span class="n">We</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">W</span><span class="w">
</span><span class="n">W0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">W</span><span class="w">
</span><span class="n">W1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ContactFractionMild</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">W</span><span class="w">
</span><span class="n">W2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ContactFractionSevere</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">W</span><span class="w">
</span><span class="n">W3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ContactFractionCritical</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">W</span><span class="w">
</span></code></pre></div></div>

<p>Where $W$ is the contact matrix of healthy individuals defined by the inputs in the contacts tab. I assume exposed and asymptomatics don’t change their contact behavior, while infections of different severity only conduct a fraction of the contacts of healthy individuals due to being sick at home or in hospital. The fractions are defined by the user.</p>

<h2 id="assumptions">Assumptions</h2>

<ul>
  <li>This model is formulated as a system of differential equations and the output therefore represents the expected values of each quantity. It does not take into account stochastic events, and so the epidemic cannot go extinct even when it gets to very low values (except when an intervention is stopped, at which case the number of individuals in each state is rounded to the nearest integer). The model does not report the expected variance in the variables, which can sometimes be large.</li>
  <li>Individuals must pass through a mild stage before reaching a severe or critical stage</li>
  <li>Individuals must pass through a severe stage before reaching a critical stage</li>
  <li>Only individuals in a critical stage die</li>
  <li>All individuals of a single age group have equal transmission rates and equal susceptibility to infection</li>
</ul>]]></content><author><name>Georgi Demirev</name><email>georgi_demirev@proton.me</email></author><category term="models" /><summary type="html"><![CDATA[As a follow up to my last post, this is a detailed description of the SEIR model I built on top of work by Alsion Hill as a self-learning exercise.]]></summary></entry><entry><title type="html">Social Distancing and Behavioural Fatigue</title><link href="https://demirev.github.io/posts/behavioural-fatigue/" rel="alternate" type="text/html" title="Social Distancing and Behavioural Fatigue" /><published>2020-03-16T00:00:00+00:00</published><updated>2020-03-16T00:00:00+00:00</updated><id>https://demirev.github.io/posts/social-distancing-and-behavioiral-fatigue</id><content type="html" xml:base="https://demirev.github.io/posts/behavioural-fatigue/"><![CDATA[<h2 id="how-do-you-contain-a-pandemic">How do you contain a pandemic?</h2>

<p>The rapid spread of the ongoing coronavirus pandemic has caught governments and health authorities around the world off-guard. As various emergency <a href="https://www.bbc.com/news/world-europe-51810673">emergency measures</a> are placed in hold, it is becoming evident that different countries are choosing <a href="https://www.bbc.com/news/world-51737226">different strategies</a> to contain the disease.</p>

<p>Big Disclaimer: I am not an epidemiologist or in any other way qualified to make judgments on effective disease prevention and containment. This post is just a layman’s attempt to understand the actions and choices of some of the world’s governments and should not be taken as advice or expert opinion.</p>

<p>The goal of pretty much all of these strategies at this point is social distancing - the reduction in frequency of contact between members of society so as to slow down the spread of the virus. However, there are stark difference in the severity and the timing of measures adopted by different governments.</p>

<p>On one hand you have a number of countries (such as <a href="https://www.ft.com/content/428babc4-66c9-11ea-800d-da70cff6e4d3">Spain and France</a>) that are implementing severe restrictions on everyday activity such as declaring school vacations, closing off bars and restaurants, canceling mass cultural events etc. Other countries (most notably the UK) however are only (as of midday 16.03.20) hinting at the <a href="https://www.bostonglobe.com/2020/03/15/business/uk-eyes-plan-have-people-over-70-isolate-coronavirus-months/">possibility</a> of enacting such measures in the future and limit their current actions to softer measures such as PSAs and certain travel restrictions.</p>

<p>What is the rationale being the strategy of the UK? The main <a href="https://www.telegraph.co.uk/global-health/science-and-disease/containment-asia-working-people-learnt-sars/">stated reason</a> is so called ‘behavioral fatigue’ - the idea that adherence to harsh distancing policies deteriorates with time, as people ‘tire out’, lose the sense of urgency or are unable to put off their jobs and obligations any more. Under this theory the government has to be careful in their decision to impose strict isolation rules, as doing so too early may mean that the measures become less effective in the future, when they could be even more necessary.</p>

<p>UK authorities have stated that their strategy is based on <a href="https://www.bbc.com/news/science-environment-51874084">computational simulations</a> of disease propagation. In this post I attempt to build a very simple toy model to illustrate that under certain assumptions holding off extreme measures until the most opportune moment is the right thing to do.</p>

<h2 id="the-effectiveness-of-social-distancing">The effectiveness of social distancing</h2>

<p>Let’s first begin by setting up a simple simulation model. I assume a fixed population comprised of $N$ citizens. Each citizen can be either <em>healthy</em>, <em>sick</em> or <em>recovered</em>.</p>

<p>The simulation is split up into phases or ‘days’. Each day each citizen has a fixed probability ($p_{contact}$) of having close contact and potentially transmitting the disease to exactly one other citizen.</p>

<p>The disease is non-lethal and lasts for a fixed number of days ($t_d$). After this the citizen becomes <em>recovered</em> and can no longer contact the disease.</p>

<p>The simulation is carried out for $T$
days and I record the total number of active cases at the beginning of each day.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span>
<span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">110011</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">Citizen</span><span class="p">():</span>
  <span class="n">status</span> <span class="o">=</span> <span class="s">'healthy'</span>
  <span class="n">sick_for</span> <span class="o">=</span> <span class="mi">0</span>

<span class="k">def</span> <span class="nf">simulate_epidemic</span><span class="p">(</span>
  <span class="n">population_size</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">,</span>
  <span class="n">simulation_length</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span>
  <span class="n">meeting_probability</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">,</span>
  <span class="n">sickness_duration</span> <span class="o">=</span> <span class="mi">21</span>
<span class="p">):</span>

  <span class="n">population</span> <span class="o">=</span> <span class="p">[</span><span class="n">Citizen</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">population_size</span><span class="p">)]</span>  
  <span class="n">population</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">status</span> <span class="o">=</span> <span class="s">'sick'</span> <span class="c1"># patient 0
</span>
  <span class="n">daily_sick</span> <span class="o">=</span> <span class="p">[]</span>
  
  <span class="k">for</span> <span class="n">day</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">simulation_length</span><span class="p">):</span>
  
    <span class="n">n_sick</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span> <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="k">else</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">citizen</span> <span class="ow">in</span> <span class="n">population</span><span class="p">]</span>
    <span class="n">daily_sick</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">n_sick</span><span class="p">))</span>
    
    <span class="k">for</span> <span class="n">citizen</span> <span class="ow">in</span> <span class="n">population</span><span class="p">:</span>
      
      <span class="k">if</span> <span class="n">random</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">meeting_probability</span><span class="p">:</span>
        <span class="n">other_citizen</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">choice</span><span class="p">(</span><span class="n">population</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="ow">and</span> <span class="n">other_citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">!=</span> <span class="s">'recovered'</span><span class="p">:</span>
          <span class="n">other_citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="s">'sick'</span>
        <span class="k">elif</span> <span class="n">other_citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="ow">and</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">!=</span> <span class="s">'recovered'</span><span class="p">:</span>
          <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="s">'sick'</span>
      
      <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span><span class="p">:</span>
        <span class="n">citizen</span><span class="p">.</span><span class="n">sick_for</span> <span class="o">+=</span> <span class="mi">1</span>
        
      <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="ow">and</span> <span class="n">citizen</span><span class="p">.</span><span class="n">sick_for</span> <span class="o">&gt;=</span> <span class="n">sickness_duration</span><span class="p">:</span>
        <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="s">'recovered'</span>
  
  <span class="k">return</span><span class="p">(</span><span class="n">daily_sick</span><span class="p">)</span>
</code></pre></div></div>

<p>The effect of implementing successful social distancing measures can be represented in our model by a lower probability of interaction between citizens ($p^∗<em>{contact}&lt;$p</em>{contact}).</p>

<p>Let’s run two simulations each consisting of population of size $N=10000$ for a total of $T=300$ days, where the disease lasts for $t_d=21$ days. The only difference between the two will be the rate of social interaction with $p_{contact}=0.1$ representing the base scenario, and $p^*_{contact}=0.04$ representing social distancing.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">simulation_no_isolation</span> <span class="o">=</span> <span class="n">simulate_epidemic</span><span class="p">(</span>
  <span class="n">population_size</span> <span class="o">=</span> <span class="mi">10000</span><span class="p">,</span>
  <span class="n">simulation_length</span> <span class="o">=</span> <span class="mi">300</span><span class="p">,</span>
  <span class="n">meeting_probability</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">,</span>
  <span class="n">sickness_duration</span> <span class="o">=</span> <span class="mi">21</span>
<span class="p">)</span>

<span class="n">simulation_isolation</span> <span class="o">=</span> <span class="n">simulate_epidemic</span><span class="p">(</span>
  <span class="n">population_size</span> <span class="o">=</span> <span class="mi">10000</span><span class="p">,</span>
  <span class="n">simulation_length</span> <span class="o">=</span> <span class="mi">300</span><span class="p">,</span>
  <span class="n">meeting_probability</span> <span class="o">=</span> <span class="mf">0.04</span><span class="p">,</span>
  <span class="n">sickness_duration</span> <span class="o">=</span> <span class="mi">21</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Plotting the resulting total cases per day in both scenarios shows a familiar graph, illustrating that social distancing can drastically reduce the maximum number of cases per day, and spread out the disease over time, thus limiting the stress on healthcare systems.</p>

<p><img src="/images/posts/bf-plot1.png" alt="No Fatigue Simulation" /></p>

<h2 id="behavioural-fatigue-and-not-jumping-the-gun">Behavioural Fatigue and not jumping the gun</h2>

<p>Now let’s try to model behavioural fatigue. For this I will assume that social distancing measures can be effective for only a limited period - $t_{iso}$. During isolation the interaction rate is $p^∗<em>{contact}&lt;p</em>{contact}$ otherwise it is $p_{contact}$.</p>

<p>The crucial thing thus becomes choosing when to enforce strict distancing measures. I will assume that measures are imposed when the total number of active cases reaches a certain proportion of the total population $P_{trigger}$. I also assume that those measures can be enacted only once (and after $t_{iso}$ days the interaction rate returns to its original value until the end of the simulation).</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">simulate_epidemic_fatigue</span><span class="p">(</span>
  <span class="n">population_size</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">,</span>
  <span class="n">simulation_length</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span>
  <span class="n">meeting_probability_base</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">,</span>
  <span class="n">meeting_probability_isolation</span> <span class="o">=</span> <span class="mf">0.04</span><span class="p">,</span>
  <span class="n">sickness_duration</span> <span class="o">=</span> <span class="mi">21</span><span class="p">,</span>
  <span class="n">isolation_duration</span> <span class="o">=</span> <span class="mi">14</span><span class="p">,</span>
  <span class="n">isolation_trigger</span> <span class="o">=</span> <span class="mf">0.01</span>
<span class="p">):</span>

  <span class="n">population</span> <span class="o">=</span> <span class="p">[</span><span class="n">Citizen</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">population_size</span><span class="p">)]</span>  
  <span class="n">population</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">status</span> <span class="o">=</span> <span class="s">'sick'</span> <span class="c1"># patient 0
</span>
  <span class="n">daily_sick</span> <span class="o">=</span> <span class="p">[]</span>
  <span class="n">days_in_isolation</span> <span class="o">=</span> <span class="mi">0</span>
  
  <span class="k">for</span> <span class="n">day</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">simulation_length</span><span class="p">):</span>
  
    <span class="n">n_sick</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span> <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="k">else</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">citizen</span> <span class="ow">in</span> <span class="n">population</span><span class="p">]</span>
    <span class="n">daily_sick</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">n_sick</span><span class="p">))</span>
    
    <span class="k">if</span> <span class="n">days_in_isolation</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
      <span class="k">if</span> <span class="nb">sum</span><span class="p">(</span><span class="n">n_sick</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="n">isolation_trigger</span> <span class="o">*</span> <span class="n">population_size</span><span class="p">):</span>
        <span class="n">days_in_isolation</span> <span class="o">=</span> <span class="mi">1</span>
        <span class="n">meeting_probability</span> <span class="o">=</span> <span class="n">meeting_probability_isolation</span>
      <span class="k">else</span><span class="p">:</span>
        <span class="n">meeting_probability</span> <span class="o">=</span> <span class="n">meeting_probability_base</span>
    <span class="k">elif</span> <span class="n">days_in_isolation</span> <span class="o">&lt;</span> <span class="n">isolation_duration</span><span class="p">:</span>
      <span class="n">days_in_isolation</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="n">meeting_probability</span> <span class="o">=</span> <span class="n">meeting_probability_isolation</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">meeting_probability</span> <span class="o">=</span> <span class="n">meeting_probability_base</span>
    
    <span class="k">for</span> <span class="n">citizen</span> <span class="ow">in</span> <span class="n">population</span><span class="p">:</span>
      
      <span class="k">if</span> <span class="n">random</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">meeting_probability</span><span class="p">:</span>
        <span class="n">other_citizen</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">choice</span><span class="p">(</span><span class="n">population</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="ow">and</span> <span class="n">other_citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">!=</span> <span class="s">'recovered'</span><span class="p">:</span>
          <span class="n">other_citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="s">'sick'</span>
        <span class="k">elif</span> <span class="n">other_citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="ow">and</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">!=</span> <span class="s">'recovered'</span><span class="p">:</span>
          <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="s">'sick'</span>
      
      <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span><span class="p">:</span>
        <span class="n">citizen</span><span class="p">.</span><span class="n">sick_for</span> <span class="o">+=</span> <span class="mi">1</span>
        
      <span class="k">if</span> <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="s">'sick'</span> <span class="ow">and</span> <span class="n">citizen</span><span class="p">.</span><span class="n">sick_for</span> <span class="o">&gt;=</span> <span class="n">sickness_duration</span><span class="p">:</span>
        <span class="n">citizen</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="s">'recovered'</span>
  
  <span class="k">return</span><span class="p">(</span><span class="n">daily_sick</span><span class="p">)</span>
</code></pre></div></div>

<p>I will simulate two different policies - one enforces strict distancing measures as soon as the total number of cases reaches $2$ of the population, the other only when it reaches $30$. In both cases the measures last for $t_{iso}=14$ days while the disease takes $t_{d}=21$ days to be cured. Other than that the two simulations are the same as before ($N=10000$, $p_{contact}=0.1$, $p^∗_{contact}=0.04$, $T=300$).</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">simulation_early_trigger</span> <span class="o">=</span> <span class="n">simulate_epidemic_fatigue</span><span class="p">(</span>
  <span class="n">population_size</span> <span class="o">=</span> <span class="mi">10000</span><span class="p">,</span>
  <span class="n">simulation_length</span> <span class="o">=</span> <span class="mi">300</span><span class="p">,</span>
  <span class="n">meeting_probability_base</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">,</span>
  <span class="n">meeting_probability_isolation</span> <span class="o">=</span> <span class="mf">0.04</span><span class="p">,</span>
  <span class="n">sickness_duration</span> <span class="o">=</span> <span class="mi">21</span><span class="p">,</span>
  <span class="n">isolation_duration</span> <span class="o">=</span> <span class="mi">14</span><span class="p">,</span>
  <span class="n">isolation_trigger</span> <span class="o">=</span> <span class="mf">0.02</span>
<span class="p">)</span>

<span class="n">simulation_delayed_trigger</span> <span class="o">=</span> <span class="n">simulate_epidemic_fatigue</span><span class="p">(</span>
  <span class="n">population_size</span> <span class="o">=</span> <span class="mi">10000</span><span class="p">,</span>
  <span class="n">simulation_length</span> <span class="o">=</span> <span class="mi">300</span><span class="p">,</span>
  <span class="n">meeting_probability_base</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">,</span>
  <span class="n">meeting_probability_isolation</span> <span class="o">=</span> <span class="mf">0.04</span><span class="p">,</span>
  <span class="n">sickness_duration</span> <span class="o">=</span> <span class="mi">21</span><span class="p">,</span>
  <span class="n">isolation_duration</span> <span class="o">=</span> <span class="mi">14</span><span class="p">,</span>
  <span class="n">isolation_trigger</span> <span class="o">=</span> <span class="mf">0.30</span>
<span class="p">)</span>
</code></pre></div></div>

<p>The results of the simulations are plotted below. Under these assumptions acting too early has postponed the peak of the epidemic, but the maximum number of cases is almost as high as in the base scenario above.</p>

<p>Delaying the strict measures on the other hand has done almost nothing in terms of postponing the peak, but the maximum number of cases is lower that the ‘act too early’ scenario.</p>

<p><img src="/images/posts/bf-plot2.png" alt="Fatigue Simulation" /></p>

<p>In this case the ‘acting too early’ scenario is no more that a postponement of the epidemic (under these assumptions the disease cannot die out due to $t_d &gt; t_{iso}$). The ‘delayed measures’ scenario allows for the population to develop some degree of herd immunity before implementing the measures (since in this model once recovered a citizen can no longer carry the disease).</p>

<h2 id="so-whats-the-better-strategy">So what’s the better strategy?</h2>

<p>I wrote this post mostly to convince myself that the claims of the UK officials about why they aren’t acting faster in response to COVID19 can be justified by some simulation model.</p>

<p>This doesn’t mean that anything written here carries over to the real world. The models described here are extremely simple (no deaths, no seasonal effect, limited interactions, fixed illness duration, same behavior if ill or not, etc., etc.) and for illustration purposes only. Even these simplest of models still rely on many assumptions, most of which I made without much thought (and some of them I choose to better underline my point).</p>

<p>The idea of beheviour fatigue is <a href="https://www.theguardian.com/commentisfree/2020/mar/16/social-distancing-coronavirus-stay-home-government">controversial</a> to <a href="https://www.theguardian.com/world/2020/mar/13/behavioural-scientists-form-new-front-in-battle-against-coronavirus">say</a> the <a href="https://www.ft.com/content/f3136d0a-663e-11ea-800d-da70cff6e4d3">least</a>, and we still don’t know enough about COVID19, how it progresses and whether recovered patients do in fact become immune or not.</p>

<p>The professional epidemiologist who are fighting the pandemic undoubtedly have access to the highly advanced models, carefully developed for years, where each assumption is justified and defensible.</p>

<p>Let’s trust that they use these tools and their knowledge to make the best decisions for all our sake.</p>]]></content><author><name>Georgi Demirev</name><email>georgi_demirev@proton.me</email></author><category term="models" /><summary type="html"><![CDATA[How do you contain a pandemic?]]></summary></entry></feed>