<?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>Wed, 06 May 2026 22:00:00 GMT</lastBuildDate>
<item>
  <title>Of birds and planes</title>
  <dc:creator>Bruno Nicenboim</dc:creator>
  <link>https://bruno.nicenboim.me/posts/posts/2026-05-07-birds-and-planes/</link>
  <description><![CDATA[ 




<p>I sometimes struggle to explain to people outside the field why psycholinguists like me use large language models to investigate how people understand language. This post breaks it down in a very simplified way: one important thing that happens when you read or listen, how language models perform something at least superficially similar, and why that comparison actually teaches us something.</p>
<section id="your-brain-is-always-one-step-ahead" class="level2">
<h2 class="anchored" data-anchor-id="your-brain-is-always-one-step-ahead">Your brain is always one step ahead</h2>
<p>Try finishing this:</p>
<blockquote class="blockquote">
<p>She put on her coat, picked up her keys, and walked out the ___</p>
</blockquote>
<p>You almost certainly thought about <em>door</em>.</p>
<p>Now this one:</p>
<blockquote class="blockquote">
<p>The scientist published her results in a peer-reviewed ___</p>
</blockquote>
<p>You probably thought about <em>journal</em>. Or maybe <em>paper</em>.</p>
<blockquote class="blockquote">
<p>He had a coffee and a ___</p>
</blockquote>
<p><em>Croissant? Slice of cake? Brownie?</em> This one is harder. Several continuations fit equally well.</p>
<p>On some occasions, words are much easier to predict than on others. It seems that predicting the next word is something your brain does automatically, all the time, without you noticing.</p>
</section>
<section id="how-do-we-actually-know-the-brain-predicts" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="how-do-we-actually-know-the-brain-predicts">How do we actually know the brain predicts?</h2>
<p>How do we know this also happens passively when you read or listen? You can’t <em>feel</em> your brain making predictions.</p>
<p>Scientists have found ways to figure this out.</p>

<div class="no-row-height column-margin column-container"><div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-05-07-birds-and-planes/Reading_Fixations_Saccades.jpg" class="figure-img" style="width:90.0%;height:50.0%"></p>
<figcaption>Image by Lucs-kho, public domain, via <a href="https://commons.wikimedia.org/wiki/File:Reading_Fixations_Saccades.jpg">Wikimedia Commons</a>.</figcaption>
</figure>
</div></div><p>One way is tracking people’s eyes while they read. Your eyes don’t slide smoothly across a page. They jump from word to word. It looks like the figure on the right: circles where your eyes stop (called fixations), and lines where they jump (called saccades). And it turns out that the eyes spend less time on predictable words, sometimes skipping them entirely, while lingering on surprising ones. The brain already has a good guess for predictable words, so it doesn’t need to look as carefully.</p>
<p>There’s also a more direct way to look into the brain. Your brain runs on electricity: billions of cells communicating through small bursts of electrical activity, these are your neurons. All those tiny signals add up, and some of that activity actually reaches the surface of your head. By placing small sensors on someone’s scalp (a technique called EEG, for electroencephalography), you can pick up those signals while the person reads. It looks like a cap covered in wires, and what it records is a kind of summary of what millions of neurons are doing at each moment. When a word shows up that the reader didn’t expect, the electrical response shifts. The more surprising the word, the bigger the shift.</p>

<div class="no-row-height column-margin column-container"><div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-05-07-birds-and-planes/330px-EEG_Recording_Cap.jpg" class="figure-img" style="width:90.0%;height:60.0%"></p>
<figcaption>Photo by Chris Hope, <a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a>, via <a href="https://commons.wikimedia.org/wiki/File:EEG_Recording_Cap.jpg">Wikimedia Commons</a>.</figcaption>
</figure>
</div></div></section>
<section id="context-is-everything" class="level2">
<h2 class="anchored" data-anchor-id="context-is-everything">Context is everything</h2>
<p>Watch what happens when you add more context to the last example, “He had a coffee and a ___“. Now we have, for example:</p>
<blockquote class="blockquote">
<p>He was celebrating his birthday. He had a coffee and a ___</p>
</blockquote>
<p>The continuation <em>slice of cake</em> is much more obvious.</p>
<p>Guesses become easier the further into a sentence you get. Your brain pulls in everything available: the words already said, what makes sense in the real world, what people typically say in that kind of situation. More context generally means fewer plausible options, and easier guesses.</p>
<p>The same thing happens when you listen. You start processing what someone is saying before they’ve finished the sentence. In conversation, people often begin composing their reply <em>while the other person is still speaking</em>, because they can already tell where it’s going.</p>
</section>
<section id="what-large-language-models-are-doing" class="level2">
<h2 class="anchored" data-anchor-id="what-large-language-models-are-doing">What large language models are doing</h2>
<p>Large language models are programs trained by reading enormous amounts of text, including books, news articles, websites, and conversations.</p>
<p>The training works like this: the model sees a sequence of words and tries to guess what comes next. When it’s wrong, it adjusts a little. After billions of rounds, it develops something like an intuition for how language flows, which words tend to follow which, in what contexts. This next-word prediction task is actually the foundation of chatbots like ChatGPT. Before a chatbot can hold a conversation or answer your questions, it first has to learn the patterns of language by practicing exactly this: predicting the next word, over and over, on enormous amounts of text.</p>
<p>What makes a language model bigger or smaller comes down to <strong>parameters</strong>: numbers inside the model that get adjusted during training. Think of them as tiny knobs. Each knob controls a small part of how the model responds to a word or a pattern. More knobs means finer distinctions and subtler patterns. Fewer knobs means a rougher, blurrier picture of the language.</p>
</section>
<section id="of-birds-and-planes" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="of-birds-and-planes">Of birds and planes</h2>
<p>Are human minds and language models the same thing? Definitely not. They are as different as birds and airplanes. One is alive and evolved over millions of years; the other is engineered metal. They also have very different motivations for flying. But they both fly, and they both succeed because they exploit the same physics: aerodynamics, the way air flows around a wing.</p>

<div class="no-row-height column-margin column-container"><div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://bruno.nicenboim.me/posts/posts/2026-05-07-birds-and-planes/birds-and-planes-chatgpt.png" class="img-fluid figure-img"></p>
<figcaption>Image generated with ChatGPT (OpenAI).</figcaption>
</figure>
</div></div><p>Studying airplanes can teach us a lot about the <em>medium</em> birds fly through. Not about feathers or muscles, but about what makes flight possible in the first place.</p>
<p>Something similar is going on with language models and human minds. They’re built completely differently, but they both navigate the same thing: <strong>language</strong>. By studying what a model finds predictable or surprising, or how it encodes words, we learn about regularities in the language: what tends to appear where, what’s common, what’s unusual. Those are the same regularities that shape how our brains process words.</p>
<p>How far this analogy stretches is genuinely controversial. Some researchers argue that language models are so different from brains that comparing them is misleading. Others think the similarities go deeper than we’d expect. Nobody has settled this, and figuring it out is an intriguing open question in the field right now.<sup>1</sup></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="cb1" style="background: #f1f3f5;"><pre class="sourceCode bibtex code-with-copy"><code class="sourceCode bibtex"><span id="cb1-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;">nicenboim2026ofbirdsandplanes</span>,</span>
<span id="cb1-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">author</span> = {Nicenboim, Bruno},</span>
<span id="cb1-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">title</span> = {Of birds and planes},</span>
<span id="cb1-4">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">year</span> = {2026},</span>
<span id="cb1-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">month</span> = {May},</span>
<span id="cb1-6">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">url</span> = {https://bruno.nicenboim.me/posts/posts/2026-05-07-of-birds-and-planes/},</span>
<span id="cb1-7">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">doi</span> = {10.5281/zenodo.20084629}</span>
<span id="cb1-8">}</span></code></pre></div></div>
<p><strong>APA:</strong></p>
<p>Nicenboim, B. (2026, May 07). <em>Of birds and planes</em>. https://doi.org/10.5281/zenodo.20084629</p>
</div>
</div>
</div>
<div id="refs">

</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>For a longer treatment of this analogy, see Grace Lindsay’s <em><a href="https://aeon.co/ideas/planes-dont-flap-their-wings-does-ai-work-like-a-brain">Planes don’t flap their wings: does AI work like a brain?</a></em> on Aeon.↩︎</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>SciCom</category>
  <category>LLM</category>
  <category>psycholinguistics</category>
  <category>computational</category>
  <guid>https://bruno.nicenboim.me/posts/posts/2026-05-07-birds-and-planes/</guid>
  <pubDate>Wed, 06 May 2026 22:00:00 GMT</pubDate>
</item>
<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.4 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.2      
[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.4 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.2       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>
