<?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://peter-stewart.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://peter-stewart.github.io/" rel="alternate" type="text/html" /><updated>2026-05-20T17:27:58+00:00</updated><id>https://peter-stewart.github.io/feed.xml</id><title type="html">Peter S. Stewart</title><subtitle>Quantitative Ecology and Conservation</subtitle><author><name>Peter S. Stewart</name></author><entry><title type="html">Classic ecological models in Stan: discrete-time logistic growth</title><link href="https://peter-stewart.github.io/blog/classic-ecological-models-discrete-logistic-growth/" rel="alternate" type="text/html" title="Classic ecological models in Stan: discrete-time logistic growth" /><published>2022-11-04T00:00:00+00:00</published><updated>2022-11-04T00:00:00+00:00</updated><id>https://peter-stewart.github.io/blog/classic-ecological-models-discrete-logistic-growth</id><content type="html" xml:base="https://peter-stewart.github.io/blog/classic-ecological-models-discrete-logistic-growth/"><![CDATA[<p>In this post, I explore the discrete-time logistic growth model in R (including various animations), and discuss the challenges of fitting the model to data in Stan.</p>

<h2 id="acknowledgements-and-other-resources">Acknowledgements and other resources</h2>

<p>The classic reference here is the paper <a href="https://www.nature.com/articles/261459a0">Simple mathematical models with very complicated dynamics</a> by Robert May.</p>

<p>The model is also mentioned in <a href="https://www.waterstones.com/book/chaos/james-gleick/9780749386061">Chaos</a> by James Gleick, which is a great read.</p>

<p>I learned about Stan and Bayesian stats in general from Richard McElreath’s book and course <a href="https://github.com/rmcelreath/stat_rethinking_2022">Statistical Rethinking</a>, Michael Betancourt’s <a href="https://betanalpha.github.io/assets/case_studies/stan_intro.html">introduction to Stan</a>, and the <a href="https://mc-stan.org/docs/stan-users-guide/index.html">Stan manual.</a> I also referred to Martin Modrák’s post on <a href="https://www.martinmodrak.cz/2018/05/14/identifying-non-identifiability/">identifying non-identifiability</a> when troubleshooting problems with the first model.</p>

<p>I also learned a lot about modeling from the book <a href="https://press.princeton.edu/books/hardcover/9780691123448/a-biologists-guide-to-mathematical-modeling-in-ecology-and-evolution">a biologist’s guide to mathematical modeling in ecology and evolution</a> by Sarah Otto and Troy Day.</p>

<h2 id="disclaimer">Disclaimer</h2>

<p>I’m mainly writing this post because a) it’s fun and b) it helps me to learn more about the material - if people find it helpful, then that’s even better!</p>

<p>Please bear in mind that there might be mistakes lurking in this post - if you spot any, I’d appreciate if you let me know via email (my address is in the sidebar) or in the comments below. As usual, use of the model etc. is at your own risk.</p>

<h2 id="introducing-the-model">Introducing the model</h2>

<p>It’s true that I’ve <a href="https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth/">already covered logistic growth in this series</a> - but that was the continuous-time version, and discrete-time logistic growth is much, much more interesting.</p>

<p>The discrete-time logistic growth model appears, unsurprisingly, almost identical to the continuous-time version - the only difference is that rather than looking at how the population changes over an infintessemally small slice of time (i.e., $\frac{dN}{dt}$) we’re looking at how it changes from time-step $t$ to time-step $t+1$</p>

<p>This gives us:</p>

\[N_{[t+1]} = rN_{[t]}\left(1 - \frac{N_{[t]}}{K} \right)\]

<p>Where $r$ is the population’s growth rate, and $K$ is the carrying capacity.s</p>

<p>If we set $K = 1$ then we end up with the form used in May’s paper:</p>

\[N_{[t+1]} = rN_{[t]}\left(1 - N_{[t]} \right)\]

<p>We’ll use this simpler version throughout this post.</p>

<h2 id="exploring-the-model-in-r">Exploring the model in R</h2>

<p>Let’s start off by writing a function for the model:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">logistic_growth</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="n">r</span><span class="p">,</span><span class="n">N0</span><span class="p">,</span><span class="n">generations</span><span class="p">){</span><span class="w">
  </span><span class="n">N</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="n">N0</span><span class="p">,</span><span class="n">numeric</span><span class="p">(</span><span class="n">generations</span><span class="m">-1</span><span class="p">))</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">generations</span><span class="m">-1</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="n">N</span><span class="p">[</span><span class="n">t</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="o">+</span><span class="n">r</span><span class="o">*</span><span class="n">N</span><span class="p">[</span><span class="n">t</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="m">1</span><span class="o">-</span><span class="p">(</span><span class="n">N</span><span class="p">[</span><span class="n">t</span><span class="p">]))}</span><span class="w">
  </span><span class="nf">return</span><span class="p">(</span><span class="n">N</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This function takes in values for the population’s growth rate <code class="language-plaintext highlighter-rouge">r</code>, the initial population size <code class="language-plaintext highlighter-rouge">N0</code>, and the number of generations (i.e., time steps) <code class="language-plaintext highlighter-rouge">generations</code> and returns the population size <code class="language-plaintext highlighter-rouge">N</code> at each time <code class="language-plaintext highlighter-rouge">t</code>.</p>

<p>For instance:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">generations</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">50</span><span class="w">
</span><span class="n">init</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.4</span><span class="w">
</span><span class="n">r</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">2</span><span class="w">
</span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> 
                      </span><span class="n">N0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init</span><span class="p">,</span><span class="w"> 
                      </span><span class="n">generations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">generations</span><span class="p">)</span><span class="w">


</span><span class="n">plot</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">generations</span><span class="w"> </span><span class="p">,</span><span class="n">Nt</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"N"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/simple_example.jpeg?msec=1667492043309" alt="" /></p>

<p>Ok, so this isn’t very interesting - the population rapidly climbs from its initial state to an equilibrium at 0.5, and then stays there.</p>

<p>The really interesting stuff starts to happen when we increase the value of $r$ - for example, when $r = 3.9$ our plot starts to look like this:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/simple_example2.jpeg?msec=1667492086466" alt="" /></p>

<p>The population appears to be fluctuating chaotically - we can see oscillations as well as more stable periods, and there is no obvious pattern to the changes.</p>

<p>Before we explore the model in more detail, let’s load a few useful packages:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">rethinking</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">animation</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">viridis</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h3 id="bifurcation-diagram">Bifurcation diagram</h3>

<p>We already have an idea that the system will become chaotic as we increase $r$, but we want to explore this behaviour in more detail. One way we can do this is to reproduce one of the most iconic graphs in the field of ecology.</p>

<p>The basic idea is that we’re going to iterate over a sequence of values for $r$, and plot the system’s long-term state (here we’re going to use the last 500 time-steps out of 3000) for each value.</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Define sequence of r values, initial state and length of time</span><span class="w">
</span><span class="n">rseq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">4</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="m">0.0025</span><span class="p">)</span><span class="w">
</span><span class="n">init</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w"> 
</span><span class="n">generations</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">3000</span><span class="w">

</span><span class="c1"># Fill matrix with values for last 500 time steps at each value of r</span><span class="w">
</span><span class="n">Nt_mat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">),</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1000</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)){</span><span class="w">
  </span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rseq</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">N0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init</span><span class="p">,</span><span class="w"> </span><span class="n">generations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">generations</span><span class="p">)</span><span class="w">
  </span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Nt</span><span class="p">[</span><span class="m">2501</span><span class="o">:</span><span class="m">3000</span><span class="p">]</span><span class="w">
  </span><span class="n">Nt_mat</span><span class="p">[</span><span class="n">i</span><span class="p">,]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Nt</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Colour palette</span><span class="w">
</span><span class="n">p</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">viridis</span><span class="p">(</span><span class="m">500</span><span class="p">,</span><span class="w"> </span><span class="n">direction</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"># Plot the diagram (NB: this can take a few minutes!)</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</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">1</span><span class="p">,</span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"r"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"N in last 1000 time steps"</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">500</span><span class="p">){</span><span class="w">
    </span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rseq</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Nt_mat</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">pch</span><span class="o">=</span><span class="s2">"."</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">col.alpha</span><span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="n">j</span><span class="p">],</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></code></pre></div></div>

<p>The result is this plot - the bifurcation diagram:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/bifurcation_1to4_v1.jpeg?" alt="" /></p>

<p>We see that at first, when $r$ is low, the system reaches a final equilibrium value, just as we saw earlier. When r reaches about 3, there is a split - the system no longer reaches a final equilibrium population, but rather oscillates between two values. As we increase $r$, further, there are further bifurcations and the system rapidly descends into chaos.</p>

<p>Looking at the chaotic section of the diagram, we can see that this isn’t just random noise - there is a complex underlying structure here, with brief windows of stability (e.g., the big white vertical band where the system briefly oscillates at period 3). We’re going to be exploring this underlying order in various other ways throughout this post.</p>

<p>To make the animated bifurcation diagram I used as the Twitter thumbnail for this post, you can do the following (note that <strong>this can take a while to run</strong> - you can make it faster by setting a larger interval e.g. <code class="language-plaintext highlighter-rouge">by = 0.01</code> for <code class="language-plaintext highlighter-rouge">rseq</code>, and adjusting <code class="language-plaintext highlighter-rouge">interval</code> to a larger number accordingly):</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Define r sequence to simulate over</span><span class="w">
</span><span class="n">rseq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">2.75</span><span class="p">,</span><span class="w"> </span><span class="m">4</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="m">0.01</span><span class="p">)</span><span class="w">
</span><span class="n">pause</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="m">30</span><span class="p">)</span><span class="w"> </span><span class="c1"># So that the gif holds on the last frame for a bit</span><span class="w">
</span><span class="n">rseq</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="n">rseq</span><span class="p">,</span><span class="w"> </span><span class="n">pause</span><span class="p">)</span><span class="w">
</span><span class="n">init</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w"> 
</span><span class="n">generations</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">3000</span><span class="w">
</span><span class="n">p</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">viridis</span><span class="p">(</span><span class="m">1000</span><span class="p">)</span><span class="w"> </span><span class="c1"># Colour palette</span><span class="w">

</span><span class="c1"># Fill matrix with time-series for each value of r</span><span class="w">
</span><span class="n">Nt_mat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">),</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1000</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)){</span><span class="w">
  </span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rseq</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">N0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init</span><span class="p">,</span><span class="w"> </span><span class="n">generations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">generations</span><span class="p">)</span><span class="w">
  </span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Nt</span><span class="p">[</span><span class="m">2001</span><span class="o">:</span><span class="m">3000</span><span class="p">]</span><span class="w">
  </span><span class="n">Nt_mat</span><span class="p">[</span><span class="n">i</span><span class="p">,]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Nt</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Save gif to working directory (with black background)</span><span class="w">
</span><span class="n">saveGIF</span><span class="p">(</span><span class="n">expr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)){</span><span class="w">
  </span><span class="n">par</span><span class="p">(</span><span class="n">bg</span><span class="o">=</span><span class="s2">"black"</span><span class="p">,</span><span class="w"> 
      </span><span class="n">oma</span><span class="o">=</span><span class="nf">rep</span><span class="p">(</span><span class="m">0.1</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">),</span><span class="w">
      </span><span class="n">mai</span><span class="o">=</span><span class="nf">rep</span><span class="p">(</span><span class="m">0.1</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w">
  </span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</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">2.75</span><span class="p">,</span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">xaxt</span><span class="o">=</span><span class="s2">"n"</span><span class="p">,</span><span class="w"> </span><span class="n">yaxt</span><span class="o">=</span><span class="s2">"n"</span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">""</span><span class="p">)</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">1000</span><span class="p">){</span><span class="w">
    </span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rseq</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Nt_mat</span><span class="p">[</span><span class="m">1</span><span class="o">:</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">pch</span><span class="o">=</span><span class="s2">"."</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">col.alpha</span><span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="n">j</span><span class="p">],</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="n">movie.name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"bifurcation.gif"</span><span class="p">,</span><span class="w">
        </span><span class="n">img.name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Rplot"</span><span class="p">,</span><span class="w">
        </span><span class="n">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.1</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/bifurcation_v5.gif" alt="" /></p>

<h3 id="poincaré-plots">Poincaré plots</h3>

<p>If you were just to see the the chaotic time series produced as $r$ gets larger, you could easily mistake it for a series of random values. For instance:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/logistic_randomcompare.jpeg" alt="" /></p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/randomseries.jpeg" alt="" /></p>

<p>From this perspective, the distance between the two is not hugely apparent - but by adopting a different perspective, the difference can immediately become clear. One such perspective is provided by the Poincaré plot, in which we plot each time step $N_{[t]}$ against the next step, $N_{[t+1]}$. When we look at a time-series like this, a stable equilibrium looks like a single point, an oscillation between two values looks like two points, etc.</p>

<p>We’re going to use a function (which I borrowed from <a href="">here</a>) to shift a copy of the time-series one place to the right, and then plot it against the original:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Logistic growth</span><span class="w">
</span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="m">3.9</span><span class="p">,</span><span class="w">
                        </span><span class="n">N0</span><span class="o">=</span><span class="m">0.5</span><span class="p">,</span><span class="w"> 
                        </span><span class="n">generations</span><span class="o">=</span><span class="m">3000</span><span class="p">)</span><span class="w">
</span><span class="c1"># Random numbers</span><span class="w">
</span><span class="n">rseries</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">runif</span><span class="p">(</span><span class="m">3000</span><span class="p">,</span><span class="w"> </span><span class="m">0.01</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">

</span><span class="c1"># Shift function</span><span class="w">
</span><span class="n">shift</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="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">lag</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">xnew</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">lag</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">xnew</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="nf">abs</span><span class="p">(</span><span class="n">lag</span><span class="p">))]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">x</span><span class="p">[(</span><span class="nf">abs</span><span class="p">(</span><span class="n">lag</span><span class="p">)</span><span class="m">+1</span><span class="p">)</span><span class="o">:</span><span class="n">n</span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">lag</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">xnew</span><span class="p">[(</span><span class="n">lag</span><span class="m">+1</span><span class="p">)</span><span class="o">:</span><span class="n">n</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">x</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="n">lag</span><span class="p">)]</span><span class="w">
  </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">xnew</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">x</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="nf">return</span><span class="p">(</span><span class="n">xnew</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Shift time series to get plus1 values</span><span class="w">
</span><span class="n">Nt_plus1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">shift</span><span class="p">(</span><span class="n">Nt</span><span class="p">,</span><span class="w"> </span><span class="m">-1</span><span class="p">)</span><span class="w"> 
</span><span class="n">rseries_plus1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">shift</span><span class="p">(</span><span class="n">rseries</span><span class="p">,</span><span class="w"> </span><span class="m">-1</span><span class="p">)</span><span class="w">

</span><span class="c1"># Plots</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">Nt_plus1</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">Nt</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</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">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"N[t]"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"N[t+1]"</span><span class="p">)</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">rseries_plus1</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">rseries</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</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">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"N[t]"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"N[t+1]"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Looking at the systems in this way, the difference is immediately apparent. The logistic growth system traces a nice, neat curve:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/Poincare_logistic.jpeg" alt="" /></p>

<p>whereas the random numbers just show up as noise:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/Poincare_random.jpeg" alt="" /></p>

<p>Since we’re in the mood for animating things today, we’re going to animate how the Poincaré plot changes as $r$ changes:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">saveGIF</span><span class="p">(</span><span class="n">expr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)){</span><span class="w">
  </span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rseq</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">N0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="n">generations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5000</span><span class="p">)</span><span class="w">
  </span><span class="n">Nt_sub</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Nt</span><span class="p">[</span><span class="m">4000</span><span class="o">:</span><span class="m">5000</span><span class="p">]</span><span class="w">
  </span><span class="n">Nt_plus1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">shift</span><span class="p">(</span><span class="n">Nt_sub</span><span class="p">,</span><span class="w"> </span><span class="m">-1</span><span class="p">)</span><span class="w">
  </span><span class="n">plot</span><span class="p">(</span><span class="n">Nt_plus1</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">Nt_sub</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="m">0</span><span class="o">:</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="m">0</span><span class="o">:</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"N[t]"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"N[t+1]"</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="n">paste</span><span class="p">(</span><span class="s2">"r ="</span><span class="p">,</span><span class="n">rseq</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span><span class="w">
</span><span class="p">},</span><span class="w">
  </span><span class="n">movie.name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"logistic_poincare.gif"</span><span class="p">,</span><span class="w">
  </span><span class="n">img.name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Rplot"</span><span class="p">,</span><span class="w">
  </span><span class="n">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.1</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/logistic_poincare_v2.gif" alt="" /></p>

<p>This provides us with another angle on the bifurcation diagram - we can see the initial equlibrium as a single point which moves diagonally up and right as the equilibrium population size increases, then the series of bifurcations, and then the arc as chaos appears.</p>

<h3 id="recurrence-plots">Recurrence plots</h3>

<p>Another way of exploring the hidden structure of the logistic growth system is by using recurrence plots. The basic idea is for each point in time, we look at the rest of the time series and see which other times have similar values of $N$. The code looks like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Simulate logistic growth</span><span class="w">
</span><span class="n">generations</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">3000</span><span class="w">
</span><span class="n">init</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w">
</span><span class="n">r</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">3.9</span><span class="w">
</span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> 
                      </span><span class="n">N0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init</span><span class="p">,</span><span class="w"> 
                      </span><span class="n">generations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">generations</span><span class="p">)</span><span class="w">
</span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Nt</span><span class="p">[</span><span class="m">2901</span><span class="o">:</span><span class="m">3000</span><span class="p">]</span><span class="w">

</span><span class="c1"># Create matrix to hold recurrence data</span><span class="w">
</span><span class="n">recurrence_mat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">),</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">))</span><span class="w">

</span><span class="n">dist_tolerance</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.01</span><span class="w"> </span><span class="c1"># How similar N has to be to be classed as the same</span><span class="w">

</span><span class="c1"># Fill matrix - 1 if similar, 0 if not</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">)){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">))</span><span class="w">
    </span><span class="k">if</span><span class="p">(</span><span class="nf">abs</span><span class="p">(</span><span class="n">Nt</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">Nt</span><span class="p">[</span><span class="n">j</span><span class="p">])</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">dist_tolerance</span><span class="p">){</span><span class="w">
      </span><span class="n">recurrence_mat</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w">
    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">recurrence_mat</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Visualise</span><span class="w">
</span><span class="n">image</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">recurrence_mat</span><span class="p">),</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nrow</span><span class="p">(</span><span class="n">recurrence_mat</span><span class="p">),</span><span class="w"> </span><span class="n">t</span><span class="p">(</span><span class="n">recurrence_mat</span><span class="p">),</span><span class="w"> 
      </span><span class="n">col</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">"white"</span><span class="p">,</span><span class="w"> </span><span class="s2">"black"</span><span class="p">),</span><span class="w"> 
      </span><span class="n">axes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
      </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">,</span><span class="w">
      </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>We can also do an animated version which cycles through all the values of $r$:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Define r sequence and array to hold values</span><span class="w">
</span><span class="n">rseq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">3</span><span class="p">,</span><span class="w"> </span><span class="m">4</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="m">0.01</span><span class="p">)</span><span class="w">
</span><span class="n">recurrence_array</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">array</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">dim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)))</span><span class="w">

</span><span class="c1"># Fill array</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)){</span><span class="w">
  </span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rseq</span><span class="p">[</span><span class="n">k</span><span class="p">],</span><span class="w"> </span><span class="n">N0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.5</span><span class="p">,</span><span class="w"> </span><span class="n">generations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3000</span><span class="p">)</span><span class="w">
  </span><span class="n">Nt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Nt</span><span class="p">[</span><span class="m">2801</span><span class="o">:</span><span class="m">3000</span><span class="p">]</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">)){</span><span class="w">
    </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">))</span><span class="w">
      </span><span class="k">if</span><span class="p">(</span><span class="nf">abs</span><span class="p">(</span><span class="n">Nt</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">Nt</span><span class="p">[</span><span class="n">j</span><span class="p">])</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">dist_tolerance</span><span class="p">){</span><span class="w">
        </span><span class="n">recurrence_array</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">,</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w">
      </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="n">recurrence_array</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">,</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0</span><span class="w">
      </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="c1"># Save gif to working directory</span><span class="w">
</span><span class="n">saveGIF</span><span class="p">(</span><span class="n">expr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">rseq</span><span class="p">)){</span><span class="w">
  </span><span class="n">image</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">recurrence_array</span><span class="p">[,,</span><span class="n">i</span><span class="p">]),</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nrow</span><span class="p">(</span><span class="n">recurrence_array</span><span class="p">[,,</span><span class="n">i</span><span class="p">]),</span><span class="w"> </span><span class="n">t</span><span class="p">(</span><span class="n">recurrence_array</span><span class="p">[,,</span><span class="n">i</span><span class="p">]),</span><span class="w"> 
        </span><span class="n">col</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">"white"</span><span class="p">,</span><span class="w"> </span><span class="s2">"black"</span><span class="p">),</span><span class="w"> 
        </span><span class="n">axes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
        </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">,</span><span class="w">
        </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">,</span><span class="w">
        </span><span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">paste</span><span class="p">(</span><span class="s2">"r="</span><span class="p">,</span><span class="n">rseq</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="n">movie.name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"recurrence.gif"</span><span class="p">,</span><span class="w">
</span><span class="n">img.name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Rplot"</span><span class="p">,</span><span class="w">
</span><span class="n">interval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.2</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/recurrence_v5.gif" alt="" /></p>

<p>The plot starts out with a checkerboard pattern as the system is still oscillating between two points (meaning that every 2nd time will be the same) - we then see a variety of interesting structures appear as the system descends into chaos. Later on we briefly see other checkerboards for the period-3 oscillation windows which we can see in the bifurcation diagram, then more chaos again.</p>

<p>In comparison, when we apply the same method to a completely random time-series, the off-diagonals of the plot just look like noise:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">randseries</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">runif</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">),</span><span class="w"> </span><span class="m">0.01</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
</span><span class="n">randmat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">),</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="nf">length</span><span class="p">(</span><span class="n">Nt</span><span class="p">))</span><span class="w">

</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">randseries</span><span class="p">)){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">randseries</span><span class="p">))</span><span class="w">
    </span><span class="k">if</span><span class="p">(</span><span class="nf">abs</span><span class="p">(</span><span class="n">randseries</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">randseries</span><span class="p">[</span><span class="n">j</span><span class="p">])</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">dist_tolerance</span><span class="p">){</span><span class="w">
      </span><span class="n">randmat</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w">
    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">randmat</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">image</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">ncol</span><span class="p">(</span><span class="n">randmat</span><span class="p">),</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nrow</span><span class="p">(</span><span class="n">randmat</span><span class="p">),</span><span class="w"> </span><span class="n">t</span><span class="p">(</span><span class="n">randmat</span><span class="p">),</span><span class="w"> 
      </span><span class="n">col</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">"white"</span><span class="p">,</span><span class="w"> </span><span class="s2">"black"</span><span class="p">),</span><span class="w"> 
      </span><span class="n">axes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
      </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">,</span><span class="w">
      </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/random_recurrence.jpeg" alt="" /></p>

<h2 id="simulating-the-observed-data">Simulating the observed data</h2>

<p>Now that we’ve explored some of the amazing features of the logistic growth model, we’re nearly ready to fit the model to data using Stan - but first, we have to simulate some data! We can do this using the following code:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">generations</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">10000</span><span class="w">
</span><span class="n">init</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.7</span><span class="w">
</span><span class="n">r</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">3.9</span><span class="w">
</span><span class="n">N_true</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> 
                          </span><span class="n">N0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init</span><span class="p">,</span><span class="w"> 
                          </span><span class="n">generations</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">generations</span><span class="p">)</span><span class="w">

</span><span class="n">N_obs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rlnorm</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="n">N_true</span><span class="p">),</span><span class="w"> </span><span class="nf">log</span><span class="p">(</span><span class="n">N_true</span><span class="p">),</span><span class="w"> </span><span class="m">0.01</span><span class="p">),</span><span class="w"> </span><span class="m">0.05</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Plotting the actual versus observed values for the first 100 time steps, we can see that the two are closely-matched:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plot</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="n">N_true</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">100</span><span class="p">],</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"N"</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">N_obs</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">16</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/observed1.jpeg" alt="" /></p>

<p>We can then put the data into a list for Stan:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dlist</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">n_times</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.integer</span><span class="p">(</span><span class="n">generations</span><span class="p">),</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">N_obs</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h2 id="how-not-to-fit-the-model-in-stan">How NOT to fit the model in Stan</h2>

<p>My first idea was to try to adapt <a href="https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth/#coding-the-model-in-stan-1">the code for the continuous-time logistic growth model,</a> which leads to something like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">int</span><span class="w"> </span><span class="n">times</span><span class="p">,</span><span class="w">
                         </span><span class="n">real</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w">
                         </span><span class="n">real</span><span class="w"> </span><span class="n">r</span><span class="p">){</span><span class="w">
    </span><span class="n">vector</span><span class="p">[</span><span class="n">times</span><span class="p">]</span><span class="w"> </span><span class="n">n</span><span class="p">;</span><span class="w">
    
    </span><span class="n">n</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w">
    
    </span><span class="k">for</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">times</span><span class="m">-1</span><span class="p">)){</span><span class="w">
      </span><span class="n">n</span><span class="p">[</span><span class="n">t</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n</span><span class="p">[</span><span class="n">t</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">n</span><span class="p">[</span><span class="n">t</span><span class="p">]</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">n</span><span class="p">[</span><span class="n">t</span><span class="p">]);</span><span class="w">
    </span><span class="p">}</span><span class="w">
    </span><span class="n">return</span><span class="w"> </span><span class="n">n</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">data</span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_times</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">steps</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="n">after</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y0_obs</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">parameters</span><span class="p">{</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Per</span><span class="o">-</span><span class="n">capita</span><span class="w"> </span><span class="n">rate</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">population</span><span class="w"> </span><span class="n">growth</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Initial</span><span class="w"> </span><span class="n">condition</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">sigma</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Standard</span><span class="w"> </span><span class="n">deviation</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">observation</span><span class="w"> </span><span class="n">model</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">mu</span><span class="p">;</span><span class="w">
  
  </span><span class="o">//</span><span class="w"> </span><span class="n">Difference</span><span class="w"> </span><span class="n">equation</span><span class="w">
  </span><span class="n">mu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">n_times</span><span class="p">,</span><span class="w"> </span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">model</span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">uniform</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">3</span><span class="p">);</span><span class="w">
  </span><span class="n">y0</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">beta</span><span class="p">(</span><span class="m">4</span><span class="p">,</span><span class="m">4</span><span class="p">);</span><span class="w">
  </span><span class="n">sigma</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">3</span><span class="p">);</span><span class="w">
  
  </span><span class="o">//</span><span class="w"> </span><span class="n">Observation</span><span class="w"> </span><span class="n">model</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">lognormal</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">y0</span><span class="p">),</span><span class="w"> </span><span class="n">sigma</span><span class="p">);</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">lognormal</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">mu</span><span class="p">),</span><span class="w"> </span><span class="n">sigma</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">

  </span><span class="n">y</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">lognormal</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="n">mu</span><span class="p">),</span><span class="w"> </span><span class="n">sigma</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>As I soon found out, this is not a good idea.</p>

<p>When we run the model, we are warned of divergent transitions and transitions hitting the treedepth limit:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/warning2.jpg" alt="" /></p>

<p>Checking the model diagnostics using the <code class="language-plaintext highlighter-rouge">dashboard</code> function, we see that things are not looking great. In particular, we’re seeing a very poor R-hat and number of effective samples for several parameters:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/dashboard3.jpeg" alt="" /></p>

<p>If we look at the model summary table using the <code class="language-plaintext highlighter-rouge">precis</code> command, we can see that one of these parameters is the initial condition, <code class="language-plaintext highlighter-rouge">y0</code>.</p>

<p>Heading over to the model’s traceplots, things are also looking bad. The chains for the initial condition, <code class="language-plaintext highlighter-rouge">y0</code>, each ended up in different modes. We know (since we simulated the data) that none of these modes is at the correct value (which is 0.7). The chains for <code class="language-plaintext highlighter-rouge">r</code>, on the other hand, have converged - but again at the wrong value!</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/traceplot_bad.jpeg" alt="" /></p>

<p>Examining the pairs plot for these parameters along with the log-posterior (<code class="language-plaintext highlighter-rouge">lp__</code>) gives us an alternative view:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/pairs_bad.jpeg" alt="" /></p>

<p>We can again see the multiple modes for <code class="language-plaintext highlighter-rouge">y0</code>, and that these all sit at similar values for <code class="language-plaintext highlighter-rouge">lp___</code>.</p>

<h2 id="changing-perspective-a-better-way-to-fit-the-model">Changing perspective: a better way to fit the model</h2>

<p>I spent a long time debugging the previous model, but without success. Perusing the <a href="https://discourse.mc-stan.org/">Stan forums</a>, it seemed to me that fitting a model to a chaotic system like this one was unlikely to work.</p>

<p>However, the model exploration we conducted earlier in this post suggests a better way forward: <strong>we fit a model to the Poincaré plot</strong>.</p>

<p>This completely sidesteps the issue of estimating the initial condition <code class="language-plaintext highlighter-rouge">y0</code>, because the shape of the Poincaré plot does not depend on the initial conditions. We can see this if we overlay the plots for different initial values (here I’ve gone for 0.1, 0.5, and 0.9) - the points all fall along the same curve:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/poincare_init.jpeg" alt="" /></p>

<p>The equation for this curve is just the logistic model:</p>

\[N[t+1] = rN[t](1-N[t])\]

<p>i.e.,</p>

\[y = rx(1-x)\]

<p>This is the model we’re going to fit to our data.</p>

<p>Plotting the observed data, we can see the points fall around the curve:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/observed_poincare.jpeg" alt="" /></p>

<p>For simplicity, I’m going to assume the distance from each point to the curve follows a normal distribution. There are certainly better options here, but it’ll do for now.</p>

<p>The Stan code to fit the model looks like:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">data</span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">N</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">observations</span><span class="w"> </span><span class="p">(</span><span class="n">i.e.</span><span class="p">,</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">steps</span><span class="p">)</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">N</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">x</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">N</span><span class="p">(</span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="n">observed</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">N</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">N</span><span class="p">(</span><span class="n">t</span><span class="m">+1</span><span class="p">)</span><span class="w"> </span><span class="n">observed</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">parameters</span><span class="p">{</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Per</span><span class="o">-</span><span class="n">capita</span><span class="w"> </span><span class="n">rate</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">population</span><span class="w"> </span><span class="n">growth</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">sigma</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Standard</span><span class="w"> </span><span class="n">deviation</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">observation</span><span class="w"> </span><span class="n">model</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">model</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">N</span><span class="p">]</span><span class="w"> </span><span class="n">mu</span><span class="p">;</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">uniform</span><span class="p">(</span><span class="m">3.5</span><span class="p">,</span><span class="w"> </span><span class="m">3.99</span><span class="p">);</span><span class="w">
  </span><span class="n">sigma</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">4</span><span class="p">);</span><span class="w">
  
  </span><span class="o">//</span><span class="w"> </span><span class="n">Likelihood</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">N</span><span class="p">){</span><span class="w">
    </span><span class="n">mu</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="m">1</span><span class="o">-</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="n">mu</span><span class="p">,</span><span class="w"> </span><span class="n">sigma</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>We can fit the model using the <code class="language-plaintext highlighter-rouge">cstan</code> function in the <code class="language-plaintext highlighter-rouge">rethinking</code> package:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Need to discard final observation to avoid having an NA value</span><span class="w">
</span><span class="n">dlist</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">N</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">N_obs</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1L</span><span class="p">,</span><span class="w">
              </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">N_obs</span><span class="p">[</span><span class="o">-</span><span class="nf">length</span><span class="p">(</span><span class="n">N_obs</span><span class="p">)],</span><span class="w">
              </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">N_obs_plus1</span><span class="p">[</span><span class="o">-</span><span class="nf">length</span><span class="p">(</span><span class="n">N_obs_plus1</span><span class="p">)])</span><span class="w">

</span><span class="n">m1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cstan</span><span class="p">(</span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:/Stan_code/poincare_model.stan"</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">dlist</span><span class="p">,</span><span class="w"> 
            </span><span class="n">chains</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="n">cores</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="n">warmup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1500</span><span class="p">,</span><span class="w">
            </span><span class="n">iter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2500</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Looking at the <code class="language-plaintext highlighter-rouge">precis</code> output and traceplots (not shown) we can see that the model appears to have sampled well, although the number of effective samples <code class="language-plaintext highlighter-rouge">n_eff</code> for <code class="language-plaintext highlighter-rouge">sigma</code> is a fair bit lower:</p>

<p><img src="/assets/images/post_images/discrete_logistic_growth/precis_good.jpg" alt="" /></p>

<p>Looking at the posterior distribution for <code class="language-plaintext highlighter-rouge">r</code>, we can see it is very close to the true value:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">post</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">extract.samples</span><span class="p">(</span><span class="n">m1</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/post_good.jpeg" alt="" /></p>

<p>We can also plot the model’s posterior predictions (as median and compatability intervals) using the following code:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#Define sequence of x values</span><span class="w">
</span><span class="n">x_seq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="nf">min</span><span class="p">(</span><span class="n">N_obs</span><span class="p">),</span><span class="nf">max</span><span class="p">(</span><span class="n">N_obs</span><span class="p">),</span><span class="m">0.01</span><span class="p">)</span><span class="w">

</span><span class="c1"># Posterior distribution of mean</span><span class="w">
</span><span class="n">r_link</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x_seq</span><span class="p">),</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">))</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">x_seq</span><span class="p">)){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">)){</span><span class="w">
    </span><span class="n">r_link</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">[</span><span class="n">j</span><span class="p">]</span><span class="o">*</span><span class="n">x_seq</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="m">1</span><span class="o">-</span><span class="n">x_seq</span><span class="p">[</span><span class="n">i</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="c1"># Show posterior median on plot</span><span class="w">
</span><span class="n">mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">r_link</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">median</span><span class="p">)</span><span class="w">

</span><span class="c1"># Simulate distribution of observed values using sigma</span><span class="w">
</span><span class="n">r_sim</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x_seq</span><span class="p">),</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">))</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">x_seq</span><span class="p">)){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">)){</span><span class="w">
    </span><span class="n">r_sim</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">r_link</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">],</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">sigma</span><span class="p">[</span><span class="n">j</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">sim_mu99CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">r_sim</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">HPDI</span><span class="p">,</span><span class="n">prob</span><span class="o">=</span><span class="m">0.99</span><span class="p">)</span><span class="w">
</span><span class="n">sim_mu95CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">r_sim</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">HPDI</span><span class="p">,</span><span class="n">prob</span><span class="o">=</span><span class="m">0.95</span><span class="p">)</span><span class="w">
</span><span class="n">sim_mu89CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">r_sim</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">HPDI</span><span class="p">,</span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">
</span><span class="n">sim_mu80CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">r_sim</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">HPDI</span><span class="p">,</span><span class="n">prob</span><span class="o">=</span><span class="m">0.80</span><span class="p">)</span><span class="w">
</span><span class="n">sim_mu70CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">r_sim</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">HPDI</span><span class="p">,</span><span class="n">prob</span><span class="o">=</span><span class="m">0.70</span><span class="p">)</span><span class="w">

</span><span class="c1"># Make the plot</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1.1</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1.1</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"N(t)"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"N(t+1)"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">mu</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">sim_mu99CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">sim_mu95CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">sim_mu89CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">sim_mu80CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">sim_mu70CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="c1"># Plot subset of the real data to assess fit</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">N_obs_plus1</span><span class="p">[</span><span class="m">2000</span><span class="o">:</span><span class="m">2500</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">N_obs</span><span class="p">[</span><span class="m">2000</span><span class="o">:</span><span class="m">2500</span><span class="p">],</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">1</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/discrete_logistic_growth/post_check.jpeg" alt="" /></p>

<p>We can see that the model generally fits the observed data quite well - although on right hand side the observed data are more dispersed than the model predicts, while on the left the opposite is true. This suggests that our observation model could be improved.</p>

<p>Although it’s certainly a vast improvement on the first attempt, this model is by no means perfect. For one thing, it ignores the fact that the $x$ variable, $N(t)$, is measured with error - I suspect this is why the model tends to underestimate <code class="language-plaintext highlighter-rouge">r</code> when <code class="language-plaintext highlighter-rouge">sigma</code> is high. There are probably much better options than a normal distribution with a static <code class="language-plaintext highlighter-rouge">sigma</code> value for the $y$ observation model - in our posterior predictive check we can see that the points are more highly spread on the right than the left. I suspect that this is why the model struggled slightly to estimate <code class="language-plaintext highlighter-rouge">sigma</code> (as evidenced by the lower <code class="language-plaintext highlighter-rouge">n_eff</code> value). I also suspect (although have not tested) that the model will perform poorly if $N$ is either stable or orscillating between two or three states, since then we’ll be fitting a curve to just a few clusters of data points.</p>

<p>However, despite the model’s imperfections I still see it as an instructive example which illustrates how it can be useful to view a problem from a different perspective.</p>]]></content><author><name>Peter S. Stewart</name></author><category term="blog" /><category term="Stan" /><category term="tutorial" /><category term="classic ecological models" /><summary type="html"><![CDATA[In this post, I explore the discrete-time logistic growth model in R (including various animations), and discuss the challenges of fitting the model to data in Stan.]]></summary></entry><entry><title type="html">On placement with the Scottish Invasive Species Initiative</title><link href="https://peter-stewart.github.io/blog/SISI-placement/" rel="alternate" type="text/html" title="On placement with the Scottish Invasive Species Initiative" /><published>2022-07-04T00:00:00+00:00</published><updated>2022-07-04T00:00:00+00:00</updated><id>https://peter-stewart.github.io/blog/SISI-placement</id><content type="html" xml:base="https://peter-stewart.github.io/blog/SISI-placement/"><![CDATA[<p>In April and May this year I was on placement with the Scottish Invasive Species Initiative (SISI). I’ve written a post about what I got up to for the SISI blog, which you can find <a href="https://invasivespeciesscot.home.blog/2022/06/30/my-placement-with-the-scottish-invasive-species-initiative/">here</a>.</p>

<p>Team photo on the Deveron - I am the one with the cool hat + sunglasses combo 😎
<img src="/assets/images/post_images/SISI_placement/spraying.jpg" alt="" /></p>]]></content><author><name>Peter S. Stewart</name></author><category term="blog" /><category term="invasive species" /><summary type="html"><![CDATA[In April and May this year I was on placement with the Scottish Invasive Species Initiative (SISI). I’ve written a post about what I got up to for the SISI blog, which you can find here.]]></summary></entry><entry><title type="html">Classic ecological models in Stan: two-species Levins metapopulation model (mutualism version)</title><link href="https://peter-stewart.github.io/blog/classic-ecological-models-levins-metapopulation-twosp-mutual/" rel="alternate" type="text/html" title="Classic ecological models in Stan: two-species Levins metapopulation model (mutualism version)" /><published>2022-06-27T00:00:00+00:00</published><updated>2022-06-27T00:00:00+00:00</updated><id>https://peter-stewart.github.io/blog/classic-ecological-models-levins-metapopulation-twosp-mutual</id><content type="html" xml:base="https://peter-stewart.github.io/blog/classic-ecological-models-levins-metapopulation-twosp-mutual/"><![CDATA[<p>In this post, I cover how to fit a version of the two-species Levins metapopulation model in Stan, as well as how to use R to simulate data to fit the model to.</p>

<h2 id="acknowledgements-and-other-resources">Acknowledgements and other resources</h2>

<p>I relied on several resources when writing this post.</p>

<p>The first is Levins’ <a href="https://academic.oup.com/ae/article-abstract/15/3/237/255899">classic paper</a> which introduced the single species metapopulation model. I also found <a href="https://core.ac.uk/download/pdf/29299949.pdf">this paper</a> by Rampal Etienne to be very useful. The main resource that I used for the two-species metapopulation model was <a href="https://oxford.universitypressscholarship.com/view/10.1093/oso/9780199209989.001.0001/isbn-9780199209989">Theoretical Ecology: Principles and Applications</a>, edited by Robert May and Angela McLean - chapter 4, <em>metapopulations and their spatial dynamics</em> by Sean Nee, is the relevant chapter.</p>

<p>I learned about Stan and Bayesian stats in general from Richard McElreath’s book and course <a href="https://github.com/rmcelreath/stat_rethinking_2022">Statistical Rethinking</a>, Michael Betancourt’s <a href="https://betanalpha.github.io/assets/case_studies/stan_intro.html">introduction to Stan</a>, the <a href="https://mc-stan.org/docs/2_29/stan-users-guide/ode-solver.html">Stan manual</a>, <a href="https://mc-stan.org/users/documentation/case-studies/convert_odes.html">this post</a> about (relatively) recent changes to the ODE interface and <a href="https://mpopov.com/tutorials/ode-stan-r/">this tutorial</a> by Mikhail Popov.</p>

<p>I also learned a lot about modeling from the book <a href="https://press.princeton.edu/books/hardcover/9780691123448/a-biologists-guide-to-mathematical-modeling-in-ecology-and-evolution">a biologist’s guide to mathematical modeling in ecology and evolution</a> by Sarah Otto and Troy Day.</p>

<h2 id="disclaimer">Disclaimer</h2>

<p>I’m mainly writing this post because a) it’s fun and b) it helps me to learn more about the material - if people find it helpful, then that’s even better!</p>

<p>Please bear in mind that there might be mistakes lurking in this post - if you spot any, I’d appreciate if you let me know via email (my address is in the sidebar) or in the comments below. As usual, use of the model etc. is at your own risk.</p>

<h2 id="introducing-the-models">Introducing the models</h2>
<h3 id="levins-single-species-metapopulation-model">Levins’ single-species metapopulation model</h3>

<p>Imagine a landscape where there are patches of habitat, each occupied by a population of a species. As some of these populations go extinct patches become empty, and empty patches are colonised by new populations which establish when individuals disperse in from occupied patches. We can study how the population of these populations - the <strong>metapopulation</strong> - rises and falls over time.</p>

<p>In Levins’ <a href="https://academic.oup.com/ae/article-abstract/15/3/237/255899">classic paper</a>, he describes the changing metapopulation using the following equation:</p>

\[\frac{dN}{dt} = MN\left(1 - \frac{N}{T} \right) - EN\]

<p>where $N$ is the number of occupied patches, $M$ is the migration (or colonisation) rate, $E$ is the extinction rate, and $T$ is the total number of occupiable patches in the landscape.</p>

<p>Looking at the model, it seems oddly familiar… If we sub in a new parameter $r$ which represents the difference between the migration and extinction rates, i.e. $r = M - E$, we see why:</p>

\[\frac{dN}{dt} = rN\left(1 - \frac{N}{T} \right)\]

<p>This is just <a href="https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth/#introducing-the-model-1">the logistic population growth model that we looked at in the last post!</a> The only difference is that what used to be called the carrying capacity $K$ is now called the total number of occupiable patches $T$.</p>

<p>Since we’ve already looked at this model, we’re going to move on to look at one of the ways that the classic metapopulation model can be extended to two species.</p>

<h3 id="two-species-metapopulation-model---mutualism-version">Two-species metapopulation model - mutualism version</h3>

<p>There are a variety of ways that the single-species metapopulation model can be extended to two species - several different extensions are described by Sean Nee in chapter 4 of <em>Theoretical Ecology: Principles and Applications</em>. In one version, the two species are mutualists: one species (the “plant”) can exist alone on a patch, but must be dispersed to new patches by the other species (the “disperser”). The disperser cannot survive on a patch by itself, and can only inhabit patches which are occupied by the plant.</p>

<p>Here is the two-species mutualism model as described in ch. 4 of <em>Theoretical Ecology</em>:</p>

\[\frac{dx}{dt} = e_{p}y + e_{d}z - c_{p}zx\\
\frac{dy}{dt} = c_{p}zx - c_{d}zy - e_{p}y\\
\frac{dz}{dt} = c_{d}zy - e{d}z\]

<p>Although it’s a two species model, we’re actually tracking the changes in three variables - the proportion of empty sites ($x$), sites with only the plant ($y$), and sites with both the plant and disperser ($z$). We also have two colonisation rate parameters ($c_p$ and $c_d$) and two extinction rate parameters ($e_p$ and $e_d$) which control the shifts between the system’s states.</p>

<p>I am a fan of Otto &amp; Day’s technique of representing ODE’s as flow diagrams, where each variable is represented as a node (circle) and the flows between them are represented as arrows. This is the flow diagram corresponding to the equations above:</p>

<p><img src="/assets/images/post_images/levins_twosp_mutual/flow_diagram.png" alt="" /></p>

<p>I find that this diagram makes some of the model’s assumptions much clearer. First, there is no arrow going directly from the “empty” node to the “plant &amp; disperser” node. This means that empty patches have to be colonised by the plant before the disperser is able to colonise too - a patch can’t go from empty to containing both species in a single leap.</p>

<p>Second, the flow of patches from empty to the plant-only state is $c_{p}zx$. Notice that $y$ isn’t involved, meaning that if there are no patches with both the plant and disperser, then no new patches can be colonised by the plant - without the disperser, the plant will go extinct!</p>

<p>Finally, there isn’t an arrow from the “plant &amp; disperser” node back to the “plant only” node - this means that once patches contain both species, they never lose only the disperser and return to a plant-only state. Thus, $e_{d}$ really represents the extinction rate of patches with the plant-disperser pair.</p>

<h2 id="simulating-the-two-species-model-in-r">Simulating the two-species model in R</h2>

<p>Let’s start by loading a couple of packages and setting the seed for the random number generator:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">rethinking</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">deSolve</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">plotly</span><span class="p">)</span><span class="w"> </span><span class="c1"># only needed for 3d phase diagram plot</span><span class="w">
</span><span class="n">set.seed</span><span class="p">(</span><span class="m">946</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Now we’re going to write the function which contains our differential equations:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">twosp_mutual</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="n">times</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">parms</span><span class="p">){</span><span class="w">
  </span><span class="n">X</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w">
  </span><span class="n">Y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w">
  </span><span class="n">Z</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w">
  
  </span><span class="n">with</span><span class="p">(</span><span class="n">as.list</span><span class="p">(</span><span class="n">p</span><span class="p">),{</span><span class="w">
    </span><span class="n">dx.dt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ep</span><span class="o">*</span><span class="n">Y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">ed</span><span class="o">*</span><span class="n">Z</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">cp</span><span class="o">*</span><span class="n">Z</span><span class="o">*</span><span class="n">X</span><span class="w">
    </span><span class="n">dy.dt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cp</span><span class="o">*</span><span class="n">Z</span><span class="o">*</span><span class="n">X</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">cd</span><span class="o">*</span><span class="n">Z</span><span class="o">*</span><span class="n">Y</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">ep</span><span class="o">*</span><span class="n">Y</span><span class="w">
    </span><span class="n">dz.dt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cd</span><span class="o">*</span><span class="n">Z</span><span class="o">*</span><span class="n">Y</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">ed</span><span class="o">*</span><span class="n">Z</span><span class="w">
    
    </span><span class="nf">return</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">dx.dt</span><span class="p">,</span><span class="w"> </span><span class="n">dy.dt</span><span class="p">,</span><span class="w"> </span><span class="n">dz.dt</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></code></pre></div></div>

<p>After this, we have to pick values for our extinction and colonisation rate parameters - there’s no particular reason for choosing the ones that I did, and you can play around with other values if you like. We also have to pick values for our initial conditions - I went for 90% empty patches, no patches with plants only, and 10% patches with both species. Finally, we have to pick a time sequence for our simulation:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Extinction and colonisation parameters</span><span class="w">
</span><span class="n">ep</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="c1"># Plant extinction rate</span><span class="w">
</span><span class="n">ed</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w"> </span><span class="c1"># Disperser extinction rate</span><span class="w">
</span><span class="n">cp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">4</span><span class="w"> </span><span class="c1"># Plant colonisation rate</span><span class="w">
</span><span class="n">cd</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">5</span><span class="w"> </span><span class="c1"># Disperser colonisation rate</span><span class="w">

</span><span class="n">p</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="n">ep</span><span class="o">=</span><span class="n">ep</span><span class="p">,</span><span class="w"> </span><span class="n">ed</span><span class="o">=</span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="n">cp</span><span class="o">=</span><span class="n">cp</span><span class="p">,</span><span class="w"> </span><span class="n">cd</span><span class="o">=</span><span class="n">cd</span><span class="p">)</span><span class="w"> </span><span class="c1"># Make a vector of all of the parameters</span><span class="w">

</span><span class="c1"># Initial conditions - most patches empty, small proportion have both species</span><span class="w">
</span><span class="n">y0</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="n">X</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">Y</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">Z</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0.1</span><span class="p">)</span><span class="w">

</span><span class="c1"># Time sequence</span><span class="w">
</span><span class="n">time</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">,</span><span class="n">by</span><span class="o">=</span><span class="m">0.1</span><span class="p">)</span><span class="w">

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

<p>Now we’ve chosen our values, we can use the <code class="language-plaintext highlighter-rouge">ode</code> function in the <code class="language-plaintext highlighter-rouge">deSolve</code> package to get our metapopulation’s true state at each time step:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">state_true</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ode</span><span class="p">(</span><span class="n">y</span><span class="o">=</span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">times</span><span class="o">=</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">func</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">twosp_mutual</span><span class="p">,</span><span class="w"> </span><span class="n">parms</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">p</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>We can now visualise how the proportion of patches which are in each state changes over time:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">3</span><span class="p">))</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">X</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">state_true</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Proportion of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Empty"</span><span class="p">)</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">Y</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">state_true</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Proportion of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant only"</span><span class="p">)</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">Z</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">state_true</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Proportion of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant &amp; Disperser"</span><span class="p">)</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/state_true.jpeg" alt="" /></p>

<p>We can see that the system eventually reaches an equilibrium where the majority of patches are occupied by both the plant and disperser, and a smaller proportion are either empty or contain only the plant.</p>

<p>Just for fun, we can plot the state of the whole system over time as a 3D phase-space diagram - this code will produce an interactive plot which you can click and drag to rotate in the plot viewer:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fig</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">plot_ly</span><span class="p">(</span><span class="n">as.data.frame</span><span class="p">(</span><span class="n">state_true</span><span class="p">),</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">X</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">Y</span><span class="p">,</span><span class="w"> </span><span class="n">z</span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">Z</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"scatter3d"</span><span class="p">,</span><span class="w"> </span><span class="n">mode</span><span class="o">=</span><span class="s2">"lines"</span><span class="p">,</span><span class="w">
               </span><span class="n">line</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">list</span><span class="p">(</span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">~</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">reverscale</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">,</span><span class="w"> </span><span class="n">colorscale</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Viridis"</span><span class="p">))</span><span class="w">
</span><span class="n">fig</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/phase_diagram.png" alt="" /></p>

<p>We can see the system track a path from the initial conditions in the bottom left, to the final equilibrium in the top right.</p>

<p>Now that we’ve simulated the system’s true underlying state, we can simulate the observation processes which will produce the actual data that we’re going to fit the model to. Since my main focus here was on learning about coding a model with more than one ODE, I went for a relatively simple observation process. The idea is we observe a finite number of sites (I went for 1000) and the number of sites in each state at a given time follows a multinomial distribution where the probabilities of each state are the true proportions contained in <code class="language-plaintext highlighter-rouge">state_true</code>. The code goes like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">  </span><span class="n">n_sites</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1000</span><span class="w"> </span><span class="c1"># Number of sites observed</span><span class="w">
  
  </span><span class="n">counts_obs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">time</span><span class="p">),</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="p">)</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">time</span><span class="p">)){</span><span class="w">
    </span><span class="n">counts_obs</span><span class="p">[</span><span class="n">i</span><span class="p">,]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rmultinom</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">1</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_sites</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">state_true</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="o">:</span><span class="m">4</span><span class="p">])</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The result is a matrix, <code class="language-plaintext highlighter-rouge">counts_obs</code>, which holds the number of sites in each state (empty, plant only, plant &amp; disperser) at each time step.</p>

<p>We can plot the observed data on top of our true state lines from above like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">3</span><span class="p">))</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">X</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">state_true</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Proportion of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Empty"</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="o">=</span><span class="n">counts_obs</span><span class="p">[,</span><span class="m">1</span><span class="p">]</span><span class="o">/</span><span class="n">n_sites</span><span class="p">)</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">Y</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">state_true</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Proportion of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant only"</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="o">=</span><span class="n">counts_obs</span><span class="p">[,</span><span class="m">2</span><span class="p">]</span><span class="o">/</span><span class="n">n_sites</span><span class="p">)</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">Z</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">state_true</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Proportion of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant &amp; Disperser"</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="o">=</span><span class="n">counts_obs</span><span class="p">[,</span><span class="m">3</span><span class="p">]</span><span class="o">/</span><span class="n">n_sites</span><span class="p">)</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/observed_data.jpeg" alt="" /></p>

<p>Finally, we can prepare our data list for Stan. Just like in the <a href="https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth/#simulating-the-data-in-r">post on exponential and logistic population growth</a>, we have to separate the initial time step from the rest of the time series - this is because of the way that Stan indexes time steps. The code is:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dlist</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">n_times</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">time</span><span class="p">)</span><span class="m">-1L</span><span class="p">,</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">counts_obs</span><span class="p">[</span><span class="m">1</span><span class="p">,],</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">counts_obs</span><span class="p">[</span><span class="m">-1</span><span class="p">,],</span><span class="w">
  </span><span class="n">t0</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">ts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">[</span><span class="m">-1</span><span class="p">],</span><span class="w">
  </span><span class="n">n_sites</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.integer</span><span class="p">(</span><span class="n">n_sites</span><span class="p">)</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h2 id="coding-the-model-in-stan">Coding the model in Stan</h2>

<p>Now that we’ve simulated the data, we can code the Stan model!</p>

<p>Here is the full model:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="w"> </span><span class="n">exp_growth</span><span class="p">(</span><span class="n">real</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w">
                    </span><span class="n">vector</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">State</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">ep</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">cp</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">cd</span><span class="p">){</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
    </span><span class="n">vector</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">dndt</span><span class="p">;</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ep</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">ed</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">cp</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">];</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">patches</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cp</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">cd</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">ep</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">];</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="o">-</span><span class="n">only</span><span class="w"> </span><span class="n">patches</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cd</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">ed</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">];</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Both</span><span class="w"> </span><span class="n">species</span><span class="w"> </span><span class="n">patches</span><span class="w">
    </span><span class="n">return</span><span class="w"> </span><span class="n">dndt</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">data</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_times</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">steps</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">one</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">,</span><span class="w"> </span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y0_obs</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">t0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">First</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">step</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">ts</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w"> </span><span class="n">steps</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_sites</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">sites</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">parameters</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">simplex</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">y0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Initial</span><span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="p">(</span><span class="n">must</span><span class="w"> </span><span class="n">sum</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ep</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w"> 
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ed</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cp</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cd</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
    </span><span class="o">//</span><span class="w"> </span><span class="n">ODE</span><span class="w"> </span><span class="n">solver</span><span class="w">
  </span><span class="n">simplex</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">theta</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ode_rk45</span><span class="p">(</span><span class="n">exp_growth</span><span class="p">,</span><span class="w"> </span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">t0</span><span class="p">,</span><span class="w"> </span><span class="n">ts</span><span class="p">,</span><span class="w"> </span><span class="n">ep</span><span class="p">,</span><span class="w"> </span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="n">cp</span><span class="p">,</span><span class="w"> </span><span class="n">cd</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">model</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">ep</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">ed</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">cp</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">cd</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">

  </span><span class="n">y0</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">dirichlet</span><span class="p">([</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">]);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Initial</span><span class="w"> </span><span class="n">state</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Observation</span><span class="w"> </span><span class="n">model</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">multinomial</span><span class="p">(</span><span class="n">y0</span><span class="p">);</span><span class="w"> 
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="p">[</span><span class="n">t</span><span class="p">,]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">multinomial</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="n">t</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">generated</span><span class="w"> </span><span class="n">quantities</span><span class="p">{</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="m">+1</span><span class="p">,</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">n_sim</span><span class="p">;</span><span class="w">
  </span><span class="n">n_sim</span><span class="p">[</span><span class="m">1</span><span class="p">,]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">multinomial_rng</span><span class="p">(</span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">n_sites</span><span class="p">);</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">){</span><span class="w">
    </span><span class="n">n_sim</span><span class="p">[</span><span class="n">t</span><span class="m">+1</span><span class="p">,]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">multinomial_rng</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="n">t</span><span class="p">],</span><span class="w"> </span><span class="n">n_sites</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></code></pre></div></div>

<p>Now let’s break it down block by block:</p>

<h3 id="the-functions-block">The functions block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="w"> </span><span class="n">exp_growth</span><span class="p">(</span><span class="n">real</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w">
                    </span><span class="n">vector</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">State</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">ep</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">cp</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">cd</span><span class="p">){</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
    </span><span class="n">vector</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">dndt</span><span class="p">;</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ep</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">ed</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">cp</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">];</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">patches</span><span class="w"> </span><span class="p">(</span><span class="n">X</span><span class="p">)</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cp</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">cd</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">ep</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">];</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="o">-</span><span class="n">only</span><span class="w"> </span><span class="n">patches</span><span class="w"> </span><span class="p">(</span><span class="n">Y</span><span class="p">)</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cd</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">ed</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">3</span><span class="p">];</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Both</span><span class="w"> </span><span class="n">species</span><span class="w"> </span><span class="n">patches</span><span class="w"> </span><span class="p">(</span><span class="n">Z</span><span class="p">)</span><span class="w">
    </span><span class="n">return</span><span class="w"> </span><span class="n">dndt</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></code></pre></div></div>

<p>This is one of the key bits of our model - it’s where we actually code the differential equations for our model. In contrast to <a href="https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth/#the-functions-block">my last post looking at the exponential and logistic models of population growth</a>, we don’t just have a single equation - we have three, describing how the proportion of empty / plant only / plant and disperser patches changes over time.</p>

<p>The great news is that it’s very simple to extend our code two include these extra equations - we just have to make <code class="language-plaintext highlighter-rouge">dndt</code> a <code class="language-plaintext highlighter-rouge">vector[3]</code> (rather than <code class="language-plaintext highlighter-rouge">vector[1]</code>), and add the extra lines for $\frac{dy}{dt}$ and $\frac{dz}{dt}$ (<code class="language-plaintext highlighter-rouge">dndt[2]</code> and <code class="language-plaintext highlighter-rouge">dndt[3]</code> respectively). We also have to make sure to put the extinction and colonisation rate parameters in the brackets at the top.</p>

<h3 id="the-data-block">The data block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_times</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">steps</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">one</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">,</span><span class="w"> </span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y0_obs</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">t0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">First</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">step</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">ts</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w"> </span><span class="n">steps</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_sites</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">sites</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>In this block, we tell Stan about the observed data that we put in <code class="language-plaintext highlighter-rouge">dlist</code> previously. Remember that we’ve had to separate the initial time step from the rest of the time series because of the way that Stan handles indexing.</p>

<h3 id="the-parameters-block">The parameters block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">parameters</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">simplex</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">y0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Initial</span><span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="p">(</span><span class="n">must</span><span class="w"> </span><span class="n">sum</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ep</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w"> 
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ed</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cp</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cd</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>In this block, we tell Stan about the model’s parameters. First we have the initial state <code class="language-plaintext highlighter-rouge">y0</code>, which is a simplex with three values (as there are 3 states a patch can be in). <code class="language-plaintext highlighter-rouge">y0</code> is a simplex because the proportion of sites in each state must sum to 1.</p>

<p>After this, we just have the four extinction and colonisation rate parameters.</p>

<h3 id="the-transformed-parameters-block">The transformed parameters block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
    </span><span class="o">//</span><span class="w"> </span><span class="n">ODE</span><span class="w"> </span><span class="n">solver</span><span class="w">
  </span><span class="n">simplex</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">theta</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ode_rk45</span><span class="p">(</span><span class="n">exp_growth</span><span class="p">,</span><span class="w"> </span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">t0</span><span class="p">,</span><span class="w"> </span><span class="n">ts</span><span class="p">,</span><span class="w"> </span><span class="n">ep</span><span class="p">,</span><span class="w"> </span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="n">cp</span><span class="p">,</span><span class="w"> </span><span class="n">cd</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Here is the bit where we actually implement the ODE solver - we’re using the <code class="language-plaintext highlighter-rouge">ode_rk45</code> solver here as we did for the exponential / logistic growth post, but there are other options which you can learn about <a href="https://mc-stan.org/docs/2_29/functions-reference/functions-ode-solver.html">here</a>.</p>

<h3 id="the-model-block">The model block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">ep</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">ed</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">extinction</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">cp</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Plant</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">
  </span><span class="n">cd</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">0.5</span><span class="p">);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Disperser</span><span class="w"> </span><span class="n">colonisation</span><span class="w"> </span><span class="n">rate</span><span class="w">

  </span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">dirichlet</span><span class="p">([</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">]);</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Initial</span><span class="w"> </span><span class="n">state</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Observation</span><span class="w"> </span><span class="n">model</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">multinomial</span><span class="p">(</span><span class="n">y0</span><span class="p">);</span><span class="w"> 
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="p">[</span><span class="n">t</span><span class="p">,]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">multinomial</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="n">t</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></code></pre></div></div>

<p>In this block, we have two main things going on. First, we define the priors for our parameters. We know that our extinction and colonisation rate parameters have to be positive, so I’ve given them exponential priors. In a real example, you might want to think about whether you have any more information that you can use to improve these, but they’ll do for this example.</p>

<p>For the initial state <code class="language-plaintext highlighter-rouge">y0</code> I’ve used a dirichlet prior - by using the values <code class="language-plaintext highlighter-rouge">[1,1,1]</code> I’ve made it completely flat (uniform), meaning that the prior probability of each of the three initial states is the same. I could have used this prior to incorporate some of the information that we used when setting up the simulation, for example that most of the sites were empty at the initial time - but decided not to, just to see how well the model coped without this information.</p>

<p>After we’ve coded the priors, we code the observation model. We first do this for the observed initial state <code class="language-plaintext highlighter-rouge">y0_obs</code>, and then loop over the rest of the time steps.</p>

<h3 id="the-generated-quantities-block">The generated quantities block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">generated</span><span class="w"> </span><span class="n">quantities</span><span class="p">{</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="m">+1</span><span class="p">,</span><span class="m">3</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">n_sim</span><span class="p">;</span><span class="w">
  </span><span class="n">n_sim</span><span class="p">[</span><span class="m">1</span><span class="p">,]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">multinomial_rng</span><span class="p">(</span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">n_sites</span><span class="p">);</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">){</span><span class="w">
    </span><span class="n">n_sim</span><span class="p">[</span><span class="n">t</span><span class="m">+1</span><span class="p">,]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">multinomial_rng</span><span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="n">t</span><span class="p">],</span><span class="w"> </span><span class="n">n_sites</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></code></pre></div></div>

<p>Just like in the exponential / logistic growth post, we’re using the generated quantities block to perform posterior predictive simulations - at each time step, we simulate the proportion of sites in each state and store this in <code class="language-plaintext highlighter-rouge">n_sim</code>. We do this using the multinomial distribution’s random number generator, <code class="language-plaintext highlighter-rouge">multinomial_rng</code>. We have to use <code class="language-plaintext highlighter-rouge">y0</code> for the initial time step, and the approproate value of <code class="language-plaintext highlighter-rouge">theta</code> for the rest of the time steps - there’s a bit of fun with the indexing to make this work.</p>

<h2 id="running-the-model-and-exploring-the-output">Running the model and exploring the output</h2>

<p>After saving the model somewhere sensible (I’ve named mine <code class="language-plaintext highlighter-rouge">twosp_mutual.stan</code>) we can run it using the <code class="language-plaintext highlighter-rouge">cstan</code> function in the <code class="language-plaintext highlighter-rouge">rethinking</code> package:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">m_twosp_mutual</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cstan</span><span class="p">(</span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:/Stan_code/twosp_mutual.stan"</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">dlist</span><span class="p">,</span><span class="w">
               </span><span class="n">chains</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="n">cores</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="n">warmup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2500</span><span class="p">,</span><span class="w">
               </span><span class="n">iter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3500</span><span class="p">,</span><span class="w">
               </span><span class="n">seed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">889</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Once it’s compiled and sampled, we can check the summary table and view some useful diagnostic plots:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">precis</span><span class="p">(</span><span class="n">m_twosp_mutual</span><span class="p">,</span><span class="w"> </span><span class="n">depth</span><span class="w"> </span><span class="o">=</span><span class="m">3</span><span class="p">)</span><span class="w">
</span><span class="n">dashboard</span><span class="p">(</span><span class="n">m_twosp_mutual</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/dashboard.jpeg" alt="" /></p>

<p>We see no divergent transitions, which is good! The number of effective samples, <code class="language-plaintext highlighter-rouge">n_eff</code> is also pretty good for each parameter. We have had one parameter where the Gelman-Rubin convergence diagnostic <code class="language-plaintext highlighter-rouge">Rhat</code> isn’t 1, meaning the chains haven’t quite converged - if you look at the summary table, you’ll see it’s actually <code class="language-plaintext highlighter-rouge">y0[2]</code>, the initial proportion of plant-only patches. However, the problem isn’t too severe, so we’re just going to ignore it in this example.</p>

<p>We can also inspect traceplots for our key parameters:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">traceplot</span><span class="p">(</span><span class="n">m_twosp_mutual</span><span class="p">,</span><span class="w"> </span><span class="n">pars</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="s2">"y0[1]"</span><span class="p">,</span><span class="s2">"y0[2]"</span><span class="p">,</span><span class="s2">"y0[3]"</span><span class="p">,</span><span class="s2">"ep"</span><span class="p">,</span><span class="s2">"ed"</span><span class="p">,</span><span class="s2">"cp"</span><span class="p">,</span><span class="s2">"cd"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/traceplots.jpeg" alt="" /></p>

<p>Now that we’re satisfied with our model’s diagnostics, we’re free to extract the posterior samples:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">post</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">extract.samples</span><span class="p">(</span><span class="n">m_twosp_mutual</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>One of the first things we can do is compare our posterior distribution for each of our key parameters with its true value (which we know because we simulated the data!). one way of doing so is with density plots like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">2</span><span class="p">,</span><span class="m">2</span><span class="p">))</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">ep</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant extinction rate"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">ep</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Disperser extinction rate"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">cp</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant colonisation rate"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">cp</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">cd</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Disperser colonisation rate"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">cd</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/key_parameters.jpeg" alt="" /></p>

<p>The model has done a good job - all of the dashed vertical lines are contained within their respective posterior distribution!</p>

<p>We can also do this for the initial states:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">3</span><span class="p">))</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">y0</span><span class="p">[,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Initial empty proportion"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">y0</span><span class="p">[,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Initial plant only proportion"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">y0</span><span class="p">[</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">y0</span><span class="p">[,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Initial both species proportion"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">y0</span><span class="p">[</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/initial_state.jpeg" alt="" /></p>

<p>Again, we see that the model has done a good job - if you look at the values on the x-axes, you can see that the estimates are very precise as well.</p>

<p>We can also plot the model’s posterior predictions (as median and compatability intervals) against the observed data, like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x_seq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">time</span><span class="w">

</span><span class="n">x_sim_mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">median</span><span class="p">)</span><span class="w">
</span><span class="n">x_sim_99CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.99</span><span class="p">)</span><span class="w">
</span><span class="n">x_sim_95CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.95</span><span class="p">)</span><span class="w">
</span><span class="n">x_sim_89CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">
</span><span class="n">x_sim_80CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.80</span><span class="p">)</span><span class="w">
</span><span class="n">x_sim_70CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.70</span><span class="p">)</span><span class="w">
</span><span class="n">x_sim_60CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.60</span><span class="p">)</span><span class="w">

</span><span class="n">y_sim_mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">median</span><span class="p">)</span><span class="w">
</span><span class="n">y_sim_99CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.99</span><span class="p">)</span><span class="w">
</span><span class="n">y_sim_95CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.95</span><span class="p">)</span><span class="w">
</span><span class="n">y_sim_89CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">
</span><span class="n">y_sim_80CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.80</span><span class="p">)</span><span class="w">
</span><span class="n">y_sim_70CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.70</span><span class="p">)</span><span class="w">
</span><span class="n">y_sim_60CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.60</span><span class="p">)</span><span class="w">

</span><span class="n">z_sim_mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">median</span><span class="p">)</span><span class="w">
</span><span class="n">z_sim_99CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.99</span><span class="p">)</span><span class="w">
</span><span class="n">z_sim_95CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.95</span><span class="p">)</span><span class="w">
</span><span class="n">z_sim_89CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">
</span><span class="n">z_sim_80CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.80</span><span class="p">)</span><span class="w">
</span><span class="n">z_sim_70CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.70</span><span class="p">)</span><span class="w">
</span><span class="n">z_sim_60CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">[,,</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.60</span><span class="p">)</span><span class="w">

</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">3</span><span class="p">))</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1000</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Number of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Empty"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">x_sim_mu</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">x_sim_99CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">x_sim_95CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">x_sim_89CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">x_sim_80CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">x_sim_70CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">x_sim_60CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">counts_obs</span><span class="p">[,</span><span class="m">1</span><span class="p">])</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1000</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Number of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant only"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">y_sim_mu</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">y_sim_99CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">y_sim_95CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">y_sim_89CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">y_sim_80CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">y_sim_70CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">y_sim_60CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">counts_obs</span><span class="p">[,</span><span class="m">2</span><span class="p">])</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1000</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Number of patches"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Plant &amp; Disperser"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">z_sim_mu</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">z_sim_99CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">z_sim_95CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">z_sim_89CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">z_sim_80CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">z_sim_70CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">z_sim_60CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">counts_obs</span><span class="p">[,</span><span class="m">3</span><span class="p">])</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/levins_twosp_mutual/posterior_predictive.jpeg" alt="" /></p>

<p>We see a great fit!</p>]]></content><author><name>Peter S. Stewart</name></author><category term="blog" /><category term="Stan" /><category term="tutorial" /><category term="classic ecological models" /><summary type="html"><![CDATA[In this post, I cover how to fit a version of the two-species Levins metapopulation model in Stan, as well as how to use R to simulate data to fit the model to.]]></summary></entry><entry><title type="html">Processing camera trap images using Windows batch files</title><link href="https://peter-stewart.github.io/blog/useful-batch-files/" rel="alternate" type="text/html" title="Processing camera trap images using Windows batch files" /><published>2022-06-06T00:00:00+00:00</published><updated>2022-06-06T00:00:00+00:00</updated><id>https://peter-stewart.github.io/blog/useful-batch-files</id><content type="html" xml:base="https://peter-stewart.github.io/blog/useful-batch-files/"><![CDATA[<p>In this post, I explain how to use Windows batch files to perform several useful operations on camera trap images - for example adding a prefix to, removing a suffix from, or copying a list of multiple camera trap images.</p>

<h2 id="introduction">Introduction</h2>

<p>When working with hundreds of thousands of camera trap images, even simple things like renaming images can suddenly become a daunting task!</p>

<p>I encounter these situations pretty often during <a href="https://www.zooniverse.org/projects/peter-dot-stewart/prickly-pear-project-kenya">my research</a>, so I make use of Windows batch files to deal with large numbers of images at once. In this post, I share how to do this.</p>

<p>Please note that <strong>this will only work if you’re on Windows</strong>!</p>

<h2 id="very-important-note">VERY IMPORTANT NOTE</h2>

<p><strong>Always work on a copy of your data. Never alter the original images, and keep the originals in a safe place away from where you are working, so that you have a backup in case things go wrong.</strong></p>

<p><strong>The code in this post worked for me, but I cannot guarantee that it will work for you - use it at your own risk. I always recommend testing batch files on a small subset of your images first, to confirm that the result is what you expected.</strong></p>

<h2 id="adding-a-common-prefix">Adding a common prefix</h2>

<p>When camera trap images come off the SD card, they usually have generic file names like <code class="language-plaintext highlighter-rouge">IMG_0001.jpg</code>. I often want to rename the images to something more useful by including the camera trap’s site ID in the filename, for instance <code class="language-plaintext highlighter-rouge">Site_18_part2_IMG_0001.jpg</code></p>

<p>To do this, I use this code:</p>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">FOR</span> <span class="na">/f </span><span class="s2">"delims="</span> <span class="vm">%%F</span> <span class="kd">IN</span> <span class="o">(</span><span class="s1">'DIR /a-d /b *.jpg'</span><span class="o">)</span>  <span class="kd">DO</span> <span class="o">(</span>
<span class="kd">RENAME</span> <span class="s2">"</span><span class="vm">%%F</span><span class="s2">"</span> <span class="s2">"Site_18_part2_</span><span class="vm">%%F</span><span class="s2">"</span><span class="o">)</span>
</code></pre></div></div>

<p>This will append all of our images with the prefix <code class="language-plaintext highlighter-rouge">Site_18_part2_</code>. To change this prefix just alter the second line of code, making sure to keep the <code class="language-plaintext highlighter-rouge">%%F</code> bit.</p>

<p>To make this run, we have to create a <code class="language-plaintext highlighter-rouge">.bat</code> file in the folder which contains the camera trap images. <strong>This file is going to rename every .jpg in the folder by adding the site prefix, so make sure that you move any images you don’t want renamed to a separate folder!</strong></p>

<p>The first step is to create a <code class="language-plaintext highlighter-rouge">.txt</code> file in our folder. To do this, right click &gt; New &gt; Text Document. Name it something sensible - apparently it is important to avoid calling your batch file the same thing as other common batch files which may exist on your computer. In this example I’ve called my file <code class="language-plaintext highlighter-rouge">prefix_camera_images.bat</code></p>

<p>We then paste the code above into this <code class="language-plaintext highlighter-rouge">.txt</code> file, and save it.</p>

<p>We then need to turn our <code class="language-plaintext highlighter-rouge">.txt</code> file into a batch file. We can do this by using save as - we need to select “All files” in the drop-down menu, and type <code class="language-plaintext highlighter-rouge">.bat</code> at the end of the file name:</p>

<p><img src="/assets/images/post_images/useful_batch_files/save_as.jpg" alt="" /></p>

<p>Then we save the file. You’ll see that the batch file has now appeared in the folder with our camera trap images:</p>

<p><img src="/assets/images/post_images/useful_batch_files/prefix_before.jpg" alt="" /></p>

<p>Double-click it, and all of your images will be renamed to include the prefix:</p>

<p><img src="/assets/images/post_images/useful_batch_files/prefix_after.jpg" alt="" /></p>

<p>If you’ve got a large number of images this might take a few seconds - but it’s still MUCH faster than doing it manually!</p>

<p>After it’s done, it’s a good idea to <strong>delete the batch file</strong> as if you accidentally double-click it again, it’s going to add an unwanted second prefix to your images!</p>

<h2 id="adding-the-folder-name-as-a-common-prefix---recursive-version">Adding the folder name as a common prefix - recursive version</h2>

<p>If you’ve got camera trap images spread across a large number of folders (e.g., representing many sites) then prefixing them using the previous approach can be quite tedious, as you have to edit an run the batch file for each folder individually.</p>

<p>However, if the prefix you want to add is the same as the name of the folder that each image is in (e.g., you want to make every image in the folder <code class="language-plaintext highlighter-rouge">Site_01_part1</code> look like <code class="language-plaintext highlighter-rouge">Site_01_part1_IMG0001.jpg</code>) there is a much easier method - we write a single batch file which goes through all of our folders, and adds the folder name as a prefix to every image file in each folder.</p>

<p>We can do this using the following batch file:</p>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">setlocal</span> <span class="na">enabledelayedexpansion</span>
<span class="kd">FOR</span> <span class="na">/F </span><span class="s2">"tokens=* delims="</span> <span class="vm">%%F</span> <span class="k">in</span> <span class="o">(</span><span class="s1">'dir /s /b /a-d *.jpg'</span><span class="o">)</span> <span class="kd">DO</span> <span class="o">(</span>
    <span class="kd">set</span> <span class="s2">"DIRPATH=</span><span class="vm">%%~dpF</span><span class="s2">"</span>
    <span class="kd">set</span> <span class="s2">"FILEPATH=</span><span class="vm">%%~F</span><span class="s2">"</span>
    <span class="kd">set</span> <span class="s2">"FILENAME=</span><span class="vm">%%~nxF</span><span class="s2">"</span>
    <span class="kd">IF</span> <span class="s2">"</span><span class="err">!</span><span class="s2">DIRPATH:~-1</span><span class="err">!</span><span class="s2">"</span> <span class="kd">EQU</span> <span class="s2">"\"</span> <span class="o">(</span>
        <span class="kd">SET</span> <span class="s2">"DIRPATH=</span><span class="err">!</span><span class="s2">DIRPATH:~0,-1</span><span class="err">!</span><span class="s2">"</span>
    <span class="o">)</span>

    <span class="kd">FOR</span> <span class="vm">%%G</span> <span class="kd">IN</span> <span class="o">(</span><span class="s2">"</span><span class="nv">!DIRPATH!</span><span class="s2">"</span><span class="o">)</span> <span class="kd">DO</span> <span class="o">(</span>
        <span class="kd">set</span> <span class="s2">"PICDIR=</span><span class="vm">%%~nxG</span><span class="s2">"</span>
        <span class="nb">rename</span> <span class="s2">"</span><span class="nv">!FILEPATH!</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">!PICDIR!</span><span class="s2">_</span><span class="nv">!FILENAME!</span><span class="s2">"</span>
    <span class="o">)</span>
<span class="o">)</span>
</code></pre></div></div>

<p>Note that I’ve set this up so that it’ll only work on <code class="language-plaintext highlighter-rouge">.jpg</code> files (which all of my camera trap images are) - if you want it to work on another file type (e.g., <code class="language-plaintext highlighter-rouge">.png</code> images) you’ll need to alter the <code class="language-plaintext highlighter-rouge">*.jpg</code> bit on the second line.</p>

<p>We save the batch file (see above for step-by-step instructions) in the same folder as all of the folders we want it to look through - in this example, we want it to look through <code class="language-plaintext highlighter-rouge">Site_43</code>, <code class="language-plaintext highlighter-rouge">Site_44</code>, and <code class="language-plaintext highlighter-rouge">Site_74</code> as well as all of the subfolders within each (<code class="language-plaintext highlighter-rouge">Site_43_part1</code>, <code class="language-plaintext highlighter-rouge">Site_43_part2</code> etc.).</p>

<p><img src="/assets/images/post_images/useful_batch_files/prefix_recursive.jpg" alt="" /></p>

<p>Running the batch file will result in all of the camera trap images being renamed to add their respective prefix.</p>

<h2 id="removing-a-bracketed-number">Removing a bracketed number</h2>

<p>I recently encountered a situation in which images from different camera trap sites had been placed in the same folder - this resulted in many of the images being appended with a space followed by a bracketed number, for example:</p>

<p><img src="/assets/images/post_images/useful_batch_files/before.jpg" alt="" /></p>

<p>I wanted to get rid of the space and bracketed number, so that e.g. <code class="language-plaintext highlighter-rouge">IMG_0001 (3).jpg</code> would become <code class="language-plaintext highlighter-rouge">IMG_0001.jpg</code></p>

<p>To do this, I used the following code:</p>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">setlocal</span> <span class="kd">enableDelayedExpansion</span>
<span class="kd">FOR</span> <span class="na">/f </span><span class="s2">"delims="</span> <span class="vm">%%F</span> <span class="kd">IN</span> <span class="o">(</span><span class="s1">'DIR /a-d /b *.jpg'</span><span class="o">)</span> <span class="kd">DO</span> <span class="o">(</span>
  <span class="kd">set</span> <span class="kd">filename</span><span class="o">=</span><span class="s2">"</span><span class="vm">%%~nxF</span><span class="s2">"</span>
  <span class="nb">ren</span> <span class="s2">"</span><span class="vm">%%F</span><span class="s2">"</span> <span class="s2">"</span><span class="err">!</span><span class="s2">filename: (3)=</span><span class="err">!</span><span class="s2">"</span>
<span class="o">)</span>
</code></pre></div></div>

<p>If you want to remove a different suffix, change the bit on the fourth line between <code class="language-plaintext highlighter-rouge">!filename:</code> and <code class="language-plaintext highlighter-rouge">=!</code> - note that there is a space between <code class="language-plaintext highlighter-rouge">!filename:</code> and <code class="language-plaintext highlighter-rouge">(3)</code> in my example, because I wanted to remove the space before the bracketed number as well.</p>

<p>We run this in the same way as for the prefix batch file above - check that section of the post for the step-by-step instructions for creating and running the batch file.</p>

<p>Here is the result:</p>

<p><img src="/assets/images/post_images/useful_batch_files/after.jpg" alt="" /></p>

<h2 id="copying-a-list-of-images">Copying a list of images</h2>

<p>Another common situation is having to copy a large number of specific images and put them into a single folder - for instance, I might want to go through my whole camera trap catalogue and copy all of the images meeting some criterion (e.g., images containing striped hyenas) into one folder.</p>

<p>For example, here we have a folder called <code class="language-plaintext highlighter-rouge">site_photos</code> containing 2218 camera trap images from three sites:</p>

<p><img src="/assets/images/post_images/useful_batch_files/site_list.jpg" alt="" /></p>

<p>Within each site’s folder, we have subfolders ending in <code class="language-plaintext highlighter-rouge">_part1</code>, <code class="language-plaintext highlighter-rouge">_part2</code>, etc.:</p>

<p><img src="/assets/images/post_images/useful_batch_files/subfolder_list.jpg" alt="" /></p>

<p>The first thing we need is a <code class="language-plaintext highlighter-rouge">.txt</code> file containing a list of images that we want to copy. I’ve called mine <code class="language-plaintext highlighter-rouge">image_list.txt</code>. The list should look like this:</p>

<p><img src="/assets/images/post_images/useful_batch_files/image_list.jpg" alt="" /></p>

<p>Notice that I’ve included the folder and subfolder in the file name - you’ll need to do this, or else the images won’t copy. There are a variety of ways of making a list like this - you could make it in R, or make it from a .csv by going to save as and selecting <code class="language-plaintext highlighter-rouge">.txt</code> in the drop-down list. You could even type it out manually if you only need to copy a few files.</p>

<p>We are now going to copy all of the files in this list into a folder called <code class="language-plaintext highlighter-rouge">interesting_images</code>. We can do this using the following batch file:</p>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">set</span> <span class="kd">src_folder</span><span class="o">=</span><span class="kd">C</span>:\temp\blog_post\site_photos
<span class="kd">set</span> <span class="kd">dst_folder</span><span class="o">=</span><span class="kd">C</span>:\temp\blog_post\interesting_images
<span class="k">for</span> <span class="na">/f </span><span class="s2">"tokens=*"</span> <span class="vm">%%i</span> <span class="k">in</span> <span class="o">(</span><span class="kd">image_list</span>.txt<span class="o">)</span> <span class="kd">DO</span> <span class="o">(</span>
    <span class="nb">xcopy</span> <span class="na">/S/E </span><span class="s2">"</span><span class="nv">%src_folder%</span><span class="s2">\</span><span class="vm">%%i</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">%dst_folder%</span><span class="s2">"</span>
<span class="o">)</span>
</code></pre></div></div>

<p>Note that you have to specify the source folder (<code class="language-plaintext highlighter-rouge">src_folder</code>, which is <code class="language-plaintext highlighter-rouge">site_photos</code>) and destination folder (<code class="language-plaintext highlighter-rouge">dst_folder</code>, which is <code class="language-plaintext highlighter-rouge">interesting_images</code>) so that the files are copied from and to the correct places.</p>

<p>I saved this file as <code class="language-plaintext highlighter-rouge">copy_list</code>, and ran it in the same way as the other batch files above (see the <em>adding a common prefix</em> example for step-by-step instructions):</p>

<p><img src="/assets/images/post_images/useful_batch_files/copy_list.jpg" alt="" /></p>

<p>If we go to the <code class="language-plaintext highlighter-rouge">interesting_images</code> folder, we can see that all of the images in our list are there:</p>

<p><img src="/assets/images/post_images/useful_batch_files/interesting_images.jpg" alt="" /></p>

<h2 id="copying-one-file-to-all-subfolders">Copying one file to all subfolders</h2>

<p>This one is useful for any <a href="https://saul.cpsc.ucalgary.ca/timelapse/pmwiki.php?n=Main.Download2">Timelapse</a> users out there - it’s a quick method for getting your Timelapse template file into all of your image subfolders so that you can load each of them into Timelapse separately.</p>

<p>As above, we have a folder called <code class="language-plaintext highlighter-rouge">site_photos</code> containing camera trap images from 3 sites, with each site split into parts like so:</p>

<p><img src="/assets/images/post_images/useful_batch_files/site_list.jpg" alt="" /></p>

<p><img src="/assets/images/post_images/useful_batch_files/subfolder_list.jpg" alt="" /></p>

<p>In the top-level folder (which I’ve called <code class="language-plaintext highlighter-rouge">copy_to_subfolders_batch_test</code> for this example) we also have a file called <code class="language-plaintext highlighter-rouge">TimelapseTemplate.tdb</code> - this is the file that we want to copy to all the subfolders.</p>

<p>To do this, we can use the following batch file:</p>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> <span class="s2">"C:\temp\copy_to_subfolders_batch_test\site_photos"</span>
<span class="k">for</span> <span class="na">/r /d </span><span class="vm">%%I</span> <span class="k">in</span> <span class="o">(*)</span> <span class="k">do</span> <span class="nb">xcopy</span> <span class="s2">"C:\temp\copy_to_subfolders_batch_test\TimelapseTemplate.tdb"</span> <span class="s2">"</span><span class="vm">%%~fsI</span><span class="s2">"</span>
</code></pre></div></div>

<p>You’ll need to change the folder path and file name as appropriate, and then save and run the batch file as above (see the <em>adding a common prefix</em> example for step-by-step instructions).</p>

<p>When you look in the subfolders, you’ll see that the batch file is there:</p>

<p><img src="/assets/images/post_images/useful_batch_files/copy_sub_results.jpg" alt="" /></p>]]></content><author><name>Peter S. Stewart</name></author><category term="blog" /><category term="camera traps" /><category term="tutorial" /><summary type="html"><![CDATA[In this post, I explain how to use Windows batch files to perform several useful operations on camera trap images - for example adding a prefix to, removing a suffix from, or copying a list of multiple camera trap images.]]></summary></entry><entry><title type="html">Classic ecological models in Stan: exponential and logistic growth</title><link href="https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth/" rel="alternate" type="text/html" title="Classic ecological models in Stan: exponential and logistic growth" /><published>2022-05-31T00:00:00+00:00</published><updated>2022-05-31T00:00:00+00:00</updated><id>https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth</id><content type="html" xml:base="https://peter-stewart.github.io/blog/classic-ecological-models-exponential-logistic-growth/"><![CDATA[<p>In this post, I cover how to fit two simple models of population growth using Stan: exponential growth, and logistic growth. I also cover how to use R to simulate data to fit these models to.</p>

<h1 id="introduction-and-an-idea-for-a-series">Introduction, and an idea for a series</h1>

<p>I recently decided that I’d like to learn more about Stan, and especially about how to fit different kinds of models - more than just the glm’s which I’ve been learning about since undergrad. I also enjoy learning about theoretical ecology, and thought it would be cool to combine these two interests - and hopefully help some people along the way! As a result, I decided it would be cool to learn how to fit some classic ecological models using Stan, and to share my learning in these posts.</p>

<p>I was also inspired by a couple of examples of people doing this already - <a href="https://onlinelibrary.wiley.com/doi/10.1002/sim.9164">this paper</a> on fitting the SIR disease transmission model by Léo Grinsztajn and colleagues made me realise just how versatile Stan could be, and was a major contributor towards me deciding to learn it. The second example is the Lotka-Volterra predator-prey model as demonstrated by Richard McElreath <a href="https://youtu.be/Doaod09YitA?t=3344">in Statistical Rethinking</a> - the impact of predators on prey populations is something I’ve been interested in for years (ever since an essay assignment I particularly enjoyed as an undergrad), so it was very exciting to learn that I could fit models like this in Stan.</p>

<p>I don’t really have a definition for what constitutes a ‘classic’ model - I thought I’d just start off with some models I was taught about as an undergrad or have read about in books, and follow my interests from there. If there’s a model that you’d like to see featured, then feel free to drop me a message - there’s no guarantee that I’ll cover it (not least because I may be unable to code it…), but I’ll do my best!</p>

<h1 id="acknowledgements-and-other-resources">Acknowledgements and other resources</h1>

<p>I learned a lot about modeling from the book <a href="https://press.princeton.edu/books/hardcover/9780691123448/a-biologists-guide-to-mathematical-modeling-in-ecology-and-evolution">a biologist’s guide to mathematical modeling in ecology and evolution</a> by Sarah Otto and Troy Day, and it was one of the main resources I used when writing this post.</p>

<p>I also learned a great deal from the classic book <a href="https://oxford.universitypressscholarship.com/view/10.1093/oso/9780199209989.001.0001/isbn-9780199209989">theoretical ecology: principles and applications (3rd ed.)</a>, edited by Robert May and Angela McLean - the chapter on single-species dynamics by Tim Coulson and Charles Godfray is most relevant here, but the whole book is fantastic and well worth reading.</p>

<p>As for my knowledge about Stan, I mainly learned from the book and online course <a href="https://github.com/rmcelreath/stat_rethinking_2022">Statistical Rethinking</a> by Richard McElreath, and also found Michael Betancourt’s <a href="https://betanalpha.github.io/assets/case_studies/stan_intro.html">introduction to Stan</a> to be very useful.</p>

<p>For more differential-equation-specific Stan knowledge, I mostly learned from the <a href="https://mc-stan.org/docs/2_29/stan-users-guide/ode-solver.html">Stan manual</a>, supplemented by <a href="https://mc-stan.org/users/documentation/case-studies/convert_odes.html">this post</a> about (relatively) recent changes to the ODE interface and <a href="https://mpopov.com/tutorials/ode-stan-r/">this tutorial</a> by Mikhail Popov.</p>

<h1 id="disclaimer">Disclaimer</h1>

<p>As I am writing this post largely as a way of learning about the material, I would not be surprised if it contains mistakes. Please bear this in mind when reading it, and remember that any use of the model etc. is at your own risk.</p>

<p>If you do happen to spot any mistakes, I’d be very grateful if you would let me know in the comments below or by email (my contact details are in the sidebar) so that I can correct them!</p>

<h1 id="exponential-growth">Exponential growth</h1>

<h2 id="introducing-the-model">Introducing the model</h2>

<p>Organisms are born, and then they die. From these two facts, we can build the most simple model of population growth:</p>

\[\frac{dn}{dt} = bn(t) - dn(t)\]

<p>where the population size is $n$, time is $t$, the birth rate is $b$, and the death rate is $d$. This model is an <strong>ordinary differential equation (ODE)</strong>, which describes how the derivative $(\frac{dn}{dt}$: the tiny change in population size n that occurs over each tiny change in time t) changes, as a function of the population size itself.</p>

<p>We can actually make this model even simpler, by replacing the birth and death rates with a single parameter $r$ which is just the difference between them:</p>

\[r = b-d\]

<p>This means that $r$ represents the population’s per-capita rate of change. We can replace $b$ and $d$ with $r$ in our equation, making it:</p>

\[\frac{dn}{dt} = rn(t)\]

<p>Our goal is now going to be to infer $r$ from the data. But first, we have to simulate the data!</p>

<h2 id="simulating-the-data-in-r">Simulating the data in R</h2>

<p>First, let’s a couple of packages and setting the seed for the random number generator:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">rethinking</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">deSolve</span><span class="p">)</span><span class="w">
</span><span class="n">set.seed</span><span class="p">(</span><span class="m">555</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>We’re going to start off by simulating the underlying exponential growth process. The first thing to do is to define a function which takes in values for the intial state <code class="language-plaintext highlighter-rouge">y0</code> and rate of change <code class="language-plaintext highlighter-rouge">r</code>, and spits out the derivative <code class="language-plaintext highlighter-rouge">dn.dt</code> for each time step:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">exp_growth</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="n">times</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">parms</span><span class="p">){</span><span class="w">
  </span><span class="n">dn.dt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">r</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w">
  </span><span class="nf">return</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">dn.dt</span><span class="p">))</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The next step is to pick some values for the parameters, as well as a time sequence to simulate for. We’re going to assume that our rate of change <code class="language-plaintext highlighter-rouge">r</code> is constant at all points in time:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">r</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w"> </span><span class="c1"># Rate of change</span><span class="w">
</span><span class="n">y0</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="c1"># Initial state</span><span class="w">
</span><span class="n">time</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">,</span><span class="n">by</span><span class="o">=</span><span class="m">0.1</span><span class="p">)</span><span class="w"> </span><span class="c1"># Time sequence</span><span class="w">
</span></code></pre></div></div>

<p>After this, we can simulate our true population size over time by using the <code class="language-plaintext highlighter-rouge">ode</code> function in the <code class="language-plaintext highlighter-rouge">deSolve</code> package to obtain the population size at each point in time:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">n_true</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ode</span><span class="p">(</span><span class="n">y</span><span class="o">=</span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">times</span><span class="o">=</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">func</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">exp_growth</span><span class="p">,</span><span class="w"> </span><span class="n">parms</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>We can plot it like so:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plot</span><span class="p">(</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">n_true</span><span class="p">[,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Population size"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/exponential_logistic_growth/exponential_true.jpeg" alt="" /></p>

<p>This allows us to see the classic exponential growth curve.</p>

<p>Now that we’ve simulated our true population growth, we’re going to simulate the observation process. I’m going to simulate the observed population size, <code class="language-plaintext highlighter-rouge">n_obs</code>, at each time-step using a $Poisson$ distribution where $\lambda$ is the true population size at a given time. This has a couple of good features: it ensures that the observed population size is always a positive integer (or zero), and it also means that the variation in observed values is greater when the population size is greater - which represents a scenario where measurement error is greater when the population is large. We can simulate and plot the observed data like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">n_obs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rpois</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">time</span><span class="p">),</span><span class="w">
               </span><span class="n">lambda</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_true</span><span class="p">[,</span><span class="m">2</span><span class="p">])</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">n_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Observed population size"</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">n_true</span><span class="p">[,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">

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

<p><img src="/assets/images/post_images/exponential_logistic_growth/observed_exponential.jpeg" alt="" /></p>

<p>Here I’ve plotted the true population trend in black, so you can see the way that the observed datapoints deviate from the line.</p>

<p>Now that we’ve simulated our data, we can prepare a data list for Stan. This requires some slight creativity due to the way that Stan indexes time steps (starting at 1, not at zero) - meaning we have to separate initial state bits <code class="language-plaintext highlighter-rouge">y0_obs</code> and <code class="language-plaintext highlighter-rouge">t0</code>, and subtract the first element from <code class="language-plaintext highlighter-rouge">y</code> and <code class="language-plaintext highlighter-rouge">ts</code>. We also have to subtract 1 from our time index <code class="language-plaintext highlighter-rouge">n_times</code>. The code looks like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dlist</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">n_times</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">time</span><span class="p">)</span><span class="m">-1L</span><span class="p">,</span><span class="w"> </span><span class="c1"># Number of timesteps</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_obs</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="c1"># Observed initial state</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_obs</span><span class="p">[</span><span class="m">-1</span><span class="p">],</span><span class="w"> </span><span class="c1"># Observed data</span><span class="w">
  </span><span class="n">t0</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"># Initial timestep</span><span class="w">
  </span><span class="n">ts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">[</span><span class="m">-1</span><span class="p">]</span><span class="w"> </span><span class="c1"># Timesteps</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>We can now go on to code our model!</p>

<h2 id="coding-the-model-in-stan">Coding the model in Stan</h2>

<p>Here is the full model:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="w"> </span><span class="n">exp_growth</span><span class="p">(</span><span class="n">real</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w">
                    </span><span class="n">vector</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">State</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">r</span><span class="p">){</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">parameter</span><span class="w">
    </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">dndt</span><span class="p">;</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">];</span><span class="w"> 
    </span><span class="n">return</span><span class="w"> </span><span class="n">dndt</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">data</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_times</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">steps</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">one</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y0_obs</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">t0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">First</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">step</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">ts</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w"> </span><span class="n">steps</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">parameters</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">y0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">r</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Per</span><span class="o">-</span><span class="n">capita</span><span class="w"> </span><span class="n">rate</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">population</span><span class="w"> </span><span class="n">change</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
    </span><span class="o">//</span><span class="w"> </span><span class="n">ODE</span><span class="w"> </span><span class="n">solver</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">lambda</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ode_rk45</span><span class="p">(</span><span class="n">exp_growth</span><span class="p">,</span><span class="w"> </span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">t0</span><span class="p">,</span><span class="w"> </span><span class="n">ts</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">model</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">y0</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">1</span><span class="p">);</span><span class="w">
  
  </span><span class="o">//</span><span class="w"> </span><span class="n">Likelihood</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">poisson</span><span class="p">(</span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">]);</span><span class="w">
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="p">[</span><span class="n">t</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">poisson</span><span class="p">(</span><span class="n">lambda</span><span class="p">[</span><span class="n">t</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">generated</span><span class="w"> </span><span class="n">quantities</span><span class="p">{</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">n_sim</span><span class="p">;</span><span class="w">
  </span><span class="n">n_sim</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poisson_rng</span><span class="p">(</span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">]);</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">){</span><span class="w">
    </span><span class="n">n_sim</span><span class="p">[</span><span class="n">t</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poisson_rng</span><span class="p">(</span><span class="n">lambda</span><span class="p">[</span><span class="n">t</span><span class="p">,</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></code></pre></div></div>

<p>Now let’s go through it step-by-step!</p>

<h3 id="the-functions-block">The functions block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="w"> </span><span class="n">exp_growth</span><span class="p">(</span><span class="n">real</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w"> 
                    </span><span class="n">vector</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">State</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">r</span><span class="p">){</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">parameter</span><span class="w">
    </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">dndt</span><span class="p">;</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">];</span><span class="w">
    </span><span class="n">return</span><span class="w"> </span><span class="n">dndt</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></code></pre></div></div>

<p>The functions block is where a lot of the action happens for ODE-based models like ours, so it’s worth spending a little bit of time on.</p>

<p>Apparently the way that ODE’s work in Stan was <a href="https://mc-stan.org/users/documentation/case-studies/convert_odes.html">changed relatively recently</a>, however it seems to me that the general idea hasn’t changed - we write a function for our ODE in the functions block, and then give this function to the <strong>ODE solver</strong> later on in our model.</p>

<p>There are essentially two parts to our function. The first bit, from left to right, tells the function that we want our output as a <code class="language-plaintext highlighter-rouge">vector</code> (this is the only option allowed), that our function is called <code class="language-plaintext highlighter-rouge">exp_growth</code> (you can name it anything you like), and that our function has three inputs: <code class="language-plaintext highlighter-rouge">real t</code>, which is a time; <code class="language-plaintext highlighter-rouge">vector y</code>, which is the system’s state; and <code class="language-plaintext highlighter-rouge">real r</code> which is our ODE’s single parameter $r$. We could add more inputs if we had more parameters - this is exactly what we’ll do in the logistic growth model later on.</p>

<p>The second part of our function is where we actually code the ODE. We first have to make a vector, which I’ve called <code class="language-plaintext highlighter-rouge">dndt</code>, to store our time derivatives (i.e., our output). We then get to do the interesting bit, which is to actually code our differential equation: <code class="language-plaintext highlighter-rouge">dndt[1] = r*y[1];</code>. Finally, we just ask the function to return our output, <code class="language-plaintext highlighter-rouge">dndt</code>.</p>

<h3 id="the-data-block">The data block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_times</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">steps</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">one</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">data</span><span class="p">,</span><span class="w"> </span><span class="n">minus</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y0_obs</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">t0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">First</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">step</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">ts</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Time</span><span class="w"> </span><span class="n">steps</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>In this block we tell Stan about our data. Remember that when we were making <code class="language-plaintext highlighter-rouge">dlist</code> ealier on, we had to separate the initial state from the rest of the time series - we have to do the same thing in this block.</p>

<p>We first have <code class="language-plaintext highlighter-rouge">n_times</code>, which tells Stan how many time steps are after the initial state.</p>

<p>We then have our observed data <code class="language-plaintext highlighter-rouge">y</code>, as well as our observed initial state <code class="language-plaintext highlighter-rouge">y0_obs</code>.</p>

<p>After this, we have our initial time step <code class="language-plaintext highlighter-rouge">t0</code> followed by an array our time steps <code class="language-plaintext highlighter-rouge">ts</code>.</p>

<h3 id="the-parameters-block">The parameters block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">parameters</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">y0</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Initial</span><span class="w"> </span><span class="n">state</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">r</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Per</span><span class="o">-</span><span class="n">capita</span><span class="w"> </span><span class="n">rate</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">population</span><span class="w"> </span><span class="n">change</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Here we tell Stan about the parameters of our model - the populaton’s initial state <code class="language-plaintext highlighter-rouge">y0</code> and the per-capita rate of change <code class="language-plaintext highlighter-rouge">r</code>.</p>

<h3 id="the-transformed-parameters-block">The transformed parameters block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
    </span><span class="o">//</span><span class="w"> </span><span class="n">ODE</span><span class="w"> </span><span class="n">solver</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">lambda</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ode_rk45</span><span class="p">(</span><span class="n">exp_growth</span><span class="p">,</span><span class="w"> </span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">t0</span><span class="p">,</span><span class="w"> </span><span class="n">ts</span><span class="p">,</span><span class="w"> </span><span class="n">r</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This block contains the ODE solver - the function that solves the ODE, giving us an equation for the population size $n$ at any point in time.</p>

<p>In this example we’re using the <code class="language-plaintext highlighter-rouge">ode_rk45</code> solver, which is often a good sover to start with. You can find out about the other solvers which Stan provides <a href="https://mc-stan.org/docs/2_29/functions-reference/functions-ode-solver.html">in the Stan functions reference</a>. We need to give the solver our function <code class="language-plaintext highlighter-rouge">exp_growth</code> that we wrote in the functions block, the initial state parameter <code class="language-plaintext highlighter-rouge">y0</code>, the first timestep <code class="language-plaintext highlighter-rouge">t0</code> and the rest of the times <code class="language-plaintext highlighter-rouge">ts</code>, and our model’s parameters - in this case, it’s just <code class="language-plaintext highlighter-rouge">r</code>.</p>

<p>This code needs to go in the transformed parameters block, rather than the model block, so that <code class="language-plaintext highlighter-rouge">lambda</code> is available to the generated quantities block later on.</p>

<h3 id="the-model-block">The model block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">y0</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">1</span><span class="p">);</span><span class="w">
  
  </span><span class="o">//</span><span class="w"> </span><span class="n">Observation</span><span class="w"> </span><span class="n">model</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">poisson</span><span class="p">(</span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">]);</span><span class="w">
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="p">[</span><span class="n">t</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">poisson</span><span class="p">(</span><span class="n">lambda</span><span class="p">[</span><span class="n">t</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></code></pre></div></div>

<p>In this block, we start of by specifying priors for the parameters in our model. I’ve assumed a $Normal(0, 1)$ prior for $r$, which is probably not a great choice since we are probably going to know that the population is increasing over time (meaning $r$ must be positive) - but it’ll do for this example. I’ve given the initial state <code class="language-plaintext highlighter-rouge">y0</code> an $exponential(1)$ prior, since we know for certain it has to be positive (it can’t be negative because a negative population size makes no sense, and if it’s zero then we’ll only ever observe zero individuals so we’ll hopefully notice pretty quickly!) and it’s also likely to be small.</p>

<p>After the priors, we have the section of the model which deals with the observation process. The first line models the initial state, and the <code class="language-plaintext highlighter-rouge">for</code> loop deals with all of the subsequent timesteps.</p>

<h3 id="the-generated-quantities-block">The generated quantities block</h3>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">generated</span><span class="w"> </span><span class="n">quantities</span><span class="p">{</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">n_sim</span><span class="p">;</span><span class="w">
  </span><span class="n">n_sim</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poisson_rng</span><span class="p">(</span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">]);</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">){</span><span class="w">
    </span><span class="n">n_sim</span><span class="p">[</span><span class="n">t</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poisson_rng</span><span class="p">(</span><span class="n">lambda</span><span class="p">[</span><span class="n">t</span><span class="p">,</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></code></pre></div></div>

<p>In this block, we’re going to perform posterior predictive simulations - at each sampling iteration, we’re going to simulate a population time series <code class="language-plaintext highlighter-rouge">n_sim</code> using the <code class="language-plaintext highlighter-rouge">lambda</code> values from that iteration and the <code class="language-plaintext highlighter-rouge">poisson_rng</code> random number generator for the Poisson distribution. Later on, we’re going to compare these simulated values against the observed data to evaluate the model’s fit.</p>

<p>Actually coding this block is a little bit of a pain, mostly due to indexing - because we separated the initial time step in the other parts of the model, we have to compute the first value of <code class="language-plaintext highlighter-rouge">n_sim</code> using our initial state parameter <code class="language-plaintext highlighter-rouge">y0[1]</code>, and then compute all of the other <code class="language-plaintext highlighter-rouge">n_sim</code> values using the appropriate <code class="language-plaintext highlighter-rouge">lambda</code> value. In order to make the indexes match up in the correct place, we have to define <code class="language-plaintext highlighter-rouge">n_sim</code> as having <code class="language-plaintext highlighter-rouge">n_times+1</code> values and then index it as <code class="language-plaintext highlighter-rouge">n_sim[t+1]</code> in the <code class="language-plaintext highlighter-rouge">for</code> loop.</p>

<h2 id="running-the-model-and-exploring-the-output">Running the model and exploring the output</h2>

<p>We first need to save our model as a <code class="language-plaintext highlighter-rouge">.stan</code> file somewhere - when that’s done, we can run the model using the <code class="language-plaintext highlighter-rouge">cstan</code> function in the <code class="language-plaintext highlighter-rouge">rethinking</code> package:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">m_exp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cstan</span><span class="p">(</span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:/Stan_code/exponential_growth.stan"</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">dlist</span><span class="p">,</span><span class="w">
               </span><span class="n">chains</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="n">cores</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="n">warmup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1500</span><span class="p">,</span><span class="w">
               </span><span class="n">iter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2500</span><span class="p">,</span><span class="w">
               </span><span class="n">seed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">654</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>The model will now compile and sample - why not <a href="https://www.zooniverse.org/projects/peter-dot-stewart/prickly-pear-project-kenya">help to classify some of my camera trap photos</a> while it does so? :wink: When it’s done, we can inspect the <code class="language-plaintext highlighter-rouge">precis</code> summary table - I’m going to just show the first few rows here, but the real thing is quite long:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">precis</span><span class="p">(</span><span class="n">m_exp</span><span class="p">,</span><span class="w"> </span><span class="n">depth</span><span class="w"> </span><span class="o">=</span><span class="m">3</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/exponential_logistic_growth/exponential_precis.jpg" alt="" /></p>

<p>As you can see, we have our initial state parameter <code class="language-plaintext highlighter-rouge">y0</code> and our rate of change <code class="language-plaintext highlighter-rouge">r</code> at the top. We then have the <code class="language-plaintext highlighter-rouge">lambda</code> values for the 100 timesteps after the initial step, followed (in the non-truncated version you’ll see if you run the code) by the 101 <code class="language-plaintext highlighter-rouge">n_sim</code> values produced by the <code class="language-plaintext highlighter-rouge">generated quantities</code> block. At this point we’re particularly interested in our effective sample size <code class="language-plaintext highlighter-rouge">n_eff</code> and Gelman-Rubin convergence diagnostic <code class="language-plaintext highlighter-rouge">Rhat4</code> - we want the first to be a reasonably high number (ours look fine here), and we want the latter to equal one. It’s also a good idea to inspect traceplots for our key parameters at this point:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">traceplot</span><span class="p">(</span><span class="n">m_exp</span><span class="p">,</span><span class="w"> </span><span class="n">pars</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="s2">"y0[1]"</span><span class="p">,</span><span class="s2">"r"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p>Now we see that the model has sampled successfully, we can figure out whether it did a good job fitting the data. The first thing that we can do (since we simulated the data and know the true parameter values!) is to see how well the model estimated the per-capita rate of change, $r$ - as this is the parameter that we’re really interested in:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/exponential_logistic_growth/dens_r.jpeg" alt="" /></p>

<p>The model has really hit the nail on the head here - the true value for $r$ is right in the middle of our posterior distribution!</p>

<p>We can also plot the model’s posterior predictions against the raw data, like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x_seq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">,</span><span class="m">0.1</span><span class="p">)</span><span class="w">

</span><span class="n">n_sim_mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">median</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_99CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.99</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_95CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.95</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_89CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_80CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.80</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_70CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.70</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_60CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.60</span><span class="p">)</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">200</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Population"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">n_sim_mu</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_99CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_95CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_89CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_80CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_70CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_60CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">

</span><span class="n">points</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">n_obs</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/exponential_logistic_growth/exponential_posterior_prediction.jpeg" alt="" /></p>

<p>The fit looks very good!</p>

<h1 id="logistic-growth">Logistic growth</h1>

<h2 id="introducing-the-model-1">Introducing the model</h2>

<p>Populations can’t just grow exponentially forever - sooner or later, intraspecific competition, limited resources and other factors prevent the population from growing further.</p>

<p>To model this idea, we can extend the exponential growth model by introducing a <strong>carrying capacity</strong> ($K$), which is the maximum size which our population will reach. We can include $K$ in our model n such a way that when the population size equals the carrying capacity, $\frac{dn}{dt}$ will equal zero and the population will cease to grow any further. This means that the rate of population growth is <strong>density dependent</strong> - as the population gets larger, the rate of increase slows. The model looks like this:</p>

\[\frac{dn}{dt} = rn(t)\left(1-\frac{n(t)}{K}\right)\]

<p>You can actually derive this specific equation by assuming that $r$ declines linearly as the population size increases - Otto and Day cover this in their book, so I’m not going to go into it here.</p>

<h2 id="simulating-the-data-in-r-1">Simulating the data in R</h2>

<p>Simulating data for logistic growth is actually very similar to the process we carried out for exponential growth, so rather than going through it step-by-step I’m just going to present the whole simulation code in one go. The main change is that we’ve changed our function to the equation for logistic growth, and we’ve added the parameter for the carrying capacity <code class="language-plaintext highlighter-rouge">K</code> in the appropriate places:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Function for logistic growth</span><span class="w">
</span><span class="n">logistic_growth</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="n">times</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">parms</span><span class="p">){</span><span class="w">
  </span><span class="n">dn.dt</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">r</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="m">1</span><span class="o">-</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="o">/</span><span class="n">K</span><span class="p">))</span><span class="w">
  </span><span class="nf">return</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">dn.dt</span><span class="p">))</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Values for parameters and time sequence </span><span class="w">
</span><span class="n">r</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w"> </span><span class="c1"># Per-capita rate of change</span><span class="w">
</span><span class="n">K</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">50</span><span class="w"> </span><span class="c1"># Carrying capacity</span><span class="w">
</span><span class="n">y0</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="c1"># Initial state</span><span class="w">
</span><span class="n">time</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">25</span><span class="p">,</span><span class="n">by</span><span class="o">=</span><span class="m">0.1</span><span class="p">)</span><span class="w"> </span><span class="c1"># Time sequence</span><span class="w">

</span><span class="c1"># Simulate true population size </span><span class="w">
</span><span class="n">n_true</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ode</span><span class="p">(</span><span class="n">y</span><span class="o">=</span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">times</span><span class="o">=</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">func</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">,</span><span class="w"> </span><span class="n">parms</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="n">K</span><span class="p">))</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">n_true</span><span class="p">[,</span><span class="m">2</span><span class="p">])</span><span class="w">

</span><span class="c1"># Simulate observation process</span><span class="w">
</span><span class="n">n_obs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rpois</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">time</span><span class="p">),</span><span class="w">
               </span><span class="n">lambda</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_true</span><span class="p">[,</span><span class="m">2</span><span class="p">])</span><span class="w">

</span><span class="c1"># Plot observed data and true line</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">n_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Number of individuals"</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">time</span><span class="p">,</span><span class="w"> </span><span class="n">n_true</span><span class="p">[,</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">type</span><span class="o">=</span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">

</span><span class="c1"># Make a data list for Stan</span><span class="w">
</span><span class="n">dlist</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">n_times</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">time</span><span class="p">)</span><span class="m">-1L</span><span class="p">,</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_obs</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">n_obs</span><span class="p">[</span><span class="m">-1</span><span class="p">],</span><span class="w">
  </span><span class="n">t0</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">ts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">[</span><span class="m">-1</span><span class="p">]</span><span class="w">
</span><span class="p">)</span><span class="w">

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

<p>Plotting the observed data along with <code class="language-plaintext highlighter-rouge">n_true</code>, we can see how our datapoints fall around the classic “S-curve”:</p>

<p><img src="/assets/images/post_images/exponential_logistic_growth/logistic_observed.jpeg" alt="" /></p>

<h2 id="coding-the-model-in-stan-1">Coding the model in Stan</h2>

<p>Now we can code the model in Stan. Here is the full model:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="w"> </span><span class="n">logistic_growth</span><span class="p">(</span><span class="n">real</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w">
                    </span><span class="n">vector</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w">
                    </span><span class="n">real</span><span class="w"> </span><span class="n">K</span><span class="p">){</span><span class="w">
    </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">dndt</span><span class="p">;</span><span class="w">
    </span><span class="n">dndt</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="o">*</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="m">1</span><span class="o">-</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="o">/</span><span class="n">K</span><span class="p">));</span><span class="w">
    </span><span class="n">return</span><span class="w"> </span><span class="n">dndt</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">data</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">n_times</span><span class="p">;</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y0_obs</span><span class="p">;</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">t0</span><span class="p">;</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">ts</span><span class="p">;</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">parameters</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">y0</span><span class="p">;</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">r</span><span class="p">;</span><span class="w">
  </span><span class="n">real</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="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
    </span><span class="o">//</span><span class="w"> </span><span class="n">ODE</span><span class="w"> </span><span class="n">solver</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="n">lambda</span><span class="p">[</span><span class="n">n_times</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ode_rk45</span><span class="p">(</span><span class="n">logistic_growth</span><span class="p">,</span><span class="w"> </span><span class="n">y0</span><span class="p">,</span><span class="w"> </span><span class="n">t0</span><span class="p">,</span><span class="w"> </span><span class="n">ts</span><span class="p">,</span><span class="w"> </span><span class="n">r</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="n">model</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">r</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">y0</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">1</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="n">exponential</span><span class="p">(</span><span class="m">0.05</span><span class="p">);</span><span class="w">
  
  </span><span class="o">//</span><span class="w"> </span><span class="n">Likelihood</span><span class="w">
  </span><span class="n">y0_obs</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">poisson</span><span class="p">(</span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">]);</span><span class="w">
  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">y</span><span class="p">[</span><span class="n">t</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">poisson</span><span class="p">(</span><span class="n">lambda</span><span class="p">[</span><span class="n">t</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">generated</span><span class="w"> </span><span class="n">quantities</span><span class="p">{</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">n_times</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="n">n_sim</span><span class="p">;</span><span class="w">
  </span><span class="n">n_sim</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poisson_rng</span><span class="p">(</span><span class="n">y0</span><span class="p">[</span><span class="m">1</span><span class="p">]);</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">n_times</span><span class="p">){</span><span class="w">
    </span><span class="n">n_sim</span><span class="p">[</span><span class="n">t</span><span class="m">+1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">poisson_rng</span><span class="p">(</span><span class="n">lambda</span><span class="p">[</span><span class="n">t</span><span class="p">,</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></code></pre></div></div>

<p>It actually looks really similar to the exponential growth model - there are <strong>only a few minor changes to the functions, parameters, transformed parameters, and model blocks.</strong> Let’s go through these step by step:</p>

<h3 id="the-functions-block-1">The functions block</h3>

<p>This is the bit with the most changes, but even here they’re pretty minor - we’ve just added a slot for our carrying capacity <code class="language-plaintext highlighter-rouge">K</code> in our function, and changed the equation to the one for the logistic growth model.</p>

<h3 id="the-data-block-1">The data block</h3>

<p>No changes here!</p>

<h3 id="the-parameters-block-1">The parameters block</h3>

<p>In this block, we’ve just added the parameter <code class="language-plaintext highlighter-rouge">K</code> for the carrying capacity.</p>

<h3 id="the-transformed-parameters-block-1">The transformed parameters block</h3>

<p>The only thing new here is adding <code class="language-plaintext highlighter-rouge">K</code> to the <code class="language-plaintext highlighter-rouge">ode_rk45</code> function.</p>

<h3 id="the-model-block-1">The model block</h3>

<p>The only thing we’ve changed here is adding a prior for the carrying capacity, <code class="language-plaintext highlighter-rouge">K</code>. There are probably better choices than the $exponential(0.05)$ prior I’ve got here, but my choice does at least constrain <code class="language-plaintext highlighter-rouge">K</code> to be positive.</p>

<h3 id="the-generated-quantities-block-1">The generated quantities block</h3>

<p>No changes here!</p>

<h2 id="running-the-model-and-exploring-the-output-1">Running the model and exploring the output</h2>

<p>We can run the model and explore the output using almost the same code as we used for the exponential growth model - we just have to substitute in the new model name where appropriate, and slightly alter the scale at which we’re plotting the data due to the different time and population ranges we’re dealing with. Here is all of the code to run the model, check the summary table and traceplots, and plot our posterior predictions against the observed data:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Run the model</span><span class="w">
</span><span class="n">m_log</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cstan</span><span class="p">(</span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:/Stan_code/logistic_growth.stan"</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">dlist</span><span class="p">,</span><span class="w">
               </span><span class="n">chains</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="n">cores</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="n">warmup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1500</span><span class="p">,</span><span class="w">
               </span><span class="n">iter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2500</span><span class="p">,</span><span class="w">
               </span><span class="n">seed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">543</span><span class="p">)</span><span class="w">

</span><span class="c1"># Summary table</span><span class="w">
</span><span class="n">precis</span><span class="p">(</span><span class="n">m_log</span><span class="p">,</span><span class="w"> </span><span class="n">depth</span><span class="w"> </span><span class="o">=</span><span class="m">3</span><span class="p">)</span><span class="w">

</span><span class="c1"># Traceplots for key parameters</span><span class="w">
</span><span class="n">traceplot</span><span class="p">(</span><span class="n">m_log</span><span class="p">,</span><span class="w"> </span><span class="n">pars</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="s2">"y0[1]"</span><span class="p">,</span><span class="s2">"r"</span><span class="p">,</span><span class="s2">"K"</span><span class="p">))</span><span class="w">

</span><span class="c1"># Extract posterior samples</span><span class="w">
</span><span class="n">post</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">extract.samples</span><span class="p">(</span><span class="n">m_log</span><span class="p">)</span><span class="w">

</span><span class="c1"># Density plots for key parameters - vertical lines for true values</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">r</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">r</span><span class="p">,</span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">K</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">K</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">)</span><span class="w">

</span><span class="c1"># Posterior predictive plot</span><span class="w">
</span><span class="n">x_seq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">25</span><span class="p">,</span><span class="m">0.1</span><span class="p">)</span><span class="w">

</span><span class="c1"># Calculate mean and compatability intervals for n_sim</span><span class="w">
</span><span class="n">n_sim_mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">median</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_99CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.99</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_95CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.95</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_89CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_80CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.80</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_70CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.70</span><span class="p">)</span><span class="w">
</span><span class="n">n_sim_60CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">n_sim</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.60</span><span class="p">)</span><span class="w">

</span><span class="c1"># Make the plot</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">25</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">80</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Time"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Population"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">n_sim_mu</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_99CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_95CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_89CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_80CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_70CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">n_sim_60CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">

</span><span class="n">points</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">n_obs</span><span class="p">)</span><span class="w"> </span><span class="c1"># Add points for raw data</span><span class="w">

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

<p>Looking at our values for $r$ (left) and $K$ (right), we can see that the model has accurately estimated both parameters:</p>

<p><img src="/assets/images/post_images/exponential_logistic_growth/logistic_parameters.jpeg" alt="" /></p>

<p>And when we plot the model’s posterior predictions against the raw data, we see a great fit:</p>

<p><img src="/assets/images/post_images/exponential_logistic_growth/logistic_posterior_predictive.jpeg" alt="" /></p>]]></content><author><name>Peter S. Stewart</name></author><category term="blog" /><category term="Stan" /><category term="tutorial" /><category term="classic ecological models" /><summary type="html"><![CDATA[In this post, I cover how to fit two simple models of population growth using Stan: exponential growth, and logistic growth. I also cover how to use R to simulate data to fit these models to.]]></summary></entry><entry><title type="html">An occupancy model with a Gaussian process for spatial autocorrelation in Stan</title><link href="https://peter-stewart.github.io/blog/gaussian-process-occupancy-tutorial/" rel="alternate" type="text/html" title="An occupancy model with a Gaussian process for spatial autocorrelation in Stan" /><published>2022-05-19T00:00:00+00:00</published><updated>2022-05-19T00:00:00+00:00</updated><id>https://peter-stewart.github.io/blog/gaussian-process-occupancy-tutorial</id><content type="html" xml:base="https://peter-stewart.github.io/blog/gaussian-process-occupancy-tutorial/"><![CDATA[<p>In this post I walk through an occupancy model which uses a Gaussian process to model spatial autocorrelation. The model is coded in Stan. I start off with a brief overview of occupancy models and Gaussian process regression. I then walk through how to simulate a dataset from the assumed data-generating process in R, and then explain the model and Stan code step-by-step. Finally, I look at some of the interesting things we can do once we have the posterior distribution.</p>

<h1 id="acknowledgements-and-other-resources">Acknowledgements and other resources</h1>

<p>I used several resources when writing this post. In particular, the majority of my knowledge about Bayesian statistics, Stan, and Gaussian processes comes from Richard McElreath’s fantastic book and lecture series <em>Statistical Rethinking</em>. The whole course is available for free online, I highly recommend you <a href="https://github.com/rmcelreath/stat_rethinking_2022">check out the most recent version here</a>. I also learned a lot about Stan from Michael Betancourt’s excellent case studies, in particular the <a href="https://betanalpha.github.io/assets/case_studies/stan_intro.html">introduction to Stan</a>. I learned about how to marginalise out the discrete occupancy state parameters from Maxwell B. Joseph’s <a href="https://mbjoseph.github.io/posts/2020-04-28-a-step-by-step-guide-to-marginalizing-over-discrete-parameters-for-ecologists-using-stan/">tutorial</a>, and the supplement of <a href="https://onlinelibrary.wiley.com/doi/full/10.1002/ece3.4850">this paper</a> by Allan Clark and Res Altwegg - my understanding mainly comes from the former, my code from the latter. Some of the other key resources which I used to learn about about occupancy models are the <a href="https://www.elsevier.com/books/occupancy-estimation-and-modeling/mackenzie/978-0-12-088766-8">classic book</a> by MacKenzie <em>et al.</em> and <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0192819">this paper</a> by Joe Northrup and Brian Gerber. I’d also like to thank the Conservation Ecology Group at Durham University, as this post was inspired by a talk I gave at one of their lab group meetings a few months ago.</p>

<h1 id="an-important-note">An important note</h1>

<p>This post should be considered a work in progress - I greatly appreciate any feedback which I can implement to improve it, particularly if you spot any mistakes!</p>

<h1 id="introduction">Introduction</h1>

<h2 id="occupancy-models">Occupancy models</h2>

<p>Occupancy models are used to study the patterns and drivers of species occurrence. They are very commonly used for camera trap data, which is <a href="https://www.zooniverse.org/projects/peter-dot-stewart/prickly-pear-project-kenya">why I got interested in them in the first place</a>. One of their key characteristics is that they deal with imperfect detection - the chance that a species which is present at a site may remain undetected. In this section, I’ll walk through a standard single-season occupancy model - I follow the model notation from <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0192819">Northrup and Gerber</a> pretty closely here. We’ll expand the model to include spatial autocorrelation in the next section.</p>

<p>Imagine that we have a bunch of sites dotted about the landscape. The state ($z$) of a site (indexed $i$) is either occupied (1) or unoccupied (0), and the probability that it is occupied is called the <strong>occupancy probability ($\psi$)</strong>. We can write this as:</p>

\[z_{i} \sim Bernoulli(\psi_{i})\]

<p>We assume that a site stays in one state (occupied or not) for the duration of the study, i.e. that there is a <em>single season</em>. You can also get multi-season or dynamic occupancy models which assume that the state may change, but I’m not going to cover those here.</p>

<p>We’re often interested in modelling $\psi$ as a function of one or more covariates, for instance because we want to know the effect of some environmental variable on occupancy. It’s important to note that it is very important to consider the purpose of the model (inference or prediction) when deciding on which covariates to put in the model - this is covered by <a href="https://www.biorxiv.org/content/10.1101/2022.03.01.482466v2">my recent preprint</a>. We can add covariates in a standard logistic regression format, for example for a single covariate $X$:</p>

\[logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \alpha + \beta_{X}X_{i}\]

<p>Now comes the bit where we account for imperfect detection. We visit each site multiple ($v_{i}$) times and record whether we observe the species on each visit. The observed data at each site ($y_{i}$) can then be modelled as:</p>

\[y_{i} \sim Binomial(v_{i}, \mu_{i})\\ \mu_{i} = p_{i}z_{i}\]

<p>In the second line, $z_{i}$ is the true occupancy state and <strong>$p_i$ is the probability of detection.</strong> Therefore, if a species is absent from a site is is never detected, and if it is present it is detected on each visit with probability $p$. We can also make the detection probability a function of covariates, for instance:</p>

\[logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i}\]

<p>It’s also pretty common to see models where detection probability is allowed to be different on each visit and you have a time-varying covariate - this would be great for situations in which the probability we detect a species on a visit varies due to factors such as weather. The only thing we need to change is the indexing - for instance, we could now have $W_{i,j}$ for the value of $W$ at site $i$ on visit $j$. This is what I do in the example model later on.</p>

<p>Taking everything we’ve got so far, the full model with priors would look like:</p>

\[y_{i} \sim Binomial(v_{i}, \mu_{i})\\ 
\mu_{i} = p_{i}z_{i} \\
z_{i} \sim Bernoulli(\psi_{i}) \\ 
logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \alpha + \beta_{X}X_{i} \\
logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i} \\
\beta_{X} \sim Normal(0,1) \\ 
\beta_{W} \sim Normal(0,1) \\
\alpha \sim Normal(0,1) \\
\alpha_{det} \sim Normal(0, 0.5)\]

<p>You’d want to pick the specific priors for your problem at hand by doing some <strong>prior predictive simulations</strong>, which we’ll do for our example later on.</p>

<p>Now, it’s time to expand our occupancy model to account for spatial autocorrelation!</p>

<h2 id="spatial-autocorrelation-and-gaussian-process-regression">Spatial autocorrelation and Gaussian process regression</h2>

<p>The occupancy model above assumes that the occupancy probability of our sites is independent, regardless of where they are situated in space. However, in many instances this is unlikely to be the case - <strong>sites which are closer together tend to be more similar than sites which are further apart</strong>. This phenomenon is called <strong>spatial autocorrelation</strong>.</p>

<p>There are a variety of ways that we could include this spatial autocorrelation in our model. One really cool way is to use <strong>Gaussian process regression.</strong></p>

<p>The theory behind Gaussian process regression is already covered by <a href="https://www.youtube.com/watch?v=PIuqxOBJqLU&amp;ab_channel=RichardMcElreath">Statistical Rethinking</a>, as well as <a href="https://betanalpha.github.io/assets/case_studies/gaussian_processes.html">this case study</a> and the <a href="https://mc-stan.org/docs/2_29/stan-users-guide/gaussian-process-regression.html">Stan Users’ guide</a>. For this reason, I’m just going to give a quick overview here so that our extensions to the occupancy model make sense.</p>

<p>Remember that the essential idea is that <strong>sites which are closer tend to be more similar</strong>. A natural way to express this idea is by having a function to link the covariance between a pair of sites (we’ll index these $i$ and $j$) to the distance between them. This function is usually called a <strong>kernel function</strong>, and one example is:</p>

\[K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j})\]

<p>Where $K_{i,j}$ is the covariance between sites $i$ and $j$, $\eta^2$ is the maximum covariance between sites, $\rho^2$ is the rate that covariance declines with distance, and $D^2_{i,j}$ is the (squared) distance between sites $i$ and $j$. Because we have more than one pair of sites, we’re going to feed in a <strong>distance matrix</strong> and end up with a <strong>covariance matrix</strong>.</p>

<p>We now want to turn this covariance matrix into something that we can use in our linear model for occupancy probability - we want some kind of <strong>varying intercept</strong>. One way of doing this is to have an average intercept across all sites ($\bar{k}$) and then a site-specific offset ($k_{i}$) like so:</p>

\[logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \bar{k} + k_{i} + \beta_{X}X_{i} \\\]

<p>Notice that what we’ve done is replace the intercept ($\alpha$) from the model we saw earlier with $\bar{k}$ and $k_{i}$. We’ve still got the covariate $X$ as before. The final thing we need to do is connect our site-level offset $k_{i}$ back to the kernel function. We can do this using the multivariate normal distribution, like this:</p>

\[\begin{pmatrix}k_{1} \\ k_{2} \\ \vdots \\ k_{i} \end{pmatrix} 
\sim
MVNormal\left(\begin{pmatrix}0\\0\\0\\0\end{pmatrix}, \textbf{K}\right)\]

<p>You can see the covariance matrix $\textbf{K}$ formed from all of the combinations of $K{i,j}$ on the right. The mean of the multivariate normal distribution is filled with zeroes because we already have $\bar{k}$ in the model above.</p>

<p>The only thing left to do is add some priors for the new parameters - with the priors included, the whole model looks like:</p>

\[y_{i} \sim Binomial(v_{i}, \mu_{i})\\ 
\mu_{i} = p_{i}z_{i} \\
z_{i} \sim Bernoulli(\psi_{i}) \\ 
logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \bar{k} + k_{i} + \beta_{X}X_{i} \\
\begin{pmatrix}k_{1} \\ k_{2} \\ \vdots \\ k_{i} \end{pmatrix} 
\sim
MVNormal\left(\begin{pmatrix}0\\0\\0\\0\end{pmatrix}, \textbf{K}\right) \\
K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j}) \\
logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i} \\
\beta_{X},  \beta_{W} \sim Normal(0,1) \\
\alpha_{det} \sim Normal(0, 0.5)\\
\bar{k} \sim Normal(0, 0.2) \\
\eta^2, \rho^2 \sim Exponential(1)\]

<p>Again, prior predictive simulations were used to help choose the priors - I cover how to do these below.</p>

<h1 id="simulating-from-the-data-generating-process-in-r">Simulating from the data-generating process in R</h1>

<p>A really good way to help develop a model is to think about the process which could have produced the data, and then make a simulation of this process. We can then fit the model to these simulated data, which is a great way of identifying and fixing any issues with our model before we fit it to real data!</p>

<p>In this section, we’ll go through the code I used to simulate the data.</p>

<p>Let’s start by loading a couple of packages that we’ll need:</p>

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

<p>Now, let’s set up some basic parameters for the simulation - we’ll go for 100 sites, with each site surveyed somewhere from 14 to 21 times. We’ll also set a seed for R’s random number generator, so that you can replicate my results exactly:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sites</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">
</span><span class="n">surveys</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sample</span><span class="p">(</span><span class="m">14</span><span class="o">:</span><span class="m">21</span><span class="p">,</span><span class="w"> </span><span class="n">size</span><span class="o">=</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="n">replace</span><span class="o">=</span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span><span class="n">set.seed</span><span class="p">(</span><span class="m">1234</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>Now we’re going to generate a random set of x and y coordinates for each site, showing where it is situated in our study region. We’re then going to use these coordinates to make a distance matrix, which contains the distance between each pair of sites:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">runif</span><span class="p">(</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">10</span><span class="p">)</span><span class="w"> </span><span class="c1"># x coordinate</span><span class="w">
</span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">runif</span><span class="p">(</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">10</span><span class="p">)</span><span class="w"> </span><span class="c1"># y coordinate</span><span class="w">

</span><span class="n">coords</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.data.frame</span><span class="p">(</span><span class="n">cbind</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">))</span><span class="w">

</span><span class="c1"># Make distance matrix from coordinates</span><span class="w">
</span><span class="n">dmat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">dist</span><span class="p">(</span><span class="n">coords</span><span class="p">,</span><span class="w"> </span><span class="n">diag</span><span class="o">=</span><span class="nb">T</span><span class="p">,</span><span class="w"> </span><span class="n">upper</span><span class="o">=</span><span class="nb">T</span><span class="p">)</span><span class="w">
</span><span class="n">dmat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">dmat</span><span class="p">)</span><span class="w">

</span><span class="n">dmat2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">dmat</span><span class="o">^</span><span class="m">2</span><span class="w"> </span><span class="c1"># Squared distances</span><span class="w">
</span></code></pre></div></div>

<p>Now that we’ve got our distance matrix, we need to code the function that calculates the covariance between each pair of sites from the distance between them. Remember that our kernel function is:</p>

\[K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j})\]

<p>with definitions as above. We’re going to pick some values for $\eta^2$ and $\rho^2$ and then generate the covariance matrix:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">eta2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.8</span><span class="w">
</span><span class="n">rho2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w">
</span><span class="n">covmat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">eta2</span><span class="o">*</span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">rho2</span><span class="o">*</span><span class="n">dmat2</span><span class="p">)</span><span class="w"> </span><span class="c1"># Max value should be eta2 on the diagonals</span><span class="w">
</span></code></pre></div></div>

<p>You can visualise how our choice of values affects the kernel function by plotting   covariance against distance. It’s also helpful here to plot the distribution of distances between sites, to see how many pairs will have each amount of covariance:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">2</span><span class="p">))</span><span class="w">
</span><span class="n">curve</span><span class="p">(</span><span class="n">eta2</span><span class="o">*</span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">rho2</span><span class="o">*</span><span class="n">x</span><span class="o">^</span><span class="m">2</span><span class="p">),</span><span class="n">from</span><span class="o">=</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">to</span><span class="o">=</span><span class="m">10</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Distance"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Covariance"</span><span class="p">)</span><span class="w"> </span><span class="c1"># Visualise covariance (y) vs distance (x)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">as.vector</span><span class="p">(</span><span class="n">dmat</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Distance"</span><span class="p">)</span><span class="w"> </span><span class="c1"># Visualise the distances between sites as well</span><span class="w">
</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/cov_distance.jpeg" alt="" /></p>

<p>You can use this code to play around with the $\eta^2$ and $\rho^2$ values to see how they affect the way that covariance declines with distance.</p>

<p>Now that we have our covariance matrix, we want to use it to generate a varying intercept for the occupancy probability. This means that <strong>sites which are closer together tend to have a more similar occupancy probability</strong>. We do this using the multivariate normal distribution with the <code class="language-plaintext highlighter-rouge">mvrnorm</code> function from the <code class="language-plaintext highlighter-rouge">MASS</code> package:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">z</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="p">(</span><span class="n">covmat</span><span class="p">))</span><span class="w">

</span><span class="n">varint</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mvrnorm</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">1</span><span class="p">,</span><span class="w"> 
                  </span><span class="n">mu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">z</span><span class="p">,</span><span class="w"> 
                  </span><span class="n">Sigma</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">covmat</span><span class="p">)</span><span class="w">
</span><span class="n">varint</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">varint</span><span class="p">)</span><span class="w">   
</span></code></pre></div></div>

<p>Note that we’re ignoring $\bar{k}$ here and making the mean in <code class="language-plaintext highlighter-rouge">mvnorm</code> zero, which is the same as assuming that $\bar{k}$ is equal to zero.</p>

<p>Now we’re going to add two covariates for the occupancy probability: $X$, which is our focal variable (i.e., we’re interested in the effect of $X$ on $\psi$ ) and $M$, which is a confounding variable. We will assume that the variables are related as shown in this DAG:</p>

<p><img src="/assets/images/post_images/spatial_occupancy/dag1.jpg" alt="" /></p>

<p>The consequence is that if we want to estimate the effect of $X$ on $\psi$, we need to condition on $M$ in our occupancy sub-model. We will also include a covariate $W_{t}$ for the detection probability. We’re giving this covariate the subscript $t$ because it is time-varying, meaning that it has a different value for each survey of the site.</p>

<p>Let’s code up the covariates, effect sizes ($\beta_{X}, \beta_{M},$ and $\beta_{MX}$), and the true occupancy and detection probabilities:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># True effect sizes:</span><span class="w">
</span><span class="n">betax</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1.0</span><span class="w"> </span><span class="c1"># Effect of x on psi</span><span class="w">
</span><span class="n">betam</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">-0.8</span><span class="w"> </span><span class="c1"># Effect of m on psi</span><span class="w">
</span><span class="n">betamx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.5</span><span class="w"> </span><span class="c1"># Effect of m on x</span><span class="w">

</span><span class="c1"># Variables</span><span class="w">
</span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
</span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="n">betamx</span><span class="o">*</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">

</span><span class="c1"># True occupancy probability at each site</span><span class="w">
</span><span class="n">psi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">varint</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">betax</span><span class="o">*</span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">betam</span><span class="o">*</span><span class="n">m</span><span class="p">)</span><span class="w">

</span><span class="c1"># Covariate for detection probability</span><span class="w">
</span><span class="n">w</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="nf">max</span><span class="p">(</span><span class="n">surveys</span><span class="p">))</span><span class="w">

</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">surveys</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span><span class="w">
    </span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">0</span><span class="p">,</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="c1"># True detection probability</span><span class="w">
</span><span class="n">alphadet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">-0.1</span><span class="w"> </span><span class="c1"># </span><span class="w">
</span><span class="n">betadet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">0.4</span><span class="w"> </span><span class="c1"># </span><span class="w">

</span><span class="n">pdet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="nf">max</span><span class="p">(</span><span class="n">surveys</span><span class="p">))</span><span class="w">

</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">surveys</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span><span class="w">
    </span><span class="n">pdet</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">alphadet</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">betadet</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">,</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></code></pre></div></div>

<p>Now we’re ready to populate our simulated landscape and conduct our simulated surveys! Let’s simulate the true occupancy state (1 = occupied, 0 = unoccupied) for each site, and then simulate the observed detection history for each site:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># True occupancy state</span><span class="w">
</span><span class="n">z</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">length</span><span class="o">=</span><span class="n">sites</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
  </span><span class="n">z</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbinom</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">psi</span><span class="p">[</span><span class="n">i</span><span class="p">])</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Observed detection history</span><span class="w">
</span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="nf">max</span><span class="p">(</span><span class="n">surveys</span><span class="p">))</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">surveys</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span><span class="w">
    </span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rbinom</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="n">pdet</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">k</span><span class="p">]</span><span class="o">*</span><span class="n">z</span><span class="p">[</span><span class="n">i</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></code></pre></div></div>

<p>Finally, let’s get the data ready for Stan.</p>

<p>As Stan can’t deal with the NA values in visits which did not occur (e.g., where a site was surveyed 17 times, the final 4 surveys will be NA), we have to replace them with a number - the specific number doesn’t really matter, because Stan should never access the values in its calculations. I prefer to use a ridiculous number like -9999 because then if Stan does access the number somehow, it should result in an error which is relatively easy to detect.</p>

<p>We can then make the data list for Stan to use. This includes the observed data <code class="language-plaintext highlighter-rouge">y</code>, the covariates <code class="language-plaintext highlighter-rouge">x</code>, <code class="language-plaintext highlighter-rouge">m</code> and <code class="language-plaintext highlighter-rouge">w</code>,  the distance matrix <code class="language-plaintext highlighter-rouge">dmat</code>, the number of sites <code class="language-plaintext highlighter-rouge">nsites</code>, the number of surveys at each site <code class="language-plaintext highlighter-rouge">V</code> and the maximum number of visits made to a site, <code class="language-plaintext highlighter-rouge">N_maxvisits</code>:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">y</span><span class="p">[</span><span class="nf">is.na</span><span class="p">(</span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">-9999</span><span class="w">
</span><span class="n">mode</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="s2">"integer"</span><span class="w">

</span><span class="n">w</span><span class="p">[</span><span class="nf">is.na</span><span class="p">(</span><span class="n">w</span><span class="p">)]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">-9999</span><span class="w">

</span><span class="c1"># Data list for Stan</span><span class="w">
</span><span class="n">dlist</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">nsites</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.integer</span><span class="p">(</span><span class="n">sites</span><span class="p">),</span><span class="w">
  </span><span class="n">N_maxvisits</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">as.integer</span><span class="p">(</span><span class="nf">max</span><span class="p">(</span><span class="n">surveys</span><span class="p">)),</span><span class="w">
  </span><span class="n">V</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">surveys</span><span class="p">,</span><span class="w">
  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">y</span><span class="p">,</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w">
  </span><span class="n">w</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w">
  </span><span class="n">dmat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dmat</span><span class="w">
</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h1 id="prior-predictive-simulations">Prior predictive simulations</h1>

<p>As I mentioned earlier, we’re going to use prior predictive simulations to help choose our priors for the model. There are two main sets of parameters that we need to think about here: the effect sizes and intercepts for the occupancy and detection linear models ($\beta_{X}, \beta_{M},\alpha_{det},\beta_{det}$) and the parameters for the Gaussian process ($\eta^2$ and $\rho^2$).</p>

<p>Let’s start with the first set of parameters - the effect sizes and intercepts. Let’s think about the detection submodel to start with. Remember that the relevant bit of our model is:</p>

\[logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i} \\
\alpha_{det} \sim Normal(?,?) \\
\beta_{W} \sim ~Normal(?,?)\]

<p>I’ve suggested that we use normal prior distributions for our two parameters, because we don’t have to constrain either parameter to be positive. I’m also going to suggest we make the mean of both priors zero. In the case of $\beta_{W}$ this means we’re assigning equal prior probability to positive and negative effects of $W$. For $\alpha_{det}$ it means that the highest prior probability of detection is 0.5 when $W$ is zero. We still have to pick some values for the variance of our priors though. The idea is we’re going pick some values, then draw a bunch of random lines from our prior distributions and plot them to see if they look sensible. In the interest of illustrating an important point about prior distributions in occupancy models, I’m going to start off with some very flat priors like $Normal(0, 10)$. Let’s see what happens:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">N</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">500</span><span class="w"> </span><span class="c1"># Number of lines to draw from priors</span><span class="w">
</span><span class="n">alpha</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="w"> </span><span class="n">N</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="m">10</span><span class="w"> </span><span class="p">)</span><span class="w"> 
</span><span class="n">beta</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="w"> </span><span class="n">N</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="m">10</span><span class="w"> </span><span class="p">)</span><span class="w">

</span><span class="c1">## Make an empty plot</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="w"> </span><span class="kc">NULL</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">-2</span><span class="p">,</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">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"x"</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"p_det"</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="w"> </span><span class="n">h</span><span class="o">=</span><span class="m">0.5</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="c1"># Add horizontal line at 0.5 detection probablity</span><span class="w">
</span><span class="c1"># Draw N lines using our priors</span><span class="w">
</span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">N</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="n">curve</span><span class="p">(</span><span class="n">inv_logit</span><span class="p">(</span><span class="w"> </span><span class="n">alpha</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">beta</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="p">,</span><span class="w">
                        </span><span class="n">from</span><span class="o">=</span><span class="m">-2</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">to</span><span class="o">=</span><span class="m">2</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">add</span><span class="o">=</span><span class="kc">TRUE</span><span class="w"> </span><span class="p">,</span><span class="w">
                        </span><span class="n">col</span><span class="o">=</span><span class="n">col.alpha</span><span class="p">(</span><span class="s2">"black"</span><span class="p">,</span><span class="m">0.2</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/flat_priors.jpeg" alt="" /></p>

<p>As we can see, something has gone badly wrong - nearly all of the prior probability is piled up on zero and one! The reason is that our priors are too flat. Remember that our priors are on the log-odds scale, and they will be turned into probability through the $logit$ link function. Because values below -4 and above 4 on the log-odds scale correspond to probabilities very close to 0 and 1 respectively, assigning a lot of our prior probability to these values by choosing a flat prior with a lot of area here makes the model assign a lot of prior probability to extreme values. Northrup and Gerber discuss this phenomenon in more detail in <a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0192819">their paper</a>, and offer recommendations for better priors. We’re going to try some tighter priors instead: $Normal(0,0.5)$ for $\alpha_{det}$ and $Normal(0,1)$ for $\beta_{W}$. If you modfy the code above to these values, then you get this plot:</p>

<p><img src="/assets/images/post_images/spatial_occupancy/better_priors.jpeg" alt="" /></p>

<p>This looks much more sensible. We are also going to learn from this experience by choosing relatively tight priors for $\bar{k}$ and our $\beta$ values in the occupancy submodel: I’m going to suggest $Normal(0,0.2)$ and $Normal(0,1)$ respectively.</p>

<p>We’re going to use a similar strategy to choose priors for our Gaussian process parameters. However, in this case I find that it can be difficult to interpret lines drawn from the priors because of the way they plot over one-another, so I’m going to plot the compatability intervals instead:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">samp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1e4</span><span class="w">
</span><span class="n">rho2_prior</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rexp</span><span class="p">(</span><span class="n">samp</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
</span><span class="n">eta2_prior</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rexp</span><span class="p">(</span><span class="n">samp</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">

</span><span class="n">x_seq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">4</span><span class="p">,</span><span class="m">0.01</span><span class="p">)</span><span class="w">
</span><span class="n">priorcov</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">eta2_prior</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">rho2_prior</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">x</span><span class="o">^</span><span class="m">2</span><span class="p">))</span><span class="w">
</span><span class="n">priorcov_mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">priorcov</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">median</span><span class="p">)</span><span class="w">
</span><span class="n">priorcov_95CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">priorcov</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.95</span><span class="p">)</span><span class="w">
</span><span class="n">priorcov_89CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">priorcov</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">
</span><span class="n">priorcov_80CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">priorcov</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.80</span><span class="p">)</span><span class="w">
</span><span class="n">priorcov_70CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">priorcov</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.70</span><span class="p">)</span><span class="w">
</span><span class="n">priorcov_60CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">priorcov</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.60</span><span class="p">)</span><span class="w">
</span><span class="n">priorcov_50CI</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">priorcov</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.50</span><span class="p">)</span><span class="w">

</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"Distance"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Covariance"</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">4</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">2</span><span class="p">),</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Prior w/ Compatability Intervals"</span><span class="p">)</span><span class="w">
</span><span class="n">lines</span><span class="p">(</span><span class="n">x_seq</span><span class="p">,</span><span class="w"> </span><span class="n">priorcov_mu</span><span class="p">,</span><span class="w"> </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">priorcov_95CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">priorcov_89CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">priorcov_80CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">priorcov_70CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">priorcov_60CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span><span class="n">shade</span><span class="p">(</span><span class="n">priorcov_50CI</span><span class="p">,</span><span class="w"> </span><span class="n">x_seq</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/gp_priors.jpeg" alt="" /></p>

<p>We can see that according to our priors, covariance will decline with distance, and will probably be low by the time that sites are 3 units apart - in a real dataset, we’d want to think about the distances between our sites and the scale that we’d expect autocorrelation to occur at when deciding whether these values are sensible. Our priors also assign more probability to smaller covariance values at each distance - they will be weakly regularising, meaning they are skeptical of more extreme values. However, they do allow for higher values if the data demand it.</p>

<h1 id="coding-the-model-in-stan">Coding the model in Stan</h1>

<p>Now that we’ve simulated from the data-generating process and chosen our priors, we’re ready to build our Stan model and fit it to the simulated data.</p>

<h2 id="the-full-model">The full model</h2>

<p>I’ll start by showing the Stan model in full. Don’t worry if it looks like a lot - we’re going to go through it step by step, and it’s really not as bad as it looks!</p>

<p>Here is the full model:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
    </span><span class="n">matrix</span><span class="w"> </span><span class="n">cov_GPL2</span><span class="p">(</span><span class="n">matrix</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">sq_alpha</span><span class="p">,</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">sq_rho</span><span class="p">,</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">delta</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="n">int</span><span class="w"> </span><span class="n">N</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dims</span><span class="p">(</span><span class="n">x</span><span class="p">)[</span><span class="m">1</span><span class="p">];</span><span class="w">
        </span><span class="n">matrix</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="w"> </span><span class="n">N</span><span class="p">]</span><span class="w"> </span><span class="n">K</span><span class="p">;</span><span class="w">
        </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">N</span><span class="m">-1</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="n">K</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sq_alpha</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">delta</span><span class="p">;</span><span class="w">
          </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="n">N</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="n">K</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="o">=</span><span class="w"> </span><span class="n">sq_alpha</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">sq_rho</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">square</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">])</span><span class="w"> </span><span class="p">);</span><span class="w">
            </span><span class="n">K</span><span class="p">[</span><span class="n">j</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">K</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="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
        </span><span class="n">K</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="w"> </span><span class="n">N</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sq_alpha</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">delta</span><span class="p">;</span><span class="w">
        </span><span class="n">return</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">data</span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">nsites</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">sites</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">N_maxvisits</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Maximum</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">survey</span><span class="w"> </span><span class="n">visits</span><span class="w"> </span><span class="n">received</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">site</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">V</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">visits</span><span class="w"> </span><span class="n">per</span><span class="w"> </span><span class="n">site</span><span class="w">

  </span><span class="n">array</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">N_maxvisits</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">upper</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">presence</span><span class="o">/</span><span class="n">absence</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="kc">NA</span><span class="s1">'s replaced with -9999)
  array[nsites] real x; // Occupancy covariate x
  array[nsites] real m; // Occupancy covariate m
  array[nsites, N_maxvisits] real w; // Detection covariate, varies with time (NA'</span><span class="n">s</span><span class="w"> </span><span class="n">replaced</span><span class="w"> </span><span class="n">with</span><span class="w"> </span><span class="m">-9999</span><span class="p">)</span><span class="w">

  </span><span class="n">matrix</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">dmat</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Distance</span><span class="w"> </span><span class="n">matrix</span><span class="w">

</span><span class="p">}</span><span class="w">

</span><span class="n">parameters</span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Effect</span><span class="w"> </span><span class="n">sizes</span><span class="w"> </span><span class="p">(</span><span class="n">on</span><span class="w"> </span><span class="n">log</span><span class="o">-</span><span class="n">odds</span><span class="w"> </span><span class="n">scale</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">betax</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Occupancy</span><span class="w"> </span><span class="n">slope</span><span class="w"> </span><span class="p">(</span><span class="n">effect</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">betam</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Occupancy</span><span class="w"> </span><span class="n">slope</span><span class="w"> </span><span class="p">(</span><span class="n">effect</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">alphadet</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Detection</span><span class="w"> </span><span class="n">intercept</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">betadet</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Detection</span><span class="w"> </span><span class="n">slope</span><span class="w"> </span><span class="p">(</span><span class="n">effect</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">k_bar</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Average</span><span class="w"> </span><span class="n">occupancy</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">entire</span><span class="w"> </span><span class="n">population</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">sites</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Gaussian</span><span class="w"> </span><span class="n">process</span><span class="w"> </span><span class="n">parameters</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">z</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">z</span><span class="o">-</span><span class="n">scores</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">intercept</span><span class="w"> </span><span class="n">term</span><span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="n">non</span><span class="o">-</span><span class="n">centred</span><span class="w"> </span><span class="n">parameterisation</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">etasq</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Maximum</span><span class="w"> </span><span class="n">covariance</span><span class="w"> </span><span class="n">between</span><span class="w"> </span><span class="n">sites</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">rhosq</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Rate</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">decline</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">covariance</span><span class="w"> </span><span class="n">with</span><span class="w"> </span><span class="n">distance</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">psi</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Probability</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">occurrence</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">site</span><span class="w"> </span><span class="n">i</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">N_maxvisits</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">pij</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Probability</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">detection</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">site</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">j</span><span class="w">

  </span><span class="n">matrix</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">L_SIGMA</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Cholesky</span><span class="o">-</span><span class="n">decomposed</span><span class="w"> </span><span class="n">covariance</span><span class="w"> </span><span class="n">matrix</span><span class="w">
  </span><span class="n">matrix</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">SIGMA</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Covariance</span><span class="w"> </span><span class="n">matrix</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">k</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Intercept</span><span class="w"> </span><span class="n">term</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">site</span><span class="w"> </span><span class="p">(</span><span class="n">perturbation</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">k_bar</span><span class="p">)</span><span class="w">

 </span><span class="o">//</span><span class="w"> </span><span class="n">Gaussian</span><span class="w"> </span><span class="n">process</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">non</span><span class="o">-</span><span class="n">centred</span><span class="w">
  </span><span class="n">SIGMA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cov_GPL2</span><span class="p">(</span><span class="n">dmat</span><span class="p">,</span><span class="w"> </span><span class="n">etasq</span><span class="p">,</span><span class="w"> </span><span class="n">rhosq</span><span class="p">,</span><span class="w"> </span><span class="m">0.01</span><span class="p">);</span><span class="w">
  </span><span class="n">L_SIGMA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cholesky_decompose</span><span class="p">(</span><span class="n">SIGMA</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="n">L_SIGMA</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">z</span><span class="p">;</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Calculate</span><span class="w"> </span><span class="n">psi_i</span><span class="w"> </span><span class="n">and</span><span class="w"> </span><span class="n">pij</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">isite</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nsites</span><span class="p">){</span><span class="w">
    </span><span class="o">//</span><span class="w"> </span><span class="n">Occupancy</span><span class="w"> </span><span class="n">submodel</span><span class="w">
    </span><span class="n">psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">k_bar</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">k</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">x</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="o">*</span><span class="n">betax</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">m</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="o">*</span><span class="n">betam</span><span class="p">);</span><span class="w">

    </span><span class="o">//</span><span class="w"> </span><span class="n">Detection</span><span class="w"> </span><span class="n">submodel</span><span class="w">
    </span><span class="k">for</span><span class="p">(</span><span class="n">ivisit</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]){</span><span class="w">
      </span><span class="n">pij</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="n">ivisit</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">alphadet</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">betadet</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="n">ivisit</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="p">}</span><span class="w">

</span><span class="n">model</span><span class="p">{</span><span class="w">

  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">log_psi</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Log</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">psi</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">log1m_psi</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Log</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="m">1</span><span class="o">-</span><span class="n">psi</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">betax</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">betam</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">alphadet</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0.5</span><span class="p">);</span><span class="w">
  </span><span class="n">betadet</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">k_bar</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0.2</span><span class="p">);</span><span class="w">

  </span><span class="n">rhosq</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">etasq</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">z</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">);</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Log</span><span class="w"> </span><span class="n">psi</span><span class="w"> </span><span class="n">and</span><span class="w"> </span><span class="nf">log</span><span class="p">(</span><span class="m">1</span><span class="o">-</span><span class="n">psi</span><span class="p">)</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">isite</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nsites</span><span class="p">){</span><span class="w">
    </span><span class="n">log_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">log</span><span class="p">(</span><span class="n">psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]);</span><span class="w">
    </span><span class="n">log1m_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">log1m</span><span class="p">(</span><span class="n">psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]);</span><span class="w">
  </span><span class="p">}</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Likelihood</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">isite</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nsites</span><span class="p">){</span><span class="w">

    </span><span class="k">if</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]])</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0</span><span class="p">){</span><span class="w">
      </span><span class="n">target</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">log_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">bernoulli_lpmf</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">pij</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]);</span><span class="w">
    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">target</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">log_sum_exp</span><span class="p">(</span><span class="n">log_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">bernoulli_lpmf</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">pij</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]),</span><span class="w"> </span><span class="n">log1m_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]);</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="o">//</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="n">likelihood</span><span class="w"> </span><span class="n">contribution</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Now let’s break it down, starting from the top.</p>

<h2 id="the-functions-block">The functions block</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">functions</span><span class="p">{</span><span class="w">
    </span><span class="n">matrix</span><span class="w"> </span><span class="n">cov_GPL2</span><span class="p">(</span><span class="n">matrix</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">sq_alpha</span><span class="p">,</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">sq_rho</span><span class="p">,</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">delta</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="n">int</span><span class="w"> </span><span class="n">N</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dims</span><span class="p">(</span><span class="n">x</span><span class="p">)[</span><span class="m">1</span><span class="p">];</span><span class="w">
        </span><span class="n">matrix</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="w"> </span><span class="n">N</span><span class="p">]</span><span class="w"> </span><span class="n">K</span><span class="p">;</span><span class="w">
        </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">N</span><span class="m">-1</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="n">K</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sq_alpha</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">delta</span><span class="p">;</span><span class="w">
          </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="n">N</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="n">K</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="o">=</span><span class="w"> </span><span class="n">sq_alpha</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">sq_rho</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">square</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">])</span><span class="w"> </span><span class="p">);</span><span class="w">
            </span><span class="n">K</span><span class="p">[</span><span class="n">j</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">K</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="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
        </span><span class="n">K</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="w"> </span><span class="n">N</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sq_alpha</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">delta</span><span class="p">;</span><span class="w">
        </span><span class="n">return</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></code></pre></div></div>

<p>This block, which I adapted from Statistical Rethinking, codes the kernel function $K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j})$. You can customise the function by changing the line:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">       </span><span class="n">K</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="o">=</span><span class="w"> </span><span class="n">sq_alpha</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">sq_rho</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">square</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">])</span><span class="w"> </span><span class="p">);</span><span class="w">
</span></code></pre></div></div>

<p>The function also allows for a parameter called <code class="language-plaintext highlighter-rouge">delta</code> which I’ve chosen to ignore here. It represents the additional covariance that sites have with themselves (i.e., when $i=j$), and you can read more about it in Rethinking.</p>

<h2 id="the-data-block">The data block</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">{</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">nsites</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">sites</span><span class="w">
  </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">N_maxvisits</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Maximum</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">survey</span><span class="w"> </span><span class="n">visits</span><span class="w"> </span><span class="n">received</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">site</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">V</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">visits</span><span class="w"> </span><span class="n">per</span><span class="w"> </span><span class="n">site</span><span class="w">

  </span><span class="n">array</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">N_maxvisits</span><span class="p">]</span><span class="w"> </span><span class="n">int</span><span class="o">&lt;</span><span class="n">upper</span><span class="o">=</span><span class="m">1</span><span class="o">&gt;</span><span class="w"> </span><span class="n">y</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Observed</span><span class="w"> </span><span class="n">presence</span><span class="o">/</span><span class="n">absence</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="kc">NA</span><span class="s1">'s replaced with -9999)
  array[nsites] real x; // Occupancy covariate x
  array[nsites] real m; // Occupancy covariate m
  array[nsites, N_maxvisits] real w; // Detection covariate, varies with time (NA'</span><span class="n">s</span><span class="w"> </span><span class="n">replaced</span><span class="w"> </span><span class="n">with</span><span class="w"> </span><span class="m">-9999</span><span class="p">)</span><span class="w">

  </span><span class="n">matrix</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">dmat</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Distance</span><span class="w"> </span><span class="n">matrix</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This block just tells Stan about the data which we supplied in <code class="language-plaintext highlighter-rouge">dlist</code> above. Have a look at <code class="language-plaintext highlighter-rouge">str(dlist)</code>and compare the output to the contents of the data block.</p>

<h2 id="the-parameters-block">The parameters block</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">parameters</span><span class="p">{</span><span class="w">
  </span><span class="o">//</span><span class="w"> </span><span class="n">Effect</span><span class="w"> </span><span class="n">sizes</span><span class="w"> </span><span class="p">(</span><span class="n">on</span><span class="w"> </span><span class="n">log</span><span class="o">-</span><span class="n">odds</span><span class="w"> </span><span class="n">scale</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">betax</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Occupancy</span><span class="w"> </span><span class="n">slope</span><span class="w"> </span><span class="p">(</span><span class="n">effect</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">betam</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Occupancy</span><span class="w"> </span><span class="n">slope</span><span class="w"> </span><span class="p">(</span><span class="n">effect</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">alphadet</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Detection</span><span class="w"> </span><span class="n">intercept</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">betadet</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Detection</span><span class="w"> </span><span class="n">slope</span><span class="w"> </span><span class="p">(</span><span class="n">effect</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="w"> </span><span class="n">k_bar</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Average</span><span class="w"> </span><span class="n">occupancy</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">entire</span><span class="w"> </span><span class="n">population</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">sites</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Gaussian</span><span class="w"> </span><span class="n">process</span><span class="w"> </span><span class="n">parameters</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">z</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">z</span><span class="o">-</span><span class="n">scores</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">intercept</span><span class="w"> </span><span class="n">term</span><span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="n">non</span><span class="o">-</span><span class="n">centred</span><span class="w"> </span><span class="n">parameterisation</span><span class="p">)</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">etasq</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Maximum</span><span class="w"> </span><span class="n">covariance</span><span class="w"> </span><span class="n">between</span><span class="w"> </span><span class="n">sites</span><span class="w">
  </span><span class="n">real</span><span class="o">&lt;</span><span class="n">lower</span><span class="o">=</span><span class="m">0</span><span class="o">&gt;</span><span class="w"> </span><span class="n">rhosq</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Rate</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">decline</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">covariance</span><span class="w"> </span><span class="n">with</span><span class="w"> </span><span class="n">distance</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This block tells Stan about the model’s parameters, specifically the intercepts and effect sizes, and the parameters for the Gaussian process. Note that we have this weird <code class="language-plaintext highlighter-rouge">z</code> parameter which we’ve not seen before - this is for the <strong>non-centered parameterisation</strong>, which I’m going to cover in the next block.</p>

<h2 id="the-transformed-parameters-block">The transformed parameters block</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">transformed</span><span class="w"> </span><span class="n">parameters</span><span class="p">{</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">psi</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Probability</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">occurrence</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">site</span><span class="w"> </span><span class="n">i</span><span class="w">
  </span><span class="n">array</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">N_maxvisits</span><span class="p">]</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="n">pij</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Probability</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">detection</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">site</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">at</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">time</span><span class="w"> </span><span class="n">j</span><span class="w">

  </span><span class="n">matrix</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">L_SIGMA</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Cholesky</span><span class="o">-</span><span class="n">decomposed</span><span class="w"> </span><span class="n">covariance</span><span class="w"> </span><span class="n">matrix</span><span class="w">
  </span><span class="n">matrix</span><span class="p">[</span><span class="n">nsites</span><span class="p">,</span><span class="w"> </span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">SIGMA</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Covariance</span><span class="w"> </span><span class="n">matrix</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">k</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Intercept</span><span class="w"> </span><span class="n">term</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">each</span><span class="w"> </span><span class="n">site</span><span class="w"> </span><span class="p">(</span><span class="n">perturbation</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">k_bar</span><span class="p">)</span><span class="w">

 </span><span class="o">//</span><span class="w"> </span><span class="n">Gaussian</span><span class="w"> </span><span class="n">process</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">non</span><span class="o">-</span><span class="n">centred</span><span class="w">
  </span><span class="n">SIGMA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cov_GPL2</span><span class="p">(</span><span class="n">dmat</span><span class="p">,</span><span class="w"> </span><span class="n">etasq</span><span class="p">,</span><span class="w"> </span><span class="n">rhosq</span><span class="p">,</span><span class="w"> </span><span class="m">0.01</span><span class="p">);</span><span class="w">
  </span><span class="n">L_SIGMA</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cholesky_decompose</span><span class="p">(</span><span class="n">SIGMA</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="n">L_SIGMA</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">z</span><span class="p">;</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Calculate</span><span class="w"> </span><span class="n">psi_i</span><span class="w"> </span><span class="n">and</span><span class="w"> </span><span class="n">pij</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">isite</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nsites</span><span class="p">){</span><span class="w">
    </span><span class="o">//</span><span class="w"> </span><span class="n">Occupancy</span><span class="w"> </span><span class="n">submodel</span><span class="w">
    </span><span class="n">psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">k_bar</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">k</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">x</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="o">*</span><span class="n">betax</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">m</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="o">*</span><span class="n">betam</span><span class="p">);</span><span class="w">

    </span><span class="o">//</span><span class="w"> </span><span class="n">Detection</span><span class="w"> </span><span class="n">submodel</span><span class="w">
    </span><span class="k">for</span><span class="p">(</span><span class="n">ivisit</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]){</span><span class="w">
      </span><span class="n">pij</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="n">ivisit</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">alphadet</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">betadet</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="n">ivisit</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="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Now we’re into some of the real action! We start off the transformed parameter blocks by defining a vector and array to hold the values for the occupancy probability $\psi_{i}$ for each site $i$ and the detection probability $p_{i,j}$ for each site $i$ at each survey $j$ respectively.</p>

<p>We then move onto a section which performs the Gaussian process part of the model, using the <code class="language-plaintext highlighter-rouge">cov_GPL2</code> function we defined in the functions block. This section also uses a computational trick called <strong>non-centred parameterisation</strong> to make the model run better. I’m not going to go over the theory behind how this works here, but you can find out all about it in the Statistical Rethinking lectures <a href="https://youtu.be/n2aJYtuGu54?t=2318">here</a> and <a href="https://youtu.be/XDoAglqd7ss?t=2307">here</a> as well as in the book.</p>

<p>We first make some vectors and matrices to hold the Cholesky-decomposed covariance matrix <code class="language-plaintext highlighter-rouge">L_SIGMA</code> (for the non-centred parameterisation) as well as the regular old covariance matrix and intercepts for each site that we covered above. After this, we run the <code class="language-plaintext highlighter-rouge">cov_GPL2</code> function, perform the Cholesky decomposition, and then multiply the resulting matrix <code class="language-plaintext highlighter-rouge">L_SIGMA</code>by the <code class="language-plaintext highlighter-rouge">z</code> scores we saw in the parameters block to obtain our intercept terms <code class="language-plaintext highlighter-rouge">k</code>.</p>

<p>In the final section, we calculate $\psi_{i}$ and $p_{i,j}$, looping over each site $i$ as well as each survey visit $j$ for the detection probability. <strong>This is the bit that contains the linear submodels for occupancy and detection</strong>. If you look at the two submodels, you can see the covariates we defined in the data block and the effect sizes from the parameters block. The occupancy submodel also contains the site-level intercept term $k_{i}$ as well as the average intercept term $\bar{k}$.</p>

<h2 id="the-model-block">The model block</h2>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="p">{</span><span class="w">

  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">log_psi</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Log</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">psi</span><span class="w">
  </span><span class="n">vector</span><span class="p">[</span><span class="n">nsites</span><span class="p">]</span><span class="w"> </span><span class="n">log1m_psi</span><span class="p">;</span><span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Log</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="m">1</span><span class="o">-</span><span class="n">psi</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Priors</span><span class="w">
  </span><span class="n">betax</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">betam</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">alphadet</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0.5</span><span class="p">);</span><span class="w">
  </span><span class="n">betadet</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">k_bar</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0.2</span><span class="p">);</span><span class="w">

  </span><span class="n">rhosq</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">etasq</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">exponential</span><span class="p">(</span><span class="m">1</span><span class="p">);</span><span class="w">
  </span><span class="n">z</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">normal</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">);</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Log</span><span class="w"> </span><span class="n">psi</span><span class="w"> </span><span class="n">and</span><span class="w"> </span><span class="nf">log</span><span class="p">(</span><span class="m">1</span><span class="o">-</span><span class="n">psi</span><span class="p">)</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">isite</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nsites</span><span class="p">){</span><span class="w">
    </span><span class="n">log_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">log</span><span class="p">(</span><span class="n">psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]);</span><span class="w">
    </span><span class="n">log1m_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">log1m</span><span class="p">(</span><span class="n">psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]);</span><span class="w">
  </span><span class="p">}</span><span class="w">

  </span><span class="o">//</span><span class="w"> </span><span class="n">Likelihood</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">isite</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">nsites</span><span class="p">){</span><span class="w">

    </span><span class="k">if</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]])</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0</span><span class="p">){</span><span class="w">
      </span><span class="n">target</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">log_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">bernoulli_lpmf</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">pij</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]);</span><span class="w">
    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">target</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">log_sum_exp</span><span class="p">(</span><span class="n">log_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">bernoulli_lpmf</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">pij</span><span class="p">[</span><span class="n">isite</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">V</span><span class="p">[</span><span class="n">isite</span><span class="p">]]),</span><span class="w"> </span><span class="n">log1m_psi</span><span class="p">[</span><span class="n">isite</span><span class="p">]);</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="o">//</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="n">likelihood</span><span class="w"> </span><span class="n">contribution</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Finally, we arrive at the model block. Again, there’s a lot going on in this section.</p>

<p>We first define two vectors to hold $log(\psi)$ and $1-log(\psi)$, which we’ll need in a minute.</p>

<p>We then move onto a section which defines the <strong>priors</strong> for each of our parameters. I picked the values for the priors based on the prior predictive simulations that we conducted above.</p>

<p>In the next section, we want to compute the likelihood, which will become part of the <code class="language-plaintext highlighter-rouge">target</code> (the log of the joint distribution of data and parameters) which is the thing that Stan actually samples. Unfortunately, there’s a problem - because Stan uses Hamiltonian Monte Carlo, we can’t use discrete parameters like the occupancy state $z_{i}$. We therefore need to get these parameters out of the model by <strong>marginalizing</strong> over them. Luckily, resources like this <a href="https://mbjoseph.github.io/posts/2020-04-28-a-step-by-step-guide-to-marginalizing-over-discrete-parameters-for-ecologists-using-stan/">excellent tutorial</a> by Maxwell B. Joseph explain how to do this! As my code here is adapted from the supplementary material of <a href="https://onlinelibrary.wiley.com/doi/full/10.1002/ece3.4850">this paper</a> by Allan Clark and Res Altwegg it appears very slightly different to Maxwell’s, but it does the same thing.</p>

<h1 id="running-the-model-and-exploring-the-output">Running the model and exploring the output</h1>

<p>Now that we’ve written the model, it’s time to run it! To do this, I use CmdStan via the <code class="language-plaintext highlighter-rouge">cstan</code> function in the <code class="language-plaintext highlighter-rouge">rethinking</code> package. First save the model as a <code class="language-plaintext highlighter-rouge">.stan</code> file (I called mine <code class="language-plaintext highlighter-rouge">occ_gp_nc.stan</code>) and then run the model like this:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">m1_nc</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cstan</span><span class="p">(</span><span class="n">file</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:/Stan_code/occupancy_models/occ_gp_nc.stan"</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">dlist</span><span class="p">,</span><span class="w">
               </span><span class="n">chains</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="n">cores</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="n">warmup</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1500</span><span class="p">,</span><span class="w">
               </span><span class="n">iter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2500</span><span class="p">,</span><span class="w">
               </span><span class="n">seed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">33221</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>The model will now compile and sample. This could take a little while, so it’s a good idea to go for a walk / play with the dog / <a href="https://www.zooniverse.org/projects/peter-dot-stewart/prickly-pear-project-kenya">classify some of my camera trap photos</a> :wink: until it’s done (<a href="[xkcd: Compiling](https://xkcd.com/303/)">relevant xkcd</a>)!</p>

<p>When the sampling is complete, you’ll get a warning message saying that some variables have undefined values - this is safe to ignore, it’s just saying that there are no $p_{i,j}$ values for visits which were never conducted. We can now inspect some model diagnostics:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">precis</span><span class="p">(</span><span class="n">m1_nc</span><span class="p">)</span><span class="w">
</span><span class="n">dashboard</span><span class="p">(</span><span class="n">m1_nc</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/precis.jpg" alt="" /></p>

<p><img src="/assets/images/post_images/spatial_occupancy/dashboard.jpeg" alt="" /></p>

<p>We’re particularly interested in the effective sample size <code class="language-plaintext highlighter-rouge">n_eff</code>which should be some healthy number relative to our actual number of samples, the Gelman-Rubin convergence diagnostic <code class="language-plaintext highlighter-rouge">Rhat</code> which should be 1 for each parameter, and the number of divergent transitions which should ideally be zero. We should also inspect the traceplots for the key parameters here:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">traceplot</span><span class="p">(</span><span class="n">m1_nc</span><span class="p">,</span><span class="w"> </span><span class="n">pars</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="s2">"betax"</span><span class="p">,</span><span class="s2">"betam"</span><span class="p">,</span><span class="s2">"alphadet"</span><span class="p">,</span><span class="s2">"betadet"</span><span class="p">,</span><span class="s2">"k_bar"</span><span class="p">,</span><span class="s2">"etasq"</span><span class="p">,</span><span class="s2">"rhosq"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/traceplot.jpeg" alt="" /></p>

<p>These ones look like “fuzzy caterpillars”, <a href="https://youtu.be/Qqz5AJjyugM?t=3050">which is good</a>.</p>

<p>Now that we’re happy that our model has performed adequately, we can extract our samples from the posterior distribution:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">post</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">extract.samples</span><span class="p">(</span><span class="n">m1_nc</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>I think at this stage it’s a good idea to look inside our posterior distribution with <code class="language-plaintext highlighter-rouge">str(post)</code>to see what it looks like. We see that it’s a list, with a separate element for each of our parameters. We can see that there are 4000 samples for each parameter, because we ran 4 chains with 1000 iterations each (remember that we set <code class="language-plaintext highlighter-rouge">iter=2500</code> but the 1500 warmup iterations don’t appear in <code class="language-plaintext highlighter-rouge">post</code>). If we look at some of the parameters, such as <code class="language-plaintext highlighter-rouge">psi</code> or <code class="language-plaintext highlighter-rouge">SIGMA</code>, we see that they have more dimensions - this is because they are vector or matrix parameters (e.g., <code class="language-plaintext highlighter-rouge">psi</code> is a vector with one value for each site). In total, we actually have 22400 parameters! Most of these (20000 of them) are for the covariance matrix <code class="language-plaintext highlighter-rouge">SIGMA</code> and the Cholesky-decomposed covariance matrix <code class="language-plaintext highlighter-rouge">L_SIGMA</code>.</p>

<p>Now that we’ve got our posterior distribution, we can start doing useful things with it! In the following sections, I go through a few of these things.</p>

<h2 id="looking-at-the-key-parameters">Looking at the key parameters</h2>

<p>One of the first things we can do is look at the values for the key parameters in our model - the effect sizes ($\beta_{X},\beta{M},\beta{det}$), intercepts ($\alpha_{det},\bar{k}$), and the Gaussian process parameters ($\eta^2,\rho^2$). We can see these in the <code class="language-plaintext highlighter-rouge">precis</code> summary table but I prefer to look at them using density plots.</p>

<p>Since we simulated the data and know the true values of each parameter, we can also check to see whether the model estimated these values accurately. This is an important part of validating our model. An easy way to do this is to just add a vertical line to each density plot to show the true value:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">4</span><span class="p">,</span><span class="m">2</span><span class="p">))</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">betax</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"betax"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">betax</span><span class="p">,</span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">betam</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"betam"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">betam</span><span class="p">,</span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">alphadet</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"alphadet"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">alphadet</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">betadet</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"betadet"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">betadet</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">k_bar</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"k_bar"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">etasq</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"eta^2"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">eta2</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">rhosq</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"rho^2"</span><span class="p">);</span><span class="w"> </span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">rho2</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/key_parameters.jpg" alt="" /></p>

<p>As you can see, in this example the model estimated all of the key parameters pretty accurately - the dashed vertical lines all fall somewhere within the posterior distribution for each parameter.</p>

<p>There’s a fair bit of uncertainty, but the model is still pretty good at identifying whether the effect size parameters are positive or negative - this is particularly important for <code class="language-plaintext highlighter-rouge">betax</code>, which is our effect of interest in this example.</p>

<h2 id="looking-at-the-occupancy-probability">Looking at the occupancy probability</h2>

<p>We can also look at the model’s predictions of the occupancy probability at each site ($\psi_{i}$). Again, because we simulated the data we can compare this against the true value. Here I do this by plotting the posterior mean and 89% compatability intervals against the true value:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">psi_mu</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">psi</span><span class="p">,</span><span class="m">2</span><span class="p">,</span><span class="n">mean</span><span class="p">)</span><span class="w">
</span><span class="n">psi_PI89</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">apply</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">psi</span><span class="p">,</span><span class="w"> </span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">HPDI</span><span class="p">,</span><span class="w"> </span><span class="n">prob</span><span class="o">=</span><span class="m">0.89</span><span class="p">)</span><span class="w">

</span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">1</span><span class="p">),</span><span class="w"> </span><span class="n">xlab</span><span class="o">=</span><span class="s2">"True value"</span><span class="p">,</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Model estimate"</span><span class="p">,</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="nf">expression</span><span class="p">(</span><span class="n">psi</span><span class="p">))</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="m">0</span><span class="p">,</span><span class="n">b</span><span class="o">=</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="c1"># true = predicted</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">psi</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">psi_mu</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">rangi2</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="nf">length</span><span class="p">(</span><span class="n">psi</span><span class="p">)){</span><span class="w">
  </span><span class="n">lines</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="n">psi</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="m">2</span><span class="p">),</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">psi_PI89</span><span class="p">[</span><span class="m">1</span><span class="p">,</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">psi_PI89</span><span class="p">[</span><span class="m">2</span><span class="p">,</span><span class="n">i</span><span class="p">]),</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">rangi2</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/psi.jpeg" alt="" /></p>

<p>As we can see, the inferences are not too bad, with many of the points lying close to the diagonal line where the model’s estimate is equal to the true value.</p>

<h2 id="visualising-the-spatial-autocorrelation">Visualising the spatial autocorrelation</h2>

<p>One of the really cool things we can do is visualise the spatial autocorrelation between sites. The first thing we need to do here is to compute the posterior median covariance among sites (i.e., the most probable covariance matrix according to the model) and convert it into a correlation matrix:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">K</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">sites</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="n">sites</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
    </span><span class="n">K</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">median</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">etasq</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">exp</span><span class="p">(</span><span class="o">-</span><span class="n">median</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">rhosq</span><span class="p">)</span><span class="o">*</span><span class="n">dmat2</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</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">diag</span><span class="p">(</span><span class="n">K</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">median</span><span class="p">(</span><span class="n">post</span><span class="o">$</span><span class="n">etasq</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">0.01</span><span class="w">

</span><span class="n">Rho</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">cov2cor</span><span class="p">(</span><span class="n">K</span><span class="p">),</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">rownames</span><span class="p">(</span><span class="n">Rho</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">100</span><span class="p">,</span><span class="m">1</span><span class="p">)</span><span class="w">
</span><span class="n">colnames</span><span class="p">(</span><span class="n">Rho</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rownames</span><span class="p">(</span><span class="n">Rho</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>We can now plot the results on a map, with darker lines representing a higher degree of correlation between sites:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">1</span><span class="p">))</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">y</span><span class="o">~</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">coords</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">rangi2</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"Correlation inferred from model"</span><span class="p">)</span><span class="w">

</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
    </span><span class="k">if</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">j</span><span class="p">){</span><span class="w">
      </span><span class="n">lines</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">coords</span><span class="o">$</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="o">$</span><span class="n">x</span><span class="p">[</span><span class="n">j</span><span class="p">]),</span><span class="w"> 
            </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">coords</span><span class="o">$</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="o">$</span><span class="n">y</span><span class="p">[</span><span class="n">j</span><span class="p">]),</span><span class="w">
            </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">col.alpha</span><span class="p">(</span><span class="s2">"black"</span><span class="p">,</span><span class="w"> </span><span class="n">Rho</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="o">^</span><span class="m">2</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="p">}</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/autocorrelation_inferred.jpeg" alt="" /></p>

<p>Again, because we simulated the data we are able to compare our model’s results against the truth:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Compute true correlation matrix</span><span class="w">
</span><span class="n">Rho_t</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">round</span><span class="p">(</span><span class="n">cov2cor</span><span class="p">(</span><span class="n">covmat</span><span class="p">),</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="n">rownames</span><span class="p">(</span><span class="n">Rho_t</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">seq</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="m">100</span><span class="p">,</span><span class="m">1</span><span class="p">)</span><span class="w">
</span><span class="n">colnames</span><span class="p">(</span><span class="n">Rho_t</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rownames</span><span class="p">(</span><span class="n">Rho</span><span class="p">)</span><span class="w">

</span><span class="c1"># Plot correlations on a map</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">y</span><span class="o">~</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="o">=</span><span class="n">coords</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">rangi2</span><span class="p">,</span><span class="w"> </span><span class="n">pch</span><span class="o">=</span><span class="m">16</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">10</span><span class="p">),</span><span class="w"> </span><span class="n">main</span><span class="o">=</span><span class="s2">"True correlation"</span><span class="p">)</span><span class="w">

</span><span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
  </span><span class="k">for</span><span class="p">(</span><span class="n">j</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">sites</span><span class="p">){</span><span class="w">
    </span><span class="k">if</span><span class="p">(</span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">j</span><span class="p">){</span><span class="w">
      </span><span class="n">lines</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">coords</span><span class="o">$</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="o">$</span><span class="n">x</span><span class="p">[</span><span class="n">j</span><span class="p">]),</span><span class="w"> 
            </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">coords</span><span class="o">$</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="o">$</span><span class="n">y</span><span class="p">[</span><span class="n">j</span><span class="p">]),</span><span class="w">
            </span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="o">=</span><span class="n">col.alpha</span><span class="p">(</span><span class="s2">"black"</span><span class="p">,</span><span class="w"> </span><span class="n">Rho_t</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span><span class="o">^</span><span class="m">2</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="p">}</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/autocorrelation_true.jpeg" alt="" /></p>

<p>We see that our model has done a great job here - the two plots looks pretty similar!</p>

<h2 id="simulating-an-intervention">Simulating an intervention</h2>

<p>The effect of intervening in the system (i.e., changing the value of $X$) on the occupancy probability is often different to the effect size for the covariate of interest (i.e., $\beta_{X}$). This is because when there are extreme values for other covariates the occupancy probabilty can get pushed close to zero or one, making the effect of our focal covariate less important - this is a <strong>floor/ceiling effect</strong>.</p>

<p>The consequence is that if we’re interested in understanding the consequences of changing $X$, then we have to consider how the other variables which effect occupancy (in this case, $M$) are distributed. <a href="https://youtu.be/n2aJYtuGu54?t=1740">One way of dealing with this challenge is by using a simulation</a>.</p>

<p>The exact code for our simulation depends on whether we’re interested in simulating an intervention for the specific sites that we surveyed, or for the (hypothetical) population of all sites. We’ll start with the specific sites we surveyed:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Simulating intervention for the sites that we surveyed</span><span class="w">
</span><span class="n">simsites</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sites</span><span class="w"> </span><span class="c1"># There were 100 sites that we surveyed</span><span class="w">
</span><span class="n">nsamples</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">4000</span><span class="w"> </span><span class="c1"># Number of samples from posterior to use (4000 = all of them)</span><span class="w">

</span><span class="c1"># Matrices to store our results</span><span class="w">
</span><span class="n">S1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">nsamples</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="n">simsites</span><span class="p">)</span><span class="w">
</span><span class="n">S2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">nsamples</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="n">simsites</span><span class="p">)</span><span class="w">

</span><span class="n">new_x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="c1"># Simulate increasing x by 1</span><span class="w">

</span><span class="c1"># Under status quo </span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">simsites</span><span class="p">){</span><span class="w">
  </span><span class="n">inv_psi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">k_bar</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">k</span><span class="p">[,</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betax</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betam</span><span class="o">*</span><span class="n">m</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w">
  </span><span class="n">psi_sim</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">inv_psi</span><span class="p">)</span><span class="w">
  </span><span class="n">S1</span><span class="p">[,</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">psi_sim</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Under distribution of x after intervention</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">simsites</span><span class="p">){</span><span class="w">
  </span><span class="n">inv_psi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">k_bar</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">k</span><span class="p">[,</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betax</span><span class="o">*</span><span class="n">new_x</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betam</span><span class="o">*</span><span class="n">m</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w">
  </span><span class="n">psi_sim</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">inv_psi</span><span class="p">)</span><span class="w">
  </span><span class="n">S2</span><span class="p">[,</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">psi_sim</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Difference between distribution under the two scenarios</span><span class="w">
</span><span class="n">Sdiff</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">S2</span><span class="o">-</span><span class="n">S1</span><span class="w">
</span><span class="n">dens</span><span class="p">(</span><span class="n">Sdiff</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">inv_logit</span><span class="p">(</span><span class="n">betax</span><span class="p">)</span><span class="o">-</span><span class="n">inv_logit</span><span class="p">(</span><span class="m">0</span><span class="p">),</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="c1"># true betax</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/intervention_sim1.jpeg" alt="" /></p>

<p>Looking at this distribution, we can see that there is a fair amount of uncertainty in what will happen to $\psi$ when we increase $X$ by 1, but the model is pretty sure that the effect of the intervention will be to increase $\psi$. The peak of our distribution is at around  0.23, which is what we’d expect from our $\beta_{X}$ parameter (as when $\bar{k}$ is 0, the true effect of $X$ is an increase $\psi$ from 0 to 1 on the log-odds scale, which is an increase from 0.5 to 0.73 on the probabilty scale, i.e. an increase of 0.23 - I’ve shown this value with a dashed vertical line).</p>

<p>However, notice that the distribution is not symmetrical - for some of the sites, increasing $X$ had less of an effect on $\psi$. This is due to ceiling and floor effects that occur at some sites where both the values of $k$ and $M$ are relatively extreme.</p>

<p>Now let’s look at simulating the intervention for the hypothetical population of all sites. The approach is generally similar to what we’ve just done, but there are a couple of key differences. The first is that we no longer need to use our $k_{i}$ values. The second is that we need to decide on a distribution for $M$ to average over - here I’ve gone for $Uniform(0,4)$ because it’ll really exaggerate the ceiling/floor effects, which will be interesting to see:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Simulating intervention for hypothetical population of sites</span><span class="w">
</span><span class="n">simsites</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">5000</span><span class="w"> </span><span class="c1"># 5000 hypothetical sites</span><span class="w">
</span><span class="n">nsamples</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">4000</span><span class="w"> </span><span class="c1"># Number of samples from posterior to use (4000 = all of them)</span><span class="w">

</span><span class="c1"># Matrices to store our results</span><span class="w">
</span><span class="n">S1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">nsamples</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="n">simsites</span><span class="p">)</span><span class="w">
</span><span class="n">S2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="o">=</span><span class="n">nsamples</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="o">=</span><span class="n">simsites</span><span class="p">)</span><span class="w">

</span><span class="n">old_x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="n">simsites</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
</span><span class="n">new_x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">old_x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="c1"># Simulate increasing x by 1</span><span class="w">
</span><span class="n">m_sim</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">runif</span><span class="p">(</span><span class="n">simsites</span><span class="p">,</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="m">4</span><span class="p">)</span><span class="w">

</span><span class="c1"># Under status quo </span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">simsites</span><span class="p">){</span><span class="w">
  </span><span class="n">inv_psi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">k_bar</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betax</span><span class="o">*</span><span class="n">old_x</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betam</span><span class="o">*</span><span class="n">m_sim</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w">
  </span><span class="n">psi_sim</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">inv_psi</span><span class="p">)</span><span class="w">
  </span><span class="n">S1</span><span class="p">[,</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">psi_sim</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># Under distribution of x after intervention</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">simsites</span><span class="p">){</span><span class="w">
  </span><span class="n">inv_psi</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">k_bar</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betax</span><span class="o">*</span><span class="n">new_x</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">post</span><span class="o">$</span><span class="n">betam</span><span class="o">*</span><span class="n">m_sim</span><span class="p">[</span><span class="n">s</span><span class="p">]</span><span class="w">
  </span><span class="n">psi_sim</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">inv_logit</span><span class="p">(</span><span class="n">inv_psi</span><span class="p">)</span><span class="w">
  </span><span class="n">S2</span><span class="p">[,</span><span class="n">s</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">psi_sim</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">Sdiff</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">S2</span><span class="o">-</span><span class="n">S1</span><span class="w">

</span><span class="n">dens</span><span class="p">(</span><span class="n">Sdiff</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">v</span><span class="o">=</span><span class="n">inv_logit</span><span class="p">(</span><span class="n">betax</span><span class="p">)</span><span class="o">-</span><span class="n">inv_logit</span><span class="p">(</span><span class="m">0</span><span class="p">),</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/intervention_sim2.jpeg" alt="" /></p>

<p>Now we can really see the ceiling and floor effects - in a large proportion of sites, changing $X$ had almost no effect on $\psi$, even though $\beta_{X}$ is strongly positive! Since $k_{i}$ is out of the picture now, this is all down to $M$ - we can see this if we plot the change in $\psi$ against $M$ like so:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plot</span><span class="p">(</span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">xlim</span><span class="o">=</span><span class="nf">range</span><span class="p">(</span><span class="n">m_sim</span><span class="p">),</span><span class="w"> </span><span class="n">ylim</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">-0.5</span><span class="p">,</span><span class="m">0.5</span><span class="p">),</span><span class="w"> </span><span class="n">ylab</span><span class="o">=</span><span class="s2">"Change in psi"</span><span class="p">,</span><span class="n">xlab</span><span class="o">=</span><span class="s2">"m_sim"</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">h</span><span class="o">=</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">lty</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="k">for</span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">simsites</span><span class="p">){</span><span class="w">
  </span><span class="n">points</span><span class="p">(</span><span class="n">m_sim</span><span class="p">[</span><span class="n">s</span><span class="p">],</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">Sdiff</span><span class="p">[,</span><span class="n">s</span><span class="p">]))</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">ate</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">Sdiff</span><span class="p">)</span><span class="w"> </span><span class="c1"># Average treatment effect</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">h</span><span class="o">=</span><span class="n">ate</span><span class="p">,</span><span class="n">lwd</span><span class="o">=</span><span class="m">2</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/images/post_images/spatial_occupancy/intervention_sim3.jpeg" alt="" /></p>

<p>We can see here that when $M$ (<code class="language-plaintext highlighter-rouge">m_sim</code>) is high, the change in $\psi$ tends to be closer to zero.</p>]]></content><author><name>Peter S. Stewart</name></author><category term="blog" /><category term="Stan" /><category term="tutorial" /><category term="occupancy modelling" /><summary type="html"><![CDATA[In this post I walk through an occupancy model which uses a Gaussian process to model spatial autocorrelation. The model is coded in Stan. I start off with a brief overview of occupancy models and Gaussian process regression. I then walk through how to simulate a dataset from the assumed data-generating process in R, and then explain the model and Stan code step-by-step. Finally, I look at some of the interesting things we can do once we have the posterior distribution. Acknowledgements and other resources I used several resources when writing this post. In particular, the majority of my knowledge about Bayesian statistics, Stan, and Gaussian processes comes from Richard McElreath’s fantastic book and lecture series Statistical Rethinking. The whole course is available for free online, I highly recommend you check out the most recent version here. I also learned a lot about Stan from Michael Betancourt’s excellent case studies, in particular the introduction to Stan. I learned about how to marginalise out the discrete occupancy state parameters from Maxwell B. Joseph’s tutorial, and the supplement of this paper by Allan Clark and Res Altwegg - my understanding mainly comes from the former, my code from the latter. Some of the other key resources which I used to learn about about occupancy models are the classic book by MacKenzie et al. and this paper by Joe Northrup and Brian Gerber. I’d also like to thank the Conservation Ecology Group at Durham University, as this post was inspired by a talk I gave at one of their lab group meetings a few months ago. An important note This post should be considered a work in progress - I greatly appreciate any feedback which I can implement to improve it, particularly if you spot any mistakes! Introduction Occupancy models Occupancy models are used to study the patterns and drivers of species occurrence. They are very commonly used for camera trap data, which is why I got interested in them in the first place. One of their key characteristics is that they deal with imperfect detection - the chance that a species which is present at a site may remain undetected. In this section, I’ll walk through a standard single-season occupancy model - I follow the model notation from Northrup and Gerber pretty closely here. We’ll expand the model to include spatial autocorrelation in the next section. Imagine that we have a bunch of sites dotted about the landscape. The state ($z$) of a site (indexed $i$) is either occupied (1) or unoccupied (0), and the probability that it is occupied is called the occupancy probability ($\psi$). We can write this as: \[z_{i} \sim Bernoulli(\psi_{i})\] We assume that a site stays in one state (occupied or not) for the duration of the study, i.e. that there is a single season. You can also get multi-season or dynamic occupancy models which assume that the state may change, but I’m not going to cover those here. We’re often interested in modelling $\psi$ as a function of one or more covariates, for instance because we want to know the effect of some environmental variable on occupancy. It’s important to note that it is very important to consider the purpose of the model (inference or prediction) when deciding on which covariates to put in the model - this is covered by my recent preprint. We can add covariates in a standard logistic regression format, for example for a single covariate $X$: \[logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \alpha + \beta_{X}X_{i}\] Now comes the bit where we account for imperfect detection. We visit each site multiple ($v_{i}$) times and record whether we observe the species on each visit. The observed data at each site ($y_{i}$) can then be modelled as: \[y_{i} \sim Binomial(v_{i}, \mu_{i})\\ \mu_{i} = p_{i}z_{i}\] In the second line, $z_{i}$ is the true occupancy state and $p_i$ is the probability of detection. Therefore, if a species is absent from a site is is never detected, and if it is present it is detected on each visit with probability $p$. We can also make the detection probability a function of covariates, for instance: \[logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i}\] It’s also pretty common to see models where detection probability is allowed to be different on each visit and you have a time-varying covariate - this would be great for situations in which the probability we detect a species on a visit varies due to factors such as weather. The only thing we need to change is the indexing - for instance, we could now have $W_{i,j}$ for the value of $W$ at site $i$ on visit $j$. This is what I do in the example model later on. Taking everything we’ve got so far, the full model with priors would look like: \[y_{i} \sim Binomial(v_{i}, \mu_{i})\\ \mu_{i} = p_{i}z_{i} \\ z_{i} \sim Bernoulli(\psi_{i}) \\ logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \alpha + \beta_{X}X_{i} \\ logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i} \\ \beta_{X} \sim Normal(0,1) \\ \beta_{W} \sim Normal(0,1) \\ \alpha \sim Normal(0,1) \\ \alpha_{det} \sim Normal(0, 0.5)\] You’d want to pick the specific priors for your problem at hand by doing some prior predictive simulations, which we’ll do for our example later on. Now, it’s time to expand our occupancy model to account for spatial autocorrelation! Spatial autocorrelation and Gaussian process regression The occupancy model above assumes that the occupancy probability of our sites is independent, regardless of where they are situated in space. However, in many instances this is unlikely to be the case - sites which are closer together tend to be more similar than sites which are further apart. This phenomenon is called spatial autocorrelation. There are a variety of ways that we could include this spatial autocorrelation in our model. One really cool way is to use Gaussian process regression. The theory behind Gaussian process regression is already covered by Statistical Rethinking, as well as this case study and the Stan Users’ guide. For this reason, I’m just going to give a quick overview here so that our extensions to the occupancy model make sense. Remember that the essential idea is that sites which are closer tend to be more similar. A natural way to express this idea is by having a function to link the covariance between a pair of sites (we’ll index these $i$ and $j$) to the distance between them. This function is usually called a kernel function, and one example is: \[K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j})\] Where $K_{i,j}$ is the covariance between sites $i$ and $j$, $\eta^2$ is the maximum covariance between sites, $\rho^2$ is the rate that covariance declines with distance, and $D^2_{i,j}$ is the (squared) distance between sites $i$ and $j$. Because we have more than one pair of sites, we’re going to feed in a distance matrix and end up with a covariance matrix. We now want to turn this covariance matrix into something that we can use in our linear model for occupancy probability - we want some kind of varying intercept. One way of doing this is to have an average intercept across all sites ($\bar{k}$) and then a site-specific offset ($k_{i}$) like so: \[logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \bar{k} + k_{i} + \beta_{X}X_{i} \\\] Notice that what we’ve done is replace the intercept ($\alpha$) from the model we saw earlier with $\bar{k}$ and $k_{i}$. We’ve still got the covariate $X$ as before. The final thing we need to do is connect our site-level offset $k_{i}$ back to the kernel function. We can do this using the multivariate normal distribution, like this: \[\begin{pmatrix}k_{1} \\ k_{2} \\ \vdots \\ k_{i} \end{pmatrix} \sim MVNormal\left(\begin{pmatrix}0\\0\\0\\0\end{pmatrix}, \textbf{K}\right)\] You can see the covariance matrix $\textbf{K}$ formed from all of the combinations of $K{i,j}$ on the right. The mean of the multivariate normal distribution is filled with zeroes because we already have $\bar{k}$ in the model above. The only thing left to do is add some priors for the new parameters - with the priors included, the whole model looks like: \[y_{i} \sim Binomial(v_{i}, \mu_{i})\\ \mu_{i} = p_{i}z_{i} \\ z_{i} \sim Bernoulli(\psi_{i}) \\ logit(\psi_{i}) = ln(\frac{\psi}{1-\psi}) = \bar{k} + k_{i} + \beta_{X}X_{i} \\ \begin{pmatrix}k_{1} \\ k_{2} \\ \vdots \\ k_{i} \end{pmatrix} \sim MVNormal\left(\begin{pmatrix}0\\0\\0\\0\end{pmatrix}, \textbf{K}\right) \\ K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j}) \\ logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i} \\ \beta_{X}, \beta_{W} \sim Normal(0,1) \\ \alpha_{det} \sim Normal(0, 0.5)\\ \bar{k} \sim Normal(0, 0.2) \\ \eta^2, \rho^2 \sim Exponential(1)\] Again, prior predictive simulations were used to help choose the priors - I cover how to do these below. Simulating from the data-generating process in R A really good way to help develop a model is to think about the process which could have produced the data, and then make a simulation of this process. We can then fit the model to these simulated data, which is a great way of identifying and fixing any issues with our model before we fit it to real data! In this section, we’ll go through the code I used to simulate the data. Let’s start by loading a couple of packages that we’ll need: library(rethinking) library(MASS) Now, let’s set up some basic parameters for the simulation - we’ll go for 100 sites, with each site surveyed somewhere from 14 to 21 times. We’ll also set a seed for R’s random number generator, so that you can replicate my results exactly: sites &lt;- 100 surveys &lt;- sample(14:21, size=sites, replace=TRUE) set.seed(1234) Now we’re going to generate a random set of x and y coordinates for each site, showing where it is situated in our study region. We’re then going to use these coordinates to make a distance matrix, which contains the distance between each pair of sites: x &lt;- runif(sites, 0, 10) # x coordinate y &lt;- runif(sites, 0, 10) # y coordinate coords &lt;- as.data.frame(cbind(x,y)) # Make distance matrix from coordinates dmat &lt;- dist(coords, diag=T, upper=T) dmat &lt;- as.matrix(dmat) dmat2 &lt;- dmat^2 # Squared distances Now that we’ve got our distance matrix, we need to code the function that calculates the covariance between each pair of sites from the distance between them. Remember that our kernel function is: \[K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j})\] with definitions as above. We’re going to pick some values for $\eta^2$ and $\rho^2$ and then generate the covariance matrix: eta2 &lt;- 0.8 rho2 &lt;- 0.5 covmat &lt;- eta2*exp(-rho2*dmat2) # Max value should be eta2 on the diagonals You can visualise how our choice of values affects the kernel function by plotting covariance against distance. It’s also helpful here to plot the distribution of distances between sites, to see how many pairs will have each amount of covariance: par(mfrow=c(1,2)) curve(eta2*exp(-rho2*x^2),from=0, to=10, lty=1, xlab="Distance", ylab="Covariance") # Visualise covariance (y) vs distance (x) dens(as.vector(dmat), xlab="Distance") # Visualise the distances between sites as well par(mfrow=c(1,1)) You can use this code to play around with the $\eta^2$ and $\rho^2$ values to see how they affect the way that covariance declines with distance. Now that we have our covariance matrix, we want to use it to generate a varying intercept for the occupancy probability. This means that sites which are closer together tend to have a more similar occupancy probability. We do this using the multivariate normal distribution with the mvrnorm function from the MASS package: z &lt;- rep(0, nrow(covmat)) varint &lt;- mvrnorm(n = 1, mu = z, Sigma = covmat) varint &lt;- as.numeric(varint) Note that we’re ignoring $\bar{k}$ here and making the mean in mvnorm zero, which is the same as assuming that $\bar{k}$ is equal to zero. Now we’re going to add two covariates for the occupancy probability: $X$, which is our focal variable (i.e., we’re interested in the effect of $X$ on $\psi$ ) and $M$, which is a confounding variable. We will assume that the variables are related as shown in this DAG: The consequence is that if we want to estimate the effect of $X$ on $\psi$, we need to condition on $M$ in our occupancy sub-model. We will also include a covariate $W_{t}$ for the detection probability. We’re giving this covariate the subscript $t$ because it is time-varying, meaning that it has a different value for each survey of the site. Let’s code up the covariates, effect sizes ($\beta_{X}, \beta_{M},$ and $\beta_{MX}$), and the true occupancy and detection probabilities: # True effect sizes: betax &lt;- 1.0 # Effect of x on psi betam &lt;- -0.8 # Effect of m on psi betamx &lt;- 0.5 # Effect of m on x # Variables m &lt;- rnorm(sites, 0, 1) x &lt;- rnorm(sites, betamx*m, 1) # True occupancy probability at each site psi &lt;- inv_logit(varint + betax*x + betam*m) # Covariate for detection probability w &lt;- matrix(NA, nrow=sites, ncol=max(surveys)) for(i in 1:sites){ for(k in 1:surveys[i]){ w[i,k] &lt;- rnorm(1,0,1) } } # True detection probability alphadet &lt;- -0.1 # betadet &lt;- 0.4 # pdet &lt;- matrix(NA, nrow=sites, ncol=max(surveys)) for(i in 1:sites){ for(k in 1:surveys[i]){ pdet[i,k] &lt;- inv_logit(alphadet + betadet*w[i,k]) } } Now we’re ready to populate our simulated landscape and conduct our simulated surveys! Let’s simulate the true occupancy state (1 = occupied, 0 = unoccupied) for each site, and then simulate the observed detection history for each site: # True occupancy state z &lt;- rep(NA, length=sites) for(i in 1:sites){ z[i] &lt;- rbinom(1,1,psi[i]) } # Observed detection history y &lt;- matrix(NA, nrow=sites, ncol=max(surveys)) for(i in 1:sites){ for(k in 1:surveys[i]){ y[i,k] &lt;- rbinom(1,1,pdet[i,k]*z[i]) } } Finally, let’s get the data ready for Stan. As Stan can’t deal with the NA values in visits which did not occur (e.g., where a site was surveyed 17 times, the final 4 surveys will be NA), we have to replace them with a number - the specific number doesn’t really matter, because Stan should never access the values in its calculations. I prefer to use a ridiculous number like -9999 because then if Stan does access the number somehow, it should result in an error which is relatively easy to detect. We can then make the data list for Stan to use. This includes the observed data y, the covariates x, m and w, the distance matrix dmat, the number of sites nsites, the number of surveys at each site V and the maximum number of visits made to a site, N_maxvisits: y[is.na(y)] &lt;- -9999 mode(y) &lt;- "integer" w[is.na(w)] &lt;- -9999 # Data list for Stan dlist &lt;- list( nsites = as.integer(sites), N_maxvisits = as.integer(max(surveys)), V = surveys, y = y, x = x, m = m, w = w, dmat = dmat ) Prior predictive simulations As I mentioned earlier, we’re going to use prior predictive simulations to help choose our priors for the model. There are two main sets of parameters that we need to think about here: the effect sizes and intercepts for the occupancy and detection linear models ($\beta_{X}, \beta_{M},\alpha_{det},\beta_{det}$) and the parameters for the Gaussian process ($\eta^2$ and $\rho^2$). Let’s start with the first set of parameters - the effect sizes and intercepts. Let’s think about the detection submodel to start with. Remember that the relevant bit of our model is: \[logit(p_{i}) = ln(\frac{p}{1-p}) = \alpha_{det} + \beta_{W}W_{i} \\ \alpha_{det} \sim Normal(?,?) \\ \beta_{W} \sim ~Normal(?,?)\] I’ve suggested that we use normal prior distributions for our two parameters, because we don’t have to constrain either parameter to be positive. I’m also going to suggest we make the mean of both priors zero. In the case of $\beta_{W}$ this means we’re assigning equal prior probability to positive and negative effects of $W$. For $\alpha_{det}$ it means that the highest prior probability of detection is 0.5 when $W$ is zero. We still have to pick some values for the variance of our priors though. The idea is we’re going pick some values, then draw a bunch of random lines from our prior distributions and plot them to see if they look sensible. In the interest of illustrating an important point about prior distributions in occupancy models, I’m going to start off with some very flat priors like $Normal(0, 10)$. Let’s see what happens: N &lt;- 500 # Number of lines to draw from priors alpha &lt;- rnorm( N , 0 , 10 ) beta &lt;- rnorm( N , 0 , 10 ) ## Make an empty plot plot( NULL , xlim=c(-2,2) , ylim=c(0,1) , xlab="x" , ylab="p_det") abline( h=0.5 , lty=2 ) # Add horizontal line at 0.5 detection probablity # Draw N lines using our priors for ( i in 1:N ) curve(inv_logit( alpha[i] + beta[i]*(x)) , from=-2 , to=2 , add=TRUE , col=col.alpha("black",0.2) ) As we can see, something has gone badly wrong - nearly all of the prior probability is piled up on zero and one! The reason is that our priors are too flat. Remember that our priors are on the log-odds scale, and they will be turned into probability through the $logit$ link function. Because values below -4 and above 4 on the log-odds scale correspond to probabilities very close to 0 and 1 respectively, assigning a lot of our prior probability to these values by choosing a flat prior with a lot of area here makes the model assign a lot of prior probability to extreme values. Northrup and Gerber discuss this phenomenon in more detail in their paper, and offer recommendations for better priors. We’re going to try some tighter priors instead: $Normal(0,0.5)$ for $\alpha_{det}$ and $Normal(0,1)$ for $\beta_{W}$. If you modfy the code above to these values, then you get this plot: This looks much more sensible. We are also going to learn from this experience by choosing relatively tight priors for $\bar{k}$ and our $\beta$ values in the occupancy submodel: I’m going to suggest $Normal(0,0.2)$ and $Normal(0,1)$ respectively. We’re going to use a similar strategy to choose priors for our Gaussian process parameters. However, in this case I find that it can be difficult to interpret lines drawn from the priors because of the way they plot over one-another, so I’m going to plot the compatability intervals instead: samp &lt;- 1e4 rho2_prior &lt;- rexp(samp, 1) eta2_prior &lt;- rexp(samp, 1) x_seq &lt;- seq(0,4,0.01) priorcov &lt;- sapply(x_seq, function(x) eta2_prior * exp(-rho2_prior * x^2)) priorcov_mu &lt;- apply(priorcov, 2, median) priorcov_95CI &lt;- apply(priorcov, 2, HPDI, prob=0.95) priorcov_89CI &lt;- apply(priorcov, 2, HPDI, prob=0.89) priorcov_80CI &lt;- apply(priorcov, 2, HPDI, prob=0.80) priorcov_70CI &lt;- apply(priorcov, 2, HPDI, prob=0.70) priorcov_60CI &lt;- apply(priorcov, 2, HPDI, prob=0.60) priorcov_50CI &lt;- apply(priorcov, 2, HPDI, prob=0.50) par(mfrow=c(1,1)) plot(NULL, xlab="Distance", ylab="Covariance", xlim=c(0,4), ylim=c(0,2), main="Prior w/ Compatability Intervals") lines(x_seq, priorcov_mu, lwd=2) shade(priorcov_95CI, x_seq) shade(priorcov_89CI, x_seq) shade(priorcov_80CI, x_seq) shade(priorcov_70CI, x_seq) shade(priorcov_60CI, x_seq) shade(priorcov_50CI, x_seq) We can see that according to our priors, covariance will decline with distance, and will probably be low by the time that sites are 3 units apart - in a real dataset, we’d want to think about the distances between our sites and the scale that we’d expect autocorrelation to occur at when deciding whether these values are sensible. Our priors also assign more probability to smaller covariance values at each distance - they will be weakly regularising, meaning they are skeptical of more extreme values. However, they do allow for higher values if the data demand it. Coding the model in Stan Now that we’ve simulated from the data-generating process and chosen our priors, we’re ready to build our Stan model and fit it to the simulated data. The full model I’ll start by showing the Stan model in full. Don’t worry if it looks like a lot - we’re going to go through it step by step, and it’s really not as bad as it looks! Here is the full model: functions{ matrix cov_GPL2(matrix x, real sq_alpha, real sq_rho, real delta) { int N = dims(x)[1]; matrix[N, N] K; for (i in 1:(N-1)) { K[i, i] = sq_alpha + delta; for (j in (i + 1):N) { K[i, j] = sq_alpha * exp(-sq_rho * square(x[i,j]) ); K[j, i] = K[i, j]; } } K[N, N] = sq_alpha + delta; return K; } } data{ int&lt;lower=1&gt; nsites; // Number of sites int&lt;lower=1&gt; N_maxvisits; // Maximum number of survey visits received by a site array[nsites] int&lt;lower=1&gt; V; // Number of visits per site array[nsites, N_maxvisits] int&lt;upper=1&gt; y; // Observed presence/absence data (NA's replaced with -9999) array[nsites] real x; // Occupancy covariate x array[nsites] real m; // Occupancy covariate m array[nsites, N_maxvisits] real w; // Detection covariate, varies with time (NA's replaced with -9999) matrix[nsites, nsites] dmat; // Distance matrix } parameters{ // Effect sizes (on log-odds scale) real betax; // Occupancy slope (effect of x) real betam; // Occupancy slope (effect of m) real alphadet; // Detection intercept real betadet; // Detection slope (effect of w) real k_bar; // Average occupancy in entire population of sites // Gaussian process parameters vector[nsites] z; // z-scores for intercept term (for non-centred parameterisation) real&lt;lower=0&gt; etasq; // Maximum covariance between sites real&lt;lower=0&gt; rhosq; // Rate of decline in covariance with distance } transformed parameters{ vector[nsites] psi; // Probability of occurrence at each site i array[nsites, N_maxvisits] real pij; // Probability of detection at each site i at each time j matrix[nsites, nsites] L_SIGMA; // Cholesky-decomposed covariance matrix matrix[nsites, nsites] SIGMA; // Covariance matrix vector[nsites] k; // Intercept term for each site (perturbation from k_bar) // Gaussian process - non-centred SIGMA = cov_GPL2(dmat, etasq, rhosq, 0.01); L_SIGMA = cholesky_decompose(SIGMA); k = L_SIGMA * z; // Calculate psi_i and pij for(isite in 1:nsites){ // Occupancy submodel psi[isite] = inv_logit(k_bar + k[isite] + x[isite]*betax + m[isite]*betam); // Detection submodel for(ivisit in 1:V[isite]){ pij[isite, ivisit] = inv_logit(alphadet + betadet*w[isite,ivisit]); } } } model{ vector[nsites] log_psi; // Log of psi vector[nsites] log1m_psi; // Log of 1-psi // Priors betax ~ normal(0,1); betam ~ normal(0,1); alphadet ~ normal(0,0.5); betadet ~ normal(0,1); k_bar ~ normal(0,0.2); rhosq ~ exponential(1); etasq ~ exponential(1); z ~ normal(0, 1); // Log psi and log(1-psi) for(isite in 1:nsites){ log_psi[isite] = log(psi[isite]); log1m_psi[isite] = log1m(psi[isite]); } // Likelihood for(isite in 1:nsites){ if(sum(y[isite, 1:V[isite]]) &gt; 0){ target += log_psi[isite] + bernoulli_lpmf(y[isite, 1:V[isite]] | pij[isite, 1:V[isite]]); } else { target += log_sum_exp(log_psi[isite] + bernoulli_lpmf(y[isite, 1:V[isite]] | pij[isite, 1:V[isite]]), log1m_psi[isite]); } }// end likelihood contribution } Now let’s break it down, starting from the top. The functions block functions{ matrix cov_GPL2(matrix x, real sq_alpha, real sq_rho, real delta) { int N = dims(x)[1]; matrix[N, N] K; for (i in 1:(N-1)) { K[i, i] = sq_alpha + delta; for (j in (i + 1):N) { K[i, j] = sq_alpha * exp(-sq_rho * square(x[i,j]) ); K[j, i] = K[i, j]; } } K[N, N] = sq_alpha + delta; return K; } } This block, which I adapted from Statistical Rethinking, codes the kernel function $K_{i,j} = \eta^2exp(-\rho^2D^2_{i,j})$. You can customise the function by changing the line: K[i, j] = sq_alpha * exp(-sq_rho * square(x[i,j]) ); The function also allows for a parameter called delta which I’ve chosen to ignore here. It represents the additional covariance that sites have with themselves (i.e., when $i=j$), and you can read more about it in Rethinking. The data block data{ int&lt;lower=1&gt; nsites; // Number of sites int&lt;lower=1&gt; N_maxvisits; // Maximum number of survey visits received by a site array[nsites] int&lt;lower=1&gt; V; // Number of visits per site array[nsites, N_maxvisits] int&lt;upper=1&gt; y; // Observed presence/absence data (NA's replaced with -9999) array[nsites] real x; // Occupancy covariate x array[nsites] real m; // Occupancy covariate m array[nsites, N_maxvisits] real w; // Detection covariate, varies with time (NA's replaced with -9999) matrix[nsites, nsites] dmat; // Distance matrix } This block just tells Stan about the data which we supplied in dlist above. Have a look at str(dlist)and compare the output to the contents of the data block. The parameters block parameters{ // Effect sizes (on log-odds scale) real betax; // Occupancy slope (effect of x) real betam; // Occupancy slope (effect of m) real alphadet; // Detection intercept real betadet; // Detection slope (effect of w) real k_bar; // Average occupancy in entire population of sites // Gaussian process parameters vector[nsites] z; // z-scores for intercept term (for non-centred parameterisation) real&lt;lower=0&gt; etasq; // Maximum covariance between sites real&lt;lower=0&gt; rhosq; // Rate of decline in covariance with distance } This block tells Stan about the model’s parameters, specifically the intercepts and effect sizes, and the parameters for the Gaussian process. Note that we have this weird z parameter which we’ve not seen before - this is for the non-centered parameterisation, which I’m going to cover in the next block. The transformed parameters block transformed parameters{ vector[nsites] psi; // Probability of occurrence at each site i array[nsites, N_maxvisits] real pij; // Probability of detection at each site i at each time j matrix[nsites, nsites] L_SIGMA; // Cholesky-decomposed covariance matrix matrix[nsites, nsites] SIGMA; // Covariance matrix vector[nsites] k; // Intercept term for each site (perturbation from k_bar) // Gaussian process - non-centred SIGMA = cov_GPL2(dmat, etasq, rhosq, 0.01); L_SIGMA = cholesky_decompose(SIGMA); k = L_SIGMA * z; // Calculate psi_i and pij for(isite in 1:nsites){ // Occupancy submodel psi[isite] = inv_logit(k_bar + k[isite] + x[isite]*betax + m[isite]*betam); // Detection submodel for(ivisit in 1:V[isite]){ pij[isite, ivisit] = inv_logit(alphadet + betadet*w[isite,ivisit]); } } } Now we’re into some of the real action! We start off the transformed parameter blocks by defining a vector and array to hold the values for the occupancy probability $\psi_{i}$ for each site $i$ and the detection probability $p_{i,j}$ for each site $i$ at each survey $j$ respectively. We then move onto a section which performs the Gaussian process part of the model, using the cov_GPL2 function we defined in the functions block. This section also uses a computational trick called non-centred parameterisation to make the model run better. I’m not going to go over the theory behind how this works here, but you can find out all about it in the Statistical Rethinking lectures here and here as well as in the book. We first make some vectors and matrices to hold the Cholesky-decomposed covariance matrix L_SIGMA (for the non-centred parameterisation) as well as the regular old covariance matrix and intercepts for each site that we covered above. After this, we run the cov_GPL2 function, perform the Cholesky decomposition, and then multiply the resulting matrix L_SIGMAby the z scores we saw in the parameters block to obtain our intercept terms k. In the final section, we calculate $\psi_{i}$ and $p_{i,j}$, looping over each site $i$ as well as each survey visit $j$ for the detection probability. This is the bit that contains the linear submodels for occupancy and detection. If you look at the two submodels, you can see the covariates we defined in the data block and the effect sizes from the parameters block. The occupancy submodel also contains the site-level intercept term $k_{i}$ as well as the average intercept term $\bar{k}$. The model block model{ vector[nsites] log_psi; // Log of psi vector[nsites] log1m_psi; // Log of 1-psi // Priors betax ~ normal(0,1); betam ~ normal(0,1); alphadet ~ normal(0,0.5); betadet ~ normal(0,1); k_bar ~ normal(0,0.2); rhosq ~ exponential(1); etasq ~ exponential(1); z ~ normal(0, 1); // Log psi and log(1-psi) for(isite in 1:nsites){ log_psi[isite] = log(psi[isite]); log1m_psi[isite] = log1m(psi[isite]); } // Likelihood for(isite in 1:nsites){ if(sum(y[isite, 1:V[isite]]) &gt; 0){ target += log_psi[isite] + bernoulli_lpmf(y[isite, 1:V[isite]] | pij[isite, 1:V[isite]]); } else { target += log_sum_exp(log_psi[isite] + bernoulli_lpmf(y[isite, 1:V[isite]] | pij[isite, 1:V[isite]]), log1m_psi[isite]); } }// end likelihood contribution } Finally, we arrive at the model block. Again, there’s a lot going on in this section. We first define two vectors to hold $log(\psi)$ and $1-log(\psi)$, which we’ll need in a minute. We then move onto a section which defines the priors for each of our parameters. I picked the values for the priors based on the prior predictive simulations that we conducted above. In the next section, we want to compute the likelihood, which will become part of the target (the log of the joint distribution of data and parameters) which is the thing that Stan actually samples. Unfortunately, there’s a problem - because Stan uses Hamiltonian Monte Carlo, we can’t use discrete parameters like the occupancy state $z_{i}$. We therefore need to get these parameters out of the model by marginalizing over them. Luckily, resources like this excellent tutorial by Maxwell B. Joseph explain how to do this! As my code here is adapted from the supplementary material of this paper by Allan Clark and Res Altwegg it appears very slightly different to Maxwell’s, but it does the same thing. Running the model and exploring the output Now that we’ve written the model, it’s time to run it! To do this, I use CmdStan via the cstan function in the rethinking package. First save the model as a .stan file (I called mine occ_gp_nc.stan) and then run the model like this: m1_nc &lt;- cstan(file = "C:/Stan_code/occupancy_models/occ_gp_nc.stan", data = dlist, chains = 4, cores = 4, warmup = 1500, iter = 2500, seed = 33221) The model will now compile and sample. This could take a little while, so it’s a good idea to go for a walk / play with the dog / classify some of my camera trap photos :wink: until it’s done (relevant xkcd)! When the sampling is complete, you’ll get a warning message saying that some variables have undefined values - this is safe to ignore, it’s just saying that there are no $p_{i,j}$ values for visits which were never conducted. We can now inspect some model diagnostics: precis(m1_nc) dashboard(m1_nc) We’re particularly interested in the effective sample size n_effwhich should be some healthy number relative to our actual number of samples, the Gelman-Rubin convergence diagnostic Rhat which should be 1 for each parameter, and the number of divergent transitions which should ideally be zero. We should also inspect the traceplots for the key parameters here: traceplot(m1_nc, pars=c("betax","betam","alphadet","betadet","k_bar","etasq","rhosq")) These ones look like “fuzzy caterpillars”, which is good. Now that we’re happy that our model has performed adequately, we can extract our samples from the posterior distribution: post &lt;- extract.samples(m1_nc) I think at this stage it’s a good idea to look inside our posterior distribution with str(post)to see what it looks like. We see that it’s a list, with a separate element for each of our parameters. We can see that there are 4000 samples for each parameter, because we ran 4 chains with 1000 iterations each (remember that we set iter=2500 but the 1500 warmup iterations don’t appear in post). If we look at some of the parameters, such as psi or SIGMA, we see that they have more dimensions - this is because they are vector or matrix parameters (e.g., psi is a vector with one value for each site). In total, we actually have 22400 parameters! Most of these (20000 of them) are for the covariance matrix SIGMA and the Cholesky-decomposed covariance matrix L_SIGMA. Now that we’ve got our posterior distribution, we can start doing useful things with it! In the following sections, I go through a few of these things. Looking at the key parameters One of the first things we can do is look at the values for the key parameters in our model - the effect sizes ($\beta_{X},\beta{M},\beta{det}$), intercepts ($\alpha_{det},\bar{k}$), and the Gaussian process parameters ($\eta^2,\rho^2$). We can see these in the precis summary table but I prefer to look at them using density plots. Since we simulated the data and know the true values of each parameter, we can also check to see whether the model estimated these values accurately. This is an important part of validating our model. An easy way to do this is to just add a vertical line to each density plot to show the true value: par(mfrow=c(4,2)) dens(post$betax, main="betax"); abline(v=betax,lty=2) dens(post$betam, main="betam"); abline(v=betam,lty=2) dens(post$alphadet, main="alphadet"); abline(v=alphadet, lty=2) dens(post$betadet, main="betadet"); abline(v=betadet, lty=2) dens(post$k_bar, main="k_bar"); abline(v=0, lty=2) dens(post$etasq, main="eta^2"); abline(v=eta2, lty=2) dens(post$rhosq, main="rho^2"); abline(v=rho2, lty=2) As you can see, in this example the model estimated all of the key parameters pretty accurately - the dashed vertical lines all fall somewhere within the posterior distribution for each parameter. There’s a fair bit of uncertainty, but the model is still pretty good at identifying whether the effect size parameters are positive or negative - this is particularly important for betax, which is our effect of interest in this example. Looking at the occupancy probability We can also look at the model’s predictions of the occupancy probability at each site ($\psi_{i}$). Again, because we simulated the data we can compare this against the true value. Here I do this by plotting the posterior mean and 89% compatability intervals against the true value: psi_mu &lt;- apply(post$psi,2,mean) psi_PI89 &lt;- apply(post$psi, 2, HPDI, prob=0.89) par(mfrow=c(1,1)) plot(NULL, xlim=c(0,1), ylim=c(0,1), xlab="True value", ylab="Model estimate", main=expression(psi)) abline(a=0,b=1, lty=2) # true = predicted points(x = psi, y = psi_mu, pch=16, col=rangi2) for(i in 1:length(psi)){ lines(x = rep(psi[i],2), y = c(psi_PI89[1,i], psi_PI89[2,i]), col=rangi2) } As we can see, the inferences are not too bad, with many of the points lying close to the diagonal line where the model’s estimate is equal to the true value. Visualising the spatial autocorrelation One of the really cool things we can do is visualise the spatial autocorrelation between sites. The first thing we need to do here is to compute the posterior median covariance among sites (i.e., the most probable covariance matrix according to the model) and convert it into a correlation matrix: K &lt;- matrix(0, nrow=sites, ncol=sites) for(i in 1:sites){ for(j in 1:sites){ K[i,j] &lt;- median(post$etasq) * exp(-median(post$rhosq)*dmat2[i,j]) } } diag(K) &lt;- median(post$etasq) + 0.01 Rho &lt;- round(cov2cor(K),2) rownames(Rho) &lt;- seq(1,100,1) colnames(Rho) &lt;- rownames(Rho) We can now plot the results on a map, with darker lines representing a higher degree of correlation between sites: par(mfrow=c(1,1)) plot(y~x, data=coords, col=rangi2, pch=16, xlim=c(0,10), ylim=c(0,10), main="Correlation inferred from model") for(i in 1:sites){ for(j in 1:sites){ if(i &lt; j){ lines(x = c(coords$x[i], coords$x[j]), y = c(coords$y[i], coords$y[j]), lwd=2, col=col.alpha("black", Rho[i,j]^2)) } } } Again, because we simulated the data we are able to compare our model’s results against the truth: # Compute true correlation matrix Rho_t &lt;- round(cov2cor(covmat),2) rownames(Rho_t) &lt;- seq(1,100,1) colnames(Rho_t) &lt;- rownames(Rho) # Plot correlations on a map plot(y~x, data=coords, col=rangi2, pch=16, xlim=c(0,10), ylim=c(0,10), main="True correlation") for(i in 1:sites){ for(j in 1:sites){ if(i &lt; j){ lines(x = c(coords$x[i], coords$x[j]), y = c(coords$y[i], coords$y[j]), lwd=2, col=col.alpha("black", Rho_t[i,j]^2)) } } } We see that our model has done a great job here - the two plots looks pretty similar! Simulating an intervention The effect of intervening in the system (i.e., changing the value of $X$) on the occupancy probability is often different to the effect size for the covariate of interest (i.e., $\beta_{X}$). This is because when there are extreme values for other covariates the occupancy probabilty can get pushed close to zero or one, making the effect of our focal covariate less important - this is a floor/ceiling effect. The consequence is that if we’re interested in understanding the consequences of changing $X$, then we have to consider how the other variables which effect occupancy (in this case, $M$) are distributed. One way of dealing with this challenge is by using a simulation. The exact code for our simulation depends on whether we’re interested in simulating an intervention for the specific sites that we surveyed, or for the (hypothetical) population of all sites. We’ll start with the specific sites we surveyed: # Simulating intervention for the sites that we surveyed simsites &lt;- sites # There were 100 sites that we surveyed nsamples &lt;- 4000 # Number of samples from posterior to use (4000 = all of them) # Matrices to store our results S1 &lt;- matrix(0, nrow=nsamples, ncol=simsites) S2 &lt;- matrix(0, nrow=nsamples, ncol=simsites) new_x &lt;- x + 1 # Simulate increasing x by 1 # Under status quo for(s in 1:simsites){ inv_psi &lt;- post$k_bar + post$k[,s] + post$betax*x[s] + post$betam*m[s] psi_sim &lt;- inv_logit(inv_psi) S1[,s] &lt;- psi_sim } # Under distribution of x after intervention for(s in 1:simsites){ inv_psi &lt;- post$k_bar + post$k[,s] + post$betax*new_x[s] + post$betam*m[s] psi_sim &lt;- inv_logit(inv_psi) S2[,s] &lt;- psi_sim } # Difference between distribution under the two scenarios Sdiff &lt;- S2-S1 dens(Sdiff) abline(v=inv_logit(betax)-inv_logit(0), lty=2) # true betax Looking at this distribution, we can see that there is a fair amount of uncertainty in what will happen to $\psi$ when we increase $X$ by 1, but the model is pretty sure that the effect of the intervention will be to increase $\psi$. The peak of our distribution is at around 0.23, which is what we’d expect from our $\beta_{X}$ parameter (as when $\bar{k}$ is 0, the true effect of $X$ is an increase $\psi$ from 0 to 1 on the log-odds scale, which is an increase from 0.5 to 0.73 on the probabilty scale, i.e. an increase of 0.23 - I’ve shown this value with a dashed vertical line). However, notice that the distribution is not symmetrical - for some of the sites, increasing $X$ had less of an effect on $\psi$. This is due to ceiling and floor effects that occur at some sites where both the values of $k$ and $M$ are relatively extreme. Now let’s look at simulating the intervention for the hypothetical population of all sites. The approach is generally similar to what we’ve just done, but there are a couple of key differences. The first is that we no longer need to use our $k_{i}$ values. The second is that we need to decide on a distribution for $M$ to average over - here I’ve gone for $Uniform(0,4)$ because it’ll really exaggerate the ceiling/floor effects, which will be interesting to see: # Simulating intervention for hypothetical population of sites simsites &lt;- 5000 # 5000 hypothetical sites nsamples &lt;- 4000 # Number of samples from posterior to use (4000 = all of them) # Matrices to store our results S1 &lt;- matrix(0, nrow=nsamples, ncol=simsites) S2 &lt;- matrix(0, nrow=nsamples, ncol=simsites) old_x &lt;- rnorm(simsites, 0, 1) new_x &lt;- old_x + 1 # Simulate increasing x by 1 m_sim &lt;- runif(simsites, 0, 4) # Under status quo for(s in 1:simsites){ inv_psi &lt;- post$k_bar + post$betax*old_x[s] + post$betam*m_sim[s] psi_sim &lt;- inv_logit(inv_psi) S1[,s] &lt;- psi_sim } # Under distribution of x after intervention for(s in 1:simsites){ inv_psi &lt;- post$k_bar + post$betax*new_x[s] + post$betam*m_sim[s] psi_sim &lt;- inv_logit(inv_psi) S2[,s] &lt;- psi_sim } Sdiff &lt;- S2-S1 dens(Sdiff) abline(v=inv_logit(betax)-inv_logit(0), lty=2) Now we can really see the ceiling and floor effects - in a large proportion of sites, changing $X$ had almost no effect on $\psi$, even though $\beta_{X}$ is strongly positive! Since $k_{i}$ is out of the picture now, this is all down to $M$ - we can see this if we plot the change in $\psi$ against $M$ like so: plot(NULL, xlim=range(m_sim), ylim=c(-0.5,0.5), ylab="Change in psi",xlab="m_sim") abline(h=0, lty=2) for(s in 1:simsites){ points(m_sim[s], mean(Sdiff[,s])) } ate &lt;- mean(Sdiff) # Average treatment effect abline(h=ate,lwd=2) We can see here that when $M$ (m_sim) is high, the change in $\psi$ tends to be closer to zero.]]></summary></entry></feed>