<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Bruno Nicenboim&#39;s Blog</title>
<link>https://bruno.nicenboim.me/posts/</link>
<atom:link href="https://bruno.nicenboim.me/posts/index.xml" rel="self" type="application/rss+xml"/>
<description>Posts about computational cognitive science, Bayesian methods, EEG, and AI.</description>
<generator>quarto-1.8.27</generator>
<lastBuildDate>Thu, 08 Jan 2026 23:00:00 GMT</lastBuildDate>
<item>
  <title>Bayesian ordinal models: A very short practical guide</title>
  <dc:creator>Bruno Nicenboim</dc:creator>
  <link>https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/</link>
  <description><![CDATA[ 




<p>I’ll first load some R packages that will be useful throughout this post.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(extraDistr)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tidytable)</span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb1-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(brms)</span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bayesplot)</span>
<span id="cb1-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(patchwork)</span>
<span id="cb1-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggridges)</span>
<span id="cb1-8"></span>
<span id="cb1-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_set</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>))</span></code></pre></div></div>
</div>
<section id="introduction" class="level2">
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
<p>This post explains some nuances of Bayesian ordinal regression with cumulative links and focuses on how to set sensible priors.<sup>1</sup> These models are appropriate for modeling data where the dependent variables are ordered responses without meaningful metric distances, such as Likert scales. The presentation builds on <span class="citation" data-cites="BurknerVuorre2019">Bürkner and Vuorre (2019)</span>, but restricts attention to cumulative models.</p>
</section>
<section id="the-cumulative-link-framework" class="level2">
<h2 class="anchored" data-anchor-id="the-cumulative-link-framework">The cumulative link framework</h2>
<p>A cumulative link model assumes an underlying continuous latent variable (typically with a normal or logistic distribution) and a set of thresholds <img src="https://latex.codecogs.com/png.latex?%5Ctau_1,%20%5Cldots,%20%5Ctau_K"> that partition this scale. An observation falls in category <img src="https://latex.codecogs.com/png.latex?k"> if the latent value, a random variable <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y">, lies between <img src="https://latex.codecogs.com/png.latex?%5Ctau_%7Bk-1%7D"> and <img src="https://latex.codecogs.com/png.latex?%5Ctau_k">, with <img src="https://latex.codecogs.com/png.latex?%5Ctau_0%20=%20-%5Cinfty"> and <img src="https://latex.codecogs.com/png.latex?%5Ctau_%7BK+1%7D%20=%20+%5Cinfty">.</p>
<section id="a-concrete-example" class="level3">
<h3 class="anchored" data-anchor-id="a-concrete-example">A concrete example</h3>
<p>Consider a 5-point Likert scale assessing understanding of Bayesian statistics with options: “nothing,” “a little,” “more or less,” “something,” and “everything.” This scale requires 4 thresholds. The probability for each category corresponds to the area between:</p>
<ul>
<li>Category 1 (“nothing”): <img src="https://latex.codecogs.com/png.latex?-%5Cinfty"> to <img src="https://latex.codecogs.com/png.latex?%5Ctau_1"></li>
<li>Category 2 (“a little”): <img src="https://latex.codecogs.com/png.latex?%5Ctau_1"> to <img src="https://latex.codecogs.com/png.latex?%5Ctau_2"></li>
<li>Category 3 (“more or less”): <img src="https://latex.codecogs.com/png.latex?%5Ctau_2"> to <img src="https://latex.codecogs.com/png.latex?%5Ctau_3"></li>
<li>Category 4 (“something”): <img src="https://latex.codecogs.com/png.latex?%5Ctau_3"> to <img src="https://latex.codecogs.com/png.latex?%5Ctau_4"></li>
<li>Category 5 (“everything”): <img src="https://latex.codecogs.com/png.latex?%5Ctau_4"> to <img src="https://latex.codecogs.com/png.latex?+%5Cinfty"></li>
</ul>
<p>Let’s visualize this with specific threshold values for a logistic distribution<sup>2</sup>. We make up the following thresholds</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">thres <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span></code></pre></div></div>
</div>
<div class="cell">
<details class="code-fold">
<summary>Code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1">category <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Nothing"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A little"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"More or less"</span>, </span>
<span id="cb3-2">                                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Something"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Everything"</span>))</span>
<span id="cb3-3">  </span>
<span id="cb3-4">x_vals <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length.out =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500</span>)</span>
<span id="cb3-5">norm_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb3-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x_vals,</span>
<span id="cb3-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dlogis</span>(x_vals)</span>
<span id="cb3-8">)</span>
<span id="cb3-9"></span>
<span id="cb3-10">category_positions <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>, thres, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>)</span>
<span id="cb3-11">category_midpoints <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (category_positions[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> category_positions[<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(category_positions)]) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb3-12">category_midpoints[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">max</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, category_midpoints[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb3-13">category_midpoints[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">min</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, category_midpoints[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>])</span>
<span id="cb3-14"></span>
<span id="cb3-15">threshold_labels <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb3-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> thres,</span>
<span id="cb3-17">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dlogis</span>(thres) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>,</span>
<span id="cb3-18">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"τ["</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"]"</span>)</span>
<span id="cb3-19">)</span>
<span id="cb3-20"></span>
<span id="cb3-21">category_labels <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb3-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> category_midpoints,</span>
<span id="cb3-23">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>,</span>
<span id="cb3-24">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.character</span>(category)</span>
<span id="cb3-25">)</span>
<span id="cb3-26"></span>
<span id="cb3-27">p1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(norm_data, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> y)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-28">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linewidth =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"steelblue"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-29">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_vline</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xintercept =</span> thres, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linetype =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dashed"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linewidth =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-30">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> threshold_labels, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> label),</span>
<span id="cb3-31">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">parse =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"red"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hjust =</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">vjust =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-32">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> category_labels, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">label =</span> label),</span>
<span id="cb3-33">            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hjust =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-34">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Standard Logistic Distribution with Thresholds"</span>,</span>
<span id="cb3-35">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Latent Variable"</span>,</span>
<span id="cb3-36">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Density"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-37">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span>
<span id="cb3-38"></span>
<span id="cb3-39"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">print</span>(p1)</span></code></pre></div></div>
</details>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/threshold-viz-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
</section>
<section id="computing-category-probabilities" class="level3">
<h3 class="anchored" data-anchor-id="computing-category-probabilities">Computing category probabilities</h3>
<p>Now let’s calculate the probability mass between each threshold:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1">probs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">diff</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plogis</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>, thres, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>)))</span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># This above is just a more general case than:</span></span>
<span id="cb4-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># probs &lt;- c(</span></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   plogis(thres[1]),  # P(Y = 1)</span></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   plogis(thres[2]) - plogis(thres[1]),  # P(Y = 2)</span></span>
<span id="cb4-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   plogis(thres[3]) - plogis(thres[2]),  # P(Y = 3)</span></span>
<span id="cb4-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   plogis(thres[4]) - plogis(thres[3]),  # P(Y = 4)</span></span>
<span id="cb4-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#   1 - plogis(thres[4])  # P(Y = 5)</span></span>
<span id="cb4-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># )</span></span>
<span id="cb4-11"></span>
<span id="cb4-12">prob_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb4-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category =</span> category,</span>
<span id="cb4-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">probability =</span> probs</span>
<span id="cb4-15">)</span>
<span id="cb4-16"></span>
<span id="cb4-17">prob_data</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>      category probability
1      Nothing  0.11920292
2     A little  0.14973850
3 More or less  0.54863305
4    Something  0.13499965
5   Everything  0.04742587</code></pre>
</div>
</div>
<div class="cell">
<details class="code-fold">
<summary>Code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(prob_data, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> category, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> probability, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> category)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_col</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_brewer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">palette =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Blues"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Category Probabilities (Baseline)"</span>,</span>
<span id="cb6-5">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Understanding Level"</span>,</span>
<span id="cb6-6">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Probability"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>,</span>
<span id="cb6-9">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.text.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">angle =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">45</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">hjust =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ylim</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span></code></pre></div></div>
</details>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/category-probs-viz-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>We observe that the middle category (“more or less”) receive the highest probability mass.</p>
</section>
<section id="the-effect-of-shifting-thresholds" class="level3">
<h3 class="anchored" data-anchor-id="the-effect-of-shifting-thresholds">The effect of shifting thresholds</h3>
<p>Now, let’s examine what happens when we shift all thresholds to the right by one unit and a half: simulating less understanding across the board.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1">thres <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> thres <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span></span></code></pre></div></div>
</div>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/unnamed-chunk-1-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<div class="cell">
<div class="cell-output cell-output-stdout">
<pre><code>      category probability
1      Nothing  0.37754067
2     A little  0.24491866
3 More or less  0.33011480
4    Something  0.03643893
5   Everything  0.01098694</code></pre>
</div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/unnamed-chunk-2-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>Notice how shifting the thresholds rightward decreases the probability of higher categories (more understanding) while increasing the probability of lower categories.</p>
</section>
</section>
<section id="modeling-an-intervention-effect" class="level2">
<h2 class="anchored" data-anchor-id="modeling-an-intervention-effect">Modeling an intervention effect</h2>
<p>In an ordinal regression with a cumulative link, our goal is to understand how a latent variable <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y"> shifts based on predictors or interventions. Suppose we want to assess how reading <a href="https://bruno.nicenboim.me/bayescogsci/">a book about Bayesian modeling</a><sup>3</sup> affects responses to our understanding question.</p>
<section id="simulating-data" class="level3">
<h3 class="anchored" data-anchor-id="simulating-data">Simulating data</h3>
<p>Let’s generate synthetic data to illustrate this. We’ll simulate a group of students before and after reading our book.</p>
<p>This type of model assumes that, underlyingly, there is a continuous latent “rating”, and we specify a regression on this latent variable <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y"> that determines how the response categories are affected by our manipulation (note that there is no intercept here; the thresholds will later act as something close to intercepts for each category):</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctilde%20Y%20=%20%5Ceta%20+%20%5Cepsilon%0A"> with</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ceta%20=%20%5Cbeta%20%5Ccdot%20%5Ctext%7Bread%7D"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bread%7D%20%5Cin%20%5C%7B0,1%5C%7D"> is a treatment indicator (in more realistic settings, one may prefer richer contrast coding; see Chapter 6 of <a href="https://bruno.nicenboim.me/bayescogsci/ch-contr.html"><span class="citation" data-cites="Nicenboimetal">Nicenboim, Schad, and Vasishth (<span>2025</span>)</span></a>).</p>
<p>In principle, one could allow the predictor to have category-specific effects by estimating regressions for each threshold. However, <span class="citation" data-cites="BurknerVuorre2019">Bürkner and Vuorre (2019)</span> argue that while this parameterization is unproblematic for some ordinal models (such as sequential or adjacent-category models), it can lead to difficulties in model fitting for cumulative models.</p>
<p>To move from the continuous latent variable <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y"> to a discrete response, we define the observed outcome as follows:</p>
<p><img src="https://latex.codecogs.com/png.latex?Y%20=%20k%20%5Cquad%20%5Ctext%7Bif%7D%20%5Cquad%20%5Ctau_%7Bk-1%7D%20%3C%20%5Ctilde%20Y%20%5Cle%20%5Ctau_k"> with boundary conditions <img src="https://latex.codecogs.com/png.latex?%5Ctau_0%20=%20-%5Cinfty"> and <img src="https://latex.codecogs.com/png.latex?%5Ctau_%7BK+1%7D%20=%20+%5Cinfty">.</p>
<p>We keep the <img src="https://latex.codecogs.com/png.latex?k"> thresholds (<img src="https://latex.codecogs.com/png.latex?%5Ctau_k">) defined above as the default cutoff points (<code>brms</code> refers to these as <em>intercepts</em>, though note the difference in sign convention below).</p>
<p>Given a cumulative link, the probability of observing category <img src="https://latex.codecogs.com/png.latex?k"> is</p>
<p><img src="https://latex.codecogs.com/png.latex?%5CPr(Y%20=%20k%20%5Cmid%20%5Ctext%7Bread%7D)%20=%0AF(%5Ctau_k%20-%20%5Ceta)%0A-%0AF(%5Ctau_%7Bk-1%7D%20-%20%5Ceta)"></p>
<p>Here, <img src="https://latex.codecogs.com/png.latex?F"> denotes the cumulative distribution function of the error term <img src="https://latex.codecogs.com/png.latex?%5Cepsilon"> of <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y">. We assume a logistic distribution for computational convenience, which yields a logistic cumulative link (assuming a normal distribution would lead to a probit link, with otherwise very similar expressions).</p>
<p>Thus, under a logistic cumulative link, the probability of observing category <img src="https://latex.codecogs.com/png.latex?k"> is</p>
<p><img src="https://latex.codecogs.com/png.latex?%5CPr(Y%20=%20k%20%5Cmid%20%5Ctext%7Bread%7D)%20=%0A%5Coperatorname%7Blogit%7D%5E%7B-1%7D(%5Ctau_k%20-%20%5Ceta)%0A-%0A%5Coperatorname%7Blogit%7D%5E%7B-1%7D(%5Ctau_%7Bk-1%7D%20-%20%5Ceta)"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%5Coperatorname%7Blogit%7D%5E%7B-1%7D"> denotes the CDF of the logistic distribution (available in R via <code>plogis()</code>). Notice that the regression term, <img src="https://latex.codecogs.com/png.latex?%5Ceta">, is subtracted from each threshold.</p>
<p>To simulate data from this model, we treat the resulting category probabilities as defining a categorical distribution and sample responses accordingly:</p>
<p><img src="https://latex.codecogs.com/png.latex?Y%20%5Csim%20%5Ctext%7BCategorical%7D%5C!%5Cleft(%20%5CPr(Y=1),%20%5Cldots,%20%5CPr(Y=K)%20%5Cright)"></p>
<p>One important insight is that <img src="https://latex.codecogs.com/png.latex?%5Cbeta"> affects all categories equally: when <img src="https://latex.codecogs.com/png.latex?%5Cbeta%20%3E%200">, <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y"> increases, increasing the probability of higher response categories at the expense of lower response categories; when <img src="https://latex.codecogs.com/png.latex?%5Cbeta%20%3C%200">, <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y"> decreases, increasing the probability of lower categories at the expense of higher response categories.</p>
<p>In the following simulation, we use the last <code>thres</code> values we defined (-0.5, 0.5, 3, 4.5) as the cutoff points of the model (<img src="https://latex.codecogs.com/png.latex?%5Ctau_k">; called <em>intercepts</em> in <code>brms</code>), and we assume that reading increases <img src="https://latex.codecogs.com/png.latex?%5Ceta"> (and also <img src="https://latex.codecogs.com/png.latex?%5Ctilde%20Y"> on average) by <img src="https://latex.codecogs.com/png.latex?1.5">. We generate data from 1000 participants before and after reading our book:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>)</span>
<span id="cb9-2"></span>
<span id="cb9-3">N <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span></span>
<span id="cb9-4">tau <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> thres</span>
<span id="cb9-5">beta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span></span>
<span id="cb9-6">eta_noread <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> beta</span>
<span id="cb9-7">eta_read  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> beta</span>
<span id="cb9-8"></span>
<span id="cb9-9">df_sim <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb9-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">read =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">each =</span> N),</span>
<span id="cb9-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb9-12">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rcat</span>(N, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">diff</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plogis</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>, tau <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> eta_noread, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>))), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> category),</span>
<span id="cb9-13">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rcat</span>(N, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">diff</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plogis</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>, tau <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> eta_read, <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">Inf</span>))), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> category)</span>
<span id="cb9-14">  )</span>
<span id="cb9-15">) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb9-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mutate</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">response =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(category))</span>
<span id="cb9-17"></span>
<span id="cb9-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Display summary statistics</span></span>
<span id="cb9-19"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table</span>(df_sim<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>read, df_sim<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>category)</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>   
    Nothing A little More or less Something Everything
  0     382      238          329        40         11
  1     114      154          549       138         45</code></pre>
</div>
</div>
<p>Let’s visualize the distribution of responses in both groups:</p>
<div class="cell">
<details class="code-fold">
<summary>Code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb11-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(df_sim, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> response, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(read, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Before reading"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"After reading"</span>)), </span>
<span id="cb11-2">                   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">group =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(read))) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb11-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_bar</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dodge"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb11-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_manual</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Before reading"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"coral"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"After reading"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"forestgreen"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb11-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Distribution of Responses"</span>,</span>
<span id="cb11-6">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Understanding Level"</span>,</span>
<span id="cb11-7">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Count"</span>,</span>
<span id="cb11-8">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Group"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb11-9">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span></code></pre></div></div>
</details>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/data-viz-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>Now that we have (simulated) data, let’s fit it with <code>brms</code>.</p>
</section>
</section>
<section id="understanding-priors-for-ordinal-models" class="level2">
<h2 class="anchored" data-anchor-id="understanding-priors-for-ordinal-models">Understanding priors for ordinal models</h2>
<p>When fitting ordinal regression models with <code>brms</code>, we need to specify priors for (at least):</p>
<ol type="1">
<li><strong>“Intercepts” which in fact are thresholds or cutoff points</strong></li>
<li><strong>Regression coefficients</strong></li>
</ol>
<p>In <code>brms</code>, the thresholds are automatically constrained to be ordered (<img src="https://latex.codecogs.com/png.latex?%5Ctau_1%20%3C%20%5Ctau_2%20%3C%20%5Cldots%20%3C%20%5Ctau_K">).</p>
<section id="why-tight-priors-concentrate-probability-on-extremes" class="level3">
<h3 class="anchored" data-anchor-id="why-tight-priors-concentrate-probability-on-extremes">Why tight priors concentrate probability on extremes</h3>
<p>Let’s start by examining an intercept-only model with tight priors on the thresholds:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb12-1">priors_tight <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb12-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">prior</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">normal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Intercept"</span>)</span>
<span id="cb12-3">)</span></code></pre></div></div>
</div>
<p>With <code>normal(0, 0.5)</code> “Intercept” priors, all thresholds are pulled toward zero with relatively little spread. This concentrates the thresholds in a narrow region. This means that when thresholds cluster tightly around zero, most of the latent distribution’s mass falls <em>outside</em> the middle thresholds: either far below <img src="https://latex.codecogs.com/png.latex?%5Ctau_1"> or far above <img src="https://latex.codecogs.com/png.latex?%5Ctau_K">. This assigns most probability to extreme categories (the first and last).</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1">fit_prior_tight <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brm</span>(</span>
<span id="cb13-2">  response <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb13-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> df_sim,</span>
<span id="cb13-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumulative</span>(),</span>
<span id="cb13-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prior =</span> priors_tight,</span>
<span id="cb13-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sample_prior =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"only"</span>,</span>
<span id="cb13-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cores =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb13-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">seed =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>,</span>
<span id="cb13-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">refresh =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,</span>
<span id="cb13-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">control =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">adapt_delta =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># avoid divergent transitions</span></span>
<span id="cb13-11">)</span></code></pre></div></div>
</div>
<div class="cell">
<details class="code-fold">
<summary>Code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1">ppd_ridgeplot <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(fit, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prior Predictive Distribution"</span>, </span>
<span id="cb14-2">                          <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ndraws =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">500</span>) {</span>
<span id="cb14-3">  </span>
<span id="cb14-4">  yrep <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">posterior_predict</span>(fit, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ndraws =</span> ndraws)</span>
<span id="cb14-5">  </span>
<span id="cb14-6">  proportions_per_draw <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lapply</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(yrep), <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(i) {</span>
<span id="cb14-7">    props <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(yrep[i, ], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">levels =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(yrep)</span>
<span id="cb14-8">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb14-9">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">draw =</span> i,</span>
<span id="cb14-10">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">response =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>),</span>
<span id="cb14-11">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">proportion =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.numeric</span>(props)</span>
<span id="cb14-12">    )</span>
<span id="cb14-13">  })</span>
<span id="cb14-14">  ppd_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">do.call</span>(rbind, proportions_per_draw)</span>
<span id="cb14-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(ppd_data, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> proportion, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> response)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-16">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_density_ridges</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"steelblue"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scale =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stat =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"binline"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-17">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_x_continuous</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">breaks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,.<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,.<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,.<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>,.<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-18">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_vline</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">xintercept =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">linetype =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dashed"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-19">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> title,</span>
<span id="cb14-20">         <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> subtitle,</span>
<span id="cb14-21">         <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Proportion"</span>,</span>
<span id="cb14-22">         <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Response Category"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-23">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-24">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">coord_flip</span>()</span>
<span id="cb14-25">}</span></code></pre></div></div>
</details>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb15-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ppd_ridgeplot</span>(fit_prior_tight, </span>
<span id="cb15-2">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prior Predictive Distribution: Tight Priors (SD = 0.5)"</span>,</span>
<span id="cb15-3">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Most probability mass concentrated in extreme categories"</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/ppd-check-tight-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>With tight priors, the model tends to predict extreme responses, as the thresholds cluster near zero and don’t span the full range of the latent distribution.</p>
</section>
<section id="intercept-only-model-medium-priors" class="level3">
<h3 class="anchored" data-anchor-id="intercept-only-model-medium-priors">Intercept-only model: Medium priors</h3>
<p>Now let’s use more dispersed priors:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb16-1">priors_medium <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb16-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">prior</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">normal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Intercept"</span>)</span>
<span id="cb16-3">)</span></code></pre></div></div>
</div>
<p>With <code>normal(0, 3)</code> “Intercept” priors, the thresholds can spread out more widely across the latent scale. This means the middle thresholds are more likely to be well-separated, capturing more of the distribution’s central mass and leading to more balanced predictions across all categories.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb17-1">fit_prior_medium <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brm</span>(</span>
<span id="cb17-2">  response <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb17-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> df_sim,</span>
<span id="cb17-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumulative</span>(),</span>
<span id="cb17-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prior =</span> priors_medium,</span>
<span id="cb17-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sample_prior =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"only"</span>,</span>
<span id="cb17-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cores =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb17-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">seed =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>,</span>
<span id="cb17-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">refresh =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb17-10">)</span></code></pre></div></div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb18-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ppd_ridgeplot</span>(fit_prior_medium, </span>
<span id="cb18-2">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prior Predictive Distribution: Medium Priors (SD = 3)"</span>,</span>
<span id="cb18-3">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"More balanced distribution across categories"</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/ppd-check-medium-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
</section>
<section id="intercept-only-model-diffuse-priors" class="level3">
<h3 class="anchored" data-anchor-id="intercept-only-model-diffuse-priors">Intercept-only model: Diffuse priors</h3>
<p>Let’s examine very diffuse priors:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb19-1">priors_diffuse <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb19-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">prior</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">normal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Intercept"</span>)</span>
<span id="cb19-3">)</span>
<span id="cb19-4"></span>
<span id="cb19-5">fit_prior_diffuse <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brm</span>(</span>
<span id="cb19-6">  response <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb19-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> df_sim,</span>
<span id="cb19-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumulative</span>(),</span>
<span id="cb19-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prior =</span> priors_diffuse,</span>
<span id="cb19-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sample_prior =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"only"</span>,</span>
<span id="cb19-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cores =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb19-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">seed =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>,</span>
<span id="cb19-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">refresh =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,</span>
<span id="cb19-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">control =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">adapt_delta =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>)</span>
<span id="cb19-15">)</span></code></pre></div></div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb20" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb20-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ppd_ridgeplot</span>(fit_prior_diffuse, </span>
<span id="cb20-2">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prior Predictive Distribution: Diffuse Priors (SD = 10)"</span>,</span>
<span id="cb20-3">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Very flat distribution - least informative"</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/ppd-check-diffuse-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>Very diffuse priors allow thresholds to be extremely spread out, often resulting in a very sparse distribution across categories.</p>
</section>
<section id="intercept-only-model-nudging-priors" class="level3">
<h3 class="anchored" data-anchor-id="intercept-only-model-nudging-priors">Intercept-only model: Nudging priors</h3>
<p>We might have substantive information suggesting most people start as beginners. We can encode this by centering the “Intercept” priors at a positive value:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb21-1">priors_mid_inf <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb21-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">prior</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">normal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Intercept"</span>)</span>
<span id="cb21-3">)</span>
<span id="cb21-4"></span>
<span id="cb21-5">fit_mid_inf <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brm</span>( </span>
<span id="cb21-6">  response <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>,</span>
<span id="cb21-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> df_sim,</span>
<span id="cb21-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumulative</span>(),</span>
<span id="cb21-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prior =</span> priors_mid_inf,</span>
<span id="cb21-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sample_prior =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"only"</span>,</span>
<span id="cb21-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cores =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb21-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">seed =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>,</span>
<span id="cb21-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">refresh =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb21-14">)</span></code></pre></div></div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb22" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb22-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ppd_ridgeplot</span>(fit_mid_inf, </span>
<span id="cb22-2">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prior Predictive Distribution: Nudged Priors (Mean = 1, SD = 3)"</span>,</span>
<span id="cb22-3">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prior belief that lower understanding is more common"</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/ppd-mid-inf-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>By centering the”Intercept” priors at 1 instead of 0, we shift thresholds rightward, making lower categories more probable a priori. On the other hand, if we had substantial information that suggests the opposite (e.g.&nbsp;that most people start advanced), we would center the “Intercept” priors at a negative value, shifting the thresholds leftward, making higher categories more probable a priori.</p>
</section>
<section id="prior-predictive-distribution-for-the-slope" class="level3">
<h3 class="anchored" data-anchor-id="prior-predictive-distribution-for-the-slope">Prior predictive distribution for the slope</h3>
<p>Let’s also examine what our prior beliefs about the treatment effect imply:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb23-1">priors_full <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(</span>
<span id="cb23-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">prior</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">normal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Intercept"</span>),</span>
<span id="cb23-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">prior</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">normal</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">class =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"b"</span>)</span>
<span id="cb23-4">)</span>
<span id="cb23-5"></span>
<span id="cb23-6">fit_slope_prior <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brm</span>(</span>
<span id="cb23-7">  response <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> read,</span>
<span id="cb23-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> df_sim,</span>
<span id="cb23-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumulative</span>(),</span>
<span id="cb23-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prior =</span> priors_full,</span>
<span id="cb23-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sample_prior =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"only"</span>,</span>
<span id="cb23-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cores =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb23-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">seed =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>,</span>
<span id="cb23-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">refresh =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>,</span>
<span id="cb23-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">control =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">adapt_delta =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>)</span>
<span id="cb23-16">)</span></code></pre></div></div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb24" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb24-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ppd_ridgeplot</span>(fit_slope_prior,</span>
<span id="cb24-2">              <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Prior Predictive Distribution: Full Model with Slope Prior"</span>,</span>
<span id="cb24-3">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"normal(0, 1.5) prior on treatment effect"</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/ppd-slope-prior-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>The prior <code>normal(0, 1.5)</code> on the slope is weakly informative: it allows for moderate effects in either direction while gently regularizing against implausibly large effects.</p>
</section>
<section id="fitting-to-the-simulated-data" class="level3">
<h3 class="anchored" data-anchor-id="fitting-to-the-simulated-data">Fitting to the simulated data</h3>
<p>Now let’s fit the model to our simulated data:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb25-1">fit_full <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">brm</span>(</span>
<span id="cb25-2">  response <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~</span> read,</span>
<span id="cb25-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> df_sim,</span>
<span id="cb25-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">family =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cumulative</span>(),</span>
<span id="cb25-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prior =</span> priors_full,</span>
<span id="cb25-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">cores =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb25-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">seed =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>,</span>
<span id="cb25-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">warmup =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1000</span>,</span>
<span id="cb25-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">iter =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2000</span>,</span>
<span id="cb25-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">refresh =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb25-11">)</span></code></pre></div></div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb26" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb26-1">fit_full</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code> Family: cumulative 
  Links: mu = logit 
Formula: response ~ read 
   Data: df_sim (Number of observations: 2000) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Regression Coefficients:
             Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept[1]    -0.50      0.06    -0.62    -0.38 1.00     4501     3153
Intercept[2]     0.50      0.06     0.37     0.62 1.00     4802     3359
Intercept[3]     2.98      0.09     2.79     3.16 1.00     3939     3068
Intercept[4]     4.55      0.15     4.26     4.85 1.00     3991     2905
read             1.50      0.09     1.32     1.66 1.00     3854     2924

Further Distributional Parameters:
     Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
disc     1.00      0.00     1.00     1.00   NA       NA       NA

Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).</code></pre>
</div>
</div>
<p>If this were real data, based on the <code>read</code> coefficient, we would conclude that reading our book affects understanding. The positive coefficient <code>read ≈ 1.5</code> indicates that reading makes higher categories (more understanding) more probable.</p>
</section>
<section id="interpreting-predicted-probabilities" class="level3">
<h3 class="anchored" data-anchor-id="interpreting-predicted-probabilities">Interpreting predicted probabilities</h3>
<p>To understand what the model estimates mean in practical terms, let’s examine the predicted probabilities:</p>
<div class="cell">
<details class="code-fold">
<summary>Code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb28" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb28-1">ce <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">conditional_effects</span>(fit_full, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">effects =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"read"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">categorical =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb28-2"></span>
<span id="cb28-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot</span>(ce, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">plot =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]] <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb28-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_brewer</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">palette =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"RdYlGn"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">direction =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb28-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Predicted Response Probabilities by Reading Status"</span>,</span>
<span id="cb28-6">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Model predictions with 95% credible intervals"</span>,</span>
<span id="cb28-7">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Read Book"</span>,</span>
<span id="cb28-8">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Predicted Probability"</span>,</span>
<span id="cb28-9">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"response"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb28-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb28-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"right"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/conditional-effects-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>The conditional effects plot shows how reading the book shifts the entire distribution toward higher understanding categories. Before reading (read = 0), lower categories have higher probabilities; after reading (read = 1), the distribution shifts substantially toward “something” and “everything.”</p>
<p>We can also examine specific fitted values:</p>
<div class="cell">
<details class="code-fold">
<summary>Code</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb29-1">new_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">read =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>))</span>
<span id="cb29-2">fitted_probs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fitted</span>(fit_full, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">newdata =</span> new_data)</span>
<span id="cb29-3"></span>
<span id="cb29-4">fitted_df <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">data.frame</span>(</span>
<span id="cb29-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">read =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Before"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"After"</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">each =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>),</span>
<span id="cb29-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">category =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">times =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>),</span>
<span id="cb29-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">probability =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(fitted_probs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate"</span>, ], fitted_probs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Estimate"</span>,]),</span>
<span id="cb29-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lower =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(fitted_probs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q2.5"</span>,], fitted_probs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> , <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q2.5"</span>,]),</span>
<span id="cb29-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">upper =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(fitted_probs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q97.5"</span>,], fitted_probs[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> , <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Q97.5"</span>,])</span>
<span id="cb29-10">)</span>
<span id="cb29-11"></span>
<span id="cb29-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(fitted_df, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(category), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> probability, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> read)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_col</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dodge"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_errorbar</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymin =</span> lower, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ymax =</span> upper), </span>
<span id="cb29-15">                <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">position =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">position_dodge</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_manual</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Before"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"coral"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"After"</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"forestgreen"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Model-Predicted Probabilities with 95% Credible Intervals"</span>,</span>
<span id="cb29-18">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Understanding Category"</span>,</span>
<span id="cb29-19">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Predicted Probability"</span>,</span>
<span id="cb29-20">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Reading Status"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb29-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span></code></pre></div></div>
</details>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/fitted-values-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>The fitted probabilities show concrete estimates: for instance, before reading, the probability of responding “nothing” is about 0.38, whereas after reading it drops to about 0.12. Conversely, the probability of responding “everything” increases from about 0.01 to 0.05.</p>
</section>
<section id="posterior-predictive-check" class="level3">
<h3 class="anchored" data-anchor-id="posterior-predictive-check">Posterior predictive check</h3>
<p>Finally, let’s verify that the model fits the observed data well:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb30" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb30-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pp_check</span>(fit_full, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bars"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ndraws =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb30-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Posterior Predictive Check: Full Model"</span>,</span>
<span id="cb30-3">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Model predictions compared to observed data"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb30-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/pp-check-posterior-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>The posterior predictive distribution closely matches the observed data patterns, indicating good model fit.</p>
</section>
</section>
<section id="recovery-of-the-parameters" class="level2">
<h2 class="anchored" data-anchor-id="recovery-of-the-parameters">Recovery of the parameters</h2>
<p>Let’s examine whether the model successfully recovered the true parameters we used to generate the data. We simulated with thresholds <img src="https://latex.codecogs.com/png.latex?%5Ctau%20=%20%5B-0.5,%200.5,%203,%204.5%5D"> and a treatment effect of <img src="https://latex.codecogs.com/png.latex?%5Cbeta%20=%201.5">.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb31-1">true_params <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(thres, beta)</span>
<span id="cb31-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">names</span>(true_params) <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"b_Intercept["</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"]"</span>), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"b_read"</span>)</span>
<span id="cb31-3"></span>
<span id="cb31-4">posterior_draws <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.array</span>(fit_full,</span>
<span id="cb31-5">                            <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">variable =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">names</span>(true_params))</span>
<span id="cb31-6"></span>
<span id="cb31-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mcmc_recover_hist</span>(posterior_draws, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">true =</span> true_params) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb31-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Parameter Recovery"</span>,</span>
<span id="cb31-9">       <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Posterior distributions (histograms) vs. true values (vertical lines)"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb31-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/index_files/figure-html/parameter-recovery-1.png" class="img-fluid figure-img" width="960"></p>
</figure>
</div>
</div>
</div>
<p>The histograms show the posterior distributions for each parameter, with vertical lines indicating the true values used in simulation. We can see that:</p>
<ol type="1">
<li><strong>Thresholds</strong>: All four threshold parameters are well-recovered, with posterior distributions centered near the true values</li>
<li><strong>Treatment effect</strong>: The <code>read</code> coefficient is also well-recovered, with the posterior centered around 1.5</li>
</ol>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>This short tutorial has demonstrated how cumulative link models provide a principled framework for ordinal regression.</p>
<p>By respecting the ordinal nature of the data, rather than treating categories as metric, cumulative link models yield more appropriate inferences for ordered categorical outcomes. The example above is intentionally simple; in cognitive science, data are typically clustered (e.g., by participants or items), which naturally calls for hierarchical models. For an application to ordinal ratings in this setting, see <span class="citation" data-cites="TaylorEtAl2022Ratingnormsshould">Taylor et al. (2022)</span>. For a more general introduction to hierarchical modeling with <code>brms</code>, see <a href="https://bruno.nicenboim.me/bayescogsci/ch-hierarchical.html">chapter 5 of <span class="citation" data-cites="Nicenboimetal">Nicenboim, Schad, and Vasishth (<span>2025</span>)</span></a>.</p>
</section>
<section id="how-to-cite-this-post" class="level2">
<h2 class="anchored" data-anchor-id="how-to-cite-this-post">How to cite this post</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center collapsed" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Citation
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p><strong>BibTeX:</strong></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb32" style="background: #f1f3f5;"><pre class="sourceCode bibtex code-with-copy"><code class="sourceCode bibtex"><span id="cb32-1"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">@misc</span>{<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">nicenboim2026bayesianordinalmodelsaveryshortpracticalguide</span>,</span>
<span id="cb32-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">author</span> = {Nicenboim, Bruno},</span>
<span id="cb32-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">title</span> = {Bayesian ordinal models: A very short practical guide},</span>
<span id="cb32-4">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">year</span> = {2026},</span>
<span id="cb32-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">month</span> = {January},</span>
<span id="cb32-6">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">url</span> = {https://bruno.nicenboim.me/posts/posts/2026-01-09-bayesian-ordinal-models-a-very-short-practical-guide/},</span>
<span id="cb32-7">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">doi</span> = {10.5281/zenodo.18225472}</span>
<span id="cb32-8">}</span></code></pre></div></div>
<p><strong>APA:</strong></p>
<p>Nicenboim, B. (2026, January 09). <em>Bayesian ordinal models: A very short practical guide</em>. https://doi.org/10.5281/zenodo.18225472</p>
</div>
</div>
</div>
</section>
<section id="session-info" class="level2">
<h2 class="anchored" data-anchor-id="session-info">Session info</h2>
<div class="cell">
<div class="cell-output cell-output-stdout">
<pre><code>R version 4.5.2 (2025-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.3 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0  LAPACK version 3.12.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Amsterdam
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggridges_0.5.7      patchwork_1.3.2     bayesplot_1.15.0   
[4] brms_2.23.0         Rcpp_1.1.1          ggplot2_4.0.1      
[7] tidytable_0.11.2    extraDistr_1.10.0.1

loaded via a namespace (and not attached):
 [1] tensorA_0.36.2.1        bridgesampling_1.2-1    tidyr_1.3.2            
 [4] generics_0.1.4          stringi_1.8.7           lattice_0.22-7         
 [7] digest_0.6.39           magrittr_2.0.4          evaluate_1.0.5         
[10] grid_4.5.2              RColorBrewer_1.1-3      mvtnorm_1.3-3          
[13] fastmap_1.2.0           plyr_1.8.9              jsonlite_2.0.0         
[16] Matrix_1.7-4            pkgbuild_1.4.8          backports_1.5.0        
[19] gridExtra_2.3           Brobdingnag_1.2-9       purrr_1.2.1            
[22] QuickJSR_1.8.1          scales_1.4.0            codetools_0.2-20       
[25] abind_1.4-8             cli_3.6.5               rlang_1.1.7            
[28] withr_3.0.2             yaml_2.3.12             otel_0.2.0             
[31] StanHeaders_2.36.0.9000 inline_0.3.21           rstan_2.36.0.9000      
[34] tools_4.5.2             parallel_4.5.2          reshape2_1.4.5         
[37] rstantools_2.6.0        checkmate_2.3.3         coda_0.19-4.1          
[40] dplyr_1.1.4             vctrs_0.7.1             posterior_1.6.1.9000   
[43] R6_2.6.1                stats4_4.5.2            matrixStats_1.5.0      
[46] lifecycle_1.0.5         stringr_1.6.0           htmlwidgets_1.6.4      
[49] pkgconfig_2.0.3         RcppParallel_5.1.11-1   pillar_1.11.1          
[52] gtable_0.3.6            loo_2.9.0.9000          data.table_1.18.2.1    
[55] glue_1.8.0              xfun_0.55               tibble_3.3.1           
[58] tidyselect_1.2.1        knitr_1.51              farver_2.1.2           
[61] htmltools_0.5.9         nlme_3.1-168            labeling_0.4.3         
[64] rmarkdown_2.30          compiler_4.5.2          S7_0.2.1               
[67] distributional_0.6.0   </code></pre>
</div>
</div>
</section>
<section id="references" class="level2">
<h2 class="anchored" data-anchor-id="references">References</h2>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-BurknerVuorre2019" class="csl-entry">
Bürkner, Paul-Christian, and Matti Vuorre. 2019. <span>“Ordinal Regression Models in Psychology: A Tutorial.”</span> <em>Advances in Methods and Practices in Psychological Science</em> 2 (1): 77–101. <a href="https://doi.org/10.1177/2515245918823199">https://doi.org/10.1177/2515245918823199</a>.
</div>
<div id="ref-Nicenboimetal" class="csl-entry">
Nicenboim, Bruno, Daniel J. Schad, and Shravan Vasishth. 2025. <em>Introduction to Bayesian Data Analysis for Cognitive Science</em>. 1st ed. Chapman; Hall/CRC. <a href="https://doi.org/10.1201/9780429342646">https://doi.org/10.1201/9780429342646</a>.
</div>
<div id="ref-TaylorEtAl2022Ratingnormsshould" class="csl-entry">
Taylor, Jack E., Guillaume A. Rousselet, Christoph Scheepers, and Sara C. Sereno. 2022. <span>“Rating Norms Should Be Calculated from Cumulative Link Mixed Effects Models.”</span> <em>Behavior Research Methods</em> 55 (5): 2175–96. <a href="https://doi.org/10.3758/s13428-022-01814-7">https://doi.org/10.3758/s13428-022-01814-7</a>.
</div>
</div>
</section>
<section id="head" class="level1">
<h1>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</h1>
<blockquote class="blockquote">
<blockquote class="blockquote">
<blockquote class="blockquote">
<blockquote class="blockquote">
<blockquote class="blockquote">
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>71d03b4d07801f8dd918ce738ef733622b3e150a</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Thanks to <a href="https://www.linkedin.com/in/lenneke-lichtenberg-a91a71172/?originalSubdomain=nl">Lenneke Lichtenberg</a> for a helpful discussion about this kind of model.↩︎</p></li>
<li id="fn2"><p>The <a href="https://en.wikipedia.org/wiki/Logistic_distribution">logistic distribution</a> is not that different from the normal distribution: it is a symmetrical bell-shaped distribution. The reason for using this distribution rather than the normal distribution is that the modeling will need to rely on the cumulative density function (CDF), and the logistic tends to be more numerically stable↩︎</p></li>
<li id="fn3"><p>Shameless self-promotion of <span class="citation" data-cites="Nicenboimetal">Nicenboim, Schad, and Vasishth (2025)</span>.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div>MIT</div></div></section></div> ]]></description>
  <category>Stan</category>
  <category>Bayesian</category>
  <category>Tutorial</category>
  <category>R</category>
  <category>brms</category>
  <guid>https://bruno.nicenboim.me/posts/posts/2026-01-09-ordinal-models/</guid>
  <pubDate>Thu, 08 Jan 2026 23:00:00 GMT</pubDate>
</item>
<item>
  <title>A simple way to model rankings with Stan</title>
  <dc:creator>Bruno Nicenboim</dc:creator>
  <link>https://bruno.nicenboim.me/posts/posts/2026-01-06-a-simple-way-to-model-rankings-with-stan/</link>
  <description><![CDATA[ 




<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Update Notice
</div>
</div>
<div class="callout-body-container callout-body">
<p><strong>This is an updated version of a post originally published on March 21, 2021.</strong></p>
<p>I’ve updated this post in January 2026 to work with current versions of Stan and R packages. The main changes include:</p>
<ul>
<li><p>Updated Stan syntax to current standards</p></li>
<li><p>Using cmdstanr instead of rstan and some other R packages.</p></li>
</ul>
<p>The core content and ideas remain the same.</p>
</div>
</div>
<section id="the-initial-problem" class="level2">
<h2 class="anchored" data-anchor-id="the-initial-problem">The initial problem</h2>
<p>I wrote what I thought was the generative process for some modeling work, and it looked too common to not have a name, so I started asking around on what it was a very popular website back in 2021, <a href="https://en.wikipedia.org/wiki/Twitter">Twitter</a>.</p>
<p>One useful clue was about the <a href="https://en.wikipedia.org/wiki/Discrete_choice#exploded_logit"><em>exploded logit distribution</em></a>.<sup>1</sup></p>
<p><img src="https://media.giphy.com/media/3osxYCsLd9qgsgqpwI/giphy.gif" class="img-fluid"></p>
<p>In this post, I’ll show how this model can be fit in the probabilistic programming language <a href="https://mc-stan.org/">Stan</a>, and how it can be used to describe the underlying order of ranking data.</p>
<p>I’m going to load some R packages that will be useful throughout this post.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(tidytable) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Nicer alternative to dplyr and purrr</span></span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Nice plots</span></span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(extraDistr) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># More distributions</span></span>
<span id="cb1-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(rcorpora) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get random words</span></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(cmdstanr) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Lightweight Stan interface</span></span>
<span id="cb1-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bayesplot) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Nice Bayesian plots</span></span>
<span id="cb1-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">set.seed</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">42</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Keep everything R the same (not for Stan though)</span></span></code></pre></div></div>
</div>
</section>
<section id="ranking-data" class="level2">
<h2 class="anchored" data-anchor-id="ranking-data">Ranking data</h2>
<p>Ranking data appear when we care about the <em>underlying</em> order that certain elements have. We might want to know which are the best horses after looking at several races <span class="citation" data-cites="gakisetal2018">(Gakis et al. 2018)</span>, which is the best candidate for a job after a series of interviewers talked to several candidates. More in line with cognitive science, we might want to know which are the best possible completions for a sentence or the best exemplars of a category.</p>
<p>One way to get a ranking of exemplars of a category, for example, is to present them to participants and ask them to order all (or a subset) of them <span class="citation" data-cites="Barsalou1985">(see Barsalou 1985)</span>.</p>
<section id="a-ranking-simulation-using-pizza-toppings" class="level3">
<h3 class="anchored" data-anchor-id="a-ranking-simulation-using-pizza-toppings">A ranking simulation using pizza toppings</h3>
<p><img src="https://media.giphy.com/media/3oEjHZhG9COPG6XjzO/giphy.gif" class="img-fluid"></p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1">toppings <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">corpora</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"foods/pizzaToppings"</span>)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>pizzaToppings</span>
<span id="cb2-2">N_toppings <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(toppings)</span>
<span id="cb2-3">toppings</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code> [1] "anchovies"        "artichoke"       
 [3] "bacon"            "breakfast bacon" 
 [5] "Canadian bacon"   "cheese"          
 [7] "chicken"          "chili peppers"   
 [9] "feta"             "garlic"          
[11] "green peppers"    "grilled onions"  
[13] "ground beef"      "ham"             
[15] "hot sauce"        "meatballs"       
[17] "mushrooms"        "olives"          
[19] "onions"           "pepperoni"       
[21] "pineapple"        "sausage"         
[23] "spinach"          "sun-dried tomato"
[25] "tomatoes"        </code></pre>
</div>
</div>
<p>Let’s say that we want to know the underlying order of pizza toppings. For the modeling, I’m going to assume that the toppings are ordered according to an underlying value, which also represents how likely it is for each topping to be <em>the</em> exemplar of their category.</p>
<p>To get a known ground truth for the ranking, I’m going to simulate an order of pizza toppings. I assign probabilities that sum up to one to the toppings by drawing a random sample from a <a href="https://en.wikipedia.org/wiki/Dirichlet_distribution">Dirichlet distribution</a>. The Dirichlet distribution is the generalization of the Beta distribution. It has a concentration parameter, usually <img src="https://latex.codecogs.com/png.latex?%5Cboldsymbol%7B%5Calpha%7D">, which is a vector as long as the probabilities we are sampling (25 here). When the vector is full of ones, the distribution is uniform: All probabilities are equally likely, so on average each one is <img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B1%7D%7B%5Ctext%7Bvector%20length%7D%7D"> (<img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B1%7D%7B25%7D"> here). By setting all the concentration parameters below one (namely <img src="https://latex.codecogs.com/png.latex?0.2">), I’m enforcing sparsity in the random values that I’m generating, that is, many probability values close to zero.</p>
<p>These is the true order that I’m assuming here:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># all elements of the vector are 0.2</span></span>
<span id="cb4-2">alpha <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(.<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, N_toppings)</span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate one draw from a Dirichlet distribution</span></span>
<span id="cb4-4">P_toppings <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rdirichlet</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, alpha)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb4-5">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Add names</span></span>
<span id="cb4-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setNames</span>(toppings) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb4-7">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Sort from the best exemplar</span></span>
<span id="cb4-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sort</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">decreasing =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>)</span>
<span id="cb4-9">P_toppings <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%&gt;%</span></span>
<span id="cb4-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">round</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code> breakfast bacon          chicken             feta 
           0.294            0.241            0.087 
       anchovies sun-dried tomato           olives 
           0.087            0.077            0.057 
       pepperoni        artichoke           cheese 
           0.056            0.049            0.010 
  Canadian bacon            bacon              ham 
           0.008            0.008            0.006 
       meatballs    chili peppers           garlic 
           0.004            0.004            0.004 
     ground beef         tomatoes        hot sauce 
           0.003            0.003            0.002 
          onions          sausage        pineapple 
           0.000            0.000            0.000 
         spinach        mushrooms   grilled onions 
           0.000            0.000            0.000 
   green peppers 
           0.000 </code></pre>
</div>
</div>
<ul>
<li>Given these values, if I were to ask a participant “What’s the most appropriate topping for a pizza?” I would assume that 29.37 percent of the time, I would get <em>breakfast bacon</em>.</li>
</ul>
<p>Essentially, we expect something like this to be happening:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctext%7Bresponse%7D%20%5Csim%20%5Ctext%7BCategorical%7D(%5CTheta_%7B%5Ctext%7Btoppings%7D%7D)%0A"></p>
<p>With <img src="https://latex.codecogs.com/png.latex?%5CTheta_%7B%5Ctext%7Btoppings%7D%7D"> representing the different probabilities for each topping. The probability mass function of the categorical distribution is absurdly simple: It’s just the probability of the outcome.</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ap(x%20=%20i)%20=%20%5CTheta_i%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?i%20=%20%5C%7B">breakfast bacon, chicken, feta, anchovies, sun-dried tomato, olives, pepperoni, artichoke, cheese, Canadian bacon, bacon, ham, meatballs, chili peppers, garlic, ground beef, tomatoes, hot sauce, onions, sausage, pineapple, spinach, mushrooms, grilled onions, green peppers<img src="https://latex.codecogs.com/png.latex?%5C%7D">.</p>
<p>We can simulate this with 100 participants as follows:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">response <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rcat</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, P_toppings, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">names</span>(P_toppings))</span></code></pre></div></div>
</div>
<p>And this should match approximately <code>P_toppings</code>.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table</span>(response)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span></span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>response
 breakfast bacon          chicken             feta 
            0.26             0.19             0.16 
       anchovies sun-dried tomato           olives 
            0.07             0.15             0.06 
       pepperoni        artichoke           cheese 
            0.01             0.08             0.00 
  Canadian bacon            bacon              ham 
            0.02             0.00             0.00 
       meatballs    chili peppers           garlic 
            0.00             0.00             0.00 
     ground beef         tomatoes        hot sauce 
            0.00             0.00             0.00 
          onions          sausage        pineapple 
            0.00             0.00             0.00 
         spinach        mushrooms   grilled onions 
            0.00             0.00             0.00 
   green peppers 
            0.00 </code></pre>
</div>
</div>
<p><em>It seems that by only asking participants to give the best topping we could already deduce the underlying order…</em></p>
<p>True, but one motivation for considering ranking data is the amount of information that we gather with a list due to their combinatorial nature. If we ask participants to rank <img src="https://latex.codecogs.com/png.latex?n"> items, an answer consists in making a single selection out of <img src="https://latex.codecogs.com/png.latex?n!"> possibilities. Ordering 7 pizza toppings, for example, constitutes making a single selection out of 5040 possibilities!</p>
<p>If we don’t relay on lists and there is sparcity, it requires a large number of participants until we get answers of low probability. (For example, we’ll need a very large number of participants until we hear something else but <em>hammer</em> as an exemplar of tools).</p>
<ul>
<li>Now, what happens if we ask about the second most appropriate topping for a pizza?</li>
</ul>
<p>Now we need to exclude the first topping that was given, and draw another sample from a categorical distribution. (We don’t allow the participant to repeat toppings, that is, to say that the best topping is pineapple and the second best is also pineapple). This means that now the probability of the topping already given is zero, and that we need to normalize our original probability values by dividing them by the new total probability (which will be lower than 1).</p>
<p>Here, the probability of getting the element <img src="https://latex.codecogs.com/png.latex?j"> (where <img src="https://latex.codecogs.com/png.latex?j%20%5Cneq%20i">) is</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ap(x%20=%20j)%20=%20%5Cfrac%7B%5CTheta_j%7D%7B%5Csum%20%5CTheta_%7B%5B-i%5D%7D%7D%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%5CTheta_%7B%5B-i%5D%7D"> represents the probabilities of all the outcomes except of <img src="https://latex.codecogs.com/png.latex?i">, which was the first one.</p>
<ul>
<li>We can go on with the third best topping, where we need to normalize the remaining probabilities by dividing by the new sum of probabilities (e.g., we remove elements <img src="https://latex.codecogs.com/png.latex?i"> and <img src="https://latex.codecogs.com/png.latex?j">).</li>
</ul>
<p><img src="https://latex.codecogs.com/png.latex?%0Ap(x%20=%20k)%20=%20%5Cfrac%7B%5CTheta_k%7D%7B%5Csum%20%5CTheta_%7B%5B-i,-j%5D%7D%7D%0A"></p>
<ul>
<li>We can do this until we get to the last element, which will be drawn with probability 1.</li>
</ul>
<p><strong>And this is the exploded logit distribution.</strong></p>
<p>This process can be simulated in R as follows:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb9-1">rexploded <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span>  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(n, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ranked =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, prob, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NULL</span>){</span>
<span id="cb9-2">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># run n times</span></span>
<span id="cb9-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lapply</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>n, <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(nn){</span>
<span id="cb9-4">    res <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rep</span>(<span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>, ranked)</span>
<span id="cb9-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is.null</span>(labels)){</span>
<span id="cb9-6">      res <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</span>(res, labels)</span>
<span id="cb9-7">    } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> {</span>
<span id="cb9-8">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># if there are no labels, just 1,2,3,...</span></span>
<span id="cb9-9">      labels <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq_along</span>(prob)</span>
<span id="cb9-10">    }</span>
<span id="cb9-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span>(i <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>ranked){</span>
<span id="cb9-12">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># normalize the probability so that it sums to 1</span></span>
<span id="cb9-13">      prob <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> prob<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sum</span>(prob)</span>
<span id="cb9-14">      res[i] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rcat</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prob =</span> prob, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> labels)</span>
<span id="cb9-15">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># remove the choice from the set:</span></span>
<span id="cb9-16">      prob[res[i]] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb9-17">    }</span>
<span id="cb9-18">    res</span>
<span id="cb9-19">  })</span>
<span id="cb9-20">}</span></code></pre></div></div>
</div>
<p>If we would like to simulate 50 subjects creating a ranking of the best 7 toppings, we would do the following:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb10-1">res <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rexploded</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span>,</span>
<span id="cb10-2">                 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ranked =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>,</span>
<span id="cb10-3">                 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">prob =</span> P_toppings,</span>
<span id="cb10-4">                 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">labels =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">names</span>(P_toppings))</span>
<span id="cb10-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># subject 1:</span></span>
<span id="cb10-6">res[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] sun-dried tomato artichoke        olives          
[4] breakfast bacon  chicken          pepperoni       
[7] anchovies       
25 Levels: breakfast bacon chicken feta ... green peppers</code></pre>
</div>
</div>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-06-a-simple-way-to-model-rankings-with-stan/index_files/figure-html/subjects-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>We have simulated ranking data of pizza toppings, can we recover the original probability values and “discover” the underlying order?</p>
</section>
</section>
<section id="fitting-the-exploded-logistic-distribution-in-stan" class="level2">
<h2 class="anchored" data-anchor-id="fitting-the-exploded-logistic-distribution-in-stan">Fitting the exploded logistic distribution in Stan</h2>
<p>To fit the model in Stan, I’m going to create a custom probability mass function that takes an array of integers, <code>x</code>, which represents a set of rankings, and a vector of probability values, <code>theta</code>, that sums up to one.</p>
<p>The logic of this function is that the probability mass function of a ranking <img src="https://latex.codecogs.com/png.latex?%5C%7Bi,j,k,%20%5Cldots,%20N%20%5C%7D"> can be written as a product of normalized categorical distributions (where the first one is just divided by 1).</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ap(x%20=%20%5C%7Bi,j,k,%5Cldots%5C%7D)%20=%20%5Cfrac%7B%5CTheta_i%7D%7B%5Csum%20%5CTheta%7D%20%5Ccdot%20%5Cfrac%7B%5CTheta_j%7D%7B%5Csum%20%5CTheta_%7B%5B-i%5D%7D%7D%20%5Ccdot%20%5Cfrac%7B%5CTheta_k%7D%7B%5Csum%20%5CTheta_%7B%5B-i,%20-j%5D%7D%7D%20%5Cldots%0A"></p>
<p>For Stan, we need the log-PDF. In log-space, products become sums, and divisions differences, and the log of <img src="https://latex.codecogs.com/png.latex?%5Csum%20%5CTheta"> will be zero:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0A%5Clog(p(x%20=%20%5C%7Bi,j,k,%5Cldots%5C%7D))%20=&amp;%20%5Clog(%5CTheta_i)%20-%20%5Clog(%5Csum%20%5CTheta)%20%5C%5C%0A&amp;%20+%20%5Clog(%5CTheta_j)%20-%20%5Clog(%5Csum%20%5CTheta_%7B%5B-i%5D%7D)%20%5C%5C%0A&amp;+%20%5Clog(%5CTheta_k)%20-%20%5Clog(%5Csum%20%5CTheta_%7B%5B-i,%20-j%5D%7D)%20%5C%5C%0A&amp;%20+%20%5Cldots%0A%5Cend%7Baligned%7D%0A"></p>
<p>The following Stan code has a custom function that follows this logic but iterating over the rankings. In each iteration, it aggregates in the variable <code>out</code> the addends of the log probability mass function, and turns the probability of selecting again the already ranked element to zero. I save this code as <code>"exploded.stan"</code>.</p>
<div class="cell" data-output.var="exploded">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode stan code-with-copy"><code class="sourceCode stan"><span id="cb12-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">functions</span> {</span>
<span id="cb12-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">real</span> exploded_lpmf(<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">array</span>[] <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> x, <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">vector</span> Theta){</span>
<span id="cb12-3">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">real</span> out = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>;</span>
<span id="cb12-4">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">vector</span>[num_elements(Theta)] thetar = Theta;</span>
<span id="cb12-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span>(pos <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> x){</span>
<span id="cb12-6">      out += log(thetar[pos]) - log(sum(thetar));</span>
<span id="cb12-7">      thetar[pos] = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>;</span>
<span id="cb12-8">    }</span>
<span id="cb12-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span>(out);</span>
<span id="cb12-10">  }</span>
<span id="cb12-11">}</span>
<span id="cb12-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">data</span>{</span>
<span id="cb12-13">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> N_ranking; <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// total times the choices were ranked</span></span>
<span id="cb12-14">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> N_ranked; <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// total choices ranked</span></span>
<span id="cb12-15">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> N_options; <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// total options</span></span>
<span id="cb12-16">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">array</span>[N_ranking, N_ranked] <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> res;</span>
<span id="cb12-17">}</span>
<span id="cb12-18"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">parameters</span> {</span>
<span id="cb12-19">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">simplex</span>[N_options] Theta;</span>
<span id="cb12-20">}</span>
<span id="cb12-21"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">model</span> {</span>
<span id="cb12-22">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">target +=</span> dirichlet_lpdf(Theta | rep_vector(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, N_options));</span>
<span id="cb12-23">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span>(r <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:N_ranking){</span>
<span id="cb12-24">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">target +=</span> exploded_lpmf(res[r] | Theta);</span>
<span id="cb12-25">  }</span>
<span id="cb12-26">}</span></code></pre></div></div>
</div>
<p>The whole model includes the usual data declaration, the parameter <code>Theta</code> declared as a simplex (i.e., it sums to one), and a uniform Dirichlet prior for <code>Theta</code>. (I’m assuming that I don’t know how sparse the probabilities are).</p>
<p>Let’s see if I can recover the parameter values.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Make the list of lists into a matrix</span></span>
<span id="cb13-2">res_matrix <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sapply</span>(res, as.numeric))</span>
<span id="cb13-3">ldata <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(</span>
<span id="cb13-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">res =</span> res_matrix, </span>
<span id="cb13-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">N_ranked =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(res[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]), </span>
<span id="cb13-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">N_options =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(P_toppings), </span>
<span id="cb13-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">N_ranking =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(res)</span>
<span id="cb13-8">) </span>
<span id="cb13-9"></span>
<span id="cb13-10">m_expl <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cmdstan_model</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"exploded.stan"</span>)</span>
<span id="cb13-11"></span>
<span id="cb13-12">f_exploded <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> m_expl<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sample</span>(</span>
<span id="cb13-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> ldata,</span>
<span id="cb13-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">seed =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">123</span>,</span>
<span id="cb13-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">parallel_chains =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb13-16">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">refresh =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb13-17">)</span></code></pre></div></div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1">f_exploded</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code> variable    mean  median   sd  mad      q5     q95 rhat
 lp__     -723.36 -723.05 3.64 3.64 -729.48 -718.06 1.00
 Theta[1]    0.29    0.29 0.04 0.04    0.23    0.35 1.00
 Theta[2]    0.27    0.27 0.03 0.03    0.21    0.33 1.00
 Theta[3]    0.08    0.08 0.01 0.01    0.06    0.11 1.00
 Theta[4]    0.09    0.08 0.01 0.01    0.06    0.11 1.00
 Theta[5]    0.07    0.07 0.01 0.01    0.05    0.09 1.00
 Theta[6]    0.05    0.04 0.01 0.01    0.03    0.06 1.00
 Theta[7]    0.04    0.04 0.01 0.01    0.03    0.06 1.00
 Theta[8]    0.05    0.05 0.01 0.01    0.03    0.06 1.00
 Theta[9]    0.01    0.01 0.00 0.00    0.00    0.02 1.00
 ess_bulk ess_tail
     1785     2746
     5569     2874
     6497     3121
     4994     3018
     6242     2855
     5037     2946
     5286     2654
     5002     3016
     4965     2937
     5453     3001

 # showing 10 of 26 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option)</code></pre>
</div>
</div>
<p>I plot the posterior distributions of the probability values and the true probability values below.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb16-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mcmc_recover_hist</span>(f_exploded<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">draws</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Theta"</span>),</span>
<span id="cb16-2">                  P_toppings,</span>
<span id="cb16-3">                  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">facet_args =</span></span>
<span id="cb16-4">                    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">scales =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fixed"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ncol =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb16-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">legend.position =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bottom"</span>)</span></code></pre></div></div>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-01-06-a-simple-way-to-model-rankings-with-stan/index_files/figure-html/recovery-plot-1.png" class="img-fluid figure-img" width="672"></p>
</figure>
</div>
</div>
</div>
<p>It looks reasonable. However, if we really want to be sure that this is working, we should probably use simulation based calibration <span class="citation" data-cites="taltsValidatingBayesianInference2018">(Talts et al. 2018)</span>.</p>
</section>
<section id="what-is-this-good-for" class="level2">
<h2 class="anchored" data-anchor-id="what-is-this-good-for">What is this good for?</h2>
<p>This super simple example shows how to get an underlying ranking based on a set of responses from a number of subjects. It’s straightforward to adapt this model to data from participants ranking elements from different sets of the <em>same size</em> (e.g., 7 out of 25 toppings, 7 out of 25 tools). It’s a little less straightforward if the sets are of different sizes, e.g., rank 7 toppings out 25, but 7 tools out 50. This is just because Stan doesn’t allow ragged arrays. See <a href="https://discourse.mc-stan.org/t/ragged-array-of-simplexes/1382/31">this Stan Discourse thread</a> for some tips on implementing the latter model.</p>
</section>
<section id="could-this-be-used-as-a-cognitive-model-of-peoples-rankings" class="level2">
<h2 class="anchored" data-anchor-id="could-this-be-used-as-a-cognitive-model-of-peoples-rankings">Could this be used as a cognitive model of people’s rankings?</h2>
<p><img src="https://media.giphy.com/media/dXcwxFuXCd8sI3VcFb/giphy.gif" class="img-fluid"></p>
<p>Maybe. And I enter here in the realm of half baked research, ideal for a blog post.</p>
<p><span class="citation" data-cites="Lee2014">Lee, Steyvers, and Miller (2014)</span> show the implementation of a cognitive model for rank order data from the latent knowledge of participants, which is based on Thurstonian models <span class="citation" data-cites="thurstone1927law thurstone1931rank">(Thurstone 1927, 1931)</span> fitted with Bayesian methods in JAGS <span class="citation" data-cites="Johnson2013">(Johnson and Kuhn 2013)</span>.</p>
<p>The exploded logit model seems to be closely related to the Thurstonian model. The Thurstonian model assumes that each participant assigns an underlying score to each item of a set, which is drawn from a true score with normally distributed error. The score determines the order that the participant gives. We can think about the exploded logit similarly. While I modeled the underlying ranking based on probability values, one could assume that each participant <img src="https://latex.codecogs.com/png.latex?s"> had their own score <img src="https://latex.codecogs.com/png.latex?%5Cmu_%7Bis%7D"> for each item (or pizza topping) <img src="https://latex.codecogs.com/png.latex?i">, which is built as a common score <img src="https://latex.codecogs.com/png.latex?%5Cmu_i"> together with some individual deviation <img src="https://latex.codecogs.com/png.latex?%5Cepsilon_%7Bis%7D">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmu_%7Bis%7D%20=%20%5Cmu_i%20+%20%5Cepsilon_%7Bis%7D%0A"></p>
<p>If we assume that <img src="https://latex.codecogs.com/png.latex?%5Cepsilon_%7Bis%7D"> has a <a href="https://en.wikipedia.org/wiki/Gumbel_distribution">Gumbel</a> distribution, then the probability of <img src="https://latex.codecogs.com/png.latex?%5Cmu_%7Bis%7D"> being ranked first out of N options is determined by a softmax function:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AP(i)%20=%20%5Cfrac%7B%5Cexp(%5Cmu_i)%7D%7B%5Csum%20%5Cexp(%5Cmu)%7D%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%5Cmu"> is the vector of scores for all elements of the set.</p>
<p>And the probability of ordering <img src="https://latex.codecogs.com/png.latex?j"> second is:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AP(i,j,%5Cldots)%20=%20%5Cfrac%7B%5Cexp(%5Cmu_j)%7D%7B%5Csum%20%5Cexp(%5Cmu_%7B%5B-i%5D%7D)%7D%0A"></p>
<p>and so forth.</p>
<p>These last equations are essentially the same categorical distributions that I used before, but the softmax function converts the unbounded scores into probabilities first. However, with the exploded logit, the error term goes away leading to a more tractable model. This is not the case for the Thurstonian model. The Thurstonian model is more complex, but at the same time we gain more flexibility. With the error term, the Thurstonian model can incorporate the reliability of the participants’ judgments and even correlations, which, as far as I know, can’t be included in the exploded logit model.</p>
</section>
<section id="how-to-cite-this-post" class="level2">
<h2 class="anchored" data-anchor-id="how-to-cite-this-post">How to cite this post</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center collapsed" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Citation
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p><strong>BibTeX:</strong></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode bibtex code-with-copy"><code class="sourceCode bibtex"><span id="cb17-1"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">@misc</span>{<span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">nicenboim2026asimplewaytomodelrankingswithstan</span>,</span>
<span id="cb17-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">author</span> = {Nicenboim, Bruno},</span>
<span id="cb17-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">title</span> = {A simple way to model rankings with Stan},</span>
<span id="cb17-4">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">year</span> = {2026},</span>
<span id="cb17-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">month</span> = {January},</span>
<span id="cb17-6">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">url</span> = {https://bruno.nicenboim.me/posts/posts/2026-01-07-a-simple-way-to-model-rankings-with-stan/},</span>
<span id="cb17-7">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">doi</span> = {10.5281/zenodo.18171805}</span>
<span id="cb17-8">}</span></code></pre></div></div>
<p><strong>APA:</strong></p>
<p>Nicenboim, B. (2026, January 07). <em>A simple way to model rankings with Stan</em>. https://doi.org/10.5281/zenodo.18171805</p>
</div>
</div>
</div>
</section>
<section id="session-info" class="level2">
<h2 class="anchored" data-anchor-id="session-info">Session info</h2>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb18-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sessionInfo</span>()</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>R version 4.5.2 (2025-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.3 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0  LAPACK version 3.12.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Amsterdam
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets 
[6] methods   base     

other attached packages:
[1] bayesplot_1.15.0    cmdstanr_0.9.0     
[3] rcorpora_2.0.1      extraDistr_1.10.0.1
[5] ggplot2_4.0.1       tidytable_0.11.2   

loaded via a namespace (and not attached):
 [1] gtable_0.3.6         jsonlite_2.0.0      
 [3] dplyr_1.1.4          compiler_4.5.2      
 [5] tidyselect_1.2.1     Rcpp_1.1.1          
 [7] stringr_1.6.0        scales_1.4.0        
 [9] yaml_2.3.12          fastmap_1.2.0       
[11] plyr_1.8.9           R6_2.6.1            
[13] labeling_0.4.3       generics_0.1.4      
[15] distributional_0.6.0 knitr_1.51          
[17] backports_1.5.0      htmlwidgets_1.6.4   
[19] checkmate_2.3.3      tibble_3.3.1        
[21] pillar_1.11.1        RColorBrewer_1.1-3  
[23] posterior_1.6.1.9000 rlang_1.1.7         
[25] stringi_1.8.7        xfun_0.55           
[27] S7_0.2.1             otel_0.2.0          
[29] cli_3.6.5            withr_3.0.2         
[31] magrittr_2.0.4       ps_1.9.1            
[33] processx_3.8.6       digest_0.6.39       
[35] grid_4.5.2           lifecycle_1.0.5     
[37] vctrs_0.7.1          tensorA_0.36.2.1    
[39] evaluate_1.0.5       glue_1.8.0          
[41] data.table_1.18.2.1  farver_2.1.2        
[43] abind_1.4-8          reshape2_1.4.5      
[45] rmarkdown_2.30       matrixStats_1.5.0   
[47] tools_4.5.2          pkgconfig_2.0.3     
[49] htmltools_0.5.9     </code></pre>
</div>
</div>
</section>
<section id="references" class="level2">
<h2 class="anchored" data-anchor-id="references">References</h2>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-Barsalou1985" class="csl-entry">
Barsalou, Lawrence W. 1985. <span>“Ideals, Central Tendency, and Frequency of Instantiation as Determinants of Graded Structure in Categories.”</span> <em>Journal of Experimental Psychology: Learning, Memory, and Cognition</em> 11 (4): 629.
</div>
<div id="ref-BEGGS19811" class="csl-entry">
Beggs, S, S Cardell, and J Hausman. 1981. <span>“Assessing the Potential Demand for Electric Cars.”</span> <em>Journal of Econometrics</em> 17 (1): 1–19. https://doi.org/<a href="https://doi.org/10.1016/0304-4076(81)90056-7">https://doi.org/10.1016/0304-4076(81)90056-7</a>.
</div>
<div id="ref-gakisetal2018" class="csl-entry">
Gakis, Konstantinos, Panos Pardalos, Chang-Hwan Choi, Jae-Hyeon Park, and Jiwun Yoon. 2018. <span>“Simulation of a Probabilistic Model for Multi-Contestant Races.”</span> <em>Athens Journal of Sports</em> 5 (2): 95–114.
</div>
<div id="ref-Johnson2013" class="csl-entry">
Johnson, Timothy R., and Kristine M. Kuhn. 2013. <span>“Bayesian <span>Thurstonian</span> Models for Ranking Data Using <span>JAGS</span>.”</span> <em>Behavior Research Methods</em> 45 (3): 857–72. <a href="https://doi.org/10.3758/s13428-012-0300-3">https://doi.org/10.3758/s13428-012-0300-3</a>.
</div>
<div id="ref-Lee2014" class="csl-entry">
Lee, Michael D., Mark Steyvers, and Brent Miller. 2014. <span>“A Cognitive Model for Aggregating People’s Rankings.”</span> <em>PLOS ONE</em> 9 (5): e96431. <a href="https://doi.org/10.1371/journal.pone.0096431">https://doi.org/10.1371/journal.pone.0096431</a>.
</div>
<div id="ref-Luce1959" class="csl-entry">
Luce, R. Duncan. 1959. <em>Individual Choice Behavior : A Theoretical Analysis</em>. Book. Wiley N.Y.
</div>
<div id="ref-Plackett" class="csl-entry">
Plackett, R. L. 1975. <span>“The Analysis of Permutations.”</span> <em>Journal of the Royal Statistical Society. Series C (Applied Statistics)</em> 24 (2): 193–202. <a href="http://www.jstor.org/stable/2346567">http://www.jstor.org/stable/2346567</a>.
</div>
<div id="ref-taltsValidatingBayesianInference2018" class="csl-entry">
Talts, Sean, Michael Betancourt, Daniel Simpson, Aki Vehtari, and Andrew Gelman. 2018. <span>“Validating <span>Bayesian Inference Algorithms</span> with <span>Simulation</span>-<span>Based Calibration</span>,”</span> April. <a href="http://arxiv.org/abs/1804.06788">http://arxiv.org/abs/1804.06788</a>.
</div>
<div id="ref-thurstone1927law" class="csl-entry">
Thurstone, Louis L. 1927. <span>“A Law of Comparative Judgement.”</span> <em>Psychological Reviews</em> 34: 273–86.
</div>
<div id="ref-thurstone1931rank" class="csl-entry">
———. 1931. <span>“Rank Order as a Psycho-Physical Method.”</span> <em>Journal of Experimental Psychology</em> 14 (3): 187.
</div>
</div>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>This model is also called the <em>rank ordered logit model</em> <span class="citation" data-cites="BEGGS19811">(Beggs, Cardell, and Hausman 1981)</span> or Plackett–Luce model due to <span class="citation" data-cites="Plackett">Plackett (1975)</span> and <span class="citation" data-cites="Luce1959">Luce (1959)</span>, but I liked the explosion part more.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div>MIT</div></div></section></div> ]]></description>
  <category>Stan</category>
  <category>Bayesian</category>
  <category>R</category>
  <category>Citable</category>
  <guid>https://bruno.nicenboim.me/posts/posts/2026-01-06-a-simple-way-to-model-rankings-with-stan/</guid>
  <pubDate>Tue, 06 Jan 2026 23:00:00 GMT</pubDate>
</item>
<item>
  <title>Welcome to my new Quarto site</title>
  <dc:creator>Bruno Nicenboim</dc:creator>
  <link>https://bruno.nicenboim.me/posts/posts/2026-01-06-welcome-to-my-new-quarto-site/</link>
  <description><![CDATA[ 




<section id="a-fresh-start" class="level2">
<h2 class="anchored" data-anchor-id="a-fresh-start">A fresh start</h2>
<p>After years of using Hugo, I’ve finally made the switch to <a href="https://quarto.org/">Quarto</a>.</p>
<p>The main reason? Hugo wasn’t allowing me to update my website anymore!</p>
<p>The old posts from my Hugo site are gone (for now), but I’m looking forward to building up new content here. Stay tuned!</p>


</section>

 ]]></description>
  <category>Meta</category>
  <category>Quarto</category>
  <guid>https://bruno.nicenboim.me/posts/posts/2026-01-06-welcome-to-my-new-quarto-site/</guid>
  <pubDate>Mon, 05 Jan 2026 23:00:00 GMT</pubDate>
</item>
</channel>
</rss>
