wip
|
@ -148,7 +148,8 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni
|
|||
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb1"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">library</span>(bayesrules)</span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="fu">library</span>(tidyverse)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="fu">library</span>(tidyverse)</span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="fu">library</span>(patchwork)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<p>The chapter is set up with an example of polling results. We are put into the scenario where we are managig the campaing for a candidate. We know that on average her support based on recent polls is around 45%. In the next few sections we’ll work through our Bayesian framework and incorporate a new tool the <strong>Beta-Binomial</strong> model. This model will take develop a continuous prior, as opposed to the discrete one’s we’ve been working with so far.</p>
|
||||
<section id="the-beta-prior" class="level2">
|
||||
|
@ -229,7 +230,7 @@ The Standard Uniform
|
|||
<div class="callout-body-container callout-body">
|
||||
<p>When <span class="math inline">\pi</span> can take equally take on any value between 0 and 1, we can model <span class="math inline">\pi</span> using the standard uniform model.</p>
|
||||
<p><span class="math display">\pi \sim Unif(0, 1)</span></p>
|
||||
<p>the pdf of <span class="math inline">Unif(0, 1)</span> is <span class="math inline">\f(\pi) = 1</span></p>
|
||||
<p>the pdf of <span class="math inline">Unif(0, 1)</span> is <span class="math inline">f(\pi) = 1</span></p>
|
||||
<p>Note that <span class="math inline">Unif(0, 1)</span> is just a special case of the Beta with hyperparameters <span class="math inline">\alpha = \beta = 1</span>, see <a href="#fig-std-unif-as-beta">Figure 2</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -282,6 +283,136 @@ The Standard Uniform
|
|||
<p>it follows that</p>
|
||||
<p><span class="math display">SD = \sqrt{Var(\pi)}</span></p>
|
||||
</section>
|
||||
<section id="tuning-the-beta-prior" class="level3">
|
||||
<h3 class="anchored" data-anchor-id="tuning-the-beta-prior">Tuning the beta prior</h3>
|
||||
<p>Now that we know we can use the beta model to represent <span class="math inline">\pi</span> for <span class="math inline">\pi \in [0, 1]</span>, what we need to try and do is tune the model so that it best represents our prior. Our options of course are chaning values of <span class="math inline">\alpha</span> and <span class="math inline">\beta</span>. We can make use of the fact that we know that on average the candidate is polling at around 45%. That is we know that</p>
|
||||
<p><span class="math display">E(\pi) \approx .45 \approx \frac{\alpha}{\alpha + \beta}</span></p>
|
||||
<p><em>so I tried to work through the algebra myself, but was unable to do so, the book gets to the following conclusion</em></p>
|
||||
<p><span class="math display">\alpha = \frac{9}{11}\beta</span></p>
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb6"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>p <span class="ot"><-</span> <span class="fu">plot_beta</span>(<span class="dv">9</span>, <span class="dv">11</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">labs</span>(<span class="at">title=</span><span class="st">"Beta(9,11)"</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">scale_x_continuous</span>(<span class="at">breaks=</span><span class="fu">seq</span>(<span class="dv">0</span>, <span class="dv">1</span>, <span class="at">by=</span>.<span class="dv">1</span>))</span>
|
||||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>p2 <span class="ot"><-</span> <span class="fu">plot_beta</span>(<span class="dv">27</span>, <span class="dv">33</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">labs</span>(<span class="at">title=</span><span class="st">"Beta(27,33)"</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">scale_x_continuous</span>(<span class="at">breaks=</span><span class="fu">seq</span>(<span class="dv">0</span>, <span class="dv">1</span>, <span class="at">by=</span>.<span class="dv">1</span>))</span>
|
||||
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>p3 <span class="ot"><-</span> <span class="fu">plot_beta</span>(<span class="dv">45</span>, <span class="dv">55</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">labs</span>(<span class="at">title=</span><span class="st">"Beta(45,55)"</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="fu">scale_x_continuous</span>(<span class="at">breaks=</span><span class="fu">seq</span>(<span class="dv">0</span>, <span class="dv">1</span>, <span class="at">by=</span>.<span class="dv">1</span>))</span>
|
||||
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>p <span class="sc">/</span> p2 <span class="sc">/</span> p3</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<div class="cell-output-display">
|
||||
<p><img src="ch3-beta-binomial_files/figure-html/unnamed-chunk-6-1.png" class="img-fluid" width="672"></p>
|
||||
</div>
|
||||
</div>
|
||||
<p>when compared to the original polling values, we can see that the closest of these is <span class="math inline">Beta(45, 55)</span></p>
|
||||
<p>we can get the pdf, and then mean, mode and variance of the pdf.</p>
|
||||
<p><span class="math display">f(\pi) = \frac{\Gamma(100)}{\Gamma(45)\Gamma(55)}\pi^{44}(1-\pi)^{54}</span></p>
|
||||
<p><span class="math display">E(\pi) = \frac{45}{45 + 55} = .4500</span> <span class="math display">\text{Mode}(\pi) = \frac{45 - 1}{45 + 55 - 2} = .449</span> <span class="math display"> SD(\pi) = .05</span></p>
|
||||
<div class="cell" data-fig.caption="the prior distrubtions with Y = 30 hihglighted">
|
||||
<div class="sourceCode cell-code" id="cb7"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>rb1 <span class="ot"><-</span> <span class="fu">rbinom</span>(<span class="dv">10000</span>, <span class="dv">50</span>, .<span class="dv">2</span>)</span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>rb2 <span class="ot"><-</span> <span class="fu">rbinom</span>(<span class="dv">10000</span>, <span class="dv">50</span>, .<span class="dv">3</span>)</span>
|
||||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>rb3 <span class="ot"><-</span> <span class="fu">rbinom</span>(<span class="dv">10000</span>, <span class="dv">50</span>, .<span class="dv">5</span>)</span>
|
||||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>rb4 <span class="ot"><-</span> <span class="fu">rbinom</span>(<span class="dv">10000</span>, <span class="dv">50</span>, .<span class="dv">6</span>)</span>
|
||||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>rb5 <span class="ot"><-</span> <span class="fu">rbinom</span>(<span class="dv">10000</span>, <span class="dv">50</span>, .<span class="dv">7</span>)</span>
|
||||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>rb6 <span class="ot"><-</span> <span class="fu">rbinom</span>(<span class="dv">10000</span>, <span class="dv">50</span>, .<span class="dv">8</span>)</span>
|
||||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>results <span class="ot"><-</span> <span class="fu">tibble</span>(</span>
|
||||
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">Bin(50, .2)</span><span class="st">`</span> <span class="ot">=</span> rb1,</span>
|
||||
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">Bin(50, .3)</span><span class="st">`</span> <span class="ot">=</span> rb2,</span>
|
||||
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">Bin(50, .5)</span><span class="st">`</span> <span class="ot">=</span> rb3,</span>
|
||||
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">Bin(50, .6)</span><span class="st">`</span> <span class="ot">=</span> rb4,</span>
|
||||
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">Bin(50, .7)</span><span class="st">`</span> <span class="ot">=</span> rb5,</span>
|
||||
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">Bin(50, .8)</span><span class="st">`</span> <span class="ot">=</span> rb6,</span>
|
||||
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>) <span class="sc">|></span></span>
|
||||
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="fu">pivot_longer</span>(<span class="at">names_to =</span> <span class="st">"bin_model"</span>, <span class="at">values_to =</span> <span class="st">"value"</span>, </span>
|
||||
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">Bin(50, .2)</span><span class="st">`</span><span class="sc">:</span><span class="st">`</span><span class="at">Bin(50, .8)</span><span class="st">`</span>) <span class="sc">|></span></span>
|
||||
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a> <span class="fu">mutate</span>(<span class="at">is_30 =</span> value <span class="sc">==</span> <span class="dv">30</span>)</span>
|
||||
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>results <span class="sc">|></span></span>
|
||||
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a> <span class="fu">ggplot</span>(<span class="fu">aes</span>(value, <span class="at">fill=</span>is_30)) <span class="sc">+</span> <span class="fu">geom_bar</span>(<span class="at">stat =</span> <span class="st">"count"</span>) <span class="sc">+</span></span>
|
||||
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a> <span class="fu">scale_fill_manual</span>(<span class="at">values =</span> <span class="fu">c</span>(<span class="st">"grey30"</span>, <span class="st">"darkblue"</span>), <span class="at">guide=</span><span class="st">"none"</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a> <span class="fu">facet_wrap</span>(<span class="fu">vars</span>(bin_model))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<div class="cell-output-display">
|
||||
<div id="fig-betas-for-poll" class="quarto-figure quarto-figure-center anchored">
|
||||
<figure class="figure">
|
||||
<p><img src="ch3-beta-binomial_files/figure-html/fig-betas-for-poll-1.png" class="img-fluid figure-img" width="672"></p>
|
||||
<p></p><figcaption class="figure-caption">Figure 4: <strong>?(caption)</strong></figcaption><p></p>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>After the finish the poll, we see that the actual value of <span class="math inline">Y</span> is 30. The vertical line in <a href="#fig-betas-for-poll">Figure 4</a></p>
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb8"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>results <span class="ot"><-</span> <span class="fu">tibble</span>(</span>
|
||||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="at">ys =</span> <span class="dv">1</span><span class="sc">:</span><span class="dv">50</span>, </span>
|
||||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .1)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">1</span>),</span>
|
||||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .2)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">2</span>),</span>
|
||||
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .3)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">3</span>),</span>
|
||||
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .4)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">4</span>),</span>
|
||||
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .5)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">5</span>),</span>
|
||||
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .6)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">6</span>),</span>
|
||||
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .7)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">7</span>),</span>
|
||||
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .8)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">8</span>),</span>
|
||||
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> <span class="st">`</span><span class="at">beta(50, .9)</span><span class="st">`</span> <span class="ot">=</span> <span class="fu">dbinom</span>(<span class="at">x =</span> ys, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> .<span class="dv">9</span>),</span>
|
||||
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>) <span class="sc">|></span></span>
|
||||
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> <span class="fu">pivot_longer</span>(<span class="at">names_to =</span> <span class="st">"beta_model"</span>, <span class="at">values_to=</span><span class="st">"value"</span>, <span class="sc">-</span>ys) <span class="sc">|></span></span>
|
||||
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a> <span class="fu">mutate</span>(</span>
|
||||
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a> <span class="at">is_30 =</span> ys <span class="sc">==</span> <span class="dv">30</span>, </span>
|
||||
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a> <span class="at">pie =</span> <span class="fu">rep</span>(<span class="fu">c</span>(.<span class="dv">1</span>, .<span class="dv">2</span>, .<span class="dv">3</span>, .<span class="dv">4</span>, .<span class="dv">5</span>, .<span class="dv">6</span>, .<span class="dv">7</span>, .<span class="dv">8</span>, .<span class="dv">9</span>), <span class="dv">50</span>))</span>
|
||||
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a></span>
|
||||
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>results <span class="sc">|></span> <span class="fu">ggplot</span>(<span class="fu">aes</span>(ys, value, <span class="at">fill =</span> is_30)) <span class="sc">+</span> </span>
|
||||
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a> <span class="fu">geom_col</span>() <span class="sc">+</span> </span>
|
||||
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a> <span class="fu">scale_fill_manual</span>(<span class="at">values =</span> <span class="fu">c</span>(<span class="st">"darkgray"</span>, <span class="st">"darkblue"</span>), <span class="at">guide=</span><span class="st">"none"</span>) <span class="sc">+</span> </span>
|
||||
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a> <span class="fu">facet_wrap</span>(<span class="fu">vars</span>(beta_model))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<div class="cell-output-display">
|
||||
<p><img src="ch3-beta-binomial_files/figure-html/unnamed-chunk-8-1.png" class="img-fluid" width="672"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb9"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>results <span class="sc">|></span></span>
|
||||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">filter</span>(is_30) <span class="sc">|></span></span>
|
||||
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">ggplot</span>(<span class="fu">aes</span>(pie, value)) <span class="sc">+</span> <span class="fu">geom_col</span>() <span class="sc">+</span> </span>
|
||||
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">scale_x_continuous</span>(<span class="at">breaks =</span> <span class="fu">seq</span>(.<span class="dv">1</span>, .<span class="dv">9</span>, <span class="at">by =</span> .<span class="dv">1</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<div class="cell-output-display">
|
||||
<p><img src="ch3-beta-binomial_files/figure-html/unnamed-chunk-9-1.png" class="img-fluid" width="672"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb10"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="fu">summarize_beta_binomial</span>(<span class="at">alpha=</span><span class="dv">45</span>, <span class="at">beta=</span><span class="dv">55</span>, <span class="at">n=</span><span class="dv">50</span>, <span class="at">y=</span><span class="dv">30</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<div class="cell-output cell-output-stdout">
|
||||
<pre><code> model alpha beta mean mode var sd
|
||||
1 prior 45 55 0.45 0.4489796 0.002450495 0.04950248
|
||||
2 posterior 75 75 0.50 0.5000000 0.001655629 0.04068942</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="simulation" class="level2">
|
||||
<h2 class="anchored" data-anchor-id="simulation">Simulation</h2>
|
||||
<p>In this simulation we first generate 10,000 random values from a beta distribution with parameter <span class="math inline">\alpha = 45, \beta = 55</span>. Recall, that this is what we believe to be support based on recent polls for the candidate, and therefore our prior. We use each one of these are the <code>prob</code> value (the <span class="math inline">\pi</span> value) into 10,000 more random this time from the binomial distribution with <span class="math inline">n = 50</span>. This binomial distribution is the model for <span class="math inline">Y</span> conditioned on <span class="math inline">\pi</span>.</p>
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb12"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>sim <span class="ot"><-</span> <span class="fu">tibble</span>(</span>
|
||||
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a> <span class="at">pi =</span> <span class="fu">rbeta</span>(<span class="dv">10000</span>, <span class="dv">45</span>, <span class="dv">55</span>)</span>
|
||||
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>) <span class="sc">|></span></span>
|
||||
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">mutate</span>(<span class="at">y =</span> <span class="fu">rbinom</span>(<span class="dv">10000</span>, <span class="at">size =</span> <span class="dv">50</span>, <span class="at">prob =</span> pi))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb13"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>sim <span class="sc">|></span></span>
|
||||
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">ggplot</span>(<span class="fu">aes</span>(pi, y)) <span class="sc">+</span> <span class="fu">geom_point</span>(<span class="fu">aes</span>(<span class="at">color =</span> (y <span class="sc">==</span> <span class="dv">30</span>))) <span class="sc">+</span> </span>
|
||||
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">scale_color_manual</span>(<span class="at">values =</span> <span class="fu">c</span>(<span class="st">"darkgrey"</span>, <span class="st">"navyblue"</span>))</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<div class="cell-output-display">
|
||||
<p><img src="ch3-beta-binomial_files/figure-html/unnamed-chunk-12-1.png" class="img-fluid" width="672"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="sourceCode cell-code" id="cb14"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>sim <span class="sc">|></span></span>
|
||||
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">filter</span>(y <span class="sc">==</span> <span class="dv">30</span>) <span class="sc">|></span> <span class="co"># the posterior</span></span>
|
||||
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">ggplot</span>(<span class="fu">aes</span>(pi)) <span class="sc">+</span> <span class="fu">geom_density</span>()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
|
||||
<div class="cell-output-display">
|
||||
<p><img src="ch3-beta-binomial_files/figure-html/unnamed-chunk-13-1.png" class="img-fluid" width="672"></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
|
|
@ -18,6 +18,7 @@ format:
|
|||
```{r}
|
||||
library(bayesrules)
|
||||
library(tidyverse)
|
||||
library(patchwork)
|
||||
```
|
||||
|
||||
The chapter is set up with an example of polling results. We are put into
|
||||
|
@ -93,7 +94,7 @@ we can model $\pi$ using the standard uniform model.
|
|||
|
||||
$$\pi \sim Unif(0, 1)$$
|
||||
|
||||
the pdf of $Unif(0, 1)$ is $\f(\pi) = 1$
|
||||
the pdf of $Unif(0, 1)$ is $f(\pi) = 1$
|
||||
|
||||
Note that $Unif(0, 1)$ is just a special case of the Beta with
|
||||
hyperparameters $\alpha = \beta = 1$, see @fig-std-unif-as-beta
|
||||
|
@ -151,3 +152,151 @@ it follows that
|
|||
|
||||
$$SD = \sqrt{Var(\pi)}$$
|
||||
|
||||
|
||||
### Tuning the beta prior
|
||||
|
||||
Now that we know we can use the beta model to represent
|
||||
$\pi$ for $\pi \in [0, 1]$, what we need to try and do is
|
||||
tune the model so that it best represents our prior. Our
|
||||
options of course are chaning values of $\alpha$ and $\beta$.
|
||||
We can make use of the fact that we know that on average
|
||||
the candidate is polling at around 45%. That is we know
|
||||
that
|
||||
|
||||
$$E(\pi) \approx .45 \approx \frac{\alpha}{\alpha + \beta}$$
|
||||
|
||||
*so I tried to work through the algebra myself, but was
|
||||
unable to do so, the book gets to the following conclusion*
|
||||
|
||||
$$\alpha = \frac{9}{11}\beta$$
|
||||
|
||||
|
||||
```{r}
|
||||
p <- plot_beta(9, 11) +
|
||||
labs(title="Beta(9,11)") +
|
||||
scale_x_continuous(breaks=seq(0, 1, by=.1))
|
||||
p2 <- plot_beta(27, 33) +
|
||||
labs(title="Beta(27,33)") +
|
||||
scale_x_continuous(breaks=seq(0, 1, by=.1))
|
||||
p3 <- plot_beta(45, 55) +
|
||||
labs(title="Beta(45,55)") +
|
||||
scale_x_continuous(breaks=seq(0, 1, by=.1))
|
||||
|
||||
p / p2 / p3
|
||||
```
|
||||
|
||||
when compared to the original polling values, we can see that the closest
|
||||
of these is $Beta(45, 55)$
|
||||
|
||||
we can get the pdf, and then mean, mode and variance of the pdf.
|
||||
|
||||
$$f(\pi) = \frac{\Gamma(100)}{\Gamma(45)\Gamma(55)}\pi^{44}(1-\pi)^{54}$$
|
||||
|
||||
$$E(\pi) = \frac{45}{45 + 55} = .4500$$
|
||||
$$\text{Mode}(\pi) = \frac{45 - 1}{45 + 55 - 2} = .449$$
|
||||
$$ SD(\pi) = .05$$
|
||||
|
||||
|
||||
```{r}
|
||||
#| label: fig-betas-for-poll
|
||||
#| fig-caption: "the prior distrubtions with Y = 30 hihglighted"
|
||||
|
||||
rb1 <- rbinom(10000, 50, .2)
|
||||
rb2 <- rbinom(10000, 50, .3)
|
||||
rb3 <- rbinom(10000, 50, .5)
|
||||
rb4 <- rbinom(10000, 50, .6)
|
||||
rb5 <- rbinom(10000, 50, .7)
|
||||
rb6 <- rbinom(10000, 50, .8)
|
||||
|
||||
results <- tibble(
|
||||
`Bin(50, .2)` = rb1,
|
||||
`Bin(50, .3)` = rb2,
|
||||
`Bin(50, .5)` = rb3,
|
||||
`Bin(50, .6)` = rb4,
|
||||
`Bin(50, .7)` = rb5,
|
||||
`Bin(50, .8)` = rb6,
|
||||
) |>
|
||||
pivot_longer(names_to = "bin_model", values_to = "value",
|
||||
`Bin(50, .2)`:`Bin(50, .8)`) |>
|
||||
mutate(is_30 = value == 30)
|
||||
|
||||
results |>
|
||||
ggplot(aes(value, fill=is_30)) + geom_bar(stat = "count") +
|
||||
scale_fill_manual(values = c("grey30", "darkblue"), guide="none") +
|
||||
facet_wrap(vars(bin_model))
|
||||
|
||||
```
|
||||
|
||||
After the finish the poll, we see that the actual value of
|
||||
$Y$ is 30. The vertical line in @fig-betas-for-poll
|
||||
|
||||
|
||||
```{r}
|
||||
results <- tibble(
|
||||
ys = 1:50,
|
||||
`beta(50, .1)` = dbinom(x = ys, size = 50, prob = .1),
|
||||
`beta(50, .2)` = dbinom(x = ys, size = 50, prob = .2),
|
||||
`beta(50, .3)` = dbinom(x = ys, size = 50, prob = .3),
|
||||
`beta(50, .4)` = dbinom(x = ys, size = 50, prob = .4),
|
||||
`beta(50, .5)` = dbinom(x = ys, size = 50, prob = .5),
|
||||
`beta(50, .6)` = dbinom(x = ys, size = 50, prob = .6),
|
||||
`beta(50, .7)` = dbinom(x = ys, size = 50, prob = .7),
|
||||
`beta(50, .8)` = dbinom(x = ys, size = 50, prob = .8),
|
||||
`beta(50, .9)` = dbinom(x = ys, size = 50, prob = .9),
|
||||
) |>
|
||||
pivot_longer(names_to = "beta_model", values_to="value", -ys) |>
|
||||
mutate(
|
||||
is_30 = ys == 30,
|
||||
pie = rep(c(.1, .2, .3, .4, .5, .6, .7, .8, .9), 50))
|
||||
|
||||
results |> ggplot(aes(ys, value, fill = is_30)) +
|
||||
geom_col() +
|
||||
scale_fill_manual(values = c("darkgray", "darkblue"), guide="none") +
|
||||
facet_wrap(vars(beta_model))
|
||||
```
|
||||
|
||||
|
||||
```{r}
|
||||
results |>
|
||||
filter(is_30) |>
|
||||
ggplot(aes(pie, value)) + geom_col() +
|
||||
scale_x_continuous(breaks = seq(.1, .9, by = .1))
|
||||
```
|
||||
|
||||
|
||||
```{r}
|
||||
summarize_beta_binomial(alpha=45, beta=55, n=50, y=30)
|
||||
```
|
||||
|
||||
## Simulation
|
||||
|
||||
In this simulation we first generate 10,000 random values from
|
||||
a beta distribution with parameter $\alpha = 45, \beta = 55$. Recall,
|
||||
that this is what we believe to be support based on recent polls for the
|
||||
candidate, and therefore our prior. We
|
||||
use each one of these as the `prob` value (the $\pi$ value) into
|
||||
10,000 more random samples from `rbinom` with
|
||||
$n = 50$. This binomial distribution is how we model the likelihood. Our
|
||||
simualtion will
|
||||
|
||||
```{r}
|
||||
sim <- tibble(
|
||||
pi = rbeta(10000, 45, 55)
|
||||
) |>
|
||||
mutate(y = rbinom(10000, size = 50, prob = pi))
|
||||
```
|
||||
|
||||
|
||||
|
||||
```{r}
|
||||
sim |>
|
||||
ggplot(aes(pi, y)) + geom_point(aes(color = (y == 30))) +
|
||||
scale_color_manual(values = c("darkgrey", "navyblue"))
|
||||
```
|
||||
|
||||
|
||||
```{r}
|
||||
sim |>
|
||||
filter(y == 30) |> # the posterior
|
||||
ggplot(aes(pi)) + geom_density()
|
||||
```
|
||||
|
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 16 KiB |
|
@ -1,6 +1,5 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Lora&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Lora';
|
||||
font-family: 'Lora'
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="quarto-1.1.251">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
|
||||
<meta name="author" content="Emanuel Rodriguez">
|
||||
|
||||
<title>Beta-Binomial Bayesian Model Example</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
div.columns{display: flex; gap: min(4vw, 1.5em);}
|
||||
div.column{flex: auto; overflow-x: auto;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
ul.task-list li input[type="checkbox"] {
|
||||
width: 0.8em;
|
||||
margin: 0 0.8em 0.2em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script src="beta-binomial_files/libs/clipboard/clipboard.min.js"></script>
|
||||
<script src="beta-binomial_files/libs/quarto-html/quarto.js"></script>
|
||||
<script src="beta-binomial_files/libs/quarto-html/popper.min.js"></script>
|
||||
<script src="beta-binomial_files/libs/quarto-html/tippy.umd.min.js"></script>
|
||||
<script src="beta-binomial_files/libs/quarto-html/anchor.min.js"></script>
|
||||
<link href="beta-binomial_files/libs/quarto-html/tippy.css" rel="stylesheet">
|
||||
<link href="beta-binomial_files/libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" id="quarto-text-highlighting-styles">
|
||||
<script src="beta-binomial_files/libs/bootstrap/bootstrap.min.js"></script>
|
||||
<link href="beta-binomial_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="beta-binomial_files/libs/bootstrap/bootstrap.min.css" rel="stylesheet" id="quarto-bootstrap" data-mode="light">
|
||||
|
||||
<script>window.backupDefine = window.define; window.define = undefined;</script><script src="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.js"></script>
|
||||
<script>document.addEventListener("DOMContentLoaded", function () {
|
||||
var mathElements = document.getElementsByClassName("math");
|
||||
var macros = [];
|
||||
for (var i = 0; i < mathElements.length; i++) {
|
||||
var texText = mathElements[i].firstChild;
|
||||
if (mathElements[i].tagName == "SPAN") {
|
||||
katex.render(texText.data, mathElements[i], {
|
||||
displayMode: mathElements[i].classList.contains('display'),
|
||||
throwOnError: false,
|
||||
macros: macros,
|
||||
fleqn: false
|
||||
});
|
||||
}}});
|
||||
</script>
|
||||
<script>window.define = window.backupDefine; window.backupDefine = undefined;</script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.css">
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body class="fullcontent">
|
||||
|
||||
<div id="quarto-content" class="page-columns page-rows-contents page-layout-article">
|
||||
|
||||
<main class="content" id="quarto-document-content">
|
||||
|
||||
<header id="title-block-header" class="quarto-title-block default">
|
||||
<div class="quarto-title">
|
||||
<h1 class="title">Beta-Binomial Bayesian Model Example</h1>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="quarto-title-meta">
|
||||
|
||||
<div>
|
||||
<div class="quarto-title-meta-heading">Author</div>
|
||||
<div class="quarto-title-meta-contents">
|
||||
<p>Emanuel Rodriguez </p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
<section id="introduction" class="level2">
|
||||
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
|
||||
<p>In this short article I will try to put together a set of notes to go over the Beta-Binomial Bayesian Model. The example used here is pretty trivial but so is this model so :shrug:</p>
|
||||
</section>
|
||||
<section id="when-to-use-the-beta-binomial" class="level2">
|
||||
<h2 class="anchored" data-anchor-id="when-to-use-the-beta-binomial">When to use the Beta-Binomial?</h2>
|
||||
<p>The situations it makes sense to use the Beta-Binomial Model (BBM) are cases where we have some conditional dependence between a random variable <span class="math inline">Y</span> and a continuous parameter <span class="math inline">\pi \in [0, 1]</span>. Where <span class="math inline">Y</span> is the number of successes in <span class="math inline">n</span> independent trials, each with a probability of success <span class="math inline">\pi</span>.</p>
|
||||
<p>For the sake of these notes, let us start by setting up an example that will allow us to illustrate the implementation of this model. Suppose there is a slot machine manufacturer who recently was accused of embedding software into the hardware that would cause more losses than usual to players. To be allowed to continue selling machines to casinos, they must go through a series of inspections. The body governing the sale of these machines has tasked us to determine whether or not the software has been completely removed or if, instead, the company refined the bias to make it less noticeable.</p>
|
||||
</section>
|
||||
<section id="what-we-already-know" class="level2">
|
||||
<h2 class="anchored" data-anchor-id="what-we-already-know">What we already know</h2>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<!-- /main column -->
|
||||
<script id="quarto-html-after-body" type="application/javascript">
|
||||
window.document.addEventListener("DOMContentLoaded", function (event) {
|
||||
const toggleBodyColorMode = (bsSheetEl) => {
|
||||
const mode = bsSheetEl.getAttribute("data-mode");
|
||||
const bodyEl = window.document.querySelector("body");
|
||||
if (mode === "dark") {
|
||||
bodyEl.classList.add("quarto-dark");
|
||||
bodyEl.classList.remove("quarto-light");
|
||||
} else {
|
||||
bodyEl.classList.add("quarto-light");
|
||||
bodyEl.classList.remove("quarto-dark");
|
||||
}
|
||||
}
|
||||
const toggleBodyColorPrimary = () => {
|
||||
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap");
|
||||
if (bsSheetEl) {
|
||||
toggleBodyColorMode(bsSheetEl);
|
||||
}
|
||||
}
|
||||
toggleBodyColorPrimary();
|
||||
const icon = "";
|
||||
const anchorJS = new window.AnchorJS();
|
||||
anchorJS.options = {
|
||||
placement: 'right',
|
||||
icon: icon
|
||||
};
|
||||
anchorJS.add('.anchored');
|
||||
const clipboard = new window.ClipboardJS('.code-copy-button', {
|
||||
target: function(trigger) {
|
||||
return trigger.previousElementSibling;
|
||||
}
|
||||
});
|
||||
clipboard.on('success', function(e) {
|
||||
// button target
|
||||
const button = e.trigger;
|
||||
// don't keep focus
|
||||
button.blur();
|
||||
// flash "checked"
|
||||
button.classList.add('code-copy-button-checked');
|
||||
var currentTitle = button.getAttribute("title");
|
||||
button.setAttribute("title", "Copied!");
|
||||
setTimeout(function() {
|
||||
button.setAttribute("title", currentTitle);
|
||||
button.classList.remove('code-copy-button-checked');
|
||||
}, 1000);
|
||||
// clear code selection
|
||||
e.clearSelection();
|
||||
});
|
||||
function tippyHover(el, contentFn) {
|
||||
const config = {
|
||||
allowHTML: true,
|
||||
content: contentFn,
|
||||
maxWidth: 500,
|
||||
delay: 100,
|
||||
arrow: false,
|
||||
appendTo: function(el) {
|
||||
return el.parentElement;
|
||||
},
|
||||
interactive: true,
|
||||
interactiveBorder: 10,
|
||||
theme: 'quarto',
|
||||
placement: 'bottom-start'
|
||||
};
|
||||
window.tippy(el, config);
|
||||
}
|
||||
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
|
||||
for (var i=0; i<noterefs.length; i++) {
|
||||
const ref = noterefs[i];
|
||||
tippyHover(ref, function() {
|
||||
// use id or data attribute instead here
|
||||
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
|
||||
try { href = new URL(href).hash; } catch {}
|
||||
const id = href.replace(/^#\/?/, "");
|
||||
const note = window.document.getElementById(id);
|
||||
return note.innerHTML;
|
||||
});
|
||||
}
|
||||
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
|
||||
for (var i=0; i<bibliorefs.length; i++) {
|
||||
const ref = bibliorefs[i];
|
||||
const cites = ref.parentNode.getAttribute('data-cites').split(' ');
|
||||
tippyHover(ref, function() {
|
||||
var popup = window.document.createElement('div');
|
||||
cites.forEach(function(cite) {
|
||||
var citeDiv = window.document.createElement('div');
|
||||
citeDiv.classList.add('hanging-indent');
|
||||
citeDiv.classList.add('csl-entry');
|
||||
var biblioDiv = window.document.getElementById('ref-' + cite);
|
||||
if (biblioDiv) {
|
||||
citeDiv.innerHTML = biblioDiv.innerHTML;
|
||||
}
|
||||
popup.appendChild(citeDiv);
|
||||
});
|
||||
return popup.innerHTML;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div> <!-- /content -->
|
||||
|
||||
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: "Beta-Binomial Bayesian Model Example"
|
||||
author: "Emanuel Rodriguez"
|
||||
execute:
|
||||
message: false
|
||||
warning: false
|
||||
format:
|
||||
html:
|
||||
monofont: "Cascadia Mono"
|
||||
highlight-style: gruvbox-dark
|
||||
css: styles.css
|
||||
callout-icon: false
|
||||
callout-apperance: simple
|
||||
toc: false
|
||||
html-math-method: katex
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
In this short article I will try to put together a set of notes to
|
||||
go over the Beta-Binomial Bayesian Model. The example used here is pretty
|
||||
trivial but so is this model so :shrug:
|
||||
|
||||
## When to use the Beta-Binomial?
|
||||
|
||||
The situations it makes sense to use the Beta-Binomial Model (BBM) are
|
||||
cases where we have some conditional dependence between a random
|
||||
variable $Y$ and a continuous parameter $\pi \in [0, 1]$. Where $Y$ is the number of successes in $n$ independent trials, each with a probability of
|
||||
success $\pi$.
|
||||
|
||||
For the sake of these notes, let us start by setting up an example that will
|
||||
allow us to illustrate the implementation of this model. Suppose there
|
||||
is a slot machine manufacturer who recently was accused of embedding software
|
||||
into the hardware that would cause more losses than usual to players.
|
||||
To be allowed to continue selling machines to casinos, they must go through a
|
||||
series of inspections. The body governing the sale of these machines has tasked
|
||||
us to determine whether or not the software has been completely removed or if,
|
||||
instead, the company refined the bias to make it less noticeable.
|
||||
|
||||
|
||||
## What we already know
|
||||
|
||||
The set of facts that we can put together to build a known distribution
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
/* quarto syntax highlight colors */
|
||||
:root {
|
||||
--quarto-hl-kw-color: #ebdbb2;
|
||||
--quarto-hl-fu-color: #689d6a;
|
||||
--quarto-hl-va-color: #458588;
|
||||
--quarto-hl-cf-color: #cc241d;
|
||||
--quarto-hl-op-color: #ebdbb2;
|
||||
--quarto-hl-bu-color: #d65d0e;
|
||||
--quarto-hl-ex-color: #689d6a;
|
||||
--quarto-hl-pp-color: #d65d0e;
|
||||
--quarto-hl-at-color: #d79921;
|
||||
--quarto-hl-ch-color: #b16286;
|
||||
--quarto-hl-sc-color: #b16286;
|
||||
--quarto-hl-st-color: #98971a;
|
||||
--quarto-hl-vs-color: #98971a;
|
||||
--quarto-hl-ss-color: #98971a;
|
||||
--quarto-hl-im-color: #689d6a;
|
||||
--quarto-hl-dt-color: #d79921;
|
||||
--quarto-hl-dv-color: #f67400;
|
||||
--quarto-hl-bn-color: #f67400;
|
||||
--quarto-hl-fl-color: #f67400;
|
||||
--quarto-hl-cn-color: #b16286;
|
||||
--quarto-hl-co-color: #928374;
|
||||
--quarto-hl-do-color: #98971a;
|
||||
--quarto-hl-an-color: #98971a;
|
||||
--quarto-hl-cv-color: #928374;
|
||||
--quarto-hl-re-color: #928374;
|
||||
--quarto-hl-in-color: #282828;
|
||||
--quarto-hl-wa-color: #282828;
|
||||
--quarto-hl-al-color: #282828;
|
||||
--quarto-hl-er-color: #cc241d;
|
||||
}
|
||||
|
||||
/* other quarto variables */
|
||||
:root {
|
||||
--quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
pre > code.sourceCode > span {
|
||||
color: #ebdbb2;
|
||||
}
|
||||
|
||||
code span {
|
||||
color: #ebdbb2;
|
||||
}
|
||||
|
||||
code.sourceCode > span {
|
||||
color: #ebdbb2;
|
||||
}
|
||||
|
||||
div.sourceCode,
|
||||
div.sourceCode pre.sourceCode {
|
||||
color: #ebdbb2;
|
||||
}
|
||||
|
||||
code span.kw {
|
||||
color: #ebdbb2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.fu {
|
||||
color: #689d6a;
|
||||
}
|
||||
|
||||
code span.va {
|
||||
color: #458588;
|
||||
}
|
||||
|
||||
code span.cf {
|
||||
color: #cc241d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.op {
|
||||
color: #ebdbb2;
|
||||
}
|
||||
|
||||
code span.bu {
|
||||
color: #d65d0e;
|
||||
}
|
||||
|
||||
code span.ex {
|
||||
color: #689d6a;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.pp {
|
||||
color: #d65d0e;
|
||||
}
|
||||
|
||||
code span.at {
|
||||
color: #d79921;
|
||||
}
|
||||
|
||||
code span.ch {
|
||||
color: #b16286;
|
||||
}
|
||||
|
||||
code span.sc {
|
||||
color: #b16286;
|
||||
}
|
||||
|
||||
code span.st {
|
||||
color: #98971a;
|
||||
}
|
||||
|
||||
code span.vs {
|
||||
color: #98971a;
|
||||
}
|
||||
|
||||
code span.ss {
|
||||
color: #98971a;
|
||||
}
|
||||
|
||||
code span.im {
|
||||
color: #689d6a;
|
||||
}
|
||||
|
||||
code span.dt {
|
||||
color: #d79921;
|
||||
}
|
||||
|
||||
code span.dv {
|
||||
color: #f67400;
|
||||
}
|
||||
|
||||
code span.bn {
|
||||
color: #f67400;
|
||||
}
|
||||
|
||||
code span.fl {
|
||||
color: #f67400;
|
||||
}
|
||||
|
||||
code span.cn {
|
||||
color: #b16286;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.co {
|
||||
color: #928374;
|
||||
}
|
||||
|
||||
code span.do {
|
||||
color: #98971a;
|
||||
}
|
||||
|
||||
code span.an {
|
||||
color: #98971a;
|
||||
}
|
||||
|
||||
code span.cv {
|
||||
color: #928374;
|
||||
}
|
||||
|
||||
code span.re {
|
||||
color: #928374;
|
||||
}
|
||||
|
||||
code span.in {
|
||||
color: #282828;
|
||||
}
|
||||
|
||||
code span.wa {
|
||||
color: #282828;
|
||||
}
|
||||
|
||||
code span.al {
|
||||
color: #282828;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code span.er {
|
||||
color: #cc241d;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prevent-inlining {
|
||||
content: "</";
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=debc5d5d77c3f9108843748ff7464032.css.map */
|
|
@ -0,0 +1,760 @@
|
|||
const sectionChanged = new CustomEvent("quarto-sectionChanged", {
|
||||
detail: {},
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
composed: false,
|
||||
});
|
||||
|
||||
window.document.addEventListener("DOMContentLoaded", function (_event) {
|
||||
const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]');
|
||||
const sidebarEl = window.document.getElementById("quarto-sidebar");
|
||||
const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left");
|
||||
const marginSidebarEl = window.document.getElementById(
|
||||
"quarto-margin-sidebar"
|
||||
);
|
||||
// function to determine whether the element has a previous sibling that is active
|
||||
const prevSiblingIsActiveLink = (el) => {
|
||||
const sibling = el.previousElementSibling;
|
||||
if (sibling && sibling.tagName === "A") {
|
||||
return sibling.classList.contains("active");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior)
|
||||
function fireSlideEnter(e) {
|
||||
const event = window.document.createEvent("Event");
|
||||
event.initEvent("slideenter", true, true);
|
||||
window.document.dispatchEvent(event);
|
||||
}
|
||||
const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
tabs.forEach((tab) => {
|
||||
tab.addEventListener("shown.bs.tab", fireSlideEnter);
|
||||
});
|
||||
|
||||
// Track scrolling and mark TOC links as active
|
||||
// get table of contents and sidebar (bail if we don't have at least one)
|
||||
const tocLinks = tocEl
|
||||
? [...tocEl.querySelectorAll("a[data-scroll-target]")]
|
||||
: [];
|
||||
const makeActive = (link) => tocLinks[link].classList.add("active");
|
||||
const removeActive = (link) => tocLinks[link].classList.remove("active");
|
||||
const removeAllActive = () =>
|
||||
[...Array(tocLinks.length).keys()].forEach((link) => removeActive(link));
|
||||
|
||||
// activate the anchor for a section associated with this TOC entry
|
||||
tocLinks.forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
if (link.href.indexOf("#") !== -1) {
|
||||
const anchor = link.href.split("#")[1];
|
||||
const heading = window.document.querySelector(
|
||||
`[data-anchor-id=${anchor}]`
|
||||
);
|
||||
if (heading) {
|
||||
// Add the class
|
||||
heading.classList.add("reveal-anchorjs-link");
|
||||
|
||||
// function to show the anchor
|
||||
const handleMouseout = () => {
|
||||
heading.classList.remove("reveal-anchorjs-link");
|
||||
heading.removeEventListener("mouseout", handleMouseout);
|
||||
};
|
||||
|
||||
// add a function to clear the anchor when the user mouses out of it
|
||||
heading.addEventListener("mouseout", handleMouseout);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const sections = tocLinks.map((link) => {
|
||||
const target = link.getAttribute("data-scroll-target");
|
||||
if (target.startsWith("#")) {
|
||||
return window.document.getElementById(decodeURI(`${target.slice(1)}`));
|
||||
} else {
|
||||
return window.document.querySelector(decodeURI(`${target}`));
|
||||
}
|
||||
});
|
||||
|
||||
const sectionMargin = 200;
|
||||
let currentActive = 0;
|
||||
// track whether we've initialized state the first time
|
||||
let init = false;
|
||||
|
||||
const updateActiveLink = () => {
|
||||
// The index from bottom to top (e.g. reversed list)
|
||||
let sectionIndex = -1;
|
||||
if (
|
||||
window.innerHeight + window.pageYOffset >=
|
||||
window.document.body.offsetHeight
|
||||
) {
|
||||
sectionIndex = 0;
|
||||
} else {
|
||||
sectionIndex = [...sections].reverse().findIndex((section) => {
|
||||
if (section) {
|
||||
return window.pageYOffset >= section.offsetTop - sectionMargin;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sectionIndex > -1) {
|
||||
const current = sections.length - sectionIndex - 1;
|
||||
if (current !== currentActive) {
|
||||
removeAllActive();
|
||||
currentActive = current;
|
||||
makeActive(current);
|
||||
if (init) {
|
||||
window.dispatchEvent(sectionChanged);
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const inHiddenRegion = (top, bottom, hiddenRegions) => {
|
||||
for (const region of hiddenRegions) {
|
||||
if (top <= region.bottom && bottom >= region.top) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const categorySelector = "header.quarto-title-block .quarto-category";
|
||||
const activateCategories = (href) => {
|
||||
// Find any categories
|
||||
// Surround them with a link pointing back to:
|
||||
// #category=Authoring
|
||||
try {
|
||||
const categoryEls = window.document.querySelectorAll(categorySelector);
|
||||
for (const categoryEl of categoryEls) {
|
||||
const categoryText = categoryEl.textContent;
|
||||
if (categoryText) {
|
||||
const link = `${href}#category=${encodeURIComponent(categoryText)}`;
|
||||
const linkEl = window.document.createElement("a");
|
||||
linkEl.setAttribute("href", link);
|
||||
for (const child of categoryEl.childNodes) {
|
||||
linkEl.append(child);
|
||||
}
|
||||
categoryEl.appendChild(linkEl);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
};
|
||||
function hasTitleCategories() {
|
||||
return window.document.querySelector(categorySelector) !== null;
|
||||
}
|
||||
|
||||
function offsetRelativeUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
return offset ? offset + url : url;
|
||||
}
|
||||
|
||||
function offsetAbsoluteUrl(url) {
|
||||
const offset = getMeta("quarto:offset");
|
||||
const baseUrl = new URL(offset, window.location);
|
||||
|
||||
const projRelativeUrl = url.replace(baseUrl, "");
|
||||
if (projRelativeUrl.startsWith("/")) {
|
||||
return projRelativeUrl;
|
||||
} else {
|
||||
return "/" + projRelativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// read a meta tag value
|
||||
function getMeta(metaName) {
|
||||
const metas = window.document.getElementsByTagName("meta");
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
if (metas[i].getAttribute("name") === metaName) {
|
||||
return metas[i].getAttribute("content");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
async function findAndActivateCategories() {
|
||||
const currentPagePath = offsetAbsoluteUrl(window.location.href);
|
||||
const response = await fetch(offsetRelativeUrl("listings.json"));
|
||||
if (response.status == 200) {
|
||||
return response.json().then(function (listingPaths) {
|
||||
const listingHrefs = [];
|
||||
for (const listingPath of listingPaths) {
|
||||
const pathWithoutLeadingSlash = listingPath.listing.substring(1);
|
||||
for (const item of listingPath.items) {
|
||||
if (
|
||||
item === currentPagePath ||
|
||||
item === currentPagePath + "index.html"
|
||||
) {
|
||||
// Resolve this path against the offset to be sure
|
||||
// we already are using the correct path to the listing
|
||||
// (this adjusts the listing urls to be rooted against
|
||||
// whatever root the page is actually running against)
|
||||
const relative = offsetRelativeUrl(pathWithoutLeadingSlash);
|
||||
const baseUrl = window.location;
|
||||
const resolvedPath = new URL(relative, baseUrl);
|
||||
listingHrefs.push(resolvedPath.pathname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const nearestListing = findNearestParentListing(
|
||||
offsetAbsoluteUrl(window.location.pathname),
|
||||
listingHrefs
|
||||
);
|
||||
if (nearestListing) {
|
||||
activateCategories(nearestListing);
|
||||
} else {
|
||||
// See if the referrer is a listing page for this item
|
||||
const referredRelativePath = offsetAbsoluteUrl(document.referrer);
|
||||
const referrerListing = listingHrefs.find((listingHref) => {
|
||||
const isListingReferrer =
|
||||
listingHref === referredRelativePath ||
|
||||
listingHref === referredRelativePath + "index.html";
|
||||
return isListingReferrer;
|
||||
});
|
||||
|
||||
if (referrerListing) {
|
||||
// Try to use the referrer if possible
|
||||
activateCategories(referrerListing);
|
||||
} else if (listingHrefs.length > 0) {
|
||||
// Otherwise, just fall back to the first listing
|
||||
activateCategories(listingHrefs[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (hasTitleCategories()) {
|
||||
findAndActivateCategories();
|
||||
}
|
||||
|
||||
const findNearestParentListing = (href, listingHrefs) => {
|
||||
if (!href || !listingHrefs) {
|
||||
return undefined;
|
||||
}
|
||||
// Look up the tree for a nearby linting and use that if we find one
|
||||
const relativeParts = href.substring(1).split("/");
|
||||
while (relativeParts.length > 0) {
|
||||
const path = relativeParts.join("/");
|
||||
for (const listingHref of listingHrefs) {
|
||||
if (listingHref.startsWith(path)) {
|
||||
return listingHref;
|
||||
}
|
||||
}
|
||||
relativeParts.pop();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const manageSidebarVisiblity = (el, placeholderDescriptor) => {
|
||||
let isVisible = true;
|
||||
|
||||
return (hiddenRegions) => {
|
||||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the last element of the TOC
|
||||
const lastChildEl = el.lastElementChild;
|
||||
|
||||
if (lastChildEl) {
|
||||
// Find the top and bottom o the element that is being managed
|
||||
const elTop = el.offsetTop;
|
||||
const elBottom =
|
||||
elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight;
|
||||
|
||||
// Converts the sidebar to a menu
|
||||
const convertToMenu = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 0;
|
||||
child.style.overflow = "hidden";
|
||||
}
|
||||
|
||||
const toggleContainer = window.document.createElement("div");
|
||||
toggleContainer.style.width = "100%";
|
||||
toggleContainer.classList.add("zindex-over-content");
|
||||
toggleContainer.classList.add("quarto-sidebar-toggle");
|
||||
toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom
|
||||
toggleContainer.id = placeholderDescriptor.id;
|
||||
toggleContainer.style.position = "fixed";
|
||||
|
||||
const toggleIcon = window.document.createElement("i");
|
||||
toggleIcon.classList.add("quarto-sidebar-toggle-icon");
|
||||
toggleIcon.classList.add("bi");
|
||||
toggleIcon.classList.add("bi-caret-down-fill");
|
||||
|
||||
const toggleTitle = window.document.createElement("div");
|
||||
const titleEl = window.document.body.querySelector(
|
||||
placeholderDescriptor.titleSelector
|
||||
);
|
||||
if (titleEl) {
|
||||
toggleTitle.append(titleEl.innerText, toggleIcon);
|
||||
}
|
||||
toggleTitle.classList.add("zindex-over-content");
|
||||
toggleTitle.classList.add("quarto-sidebar-toggle-title");
|
||||
toggleContainer.append(toggleTitle);
|
||||
|
||||
const toggleContents = window.document.createElement("div");
|
||||
toggleContents.classList = el.classList;
|
||||
toggleContents.classList.add("zindex-over-content");
|
||||
toggleContents.classList.add("quarto-sidebar-toggle-contents");
|
||||
for (const child of el.children) {
|
||||
if (child.id === "toc-title") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const clone = child.cloneNode(true);
|
||||
clone.style.opacity = 1;
|
||||
clone.style.display = null;
|
||||
toggleContents.append(clone);
|
||||
}
|
||||
toggleContents.style.height = "0px";
|
||||
toggleContainer.append(toggleContents);
|
||||
el.parentElement.prepend(toggleContainer);
|
||||
|
||||
// Process clicks
|
||||
let tocShowing = false;
|
||||
// Allow the caller to control whether this is dismissed
|
||||
// when it is clicked (e.g. sidebar navigation supports
|
||||
// opening and closing the nav tree, so don't dismiss on click)
|
||||
const clickEl = placeholderDescriptor.dismissOnClick
|
||||
? toggleContainer
|
||||
: toggleTitle;
|
||||
|
||||
const closeToggle = () => {
|
||||
if (tocShowing) {
|
||||
toggleContainer.classList.remove("expanded");
|
||||
toggleContents.style.height = "0px";
|
||||
tocShowing = false;
|
||||
}
|
||||
};
|
||||
|
||||
const positionToggle = () => {
|
||||
// position the element (top left of parent, same width as parent)
|
||||
const elRect = el.getBoundingClientRect();
|
||||
toggleContainer.style.left = `${elRect.left}px`;
|
||||
toggleContainer.style.top = `${elRect.top}px`;
|
||||
toggleContainer.style.width = `${elRect.width}px`;
|
||||
};
|
||||
|
||||
// Get rid of any expanded toggle if the user scrolls
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
closeToggle();
|
||||
}, 50)
|
||||
);
|
||||
|
||||
// Handle positioning of the toggle
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
positionToggle();
|
||||
}, 50)
|
||||
);
|
||||
positionToggle();
|
||||
|
||||
// Process the click
|
||||
clickEl.onclick = () => {
|
||||
if (!tocShowing) {
|
||||
toggleContainer.classList.add("expanded");
|
||||
toggleContents.style.height = null;
|
||||
tocShowing = true;
|
||||
} else {
|
||||
closeToggle();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Converts a sidebar from a menu back to a sidebar
|
||||
const convertToSidebar = () => {
|
||||
for (const child of el.children) {
|
||||
child.style.opacity = 1;
|
||||
child.style.overflow = null;
|
||||
}
|
||||
|
||||
const placeholderEl = window.document.getElementById(
|
||||
placeholderDescriptor.id
|
||||
);
|
||||
if (placeholderEl) {
|
||||
placeholderEl.remove();
|
||||
}
|
||||
|
||||
el.classList.remove("rollup");
|
||||
};
|
||||
|
||||
if (isReaderMode()) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
} else {
|
||||
if (!isVisible) {
|
||||
// If the element is current not visible reveal if there are
|
||||
// no conflicts with overlay regions
|
||||
if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToSidebar();
|
||||
isVisible = true;
|
||||
}
|
||||
} else {
|
||||
// If the element is visible, hide it if it conflicts with overlay regions
|
||||
// and insert a placeholder toggle (or if we're in reader mode)
|
||||
if (inHiddenRegion(elTop, elBottom, hiddenRegions)) {
|
||||
convertToMenu();
|
||||
isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Find any conflicting margin elements and add margins to the
|
||||
// top to prevent overlap
|
||||
const marginChildren = window.document.querySelectorAll(
|
||||
".column-margin.column-container > * "
|
||||
);
|
||||
let lastBottom = 0;
|
||||
for (const marginChild of marginChildren) {
|
||||
const top = marginChild.getBoundingClientRect().top;
|
||||
if (top < lastBottom) {
|
||||
const margin = lastBottom - top;
|
||||
marginChild.style.marginTop = `${margin}px`;
|
||||
}
|
||||
const styles = window.getComputedStyle(marginChild);
|
||||
const marginTop = parseFloat(styles["marginTop"]);
|
||||
|
||||
lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
|
||||
}
|
||||
|
||||
// Manage the visibility of the toc and the sidebar
|
||||
const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, {
|
||||
id: "quarto-toc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, {
|
||||
id: "quarto-sidebarnav-toggle",
|
||||
titleSelector: ".title",
|
||||
dismissOnClick: false,
|
||||
});
|
||||
let tocLeftScrollVisibility;
|
||||
if (leftTocEl) {
|
||||
tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, {
|
||||
id: "quarto-lefttoc-toggle",
|
||||
titleSelector: "#toc-title",
|
||||
dismissOnClick: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Find the first element that uses formatting in special columns
|
||||
const conflictingEls = window.document.body.querySelectorAll(
|
||||
'[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]'
|
||||
);
|
||||
|
||||
// Filter all the possibly conflicting elements into ones
|
||||
// the do conflict on the left or ride side
|
||||
const arrConflictingEls = Array.from(conflictingEls);
|
||||
const leftSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return false;
|
||||
}
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("right") &&
|
||||
!className.endsWith("container") &&
|
||||
className !== "column-margin"
|
||||
);
|
||||
});
|
||||
});
|
||||
const rightSideConflictEls = arrConflictingEls.filter((el) => {
|
||||
if (el.tagName === "ASIDE") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hasMarginCaption = Array.from(el.classList).find((className) => {
|
||||
return className == "margin-caption";
|
||||
});
|
||||
if (hasMarginCaption) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Array.from(el.classList).find((className) => {
|
||||
return (
|
||||
className !== "column-body" &&
|
||||
!className.endsWith("container") &&
|
||||
className.startsWith("column-") &&
|
||||
!className.endsWith("left")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const kOverlapPaddingSize = 10;
|
||||
function toRegions(els) {
|
||||
return els.map((el) => {
|
||||
const top =
|
||||
el.getBoundingClientRect().top +
|
||||
document.documentElement.scrollTop -
|
||||
kOverlapPaddingSize;
|
||||
return {
|
||||
top,
|
||||
bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const hideOverlappedSidebars = () => {
|
||||
marginScrollVisibility(toRegions(rightSideConflictEls));
|
||||
sidebarScrollVisiblity(toRegions(leftSideConflictEls));
|
||||
if (tocLeftScrollVisibility) {
|
||||
tocLeftScrollVisibility(toRegions(leftSideConflictEls));
|
||||
}
|
||||
};
|
||||
|
||||
window.quartoToggleReader = () => {
|
||||
// Applies a slow class (or removes it)
|
||||
// to update the transition speed
|
||||
const slowTransition = (slow) => {
|
||||
const manageTransition = (id, slow) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
if (slow) {
|
||||
el.classList.add("slow");
|
||||
} else {
|
||||
el.classList.remove("slow");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
manageTransition("TOC", slow);
|
||||
manageTransition("quarto-sidebar", slow);
|
||||
};
|
||||
|
||||
const readerMode = !isReaderMode();
|
||||
setReaderModeValue(readerMode);
|
||||
|
||||
// If we're entering reader mode, slow the transition
|
||||
if (readerMode) {
|
||||
slowTransition(readerMode);
|
||||
}
|
||||
highlightReaderToggle(readerMode);
|
||||
hideOverlappedSidebars();
|
||||
|
||||
// If we're exiting reader mode, restore the non-slow transition
|
||||
if (!readerMode) {
|
||||
slowTransition(!readerMode);
|
||||
}
|
||||
};
|
||||
|
||||
const highlightReaderToggle = (readerMode) => {
|
||||
const els = document.querySelectorAll(".quarto-reader-toggle");
|
||||
if (els) {
|
||||
els.forEach((el) => {
|
||||
if (readerMode) {
|
||||
el.classList.add("reader");
|
||||
} else {
|
||||
el.classList.remove("reader");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setReaderModeValue = (val) => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
window.localStorage.setItem("quarto-reader-mode", val);
|
||||
} else {
|
||||
localReaderMode = val;
|
||||
}
|
||||
};
|
||||
|
||||
const isReaderMode = () => {
|
||||
if (window.location.protocol !== "file:") {
|
||||
return window.localStorage.getItem("quarto-reader-mode") === "true";
|
||||
} else {
|
||||
return localReaderMode;
|
||||
}
|
||||
};
|
||||
let localReaderMode = null;
|
||||
|
||||
// Walk the TOC and collapse/expand nodes
|
||||
// Nodes are expanded if:
|
||||
// - they are top level
|
||||
// - they have children that are 'active' links
|
||||
// - they are directly below an link that is 'active'
|
||||
const walk = (el, depth) => {
|
||||
// Tick depth when we enter a UL
|
||||
if (el.tagName === "UL") {
|
||||
depth = depth + 1;
|
||||
}
|
||||
|
||||
// It this is active link
|
||||
let isActiveNode = false;
|
||||
if (el.tagName === "A" && el.classList.contains("active")) {
|
||||
isActiveNode = true;
|
||||
}
|
||||
|
||||
// See if there is an active child to this element
|
||||
let hasActiveChild = false;
|
||||
for (child of el.children) {
|
||||
hasActiveChild = walk(child, depth) || hasActiveChild;
|
||||
}
|
||||
|
||||
// Process the collapse state if this is an UL
|
||||
if (el.tagName === "UL") {
|
||||
if (depth === 1 || hasActiveChild || prevSiblingIsActiveLink(el)) {
|
||||
el.classList.remove("collapse");
|
||||
} else {
|
||||
el.classList.add("collapse");
|
||||
}
|
||||
|
||||
// untick depth when we leave a UL
|
||||
depth = depth - 1;
|
||||
}
|
||||
return hasActiveChild || isActiveNode;
|
||||
};
|
||||
|
||||
// walk the TOC and expand / collapse any items that should be shown
|
||||
|
||||
if (tocEl) {
|
||||
walk(tocEl, 0);
|
||||
updateActiveLink();
|
||||
}
|
||||
|
||||
// Throttle the scroll event and walk peridiocally
|
||||
window.document.addEventListener(
|
||||
"scroll",
|
||||
throttle(() => {
|
||||
if (tocEl) {
|
||||
updateActiveLink();
|
||||
walk(tocEl, 0);
|
||||
}
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 5)
|
||||
);
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
throttle(() => {
|
||||
if (!isReaderMode()) {
|
||||
hideOverlappedSidebars();
|
||||
}
|
||||
}, 10)
|
||||
);
|
||||
hideOverlappedSidebars();
|
||||
highlightReaderToggle(isReaderMode());
|
||||
});
|
||||
|
||||
// grouped tabsets
|
||||
window.addEventListener("pageshow", (_event) => {
|
||||
function getTabSettings() {
|
||||
const data = localStorage.getItem("quarto-persistent-tabsets-data");
|
||||
if (!data) {
|
||||
localStorage.setItem("quarto-persistent-tabsets-data", "{}");
|
||||
return {};
|
||||
}
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
function setTabSettings(data) {
|
||||
localStorage.setItem(
|
||||
"quarto-persistent-tabsets-data",
|
||||
JSON.stringify(data)
|
||||
);
|
||||
}
|
||||
|
||||
function setTabState(groupName, groupValue) {
|
||||
const data = getTabSettings();
|
||||
data[groupName] = groupValue;
|
||||
setTabSettings(data);
|
||||
}
|
||||
|
||||
function toggleTab(tab, active) {
|
||||
const tabPanelId = tab.getAttribute("aria-controls");
|
||||
const tabPanel = document.getElementById(tabPanelId);
|
||||
if (active) {
|
||||
tab.classList.add("active");
|
||||
tabPanel.classList.add("active");
|
||||
} else {
|
||||
tab.classList.remove("active");
|
||||
tabPanel.classList.remove("active");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAll(selectedGroup, selectorsToSync) {
|
||||
for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
|
||||
const active = selectedGroup === thisGroup;
|
||||
for (const tab of tabs) {
|
||||
toggleTab(tab, active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findSelectorsToSyncByLanguage() {
|
||||
const result = {};
|
||||
const tabs = Array.from(
|
||||
document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
|
||||
);
|
||||
for (const item of tabs) {
|
||||
const div = item.parentElement.parentElement.parentElement;
|
||||
const group = div.getAttribute("data-group");
|
||||
if (!result[group]) {
|
||||
result[group] = {};
|
||||
}
|
||||
const selectorsToSync = result[group];
|
||||
const value = item.innerHTML;
|
||||
if (!selectorsToSync[value]) {
|
||||
selectorsToSync[value] = [];
|
||||
}
|
||||
selectorsToSync[value].push(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function setupSelectorSync() {
|
||||
const selectorsToSync = findSelectorsToSyncByLanguage();
|
||||
Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
|
||||
Object.entries(tabSetsByValue).forEach(([value, items]) => {
|
||||
items.forEach((item) => {
|
||||
item.addEventListener("click", (_event) => {
|
||||
setTabState(group, value);
|
||||
toggleAll(value, selectorsToSync[group]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return selectorsToSync;
|
||||
}
|
||||
|
||||
const selectorsToSync = setupSelectorSync();
|
||||
for (const [group, selectedName] of Object.entries(getTabSettings())) {
|
||||
const selectors = selectorsToSync[group];
|
||||
// it's possible that stale state gives us empty selections, so we explicitly check here.
|
||||
if (selectors) {
|
||||
toggleAll(selectedName, selectors);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function throttle(func, wait) {
|
||||
let waiting = false;
|
||||
return function () {
|
||||
if (!waiting) {
|
||||
func.apply(this, arguments);
|
||||
waiting = true;
|
||||
setTimeout(function () {
|
||||
waiting = false;
|
||||
}, wait);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
|
@ -0,0 +1,6 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Lora&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Lora';
|
||||
}
|