<?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>Ven Popov</title>
<link>https://venpopov.com/posts/</link>
<atom:link href="https://venpopov.com/posts/index.xml" rel="self" type="application/rss+xml"/>
<description>Ven Popov is a senior scientist in computational modeling at the Department of Psychology, University of Zurich.</description>
<image>
<url>https://venpopov.com/images/venpopov.png</url>
<title>Ven Popov</title>
<link>https://venpopov.com/posts/</link>
<height>144</height>
<width>144</width>
</image>
<generator>quarto-1.8.27</generator>
<lastBuildDate>Fri, 20 Feb 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Are Cognitive Representations Propositional, Analog, or Something Else?</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/</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">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>This post was motivated by a question from a student after my introductory cognitive psychology lecture. I wrote it as a short, self-contained explainer and then realized it would also work well as a blog post.</p>
</div>
</div>
<p>One of the oldest and most persistent questions in cognitive science is deceptively simple:</p>
<p><strong>What form do mental representations take?</strong></p>
<p>Are thoughts stored as symbolic propositions, like sentences in a mental language? Are they analog, resembling the structure of perception and action? Or are they something else entirely, patterns that do not neatly fit either category?</p>
<p>This question sits at the heart of debates between symbolic theories of cognition, production systems, and connectionist (neural network) models.</p>
<p>In this post, we will look at the <em>same basic cognitive problems</em> through three different lenses. Each lens makes different assumptions about what counts as a representation and how cognition operates on it. The goal is not to declare a winner, but to clarify what each approach is actually committing to.</p>
<p>To do that, it helps to step back, look briefly at some history, and ground the discussion in a few concrete toy examples.</p>
<section id="a-motivating-toy-problem-deciding-whether-two-things-are-the-same" class="level2">
<h2 class="anchored" data-anchor-id="a-motivating-toy-problem-deciding-whether-two-things-are-the-same">A motivating toy problem: deciding whether two things are the same</h2>
<p>Consider a very simple task: you see two shapes on a screen and must decide whether they are <em>the same</em> or <em>different</em>.</p>
<p>At first glance, this looks trivial. But it turns out to be an excellent test case for thinking about representations.</p>
<ul>
<li>A <strong>symbolic model</strong> might represent the shapes as discrete descriptions (e.g., <code>shape = square</code>, <code>color = red</code>) and apply an explicit rule: <em>IF description(A) = description(B), THEN respond SAME</em>.</li>
<li>An <strong>analog model</strong> might represent each shape as a point in a continuous feature space (size, orientation, curvature) and compute a distance between them.</li>
<li>A <strong>connectionist model</strong> might take raw pixel inputs, learn internal representations through exposure, and produce a SAME or DIFFERENT response without ever explicitly representing the rule “compare features.”</li>
</ul>
<p>All three approaches can solve the task, but they do so by making very different assumptions about what is represented and how.</p>
</section>
<section id="the-classical-view-cognition-as-symbolic-computation" class="level2">
<h2 class="anchored" data-anchor-id="the-classical-view-cognition-as-symbolic-computation">The classical view: cognition as symbolic computation</h2>
<p>Early cognitive science, emerging in the 1950s and 1960s, was strongly shaped by developments in logic, computer science, and artificial intelligence. Influential figures such as Allen Newell and Herbert A. Simon proposed that thinking is best understood as <strong>symbol manipulation</strong>.</p>
<p>In this view:</p>
<ul>
<li>Mental representations are <strong>explicit symbols</strong> with internal structure.</li>
<li>These symbols express <strong>propositional content</strong> (e.g., <em>IF goal is X AND condition Y holds, THEN do Z</em>).</li>
<li>Cognition consists of <strong>rule-based operations</strong> over these symbols.</li>
</ul>
<p>Production systems, formal models built from rules that fire when their conditions are met, became a central formalism for this approach. Importantly, these theories were not meant as loose metaphors. They were intended as literal computational accounts of mental processes.</p>
<section id="a-concrete-example-mental-arithmetic" class="level3">
<h3 class="anchored" data-anchor-id="a-concrete-example-mental-arithmetic">A concrete example: mental arithmetic</h3>
<p>Take mental arithmetic, such as calculating <code>23 + 48</code>.</p>
<p>A symbolic model assumes that numbers are represented as structured symbols (digits, place values) and that cognition consists of applying learned rules (carry the one, add digits, etc.). The process is discrete, stepwise, and explicitly rule governed. There is no sense in which the representation of <code>23</code> is <em>closer</em> to <code>24</code> than to <code>99</code>. Those are simply different symbols (although you can include an explicit algorithm in the model to compute a distance based on mathematical rules, and then use that information in processing; but that is simply another set of symbolic rules).</p>
<p>In their <em>purest</em> form, symbolic theories do <strong>not</strong> assume analog or continuous representations. Symbols are discrete, structured, and combinatorial. Logical reasoning, formal problem solving, and language were the paradigmatic examples where this framework seemed especially powerful.</p>
</section>
</section>
<section id="hybrids-and-pragmatism-symbolic-systems-in-practice" class="level2">
<h2 class="anchored" data-anchor-id="hybrids-and-pragmatism-symbolic-systems-in-practice">Hybrids and pragmatism: symbolic systems in practice</h2>
<p>As cognitive modeling matured, it became clear that purely symbolic systems struggled with issues like learning, noise, timing, and graded behavior. Human cognition is slow, error prone, and sensitive to frequency and recency, properties that are awkward to capture with all or none rules.</p>
<p>In response, many symbolic architectures incorporated continuous elements.</p>
<p>A prominent example is ACT-R, which retains symbolic chunks and production rules but augments them with:</p>
<ul>
<li>activation levels,</li>
<li>continuous decay and learning functions,</li>
<li>noise and utility parameters.</li>
</ul>
<section id="a-concrete-example-memory-retrieval" class="level3">
<h3 class="anchored" data-anchor-id="a-concrete-example-memory-retrieval">A concrete example: memory retrieval</h3>
<p>In ACT-R, whether you successfully recall a fact (e.g., <em>Paris is the capital of France</em>) depends not only on whether the symbol exists, but also on its <strong>activation</strong>, which reflects how often and how recently it has been used. Retrieval is probabilistic and graded, even though the retrieved item itself is symbolic.</p>
<p>Crucially, these quantities <strong>modulate access to symbols</strong> rather than replacing them. ACT-R is still fundamentally propositional, but it acknowledges that cognition is not cleanly binary or deterministic.</p>
<p>This illustrates an important point: even within symbolic traditions, researchers have long recognized the need for graded, quantitative mechanisms. The real disagreement is not about whether numbers exist in the system, but about <strong>what does the representing</strong>.</p>
<blockquote class="blockquote">
<p><strong>Common confusions to avoid</strong></p>
<ul>
<li><em>Distributed</em> does <strong>not</strong> mean vague or unstructured.</li>
<li><em>Continuous</em> does <strong>not</strong> automatically mean analog.</li>
<li><em>Symbolic</em> does <strong>not</strong> mean rigid, static, or unrealistic.</li>
</ul>
<p>Much confusion in this area comes from sliding between these ideas without noticing.</p>
</blockquote>
</section>
</section>
<section id="the-connectionist-alternative-representations-as-patterns" class="level2">
<h2 class="anchored" data-anchor-id="the-connectionist-alternative-representations-as-patterns">The connectionist alternative: representations as patterns</h2>
<section id="a-minimal-neural-network-just-for-context" class="level3">
<h3 class="anchored" data-anchor-id="a-minimal-neural-network-just-for-context">A minimal neural network, just for context</h3>
<p>Before going further, it helps to have a very simple picture in mind of what people mean by a <em>neural network</em> in cognitive modeling.</p>
<p>At its most basic, a neural network consists of simple units, artificial neurons, which aim to be a simplified version of real neurons. These units receive activation from other units. Each connection has a <strong>weight</strong> that determines how strongly activity is passed along. Units combine their inputs, apply a simple transformation (often a nonlinear one), and pass the result forward. Learning consists of gradually adjusting the weights so that the network produces better outputs for the task it is trained on.</p>
<p><img src="https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/artificial-neuron.svg" class="dark-invert img-fluid"></p>
<p>These simple units can be organized in several layers, often labeled as:</p>
<ul>
<li><strong>Input units</strong>, which receive information from the environment (for example, stimulus features or pixel values).</li>
<li><strong>Hidden units</strong>, which transform that information through weighted connections.</li>
<li><strong>Output units</strong>, which produce a response (for example, a decision or classification).</li>
</ul>
<p>You can think of this schematically as:</p>
<blockquote class="blockquote">
<p>inputs → hidden units → outputs</p>
</blockquote>
<p><img src="https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/nn-layers.svg" class="dark-invert img-fluid"></p>
<p>Nothing in this basic setup specifies symbols, rules, or explicit features. Those, if they appear at all, arise from how the network is trained and what problem it is asked to solve.</p>
<p>With that minimal picture in mind, we can return to the representational questions.</p>
<p>Connectionist models, often called neural networks, approach the problem from a very different angle. Rather than assuming a particular representational format, they specify:</p>
<ul>
<li>a set of simple processing units,</li>
<li>weighted connections between them,</li>
<li>a learning rule,</li>
<li>and a task or environment.</li>
</ul>
<p>What they <em>do not</em> specify in advance is the representational vocabulary.</p>
<p>Instead, representations <strong>emerge</strong> through learning as patterns of activation across units. These patterns are typically:</p>
<ul>
<li>distributed (many units participate),</li>
<li>graded (activation varies continuously),</li>
<li>and context sensitive.</li>
</ul>
</section>
<section id="a-concrete-example-category-learning" class="level3">
<h3 class="anchored" data-anchor-id="a-concrete-example-category-learning">A concrete example: category learning</h3>
<p>Imagine training a network to categorize animals as <em>birds</em> or <em>not birds</em> based on features such as wings, beaks, feathers, and flight.</p>
<p>Early in training, the network may respond inconsistently. Over time, it develops internal activation patterns that reliably separate birds from non birds. Importantly, there may be no single unit that represents “bird.” Instead, <em>birdness</em> is encoded as a pattern across many units.</p>
<p>From the connectionist perspective, asking whether representations are “propositional” or “analog” is often the wrong question. The more relevant question is: <em>What patterns are useful for solving the task, given the constraints of the system and its learning history?</em></p>
</section>
</section>
<section id="where-is-knowledge-stored-weights-or-activations" class="level2">
<h2 class="anchored" data-anchor-id="where-is-knowledge-stored-weights-or-activations">Where is knowledge stored: weights or activations?</h2>
<p>A common confusion concerns where “knowledge” resides in a neural network. The answer is: <strong>in both weights and activations, but in different ways</strong>.</p>
<ul>
<li><strong>Weights</strong> encode <strong>persistent</strong> knowledge. They determine the space of possible representations and transformations the network can produce. One helpful way to think about weights is as storing <em>dispositions</em> or <em>potentials</em>, what the system is capable of representing.</li>
<li><strong>Activations</strong> encode <strong>momentary representations</strong>. At any given moment, the current pattern of activity across units reflects what the system is representing or processing right now.</li>
</ul>
<p>An analogy: weights define the grammar of a language; activations are the sentences currently being spoken.</p>
<p>So knowledge is not “only in the weights,” nor are activations meaningless without them. Representation arises from their interaction.</p>
</section>
<section id="what-do-different-layers-represent" class="level2">
<h2 class="anchored" data-anchor-id="what-do-different-layers-represent">What do different layers represent?</h2>
<p>Although details vary across architectures, it is useful to distinguish roles typically played by different layers:</p>
<ul>
<li><strong>Input units</strong> represent task relevant information from the environment. These might correspond to sensory features, stimulus dimensions, or structured encodings supplied by the modeler.</li>
<li><strong>Hidden units</strong> develop internal representations shaped by learning. These are often the most theoretically interesting part of the network. They may reflect abstract dimensions, latent variables, or compressed structure in the task.</li>
<li><strong>Output units</strong> represent the system’s response, choices, classifications, actions, predictions.</li>
</ul>
<section id="a-concrete-example-perception-to-action" class="level3">
<h3 class="anchored" data-anchor-id="a-concrete-example-perception-to-action">A concrete example: perception to action</h3>
<p>In a simple vision to action network, input units might encode pixel intensities, hidden units might come to represent edges or object parts, and output units might encode motor responses such as <em>grasp</em> or <em>avoid</em>. None of these representations is explicitly symbolic, but they can still support behavior that looks rule governed.</p>
<p>None of these layers is intrinsically symbolic or analog. A network trained on logical inference may develop symbol like internal states; one trained on perception may develop smooth, metric representations. The architecture does not dictate the representational format. The task does.</p>
</section>
</section>
<section id="a-crucial-unifying-insight" class="level2">
<h2 class="anchored" data-anchor-id="a-crucial-unifying-insight">A crucial unifying insight</h2>
<p>Modern neural networks are <strong>universal function approximators</strong>. In principle, they can implement symbolic computations just as well as continuous mappings.</p>
<p>What distinguishes connectionist models is not what they <em>can</em> compute, but <strong>how the computations are specified</strong>:</p>
<ul>
<li><strong>Symbolic models</strong> rely on <strong>hand crafted representations and rules</strong> supplied by the theorist.</li>
<li><strong>Connectionist models</strong> rely on <strong>learning</strong>, allowing representations and transformations to be discovered rather than prescribed.</li>
</ul>
<p>This shifts the theoretical focus. Instead of asking <em>What is the right representational format?</em>, cognitive science increasingly asks:</p>
<blockquote class="blockquote">
<p>How do structured representations arise from learning, constraints, and interaction with the environment?</p>
</blockquote>
<p>That question, rather than a strict symbolic vs.&nbsp;analog dichotomy, captures much of what is at stake in contemporary theories of cognition.</p>
</section>
<section id="why-this-distinction-matters" class="level2">
<h2 class="anchored" data-anchor-id="why-this-distinction-matters">Why this distinction matters</h2>
<p>At an introductory level, it might seem like this debate is mostly philosophical. But it has very practical consequences.</p>
<ul>
<li>It affects <strong>how we design experiments</strong>. Are we testing explicit rules, similarity spaces, or learned representations?</li>
<li>It affects <strong>how we interpret behavior</strong>. Is an error a failure of a rule, a noisy comparison, or a learned bias?</li>
<li>It affects <strong>how we think about artificial intelligence</strong>. Modern AI systems succeed largely by learning representations rather than being given them, which brings connectionist ideas back to center stage.</li>
</ul>
<p>Most importantly, it affects how we think about explanation in psychology. A good cognitive theory does not just reproduce behavior. It tells us <em>what kind of internal structure</em> could plausibly give rise to that behavior.</p>
<p>Understanding the representational commitments of different models is the first step toward making sense of that structure.</p>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2026,
  author = {Popov, Vencislav},
  title = {Are {Cognitive} {Representations} {Propositional,} {Analog,}
    or {Something} {Else?}},
  date = {2026-02-20},
  url = {https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2026" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2026. <span>“Are Cognitive Representations
Propositional, Analog, or Something Else?”</span> February 20, 2026. <a href="https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/">https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/</a>.
</div></div></section></div> ]]></description>
  <category>cognitive science</category>
  <category>explainer</category>
  <category>2026</category>
  <guid>https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/</guid>
  <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2026/are-cognitive-representations-propositional-analog-or-something-else/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Simple rules, hard problems and the emergence of meaning in mathematics</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2026/simple-rules-hard-problems/</link>
  <description><![CDATA[ 






<p>I watched <a href="https://youtu.be/8hcSMV6DQ9A?si=mII8aWJjCsS2iM2T">another video</a> about the <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">Collatz conjecture</a> last week. Thanks, algorithm. I don’t even know why I do this to myself. It reliably produces the same mixture of fascination and irritation: the rules are tiny, the behavior is bizarre, and my mind insists that the whole thing should be graspable in some clean way, and then fails to grasp it.</p>
<p>This is not a crank post. It’s barely even about Collatz, which was merely a trigger. And yet, as in many number theory questions, whatever the answer is, there surely is one. The system is fully specified. Either every starting value eventually reaches 1, or some don’t. Either there is a nontrivial cycle, or there isn’t. Whether we can find a proof is a separate question.</p>
<p>Of course, Kurt Gödel is always waiting like an ominous gargoyle to tell us that some things are undecidable (relative to some standard axiom systems). But even if that were the case for Collatz, there is still a fact of the matter: within whatever framework you pick, the statement is either provable, disprovable, or independent. In any case, it’s not like the conjecture floats in some metaphysical fog. It is determined by properties of the positive integers, together with the transition rule.</p>
<p>What interests me, though, is less the conjecture itself than what it does to my head. It’s a very peculiar experience: the sense that something is so “obviously constrained” and yet infuriatingly inacessible and hard. I don’t mean “hard” as in “requires 200 pages of technical machinery.” I mean hard as in: I repeatedly form ideas that feel coherent and so obviously about to crack the problem once and for all; and then they of course evaporate or lead to the same dead ends.</p>
<p>And that experience, is what pushed me into a broader line of thought: what are mathematical objects, what does it mean for them to have properties, and what does it feel like (cognitively) to search for structure in a system when you don’t yet have the right concepts to see it? What does it mean to understand something that by some accounts doesn’t really exist?</p>
<p>Whatever the ontological status of mathematical objects ultimately is, Platonic, fictionalist, structuralist, model-theoretic, “just” patterns in computation, we can say a few things with relative certainty. One of them is that mathematical objects and structures are essentially relational. They do not have meaningful properties in isolation. Their “properties” are about the role they play inside a system: how they behave under defined operations, and what they entail when they interact with other objects.</p>
<section id="meaning-without-pointing" class="level2">
<h2 class="anchored" data-anchor-id="meaning-without-pointing">Meaning without pointing</h2>
<blockquote class="blockquote">
<p>A mathematical object is what it does inside a system of relations and operations</p>
</blockquote>
<p>Human language lets us form grammatically clean statements that carry no content. I can say “X is roupy” just as easily as I can say “2 is even.” But unless “roupy” connects to something, I haven’t said anything.</p>
<p>If I define “roupy” as “paternally upy,” I haven’t fixed the problem. I’ve just stepped one move deeper into a dictionary loop. The words fit together, but nothing constrains anything.</p>
<p>With everyday words, we eventually escape this loop by pointing to the world. Some terms get grounded in perception and action. Even if definitions remain fuzzy, there’s friction: you can be wrong about what “red” applies to, and the world pushes back.</p>
<p>Mathematics doesn’t get this kind of grounding, at least not in the same direct way. The mathematician isn’t allowed to point at “two-ness” floating in the air. The only thing left is this: a property of X becomes meaningful only insofar as it connects X to other objects through formal operations and relations. In other words: if the definition doesn’t place X in a structure, it’s not doing anything.</p>
<p>The symbols 1, 2, 3, … only mean something, i.e., have properties, once we define operations and rules: Peano’s axioms, or Church numerals, or a set-theoretic construction. Only within such a system does the statement “6 is divisible by 2” have content, because “divisible by 2” can be unpacked and checked under the representation you’re using. It defines a relation between objects of the same class (6 and 2) and the outcome of an operation involving them (like “remainder 0”).</p>
<p>The point is not that “definitions matter.” That’s trivial. The point is that mathematical meaning is operational and relational. It lives in what the symbol lets you do and what it forces to be true.</p>
</section>
<section id="a-seemingly-silly-object-called-a-bazz" class="level2">
<h2 class="anchored" data-anchor-id="a-seemingly-silly-object-called-a-bazz">A seemingly silly object called a bazz</h2>
<p>To make this feel less like a slogan, let’s take a deliberately silly example.</p>
<p>Imagine an object called a <em>bazz</em>. It interacts with <em>pins</em>. There are three operations:</p>
<ul>
<li><strong>kuua</strong> takes a pin and a bazz and returns a bazz</li>
<li><strong>ouz</strong> takes a bazz and returns a bazz plus a pin</li>
<li><strong>heff</strong> takes a bazz and returns a pin</li>
</ul>
<p>Also:</p>
<ul>
<li>you can always <strong>fish</strong> a bazz from the void</li>
<li>you can ask a bazz whether it is a <strong>jazz</strong>, and it must answer by nodding or shaking</li>
</ul>
<p>And bazzes satisfy these rules:</p>
<ol type="1">
<li><p>If you kuua a bazz with a pin and then immediately ouz it, you get the original bazz and pin back.</p></li>
<li><p>If you heff a bazz after kuua-ing it with a pin, you get back that pin – and if you then ouz the bazz, you mysteriously get another free copy of the same pin and the original bazz.</p></li>
<li><p>A freshly fished void bazz always nods when asked if it is a jazz. After kuaa-ing it with a pin it shakes, and once it shakes, it keeps shaking.</p></li>
</ol>
<p>It sounds like nonsense (it is), but it is also just a <a href="https://en.wikipedia.org/wiki/Abstract_data_type">stack, an Abstract Data Type</a>, in disguise: push(), pop(), peek(), create(), isEmpty():</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>Bazz operation</th>
<th>Stack operation</th>
<th>Effect</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>kuua</strong>(pin, bazz)</td>
<td>push(item, stack)</td>
<td>Add a pin to the bazz</td>
</tr>
<tr class="even">
<td><strong>ouz</strong>(bazz)</td>
<td>pop(stack)</td>
<td>Remove and return the top pin</td>
</tr>
<tr class="odd">
<td><strong>heff</strong>(bazz)</td>
<td>peek(stack)</td>
<td>View the top pin without removing it</td>
</tr>
<tr class="even">
<td><strong>fish</strong>()</td>
<td>create()</td>
<td>Make a new empty bazz/stack</td>
</tr>
<tr class="odd">
<td>is <strong>jazz</strong>?</td>
<td>isEmpty()</td>
<td>Check if the bazz/stack has no pins</td>
</tr>
</tbody>
</table>
<p>While the third column of this table clearly articulates the <em>intended</em> effect of the operation, it is nothing more than a “design specification”: what we want it to do and mean. It is the equational rules that endow the system with these behaviors:</p>
<ol type="1">
<li><p><code>ouz(kuua(p, b)) = (b, p)</code> — If you push a pin onto a bazz and immediately pop, you get back the original bazz and the pin</p></li>
<li><p><code>heff(kuua(p, b)) = p</code> — If you peek at a bazz after pushing a pin, you see that pin</p></li>
<li><p><code>jazz(fish()) = true</code> — A freshly created bazz is empty</p></li>
<li><p><code>jazz(kuua(p, b)) = false</code> — Any bazz with at least one pin is not empty</p></li>
</ol>
<p>That’s it - the system is fully specified, regardless of the symbols we use, and the helpful descriptions we attach them and to the rules that govern them. Notice that nothing above ever specifies anything about the internal mechanism of any operation; what is more, it doesn’t even specify the outcome of individual operations, except for what <em>type</em> of object it returns. It only specifies how the behavior of some rules affects others.</p>
<p>What I like about this example is how it separates three things that we often blur together:</p>
<ol type="1">
<li><p><strong>The interface:</strong> the operations you’re allowed to do.</p></li>
<li><p><strong>The axioms:</strong> the laws those operations must satisfy.</p></li>
<li><p><strong>The interpretation:</strong> the story we tell ourselves (“it stores things,” “it has an inside,” “it grows,” etc.).</p></li>
</ol>
<p>The axioms never say “last-in-first-out.”, “inside.”, nor “storage.” And yet if you take the rules seriously, you can prove an emergent structural truth, a consequence that wasn’t spelled out:</p>
<blockquote class="blockquote">
<p>If you kuua ten pins in a row and then repeatedly ouz, you recover the pins in reverse order.</p>
</blockquote>
<p>At the same time, some questions you’re tempted to ask are not merely unanswered; they’re not even well-formed in the language of the system. For example: “what happens to the pins inside the bazz?” There is no “inside” predicate. That question sneaks in an external metaphor (containers, interiors) that the abstract description never granted you.</p>
<p>This is a genuine constraint on cognition: we naturally reach for metaphors that feel meaningful, and those metaphors can be helpful, but they can also generate pseudo-questions that the formal structure does not support. And I think this distinction between what the system actually says, what it implies, and what our metaphors tempt us to ask is one of the most useful lenses I’ve found for thinking about mathematics.</p>
</section>
<section id="the-integers-as-a-deceptively-thin-interface" class="level2">
<h2 class="anchored" data-anchor-id="the-integers-as-a-deceptively-thin-interface">The integers as a deceptively “thin” interface</h2>
<p>Now we can return to the question that started all of this.</p>
<p>If mathematical objects are defined by their role in a web of relations, and if that web contains emergent structure that may be deeply non-obvious, then we can ask:</p>
<p>Is there some deep structural relation among the integers that we have simply not yet uncovered? A structure that plays little to no role in most of our everyday experience with numbers, a structure we cannot currently “see,” but that must be there – because the truth of these edge-case statements depends on it.</p>
<p>Think about how many of the hardest problems in mathematics are absurdly simple statements about the most elementary object imaginable: the counting numbers.</p>
<p>One apple, two apples, three apples. Each of my three kids can have one. If the dog steals one, I either have to cut them or only give apples to two kids. Or my husband and I can enjoy them ourselves. This is the mental world in which the natural numbers first appear: a thin, practical sequence. Knots on a rope.</p>
<p>Then you learn that primes are numbers divisible only by themselves and 1. Simple definition. A few steps from axioms to an algorithm. Not useful for most of human history; then suddenly the foundation of modern cryptography. And Euclid proves, 2300 years ago, that you’ll never find the biggest prime. There will always be another. Infinitely many.</p>
<p>But then you hit questions that look just as simple and fall off a cliff:</p>
<ul>
<li>Are there infinitely many twin primes? Unknown.</li>
<li>Is every even number the sum of two primes? Unknown.</li>
<li>Does the rule “if (x) is even, divide by 2; otherwise multiply by 3 and add 1” always reach 1? Unknown.</li>
</ul>
<p>Before you take these questions seriously, it feels like there is almost no structure to whole numbers at all. They feel like the least interesting object: a mere successor relation repeated forever. And yet the true structure runs deep, as deep as mathematical structures go, and contains relations that feel completely disconnected from the “one apple, two apples” story.</p>
<p>Quadratic reciprocity. p-adics. Modular forms. The list goes on.</p>
<p>And this is the part that always hits me with the same weird emotion: these consequences were “there” long before we had names for them, as certainly in 100,000 BC when the first humans were learning to count as it is today in number theory textbooks. We just didn’t know it. It still feels like an utter miracle that such complexity was hiding there as an inevitable consequence of logic and formal rules.</p>
<p>We know for sure that even extremely sophisticated facts about integers (like Fermat’s Last Theorem) ultimately derive from whatever basic axioms you accept. They have to. There is no other source of truth available. Yet finding that path took centuries and some of the best minds in history.</p>
<p>It seems that so much of the complexity arises because the interaction between addition (linear structure) and multiplication (multiplicative/prime structure) is surprisingly “thick.” Most of the deep mysteries in number theory, live exactly in the friction between these two operations. And still, unless you really think about it, on the surface there appears to be nothing particularly interesting about this interaction. Multiplication distributes over addition. They feel separable, almost independent, and yet that feeling is completely misleading.</p>
</section>
<section id="what-collatz-does-to-my-mind" class="level2">
<h2 class="anchored" data-anchor-id="what-collatz-does-to-my-mind">What Collatz does to my mind</h2>
<p>This is where Collatz comes back in, not as the main topic, but as a trigger.</p>
<p>The thing I find psychologically striking is that the Collatz rule has a certain interface-thinness to it. It’s made of the simplest arithmetic operations imaginable (parity, division by 2, multiplication, addition). And yet whatever global behavior it has is not something I can see directly from those operations in the way I can see, say, why a stack is last-in-first-out once I have the right lens.</p>
<p>When I tried to think seriously about it last year, the recurring experience was this:</p>
<ul>
<li>I’d have an idea that felt like it captured the behavior</li>
<li>it would feel obviously promising for a few minutes</li>
<li>and then, as soon as I tried to formalize it, the idea would dissolve into fog</li>
</ul>
<p>The dissolving is the interesting part. It’s like my mind keeps proposing consequences or candidate invariants that are almost_meaningful. And then I discover they’re not stable under the operations, or not expressible in the right language, or they fail on some annoying corner case that I can’t rule out. Or most commonly, just lead me to a gaping hole between the properties of numbers I think are relevant to the problem, and how the numbers acted in this system.</p>
<p>This feels, to me, like a lesson about cognition as much as about mathematics. And it also makes me appreciate what fundamental mathematical progress often is: not “more cleverness,” but deep and novel structural insight - discovering the right language, the right predicates, invariants, decompositions, and abstractions in which the problem becomes legible.</p>
</section>
<section id="final-notes" class="level2">
<h2 class="anchored" data-anchor-id="final-notes">Final notes</h2>
<p>I wasted a month of my life on Collatz last year, despite reading all the jokes and warnings. Never again, at least not in that mode.</p>
<p>But I don’t regret the detour entirely, because it pushed me to think about the gap between a formal specification and the space of meanings we think we’re entitled to attach to it.</p>
<p>The bazz story is a toy version of this. A few equational laws define an interface. From that interface, real structure follows by logical consequence. At the same time, many of the questions that feel most natural (“what’s inside?” “where do the pins go?”) are not deep mysteries, but category errors.</p>
<p>Something similar happens, I think, whenever we look at an austere set of rules like arithmetic, a dynamical map, a formal grammar, rewriting rules, celluar automata, and then try to reason about it with mental imagery that isn’t actually tied to the operations. We drift toward stories, we drift toward pictures, we drift toward “it must behave like…” And sometimes those stories guide discovery. But sometimes they generate pseudo-problems and pseudo-solutions: things that feel meaningful only because we imported them.</p>
<p>So for me the interesting thing about Collatz is not the conjecture itself. It’s what it highlights about mathematical meaning. If meaning is relational, constituted by inferential role inside a structure, then “understanding” is about having the right conceptual vocabulary. The vocabulary in which statements become connected to consequences you can control.</p>
<p>And that vocabulary is not guaranteed to sit near the surface of the original definition. Sometimes it’s ridiculously far away. The integers are the canonical example: successor and induction look like a thin interface, and yet the emergent structure is vast, deep and even today poorly understood. The fact that we needed centuries to discover much of that structure for one of the most fundamental everyday concepts is evidence that the inferential consequences of even a small axiomatic core can be profoundly non-obvious.</p>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2026,
  author = {Popov, Vencislav},
  title = {Simple Rules, Hard Problems and the Emergence of Meaning in
    Mathematics},
  date = {2026-02-11},
  url = {https://venpopov.com/posts/2026/simple-rules-hard-problems/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2026" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2026. <span>“Simple Rules, Hard Problems and the
Emergence of Meaning in Mathematics.”</span> February 11, 2026. <a href="https://venpopov.com/posts/2026/simple-rules-hard-problems/">https://venpopov.com/posts/2026/simple-rules-hard-problems/</a>.
</div></div></section></div> ]]></description>
  <category>math</category>
  <category>philosophy</category>
  <category>emergence</category>
  <category>essay</category>
  <category>meaning</category>
  <category>structure</category>
  <category>2026</category>
  <guid>https://venpopov.com/posts/2026/simple-rules-hard-problems/</guid>
  <pubDate>Wed, 11 Feb 2026 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2026/simple-rules-hard-problems/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Package management and reproducibility are a nightmare</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2026/package-management-and-reproducibility-are-a-nightmare/</link>
  <description><![CDATA[ 






<p>I try my best. <a href="https://venpopov.com/posts/2024/reproducibility-is-hard/">I really do.</a></p>
<p>I use <code>renv</code> for package management. My blog is built with Quarto. I use caching and freezing so posts don’t get recomputed every time. I hadn’t touched the blog in a few months. Today, I just wanted to fix a couple of typos on my publications page. Two minutes of work. Run <code>quarto render</code>. Done.</p>
<p>Except, of course, not done.</p>
<p>I open the project and <code>renv</code> tells me the project is out of sync. Cannot render because package Rmarkdown is missing. Fine. I run <code>renv::restore()</code>, whose entire purpose is to restore the package environment to its previous state.</p>
<p>It immediately fails while trying to install <strong>Matrix</strong> from source.</p>
<p>I’ve never had that problem before. A bit of googling reveals that installing Matrix requires a Fortran compiler (???). The official instructions are here: <a href="https://mac.r-project.org/tools/" class="uri">https://mac.r-project.org/tools/</a>. I do not remember ever needing this in the past. I try to force <code>renv</code> to use binaries instead of building from source. That also fails.</p>
<p>Alright. Fine. I install a Fortran compiler. Matrix installs successfully.</p>
<p>One minute later, <code>renv::restore()</code> fails again—this time because it can’t install <strong>stringi</strong>. The error message is completely opaque. I paste it into an AI, which helpfully explains:</p>
<blockquote class="blockquote">
<p>“This error occurs because of a conflict between the modern <code>[[noreturn]]</code> attribute in R 4.5 and an older <code>NORET</code> declaration within the <code>stringi</code> package, specifically on ARM64 macOS.”</p>
</blockquote>
<p>Jesus fucking christ.</p>
<p>Yes, I updated R last month from 4.4.2 to 4.5. And now one of the most fundamental string manipulation packages in the entire R ecosystem—on which half the world depends—can’t be installed?</p>
<p>Okay. Fine. Surely I can just install a newer version of <code>stringi</code>. I stop the restore process and run:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb1-1">renv<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;">install</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"stringi"</span>)</span></code></pre></div></div>
<p>That works.</p>
<p>But now I have a problem: if I run <code>renv::restore()</code> again, it will just try to install the <em>older</em> version and fail all over again. So I need to snapshot the current state.</p>
<p>I run <code>renv::snapshot()</code>, and it tells me:</p>
<pre class="plain"><code>The following required packages are not installed:
- ambient
- bmm
- fields
- ggdark
- ggplot2
- here
- httpgd
- kableExtra
- patchwork
- rmarkdown
- tidyr
- viridis
- wesanderson
Packages must first be installed before renv can snapshot them.</code></pre>
<p>Why are they not installed? I’m on the same machine. Same project. I haven’t changed anything.</p>
<p>Oh. Right. I updated R. Since R is a global installation, <code>renv</code> considers this a fresh library and wants everything reinstalled.</p>
<p>Fine. I choose option 2: <em>install the packages, then snapshot</em>.</p>
<p>Everything installs—except <strong>ggdark</strong>.</p>
<pre class="plain"><code>Error: package 'ggdark' is not available</code></pre>
<p>What.</p>
<p>A quick google search leads me to CRAN, which cheerfully explains:</p>
<pre class="plain"><code>Package ‘ggdark’ was removed from the CRAN repository.
Formerly available versions can be obtained from the archive.
Archived on 2025-11-07 as issues were not corrected despite reminders.</code></pre>
<p>Oh for fuck’s sake.</p>
<p>I recognize this exact message. It recently bit me with another ggplot utility package, <strong>gghalves</strong>. ggplot just went through a major version bump to v4, and it broke a bunch of extension packages. If the maintainer doesn’t fix things quickly enough, CRAN just removes the package.</p>
<p>Unfortunately, R’s entire package ecosystem is built on the assumption that every package should work with the <em>latest version of every other package</em>. That assumption collapses completely the moment you care about reproducibility.</p>
<p>The whole point of <code>renv</code> is that I’ve already specified which versions of <code>ggplot2</code> <em>and</em> <code>ggdark</code> this project used. When I chose “install the packages” during snapshotting, I assumed <code>renv</code> would install the versions recorded in the lockfile.</p>
<p>But something clearly didn’t work.</p>
<p>What exactly failed?</p>
<ul>
<li>Did <code>renv</code> try to download the correct version of <code>ggdark</code>, but CRAN refused because it’s archived?</li>
<li>Or did <code>renv</code> ignore the lockfile at that point and try to install the latest version instead?</li>
</ul>
<p>CRAN does link to the archived sources, so in principle the old version exists. But apparently not in a way that <code>renv</code> can use automatically.</p>
<p>I snapshot without installing the missing packages. That technically works, but I’m stuck with an inconsistent environment.</p>
<p>I’m not even <em>using</em> ggplot v4. This isn’t a usage problem. It’s an infrastructure problem: R’s dependency system forces compatibility with the present, and fights you when you try to live in the past.</p>
<p>Fine. I try installing from the archive manually:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">renv<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;">install</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ggdark@0.2.1"</span>)</span></code></pre></div></div>
<p>That works.</p>
<p>I try <code>renv::snapshot()</code> again.</p>
<p>Now it fails on <strong>httpgd</strong>.</p>
<p>Same story. Package removed from CRAN. Archived. The version in my lockfile is <code>httpgd@2.0.2</code>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">renv<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;">install</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"httpgd@2.0.2"</span>)</span></code></pre></div></div>
<p>Works.</p>
<p>Why am I doing this manually? Isn’t this <em>exactly</em> what <code>renv</code> is supposed to manage for me?</p>
<p>At this point I start wondering whether this has something to do with Quarto. <a href="https://github.com/venpopov/venpopov.github.io/blob/main/posts/2025/arto-pollo/index.qmd">Some posts</a> use post-specific packages declared via <code>renv::use("packagename@version")</code>, <a href="https://rstudio.github.io/renv/articles/use.html">as recommended in the documentation</a>. Maybe those dependencies don’t fully participate in the main restore/snapshot logic? Maybe I’ve built myself a beautiful little Rube Goldberg machine of reproducibility.</p>
<p>One last attempt:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1">renv<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;">snapshot</span>()</span></code></pre></div></div>
<p>Finally:</p>
<pre class="plain"><code>renv::status()
No issues found -- the project is in a consistent state.</code></pre>
<p>What time is it?</p>
<p>Two hours have passed. I wanted to fix two typos.</p>
<p>I run <code>quarto render</code>.</p>
<p>Hallelujah—it works.</p>
<p>I need a drink.</p>
<p>Never mind. I forgot I don’t drink.</p>



<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2026,
  author = {Popov, Vencislav},
  title = {Package Management and Reproducibility Are a Nightmare},
  date = {2026-02-03},
  url = {https://venpopov.com/posts/2026/package-management-and-reproducibility-are-a-nightmare/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2026" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2026. <span>“Package Management and Reproducibility
Are a Nightmare.”</span> February 3, 2026. <a href="https://venpopov.com/posts/2026/package-management-and-reproducibility-are-a-nightmare/">https://venpopov.com/posts/2026/package-management-and-reproducibility-are-a-nightmare/</a>.
</div></div></section></div> ]]></description>
  <category>R</category>
  <category>R package</category>
  <category>2026</category>
  <category>package management</category>
  <category>reproducibility</category>
  <category>workflow</category>
  <category>2026</category>
  <guid>https://venpopov.com/posts/2026/package-management-and-reproducibility-are-a-nightmare/</guid>
  <pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2026/package-management-and-reproducibility-are-a-nightmare/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Open Science needs reliable infrastructure</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2025/open-science-needs-reliable-infrastructure/</link>
  <description><![CDATA[ 






<p>In October 2025, a redesign of the Open Science Framework (OSF) led to widespread access failures across the platform. What began as a few broken download links became, in my case, a total disappearance of eight years of DOI-registered work. This is the story of what happened, how it was resolved, and what it reveals about trust and infrastructure in open science.</p>
<section id="widespread-glitches" class="level2">
<h2 class="anchored" data-anchor-id="widespread-glitches">Widespread glitches</h2>
<p>On October 11th, 2025, the Center for Open Science (COS) <a href="https://bsky.app/profile/cos.io/post/3m2x2af4jwc2o">announced</a> that the Open Science Framework — its flagship platform for preprints, data, and code — had received a major redesign. The update promised <a href="https://www.cos.io/blog/now-live-refreshed-osf-interface">“to make managing research projects easier, faster, and more intuitive.”</a></p>
<p>Within days, many discovered that the new redesign ironically came with slower loading times, but more troubling, with a host of bugs and glitches:</p>
<blockquote class="bluesky-embed blockquote" data-bluesky-uri="at://did:plc:rbamsrq5xdc25fdrmiicvi3f/app.bsky.feed.post/3m2yc4yxe5k2r" data-bluesky-cid="bafyreiadcxtyo4zj7qmsdsfhpd65vowglwopqoz6apy4znyfublyayyhhq" data-bluesky-embed-color-mode="system">
<p lang="en">
</p><p>PSA: seems like the update broke the ability to directly download files with a link, like osf.io/XXXX/download this lead to an error in one of my R package vignettes, and presumably will break lots of code.<br><br><a href="https://bsky.app/profile/did:plc:rbamsrq5xdc25fdrmiicvi3f/post/3m2yc4yxe5k2r?ref_src=embed">[image or embed]</a></p>
<p></p>
<p>— Ruben C. Arslan (<a href="https://bsky.app/profile/did:plc:rbamsrq5xdc25fdrmiicvi3f?ref_src=embed"><span class="citation" data-cites="ruben.the100.ci">@ruben.the100.ci</span></a>) <a href="https://bsky.app/profile/did:plc:rbamsrq5xdc25fdrmiicvi3f/post/3m2yc4yxe5k2r?ref_src=embed">October 12, 2025 at 10:34 AM</a></p>
</blockquote>
<blockquote class="bluesky-embed blockquote" data-bluesky-uri="at://did:plc:rbamsrq5xdc25fdrmiicvi3f/app.bsky.feed.post/3m3chsxwxps2m" data-bluesky-cid="bafyreiaeakvrevoo2vfyt26arzvgxs4emvvl7g4du7x5ccjesotetoyehi" data-bluesky-embed-color-mode="system">
<p lang="en">
</p><p>Thanks. I think this is urgent. I suspect it also broke Google Scholar listing PDFs from OSF, at least I get broken links for some, no links for others and generally OSF seems to be massively downranked in the list of sources. Started getting email requests for PDFs again.</p>
<p></p>
<p>— Ruben C. Arslan (<a href="https://bsky.app/profile/did:plc:rbamsrq5xdc25fdrmiicvi3f?ref_src=embed"><span class="citation" data-cites="ruben.the100.ci">@ruben.the100.ci</span></a>) <a href="https://bsky.app/profile/did:plc:rbamsrq5xdc25fdrmiicvi3f/post/3m3chsxwxps2m?ref_src=embed">October 16, 2025 at 11:43 AM</a></p>
</blockquote>
<blockquote class="bluesky-embed blockquote" data-bluesky-uri="at://did:plc:gansgyiuhvaa25asqnjtfj6r/app.bsky.feed.post/3m2wkfr5ick2c" data-bluesky-cid="bafyreielfjskux4rxlzj66hinsoi43ztxabzuzczqd4xr6imw4rtrpjtrq" data-bluesky-embed-color-mode="system">
<p lang="en">
</p><p><span class="citation" data-cites="cos.io">@cos.io</span> Why cant I read in rds files from my repo? I was trying to reproduce my manuscript and now I cant read in rds files from the site. I am going to assume it has to do with the new UI?</p>
<p></p>
<p>— jgeller1phd.bsky.social (<a href="https://bsky.app/profile/did:plc:gansgyiuhvaa25asqnjtfj6r?ref_src=embed"><span class="citation" data-cites="jgeller1phd.bsky.social">@jgeller1phd.bsky.social</span></a>) <a href="https://bsky.app/profile/did:plc:gansgyiuhvaa25asqnjtfj6r/post/3m2wkfr5ick2c?ref_src=embed">October 11, 2025 at 5:57 PM</a></p>
</blockquote>
<blockquote class="bluesky-embed blockquote" data-bluesky-uri="at://did:plc:gwmol5ml44miarkuvymvq6ax/app.bsky.feed.post/3m3sfvwn5ck2x" data-bluesky-cid="bafyreie4inpgiob6rw4h4pfyrrllrrkduoqxv6h7rsu334ncqzx4rfdyzm" data-bluesky-embed-color-mode="system">
<p lang="en">
</p><p>Is anyone else experiencing significant issues (lags, not loading) with OSF (<span class="citation" data-cites="cos.io">@cos.io</span>) since the interface update?</p>
<p></p>
<p>— Charlotte Pennington 🌻 (<a href="https://bsky.app/profile/did:plc:gwmol5ml44miarkuvymvq6ax?ref_src=embed"><span class="citation" data-cites="drcpennington.bsky.social">@drcpennington.bsky.social</span></a>) <a href="https://bsky.app/profile/did:plc:gwmol5ml44miarkuvymvq6ax/post/3m3sfvwn5ck2x?ref_src=embed">October 22, 2025 at 7:51 PM</a></p>
</blockquote>
<blockquote class="bluesky-embed blockquote" data-bluesky-uri="at://did:plc:pi4upwjkevc3thmjn4k3bqic/app.bsky.feed.post/3m3ftbca4aw2q" data-bluesky-cid="bafyreihm7jppz7tlk77p5i7ots4imsbtxfqna5texo2oxkrek4sijqm7cq" data-bluesky-embed-color-mode="system">
<p lang="en">
</p><p>🪲 OSF Update: Ongoing Fixes Following the OSF redesign, some issues have been identified:</p>
<ul>
<li>Some custom registry templates may not showall fields</li>
<li>OSF shows a max. of 10 contributors</li>
<li>Some pages load slowly or seem unresponsive</li>
</ul>
<p>These are on our critical fix list &amp; should be resolved soon. (1/2)</p>
<p></p>
<p>— Center for Open Science (<a href="https://bsky.app/profile/did:plc:pi4upwjkevc3thmjn4k3bqic?ref_src=embed"><span class="citation" data-cites="cos.io">@cos.io</span></a>) <a href="https://bsky.app/profile/did:plc:pi4upwjkevc3thmjn4k3bqic/post/3m3ftbca4aw2q?ref_src=embed">October 17, 2025 at 7:46 PM</a></p>
</blockquote>
<blockquote class="bluesky-embed blockquote" data-bluesky-uri="at://did:plc:rau7a3uk7uaz27xnj53jum7r/app.bsky.feed.post/3m3ssm2ro522o" data-bluesky-cid="bafyreid3goqipiruikkux2ywbcaohch6dllxc3wbzud6fhulzffwyf547m" data-bluesky-embed-color-mode="system">
<p lang="en">
</p><p>Also, - Login screen doesn’t work on Safari - Preregistrations impossible (got many error messages &amp; after I was finally able to submit got an email saying trouble archiving my preregistration so it wasn’t completed)… this means I can’t actually run my study :( - Symbols not rendering (e.g., &amp;, &lt;)</p>
<p></p>
<p>— Cassandra Chapman (<a href="https://bsky.app/profile/did:plc:rau7a3uk7uaz27xnj53jum7r?ref_src=embed"><span class="citation" data-cites="cassandrachapman.bsky.social">@cassandrachapman.bsky.social</span></a>) <a href="https://bsky.app/profile/did:plc:rau7a3uk7uaz27xnj53jum7r/post/3m3ssm2ro522o?ref_src=embed">October 22, 2025 at 11:38 PM</a></p>
</blockquote>
<script async="" src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>
<p>I do not mention complaints about design, as those are subjective. But it was worrisome that a redesign would break existing download link patterns without a redirect - this should not happen to an archival service. To their credit, COS fixed that particular problem quickly, though it should never needed fixing in the first place. The performance issues remain, and the irony that this performance update made performance worse is not lost to anyone. What seemed like transitional friction soon turned into something stranger.</p>
</section>
<section id="the-disappearance" class="level2">
<h2 class="anchored" data-anchor-id="the-disappearance">The Disappearance</h2>
<p>On November 2, I realized that none of my OSF preprints would open. Every single one — roughly twenty projects spanning eight years — returned either a blank page with a red banner reading “Not found” or a JSON message declaring the resource “deleted by the user.”</p>
<p>At first, I assumed it was a global outage consistent with the widespread performance issues mentioned above. But when I double checked, the pattern was unmistakable:</p>
<ul>
<li>Other authors’ preprints worked fine.</li>
<li>The error appeared for every preprint and dataset linked to my name — including one uploaded by a colleague just four days earlier.</li>
<li>Even Google Scholar links that had functioned for years now led to “Resource deleted” errors:</li>
</ul>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode json code-with-copy"><code class="sourceCode json"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"message_short"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Resource deleted"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"message_long"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"User has deleted this content. If this should not have occurred and the issue persists, please report it to &lt;a href=</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">mailto:support@osf.io</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">&gt;support@osf.io&lt;</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\/</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">a&gt;."</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"code"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">410</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"referrer"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">null</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>Most worrisome of all - the “blackout” included files linked in published journal articles and materials for projects currently under &nbsp;peer review.</p>
<p>I reported the issue to OSF support with screenshots and links, expecting acknowledgment that something had gone wrong with the migration:</p>
<blockquote class="blockquote">
<p>&lt;On Nov 2, 2025 at 23:40 +0100, Ven Popov, wrote:&gt;</p>
<p>I am experiencing a serious issue with my preprints on OSF. At first I thought this was a global OSF issue, but it seems like it is affecting all and only my preprints, which makes me very concerned.</p>
<p>When I try to access any of the preprints on which I am a co-author, I get an error.</p>
<p>If I try to access the preprint via and OSF url, I get a blank page with a red banner “Not found” (see attached screenshot). Examples:</p>
<ul>
<li><a href="https://osf.io/yr9xb" class="uri">https://osf.io/yr9xb</a></li>
<li><a href="https://osf.io/gwd4s" class="uri">https://osf.io/gwd4s</a></li>
<li><a href="https://osf.io/hmu9r" class="uri">https://osf.io/hmu9r</a></li>
<li><a href="https://osf.io/dsx6y" class="uri">https://osf.io/dsx6y</a></li>
<li><a href="https://osf.io/vj8pn" class="uri">https://osf.io/vj8pn</a> (this one was uploaded only 4 days ago by a colleague!)</li>
</ul>
<p>If I try to access the PDF links on Google Scholar (e.g.&nbsp;this link), I get the following JSON error message:</p>
<p>[…]</p>
<p>I tested this with all ~20 preprints listed on <a href="https://osf.io/et763/">my profile</a> and I get the error on all of them. When I try to open any other preprint that is not authored by me, I can open it just fine. I’d appreciate any assistance with resolving this issue.</p>
</blockquote>
<p>Instead, I was asked to fill out a form for “accounts flagged as spam.”</p>
<blockquote class="blockquote">
<p>&lt; On Mon, Nov 3, 2025 at 7:30 AM EST, Blaine Support <a href="mailto:support@osf.io" class="email">support@osf.io</a> wrote:&gt;</p>
<p>If you received this email, it means that we need your assistance in investigating your account. Please help us by providing more information through this form: <a href="https://forms.gle/jc5D1ENdkL5WaY4F8">Account Disabled or Falsely Flagged as SPAM reporting</a>. If we don’t receive your form submission, we won’t be able to investigate the issue. Your input is invaluable as we work to improve our spam detection systems.</p>
</blockquote>
</section>
<section id="the-support-loop" class="level2">
<h2 class="anchored" data-anchor-id="the-support-loop">The Support Loop</h2>
<p>I filled out the spam-flag form, though I knew my account was active and clean. I was logged into my account and could change my account information. I could also see all my project listings, though not the details. I replied back:</p>
<blockquote class="blockquote">
<p>&lt; On Mon, Nov 3, 2025 at 7:47 AM EST, Ven Popov, wrote:&gt;</p>
<p>I filled out the form, but my account is neither deactivated nor flagged as SPAM. I simply cannot access any of the 20+ preprints that I or my colleagues have uploaded over the last 8 years! In the form I supplied a link to one preprint, but all of my preprints are inaccessible.</p>
</blockquote>
<p>A few hours later I received a message from a new support representative, asking me again to fill out the same form:</p>
<blockquote class="blockquote">
<p>&lt; On Nov 3, 2025 at 16:04 +0100, Michaela Support <a href="mailto:support@osf.io" class="email">support@osf.io</a>, wrote:&gt;</p>
<p>Please fill out the form: <a href="https://forms.gle/jc5D1ENdkL5WaY4F8">Account Disabled or Falsely Flagged as SPAM reporting</a>. Your preprint have been flagged as spam. This is an important step in tracking how effective (or not) our spam filters are, and to review and resolve the issue.</p>
</blockquote>
<p>Frustrated, I copied Brian Nosek on a detailed escalation email explaining that this was not a single-ticket problem but a catastrophic integrity failure: twenty preprints, hundreds of files, dozens of co-authors, and years of published work were all inaccessible. It was not one “preprint flagged as spam.” Anything I — or anyone associated with me — had uploaded since 2017 was completely inaccessible, and OSF was effectively communicating to the world that this work no longer existed.</p>
<blockquote class="blockquote">
<p>&lt;On Nov 3, 2025 at 16:45 +0100, Ven Popov, wrote:&gt;</p>
<p>As I replied in my previous email, I already filled out the form.</p>
<p>Furthermore, it is not one preprint that is inaccessible. All of the 20+ preprints that I have published on OSF since 2017 are unavailable. Neither the OSF links from my profile, nor the Google Scholar links that have worked for years can be opened.</p>
<p>Please escalate this issue. This is a serious misstep on OSF’s part. I have had an account with OSF for 8 years, I have published about 20 preprints during this time, and none of them are available. For some of them, the OSF links might be the only record online of these published works, and it is a serious issue concerning the longevity of scholarship that an archival platform like OSF cannot allow to happen.</p>
<p><span class="citation" data-cites="Brian">@Brian</span>, can you please get your support team to take this seriously? I’m getting repeated emails asking me to do something I already did and overlooking the extent of the problem. Please see the message history for the detailed report and links to my profile, a subset of broken preprint links, and an API error from Google Scholar links.</p>
</blockquote>
<p>Within hours, OSF staff identified the cause: my account had been automatically flagged as spam by their filters. Once the flag was removed, everything reappeared.</p>
</section>
<section id="the-explanation" class="level2">
<h2 class="anchored" data-anchor-id="the-explanation">The Explanation</h2>
<p>Here’s the message I received:</p>
<blockquote class="blockquote">
<p>&lt; On Nov 3, 2025 at 19:31 +0100, Michaela Support <a href="mailto:support@osf.io" class="email">support@osf.io</a>, wrote&gt;</p>
<p>Thank you for reaching out to the OSF support team! You are correct it looks like your project was caught in our spam filter. As a free open access platform we are frequently the target of spammers, and therefore we must use multiple different spam filters that take a plethora of different factors into account when flagging spam, everything from odd titles to suspicious activity from a nearby IP address could get flagged. Truthfully it’s hard to tell why your content got its attention. I’m sorry for the inconvenience. I’ve removed the flag from material and your content should be active again!</p>
</blockquote>
<p>It was an honest reply — but also a disturbing one.</p>
<p>OSF’s filters had quarantined everything I or <em>any of my colleagues</em> had <em>ever</em> uploaded, without warning, without a visible flag on my dashboard, and without preserving any metadata or landing pages. Public DOIs and Google Scholar entries now pointed to HTTP 410 “Resource deleted” responses — a code that explicitly signals permanent removal to search engines.</p>
<p>In other words, for several days, OSF’s infrastructure told the world that our work no longer existed.</p>
</section>
<section id="the-deeper-problem" class="level2">
<h2 class="anchored" data-anchor-id="the-deeper-problem">The Deeper Problem</h2>
<p>This incident isn’t about data loss — most of my materials were mirrored elsewhere. The issue is trust.</p>
<p>Researchers use OSF because it promises persistence. It issues DOIs, integrates with journals, and serves as part of the scholarly record. That promise was violated — not through malice, but through a structural design choice: an automated filter was allowed to silently deindex archival content.</p>
<p>Spam detection is necessary. But a true archive must never delete first and explain later. An archive is defined by its immutability and accountability.</p>
<p>The design flaw wasn’t that a spam flag existed — it’s that its activation could unpublish a decade’s worth of DOI-assigned material without human review, notification, or even the retention of metadata.</p>
<p>This episode highlights a broader fragility in the open science ecosystem. The infrastructure of modern scholarship — repositories, indexing services, DOI registries — runs on trust. When that infrastructure silently fails, the damage is epistemic: we lose not just access to data, but confidence in the permanence of the scientific record.</p>
<p>An archival platform must therefore:</p>
<ul>
<li>Retain metadata and landing pages even when content is quarantined.</li>
<li>Notify users when materials are flagged or hidden</li>
<li>Require human verification before suppressing DOI-linked material.</li>
<li>Use non-destructive HTTP codes for temporary removals.</li>
</ul>
<p>These are not minor UX fixes — they are the foundations of reliability in digital scholarship. I made this much clear in an email reply:</p>
<blockquote class="blockquote">
<p>&lt;On Mon, Nov 3, 2025 at 2:26 PM Ven Popov wrote:&gt;</p>
<p>Thank you for resolving the issue and restoring access to my materials. I understand that OSF, as a free and open platform, must protect itself against spam.</p>
<p>However, the way your spam-filtering system currently operates is unacceptable and incompatible with OSF’s stated mission as an archival repository. Automatically blocking access to more than twenty DOI-registered preprints, datasets, and supplementary materials spanning nearly a decade — including work co-authored by dozens of researchers and cited in published papers — is not a minor glitch. It constitutes a violation of the very FAIR principles OSF promotes and undermines trust in your platform as part of the scholarly record.</p>
<p>Worse, the system suppressed all metadata and landing pages, returning an HTTP 410 “Resource deleted” response. That code explicitly signals to search engines that a resource has been permanently removed and should be de-indexed. This means the issue not only disrupted access but actively damaged the discoverability and citation continuity of legitimate research outputs.</p>
<p>I fully support the need for robust spam protection. But the current implementation — silent removal of legitimate archival content, without notice or human verification, and with a response that signals permanent deletion — is deeply inconsistent with COS’s commitments to openness, transparency, and persistence.</p>
<p>I urge COS to review this policy and implement safeguards such as:</p>
<ul>
<li>Human review before suppressing any DOI-assigned or public scholarly record.</li>
<li>Retention of metadata and landing pages for any quarantined content.</li>
<li>Clear notifications to affected users.</li>
<li>Use of non-destructive HTTP status codes (e.g., 451 or 503) for temporary suppression.</li>
</ul>
<p>These are not just usability issues; they concern the credibility of OSF as a reliable component of the scientific infrastructure.</p>
<p>I am deeply troubled by this incident, and I do not say this lightly. I have been a staunch supporter of COS and its mission for years. But this event exposes an infrastructural weakness that seriously undermines trust in the OSF platform.</p>
<p>I would appreciate a response from COS leadership that demonstrates an understanding of the gravity of this failure and provides a clear plan to prevent similar occurrences in the future.</p>
<p>I intend to publish this correspondence publicly, and I hope that I can do so alongside a response that acknowledges these concerns and reaffirms COS’s commitment to openness, transparency, and durability in scientific communication.</p>
</blockquote>
</section>
<section id="aftermath" class="level2">
<h2 class="anchored" data-anchor-id="aftermath">Aftermath</h2>
<p>After I sent my detailed critique, Brian Nosek responded personally and with grace. He acknowledged that this case was unprecedented, thanked me for the thorough documentation, and assured me that it would help the team identify whether others had been similarly affected.</p>
<blockquote class="blockquote">
<p>&lt; On Nov 3, 2025 at 22:05 +0100, Brian Nosek, wrote:&gt;</p>
<p>I see from the&nbsp;thread that your issue is resolved, but I am sorry that you had to go through that!</p>
<p>I really appreciate the detailed notes that you provided to help identify the source of the problem, and even more the solutioning that you offered to improve user experience and more gracefully manage the identification and mitigation of spam. I have not heard of a case like this, so while it was a painful experience that you had to deal with, hopefully the exposure of the problem with your diligent reporting will help us to identify the extent to which it is occurring more widely and develop effective solutions.</p>
<p>Thanks again for giving us such useful reporting on your experience with this to help us continue to improve the service.</p>
</blockquote>
<p>Despite this response, I was still deeply troubled. I briefly considered whether I’m overreacting. After consulting several colleagues, they were unanimous: the severity is not that it happened to me, but that it could happen at all — silently and retroactively. As one colleague put it:</p>
<blockquote class="blockquote">
<p>I agree, I think the issue is more severe than it sounds like. The effect is crazy to me. Not only that they retrospectively delete the stuff but also how many people can be affected by it. It’s already severe in your case with a large network of collaborators. But imagine Klaus’s account gets flagged and he doesn’t realize it. This would probably take hundreds of preprints offline, which are primary the work of others [junior researchers]. It’s bad enough that they take the stuff down that you put on OSF but it also deletes the work where someone else just linked your name with? This also can’t be right…</p>
</blockquote>
<p>After some reflection, I followed up with Brian Nosek with a longer message explaining why the issue mattered so deeply: not because of personal inconvenience, but because silent retroactive suppression of long-standing records is incompatible with the role of an archive.</p>
<blockquote class="blockquote">
<p>&lt; On Nov 4, 2025 at 13:49 +0100, Ven Popov wrote:&gt;</p>
<p>Thank you for the thoughtful response. I appreciate that you will take this case seriously in developing future solutions.</p>
<p>I just really want to stress why I am so concerned about this having happened. It’s not about my work and that it affected me personally, although that clearly made me more motivated to get to the bottom of it. My work is also backed up on other platforms, including ResearchGate, a University of Zurich repository and my personal website. The real problem is that something like this should not be possible to happen in the first place, let alone as the result of an automated filter.</p>
<p>Whatever the reason my account was flagged, an automated filter should never have the authority to retroactively block access to the work of hundreds of researchers simply because I’m listed as a co-author. These were not new uploads but materials that had been part of the scholarly record for nearly a decade.</p>
<p>I was lucky to notice the disappearance within days; others might not. Under different circumstances, months could have passed during which all these digital objects appeared effectively erased. The consequences for more prominent researchers with hundreds of linked projects - and for junior collaborators relying on those links - could be far more severe.</p>
<p>Your own guidelines stress that even authors themselves cannot delete accepted preprints - that preprints can only be withdrawn, and that the associated metadata and reasons for withdrawn will always remain part of the record.</p>
<p>I considered whether I might be overreacting, but after discussing the issue with several colleagues, everyone agrees that this is a critical infrastructure-level bug. This requires more than a patch for this particular case, but systems in place to ensure that nothing can in principle cause something like it to happen in the future.</p>
<p>I am writing all this out in detail not to attack COS or its work on maintaining OSF. <em>It’s exactly the opposite - I share all COS values and view it as a fundamentally important institution. The traditional academic publishing system is rotten to core, but in order for any reform to succeed long-term, people must trust the infrastructure and systems in place.</em> I want to see COS and OSF continue to succeed and continue to play the vital role that it has attained. This is why I am being so adamant about this incident being taken so seriously.</p>
</blockquote>
<p>His reply was thoughtful and for the moment I am satisfied that this incident will be taken seriously:</p>
<blockquote class="blockquote">
<p>&lt;On Nov 4, 2025 at 14:45 +0100, Brian Nosek, wrote:&gt;</p>
<p>Thanks for the follow-up. There is nothing inappropriate at all about your comments or approach. It is very clear that your comments are made in good faith to help us improve the services and meet the aspirations of reliable, persistent open scholarship. We truly appreciate your willingness to put time into documenting and sharing the challenges you experienced here.</p>
</blockquote>
<p>That, to me, felt like the right place to stop. Ideally, I’d still like an official statement from COS about what they plan to do to ensure that something like this doesn’t happen again.</p>
</section>
<section id="data-vs.-trust" class="level2">
<h2 class="anchored" data-anchor-id="data-vs.-trust">Data vs.&nbsp;Trust</h2>
<p>No data were ultimately lost. But something far more important was at stake: the continuity of the scholarly record.</p>
<p>If this episode serves any purpose, let it be a reminder that openness is not just about making information accessible — it’s about ensuring it stays accessible. Scientific infrastructure must be engineered for trustworthiness, not just availability.</p>
<p>As we argued in a recent perspective paper, <a href="https://www.pnas.org/doi/abs/10.1073/pnas.2401231121">the traditional publishing system is rotten to the core</a>. But for any reform to succeed long-term, people must trust the infrastructure and systems in place.</p>
<p>I also don’t know if this incident is related to the OSF redesign - I described it here within that context - or whether it would have occurred with the previous platform. Ultimately it doesn’t matter.</p>
<p>Trust is slow to earn and quick to erode. I hope OSF uses this experience to reinforce that trust — not only by fixing the bug, but by institutionalizing the safeguards that make open scholarship durable.</p>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2025,
  author = {Popov, Vencislav},
  title = {Open {Science} Needs Reliable Infrastructure},
  date = {2025-11-06},
  url = {https://venpopov.com/posts/2025/open-science-needs-reliable-infrastructure/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2025" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2025. <span>“Open Science Needs Reliable
Infrastructure.”</span> November 6, 2025. <a href="https://venpopov.com/posts/2025/open-science-needs-reliable-infrastructure/">https://venpopov.com/posts/2025/open-science-needs-reliable-infrastructure/</a>.
</div></div></section></div> ]]></description>
  <category>open science</category>
  <category>reproducibility</category>
  <category>academic publishing</category>
  <category>2025</category>
  <guid>https://venpopov.com/posts/2025/open-science-needs-reliable-infrastructure/</guid>
  <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2025/open-science-needs-reliable-infrastructure/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>aRt-o Pollo</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2025/arto-pollo/</link>
  <description><![CDATA[ 






<p>Imagine a universe where light behaves differently than ours. Rather than diffusing its intensity with distance, and simply increasing it with multiple sources, light in this universe is different. When two light beams meet, their combined intensity depends on the angle between them. Right angles are the best - when two light beams meet at a point perpendicularly, they have the highest possible intensity at that point. As the angle becomes smaller or bigger than 90 degrees (pi/2 radians), the intensity becomes weaker.</p>
<p>Why would you imagine such a thing? I don’t know why I wonder about such things, but I often do. As an example, I drew the image below while thinking about this - we have two “suns”, the red dots are 90 degree intersections, the blue points are intersections with a smaller angle.</p>
<p><img src="https://venpopov.com/posts/2025/arto-pollo/two-source-rays.png" class="img-fluid"></p>
<p>Drawing can only get me so far, but this is an easy enough simulation to do in R, and the results are pretty, especially when we change some of those initial assumptions. I’ve long been a fan of Danielle Navarro’s <a href="https://art-from-code.netlify.app/">approach to generative art</a>, and it has been years since I dipped my toes in these waters, so here goes nothing. This post will be light on details, but hopefully it makes sense. Let’s explore how this imaginary physics creates some beautiful patterns.</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>(ggplot2)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(purrr)</span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(patchwork)</span>
<span id="cb1-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ambient)</span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(wesanderson)</span>
<span id="cb1-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(httpgd)</span>
<span id="cb1-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggdark)</span>
<span id="cb1-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(viridis)</span>
<span id="cb1-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(fields)</span>
<span id="cb1-10"><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;">12345</span>)</span>
<span id="cb1-11"></span>
<span id="cb1-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_set</span>(</span>
<span id="cb1-13">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dark_theme_void</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb1-14">    <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;">plot.background =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</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;">"#1F1F1F"</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;">"#1F1F1F"</span>))</span>
<span id="cb1-15">)</span></code></pre></div></div>
</div>
<p>What things do we need for the simplest possible simulation?</p>
<ul>
<li>two fixed points A and B for our light sources</li>
<li>a way to calculate the angle formed at any other point X where beams from A and B intersect - a way to convert this angle into an intensity value</li>
<li>calculate the intensity for many different points in the plane</li>
<li>visualize the result</li>
</ul>
<p>Here are some basic functions to do this:</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"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Calculates the angle formed by AX and BX</span></span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param X a numeric vector of length 2 or a matrix with two columns in which each row represents the x and y coordinates of a point</span></span>
<span id="cb2-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param A A numeric vector of length 2 representing the first source</span></span>
<span id="cb2-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param B A numeric vector of length 2 representing the second source</span></span>
<span id="cb2-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return The angle in radians between vectors A and B with respect to point(s) X.</span></span>
<span id="cb2-7">angle <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>(X, A, B) {</span>
<span id="cb2-8">  X <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.matrix</span>(X)</span>
<span id="cb2-9">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(X) <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>) X <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>(X)</span>
<span id="cb2-10">  A <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>(A, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">each =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(X)<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="cb2-11">  B <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>(B, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">each =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length</span>(X)<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="cb2-12"></span>
<span id="cb2-13">  v1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> X <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> A</span>
<span id="cb2-14">  v2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> X <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> B</span>
<span id="cb2-15">  dot_product <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;">rowSums</span>(v1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> v2)</span>
<span id="cb2-16">  norm <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;">sqrt</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rowSums</span>(v1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> v1)) <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;">sqrt</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rowSums</span>(v2 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> v2))</span>
<span id="cb2-17">  cos_theta <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> dot_product <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> norm</span>
<span id="cb2-18">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">acos</span>(cos_theta)</span>
<span id="cb2-19">}</span>
<span id="cb2-20"></span>
<span id="cb2-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Convert angle to intensity</span></span>
<span id="cb2-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'</span></span>
<span id="cb2-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param theta A numeric value representing the angle in radians.</span></span>
<span id="cb2-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A numeric value representing the intensity.</span></span>
<span id="cb2-25">angle_to_intensity <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>(theta) {</span>
<span id="cb2-26">  <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;">abs</span>(theta <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> pi <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> (pi <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="cb2-27">}</span>
<span id="cb2-28"></span>
<span id="cb2-29"></span>
<span id="cb2-30"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' Calculate intensity at a point</span></span>
<span id="cb2-31"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' </span></span>
<span id="cb2-32"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param x a numeric vector of length 2 or a matrix with two columns in which each row represents the x and y coordinates of a point</span></span>
<span id="cb2-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param source1 A numeric vector of length 2 representing the first source</span></span>
<span id="cb2-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param source2 A numeric vector of length 2 representing the second source</span></span>
<span id="cb2-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @param angle_transform_fun A function that takes an angle in radians and returns</span></span>
<span id="cb2-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'  a value to be plotted as the coordinate intensity. Default is a linear function of </span></span>
<span id="cb2-37"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'  the absolute deviation from a right angle.</span></span>
<span id="cb2-38"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#' @return A numeric value in [0, 1] representing the intensity at point x.</span></span>
<span id="cb2-39">intensity <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>(x, source1, source2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">angle_transform_fun =</span> angle_to_intensity) {</span>
<span id="cb2-40">  theta <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;">angle</span>(x, source1, source2)</span>
<span id="cb2-41">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">angle_transform_fun</span>(theta)</span>
<span id="cb2-42">}</span></code></pre></div></div>
</div>
<p>With these functions in hand we can play around. We need two sources:</p>
<div class="cell">
<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">A <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;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb3-2">B <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="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span></code></pre></div></div>
</div>
<p>Just as an illustration, the maximum intensity should be at point (0, 1) which forms a right angle. The minimum intensity should be at any point on the AB line, e.g.&nbsp;(0, 0) as it forms a 180 degree angle:</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="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">intensity</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>), A, B)</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] 1</code></pre>
</div>
<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;">intensity</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;">0</span>), A, B)</span></code></pre></div></div>
<div class="cell-output cell-output-stdout">
<pre><code>[1] 0</code></pre>
</div>
</div>
<p>Now we need a grid of points where their beams will intersect:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1">grid <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;">expand.grid</span>(</span>
<span id="cb8-2">  <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;">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;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>),</span>
<span id="cb8-3">  <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;">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;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.01</span>)</span>
<span id="cb8-4">)</span></code></pre></div></div>
</div>
<p>and evaluate their intensity:</p>
<div class="cell" data-layout-align="center">
<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">grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity1 <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;">intensity</span>(grid[<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;">"x"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y"</span>)], A, B)</span>
<span id="cb9-2"></span>
<span id="cb9-3">main_plot <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grid <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> </span>
<span id="cb9-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(x, y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> intensity1, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> intensity1)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb9-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_raster</span>()</span>
<span id="cb9-6">  </span>
<span id="cb9-7">main_plot <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;">scale_fill_gradientn</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colors =</span> viridis<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;">inferno</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>))</span></code></pre></div></div>
<div class="cell-output-display">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-5-1.png" class="img-fluid quarto-figure quarto-figure-center figure-img" width="576"></p>
</figure>
</div>
</div>
</div>
<p>That’s kinda cool. Mostly what I expected, but it’s pretty. We can try a few more color schemes and plot settings. To make things a bit easier, I want to simultaneously plot multiple versions with different binning granularities:</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">plot3s <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>(main, pallette) {</span>
<span id="cb10-2">  p1 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> main <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> </span>
<span id="cb10-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_stepsn</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colors =</span> pallette, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n.breaks =</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="cb10-4">    <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="cb10-5">  p2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> main <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> </span>
<span id="cb10-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_stepsn</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colors =</span> pallette, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">n.breaks =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">24</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> </span>
<span id="cb10-7">    <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="cb10-8">  p3 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> main <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> </span>
<span id="cb10-9">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_fill_gradientn</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colors =</span> pallette) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> </span>
<span id="cb10-10">    <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="cb10-11"></span>
<span id="cb10-12">  p1 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> p2 <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> p3</span>
<span id="cb10-13">}</span></code></pre></div></div>
</div>
<p>For example with out original palette we get:</p>
<div class="cell page-columns page-full" data-layout-align="center">
<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;">plot3s</span>(main_plot, viridis<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;">inferno</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>))</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-7-1.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
</div>
<p>With the binned version on the left, I notice something that wasn’t as obvious in the smooth one: there is more than one circle! In fact, all points that form the same angle with A and B lie on a circle. This wasn’t immediately obvious to me, but I eventually remembered the <a href="https://www.wikiwand.com/en/articles/Inscribed_angle">inscribed angle theorem</a> from high school geometry. This theorem is a more general case of the well-known Thales’ theorem, which states that all points on a circle form 90-degree angles with any diameter line of the circle. The resulting figure also represents one half of what is known as the <a href="https://www.wikiwand.com/en/articles/Apollonian_circles">Apollonian circles</a>. This realization led me down a Wikipedia rabbit hole, where I read more about alternative coordinate systems like the <a href="https://www.wikiwand.com/en/articles/Bipolar_coordinates">bipolar coordinate system</a>. At first glance, it seems esoteric and pointless, but it turns out it can <a href="https://arc.net/l/quote/jfwaoyhl">simplify many problems</a> that are otherwise too complicated to compute in standard Cartesian coordinate systems.</p>
<p>Anyway, let’s go on with making pretty variations on this theme.</p>
<div class="cell page-columns page-full" data-layout-align="ce#| nter">
<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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(main_plot, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">hcl.colors</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"YlOrRd"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rev =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb12-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(main_plot, <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;">"#00FFFF"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#8A2BE2"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#FFD700"</span>))</span>
<span id="cb12-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(main_plot, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">wes_palette</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Royal1"</span>))</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-ce#| nter page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-8-1.png" class="img-fluid quarto-figure quarto-figure-ce#| nter figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-ce#| nter page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-8-2.png" class="img-fluid quarto-figure quarto-figure-ce#| nter figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-ce#| nter page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-8-3.png" class="img-fluid quarto-figure quarto-figure-ce#| nter figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
</div>
<p>Alright, how about we add some correlated noise for variety?</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">gaussian_kernel <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>(sigma_x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sigma_y =</span> sigma_x) {</span>
<span id="cb13-2">  size_x <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;">ceiling</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> sigma_x) <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">  size_y <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;">ceiling</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> sigma_y) <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-4">  </span>
<span id="cb13-5">  kernel <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;">outer</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>size_x<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>size_x, <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>size_y<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>size_y, <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(x, y) {</span>
<span id="cb13-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">exp</span>(<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>(x<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;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> sigma_x<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> y<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;">2</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> sigma_y<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="cb13-7">  })</span>
<span id="cb13-8"></span>
<span id="cb13-9">  kernel <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>(kernel)</span>
<span id="cb13-10">}</span>
<span id="cb13-11"></span>
<span id="cb13-12">convolve2d <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>(mat, kernel) {</span>
<span id="cb13-13">  fft_mat <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;">fft</span>(mat)</span>
<span id="cb13-14">  fft_kernel <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;">fft</span>(kernel, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dim</span>(mat))</span>
<span id="cb13-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Re</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fft</span>(fft_mat <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> fft_kernel, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">inverse =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</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;">length</span>(mat))</span>
<span id="cb13-16">}</span>
<span id="cb13-17"></span>
<span id="cb13-18">smooth_matrix <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>(mat, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gaussian_kernel</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)) {</span>
<span id="cb13-19">  pad_kernel <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;">matrix</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nrow =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(mat), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ncol =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ncol</span>(mat))</span>
<span id="cb13-20">  pad_kernel[<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>(kernel), <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;">ncol</span>(kernel)] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> kernel</span>
<span id="cb13-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">convolve2d</span>(mat, pad_kernel)</span>
<span id="cb13-22">}</span>
<span id="cb13-23"></span>
<span id="cb13-24">grid_size <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;">sqrt</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(grid))</span>
<span id="cb13-25">noise <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;">matrix</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rnorm</span>(grid_size<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>), grid_size)</span>
<span id="cb13-26">noise <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;">smooth_matrix</span>(noise, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gaussian_kernel</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>))</span></code></pre></div></div>
</div>
<p>Now won’t you look at that!</p>
<div class="cell page-columns page-full" data-layout-align="center">
<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">grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity2 <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity1 <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;">as.vector</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(noise))</span>
<span id="cb14-2"></span>
<span id="cb14-3">noise_plot <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grid <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> </span>
<span id="cb14-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(x, y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> intensity2, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> intensity2)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_raster</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb14-6">  <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="cb14-7"></span>
<span id="cb14-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rev</span>(viridis<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;">inferno</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>)))</span>
<span id="cb14-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">hcl.colors</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"YlOrRd"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rev =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb14-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot, <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;">"#00FFFF"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#8A2BE2"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#FFD700"</span>))</span>
<span id="cb14-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">wes_palette</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Royal1"</span>))</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-10-1.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-10-2.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-10-3.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-10-4.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
</div>
<p>Alright, one last one. Instead of clamping the intensity to be a linear function of how much the angle deviates from 90 degrees, let’s introduce some oscillations. We’ll also use a larger range of x and y coordinates.</p>
<div class="cell page-columns page-full" data-layout-align="center">
<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">nonlinear_intensity <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>(theta) {</span>
<span id="cb15-2">  deviation <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;">abs</span>(theta <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> pi<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> (pi<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="cb15-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sin</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> pi <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> deviation)<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="cb15-4">}</span>
<span id="cb15-5"></span>
<span id="cb15-6">bigger_grid <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;">expand.grid</span>(</span>
<span id="cb15-7">  <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;">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;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>),</span>
<span id="cb15-8">  <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;">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;">4</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>)</span>
<span id="cb15-9">)</span>
<span id="cb15-10"></span>
<span id="cb15-11">bigger_grid_size <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;">sqrt</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(bigger_grid)) </span>
<span id="cb15-12">bigger_noise <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;">matrix</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rnorm</span>(bigger_grid_size<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="at" style="color: #657422;
background-color: null;
font-style: inherit;">sd =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>), bigger_grid_size)</span>
<span id="cb15-13">bigger_noise <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;">smooth_matrix</span>(bigger_noise, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gaussian_kernel</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>))</span>
<span id="cb15-14"></span>
<span id="cb15-15">bigger_grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity3 <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;">intensity</span>(bigger_grid[<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;">1</span>,<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)], A, B, nonlinear_intensity) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> </span>
<span id="cb15-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.vector</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t</span>(bigger_noise))</span>
<span id="cb15-17"></span>
<span id="cb15-18">noise_plot_nl <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> bigger_grid <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> </span>
<span id="cb15-19">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(y, x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> intensity3, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> intensity3)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb15-20">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_raster</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb15-21">  <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="cb15-22"></span>
<span id="cb15-23"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot_nl, viridis<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;">inferno</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>))</span>
<span id="cb15-24"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot_nl, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">hcl.colors</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"YlOrRd"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rev =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span>
<span id="cb15-25"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot_nl, <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;">"#00FFFF"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#8A2BE2"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#FFD700"</span>))</span>
<span id="cb15-26"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(noise_plot_nl, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grey.colors</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-11-1.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-11-2.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-11-3.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-11-4.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
</div>
<p>Last one, for real this time:</p>
<div class="cell page-columns page-full" data-layout-align="center">
<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="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Generate and smooth directional noise</span></span>
<span id="cb16-2">grid_size <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;">sqrt</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(grid))</span>
<span id="cb16-3">noise_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;">matrix</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rnorm</span>(grid_size<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="at" style="color: #657422;
background-color: null;
font-style: inherit;">nrow =</span> grid_size)</span>
<span id="cb16-4">smoothed_noise <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;">smooth_matrix</span>(noise_matrix, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">kernel =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gaussian_kernel</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">sigma_x =</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;">sigma_y =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb16-5"></span>
<span id="cb16-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Apply noise only on the bottom part of the image and clip intensity between 0 and 1</span></span>
<span id="cb16-7">is_lower_half <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y <span class="sc" style="color: #5E5E5E;
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="cb16-8">grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity4 <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;">ifelse</span>(</span>
<span id="cb16-9">  is_lower_half,</span>
<span id="cb16-10">  grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity1<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.75</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;">as.vector</span>(smoothed_noise),</span>
<span id="cb16-11">  grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity1</span>
<span id="cb16-12">) </span>
<span id="cb16-13"></span>
<span id="cb16-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># somewhat random aesthetic choices</span></span>
<span id="cb16-15">inside_upper_circle <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> (grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>y<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> grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x<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;">&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;">&amp;</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>is_lower_half</span>
<span id="cb16-16">grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity4 <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;">pmax</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pmin</span>(grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity4, <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;">0</span>)</span>
<span id="cb16-17">grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity4[inside_upper_circle] <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grid<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>intensity4[inside_upper_circle]<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.4</span></span>
<span id="cb16-18"></span>
<span id="cb16-19">streak_noise_plot <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> grid <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> </span>
<span id="cb16-20">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(x, y, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> intensity4, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> intensity4)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb16-21">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_raster</span>() <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb16-22">  <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="cb16-23"></span>
<span id="cb16-24"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">plot3s</span>(streak_noise_plot, viridis<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;">inferno</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>))</span></code></pre></div></div>
<div class="cell-output-display page-columns page-full">
<div class="quarto-figure quarto-figure-center page-columns page-full">
<figure class="figure page-columns page-full">
<p class="page-columns page-full"><img src="https://venpopov.com/posts/2025/arto-pollo/index_files/figure-html/unnamed-chunk-12-1.png" class="img-fluid quarto-figure quarto-figure-center figure-img column-page" width="1440"></p>
</figure>
</div>
</div>
</div>



<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2025,
  author = {Popov, Vencislav},
  title = {aRt-o {Pollo}},
  date = {2025-02-21},
  url = {https://venpopov.com/posts/2025/arto-pollo/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2025" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2025. <span>“aRt-o Pollo.”</span> February 21, 2025.
<a href="https://venpopov.com/posts/2025/arto-pollo/">https://venpopov.com/posts/2025/arto-pollo/</a>.
</div></div></section></div> ]]></description>
  <category>R</category>
  <category>art</category>
  <category>2025</category>
  <guid>https://venpopov.com/posts/2025/arto-pollo/</guid>
  <pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2025/arto-pollo/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>R love you, R hate you</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2025/r-love-you-r-hate-you/</link>
  <description><![CDATA[ 






<p>I do the majority of my coding in R. I’ve grown to love the language despite many of its quirks. Perhaps this is just Stockholm Syndrome as I joked in my “Intro to programming” class recently, though I genuinely enjoy the language and its functional programming roots.</p>
<p>That said, there are things that are objectively bad about it, with defaults that no sane person would choose. The more time I spend writing code for packages rather than data analysis, the more I’ve grown annoyed at how much extra work you need to do to avoid perplexing bugs and behavior. This is partly a weakness of dynamically typed languages - rather than depending on type-checking built into the language, you need to program defensively and explicitly handle user-input and whether it is what you expect it to be (see excellent discussions <a href="https://blog.r-hub.io/2022/03/10/input-checking/#checking-function-inputs-using-r-packages">here</a> and <a href="https://blog.djnavarro.net/posts/2023-08-08_being-assertive/#just-assertr-carr">here</a>). It’s a problem important enough that it has led to the proliferation of many packages that aim to assist you with it - e.g.&nbsp;<a href="https://mllg.github.io/checkmate/articles/checkmate.html">checkmate</a>, <a href="https://github.com/smbache/ensurer">ensurer</a>, <a href="https://github.com/gastonstat/tester">tester</a>, <a href="https://cran.r-project.org/web/packages/assertive/index.html">assertive</a>, <a href="https://docs.ropensci.org/assertr/index.html">assertr</a>. But another part is just due to some genuinely weird choices built in many of R’s base functions.</p>
<section id="a-case-of-bait-and-switch" class="level3">
<h3 class="anchored" data-anchor-id="a-case-of-bait-and-switch">A case of bait and switch</h3>
<p>Here’s a simple case which recently led to a head-scratcher of a bug in our <a href="../../2024/introducing-bmm">bmm package</a>. Consider a simple a function which gives a different greeting depending on a language parameter:</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">greeting <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>(name, language) {</span>
<span id="cb1-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (language <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chinese"</span>) {</span>
<span id="cb1-3">    out <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;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"嗨 "</span>, name, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"! 多么美好的一天"</span>)</span>
<span id="cb1-4">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (language <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"german"</span>) {</span>
<span id="cb1-5">    out <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;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hallo "</span>, name, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"! Was für ein toller Tag!"</span>)</span>
<span id="cb1-6">  } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (language <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bulgarian"</span>) {</span>
<span id="cb1-7">    out <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;">paste0</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Здравей "</span>, name, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"! Какъв страхотен ден!"</span>)</span>
<span id="cb1-8">  }</span>
<span id="cb1-9">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(out, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb1-10">}</span></code></pre></div></div>
</div>
<p>Simple enough, right?</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">greeting</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ven"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"german"</span>)</span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Hallo Ven! Was für ein toller Tag!</span></span></code></pre></div></div>
</div>
<p>Currently, you will get weird errors with non-character input if you don’t properly check that the language argument is a character or that it is a valid choice. But at least the user will get an error:</p>
<div class="cell">
<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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">greeting</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ven"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Error in greeting("Ven", 2): object 'out' not found</span></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">greeting</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ven"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"japanese"</span>)</span>
<span id="cb3-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Error in greeting("Ven", "japanese"): object 'out' not found</span></span></code></pre></div></div>
</div>
<p>Ok, maybe a little confusing for the user who doesn’t know how the function is implemented (“object ‘out’ not found? What is object ‘out’?”), but fine. You can <em>and should</em> go the extra mile and validate the input, but let’s keep going. Now, if you had many different language options, you might decide to use a more efficient <code>switch</code> statement instead of an if/else structure:</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">greeting2 <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>(name, language) {</span>
<span id="cb4-2">  out <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;">switch</span>(language,</span>
<span id="cb4-3">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chinese =</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>, name, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"! 多么美好的一天"</span>),</span>
<span id="cb4-4">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">german =</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;">"Hallo "</span>, name, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"! Was für ein toller Tag!"</span>),</span>
<span id="cb4-5">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bulgarian =</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>, name, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"! Какъв страхотен ден!"</span>)</span>
<span id="cb4-6">  )</span>
<span id="cb4-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(out, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb4-8">}</span></code></pre></div></div>
</div>
<p>Our <code>switch</code> handles 3 explicitly defined cases, just like the if/else version, so unless you read the documentation, or have been already burnt by this, you might expect that this should fail:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">greeting2</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ven"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb5-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Hallo Ven! Was für ein toller Tag!</span></span></code></pre></div></div>
</div>
<p>Oh, it runs! Ok, so even though we have explicitly defined the cases by name, switch tries to be clever and interpret integer input as a case selection. Not only that, but it will try really hard to make the input an integer if it can. The following makes no sense as a language selector, but R happily forces 2.8 into an integer sleeve and greets us again:</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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">greeting2</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ven"</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.8</span>)</span>
<span id="cb6-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Hallo Ven! Was für ein toller Tag!</span></span></code></pre></div></div>
</div>
<p>Fine, it’s strange, but who in their right mind will try to pass numeric values for a language variable? Maybe no one on purpose, but some things in R are (sometimes) secretly integers. Namely, factors. Consider this - we have some <code>data.frame</code> with names and language preferences of users, the latter of which is coded as a factor variable.</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">users <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="cb7-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">name =</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;">"Ven"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Gidon"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Chenyu"</span>),</span>
<span id="cb7-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">language =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">factor</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;">"bulgarian"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"german"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"chinese"</span>))</span>
<span id="cb7-4">)</span>
<span id="cb7-5"></span>
<span id="cb7-6">users</span>
<span id="cb7-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;     name  language</span></span>
<span id="cb7-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1    Ven bulgarian</span></span>
<span id="cb7-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 2  Gidon    german</span></span>
<span id="cb7-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 3 Chenyu   chinese</span></span></code></pre></div></div>
</div>
<p>If we were to use our if/else-based greeting function to greet each user, everything works as we would expect:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1">purrr<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;">pwalk</span>(users, greeting)</span>
<span id="cb8-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Здравей Ven! Какъв страхотен ден! </span></span>
<span id="cb8-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Hallo Gidon! Was für ein toller Tag! </span></span>
<span id="cb8-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 嗨 Chenyu! 多么美好的一天</span></span></code></pre></div></div>
</div>
<p>What about the greeting2, which uses <code>switch</code> to determine which greeting to use?</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">purrr<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;">pwalk</span>(users, greeting2)</span>
<span id="cb9-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 嗨 Ven! 多么美好的一天</span></span>
<span id="cb9-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Здравей Gidon! Какъв страхотен ден!</span></span>
<span id="cb9-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Hallo Chenyu! Was für ein toller Tag!</span></span></code></pre></div></div>
</div>
<p>What the hell? Well, I cheated a bit and hid the warning R helpfully gave us (naughty!). Here is the output again without suppressing the warning:</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-overflow-wrap code-with-copy"><code class="sourceCode r"><span id="cb10-1">purrr<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;">pwalk</span>(users, greeting2)</span>
<span id="cb10-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Warning in switch(language, chinese = paste0("嗨 ", name, "! 多么美好的一天"), : EXPR is a "factor", treated as integer.</span></span>
<span id="cb10-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  Consider using 'switch(as.character( * ), ...)' instead.</span></span>
<span id="cb10-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 嗨 Ven! 多么美好的一天</span></span>
<span id="cb10-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Warning in switch(language, chinese = paste0("嗨 ", name, "! 多么美好的一天"), : EXPR is a "factor", treated as integer.</span></span>
<span id="cb10-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  Consider using 'switch(as.character( * ), ...)' instead.</span></span>
<span id="cb10-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Здравей Gidon! Какъв страхотен ден!</span></span>
<span id="cb10-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Warning in switch(language, chinese = paste0("嗨 ", name, "! 多么美好的一天"), : EXPR is a "factor", treated as integer.</span></span>
<span id="cb10-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  Consider using 'switch(as.character( * ), ...)' instead.</span></span>
<span id="cb10-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Hallo Chenyu! Was für ein toller Tag!</span></span></code></pre></div></div>
</div>
<p>Ah, that explains it (although doesn’t excuse it). When a factor variable is passed to a switch statement, the variable is treated as an integer. By default factor levels are ordered alphabetically, which we can see if we examine our <code>language</code> factor structure:</p>
<div class="cell">
<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">users<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>language</span>
<span id="cb11-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] bulgarian german    chinese  </span></span>
<span id="cb11-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Levels: bulgarian chinese german</span></span>
<span id="cb11-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str</span>(users<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>language)</span>
<span id="cb11-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  Factor w/ 3 levels "bulgarian","chinese",..: 1 3 2</span></span></code></pre></div></div>
</div>
<p>So Gidon gets greeted in Bulgarian, because his “german” language is coded as 3 in the factor variable, and the third check in <code>switch</code> corresponds to “bulgarian”. The documentation (<code>?switch</code>) helpfully explains that</p>
<blockquote class="blockquote">
<p><code>switch</code> works in two distinct ways depending whether the first argument evaluates to a character string or a number.</p>
<p>If the value of <code>EXPR</code> is not a character string it is coerced to integer. Note that this also happens for <a href="vscode-webview://1j32ru5leek8sf3kqosuqvfte16jrk6ppg82mr9lgucrkob51ojg/base/help/factor" title="../../base/help/factor"><code>factor</code></a>s, with a warning, as typically the character level is meant. If the integer is between 1 and <code>nargs()-1</code> then the corresponding element of <code>...</code> is evaluated and the result returned: thus if the first argument is <code>3</code> then the fourth argument is evaluated and returned.</p>
<p>If <code>EXPR</code> evaluates to a character string then that string is matched (exactly) to the names of the elements in <code>...</code>…</p>
</blockquote>
<p>Wow, ok, I never knew this, or if I did I have completely forgotten about it.</p>
</section>
<section id="so-what-we-do-get-a-warning-dont-we" class="level2">
<h2 class="anchored" data-anchor-id="so-what-we-do-get-a-warning-dont-we">So what, we do get a warning don’t we?</h2>
<p>We sure do, but warnings can be suppressed, just like I did above. It’s common to suppress output of functions when running many iterations of a chatty function in an analysis script and problems can easily go unnoticed. That’s exactly what happened recently in our Bayesian measurement modeling R package when a colleage reported <a href="https://github.com/venpopov/bmm/issues/269">a weird bug</a>. We have one computational model that can apply different forms of Luce’s choice decision rule - a standard version and a version passed through a softmax normalization. Due to this weird way that <code>switch</code> treats factors, the wrong normalization was applied. This is a recent and not yet officially released model, so we are yet to write all input validations. This could have easily gone unnoticed and led to incorrect model specification that nevertheless lets the model run.</p>
<p><em>Warnings are not a reliable way to signal undesired behavior</em>. Especially when the documentation of switch’s warning itself notes that “typically the character level is meant”. Well, if typically a character level is meant, why is the default EXACTLY THE OPPOSITE?!?</p>
</section>
<section id="fine-but-you-tell-users-language-should-be-a-character-right" class="level2">
<h2 class="anchored" data-anchor-id="fine-but-you-tell-users-language-should-be-a-character-right">Fine, but you tell users “language” should be a character… right?</h2>
<p>We do, but here’s the kicker - R <em>loves</em> to turn character vectors into factors. So much so that disabling such behavior was one of the main motivation behind the development of <code>tibbles.</code> Until R4.0.0, whenever you used a function like <code>read.csv()</code> to read a file as a data.frame, R by default <a href="https://blog.r-project.org/2020/02/16/stringsasfactors/">converted character columns to factors</a>. Thankfully now this default has been reversed, but here’s the deal - NOT EVERYWHERE!</p>
<p>One place which I never knew R created factors out of character vectors is <code>expand.grid</code>. Expand.grid is a commonly used function to get a data.frame with all combinations of several variables, which is useful for running models with orthogonality manipulated conditions. E.g.:</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">conditions <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;">expand.grid</span>(</span>
<span id="cb12-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</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;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>),</span>
<span id="cb12-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">version =</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;">"cs"</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ss"</span>), </span>
<span id="cb12-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">choice_rule =</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;">"simple"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"softmax"</span>)</span>
<span id="cb12-5">)</span>
<span id="cb12-6">conditions</span>
<span id="cb12-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;   value version choice_rule</span></span>
<span id="cb12-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 1     1      cs      simple</span></span>
<span id="cb12-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 2   100      cs      simple</span></span>
<span id="cb12-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 3     1      ss      simple</span></span>
<span id="cb12-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 4   100      ss      simple</span></span>
<span id="cb12-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 5     1      cs     softmax</span></span>
<span id="cb12-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 6   100      cs     softmax</span></span>
<span id="cb12-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 7     1      ss     softmax</span></span>
<span id="cb12-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 8   100      ss     softmax</span></span></code></pre></div></div>
</div>
<p>Can you tell that version and choice_rule are factors? I’ve used <code>expand.grid</code> for years without knowing that default behavior of expand.grid, and it’s partly that standard data.frame print method does not differentiate character and factor columns in any way. You can see that indeed we have factors underneath by using <code>str</code> for more details:</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="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str</span>(conditions, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">give.attr =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb13-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 'data.frame':    8 obs. of  3 variables:</span></span>
<span id="cb13-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ value      : num  1 100 1 100 1 100 1 100</span></span>
<span id="cb13-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ version    : Factor w/ 2 levels "cs","ss": 1 1 2 2 1 1 2 2</span></span>
<span id="cb13-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ choice_rule: Factor w/ 2 levels "simple","softmax": 1 1 1 1 2 2 2 2</span></span></code></pre></div></div>
</div>
<p>You can examine the documentation, or directly check with <code>formals</code> that expand.grid, just like <code>read.csv</code> has a argument <code>stringsAsFactors</code>, which defaults to TRUE:</p>
<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"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formals</span>(expand.grid)</span>
<span id="cb14-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $...</span></span>
<span id="cb14-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $KEEP.OUT.ATTRS</span></span>
<span id="cb14-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] TRUE</span></span>
<span id="cb14-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb14-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $stringsAsFactors</span></span>
<span id="cb14-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] TRUE</span></span></code></pre></div></div>
</div>
<p>Wait, but didn’t I just write that R4.0.0 solves the problem? As of R4.4.2, it only does that for <code>read.table</code> and <code>data.frame</code>, but not <code>expand.grid</code></p>
<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">version<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>version.string</span>
<span id="cb15-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "R version 4.4.2 (2024-10-31)"</span></span>
<span id="cb15-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formals</span>(read.table)[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"stringsAsFactors"</span>]</span>
<span id="cb15-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $stringsAsFactors</span></span>
<span id="cb15-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] FALSE</span></span>
<span id="cb15-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formals</span>(data.frame)[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"stringsAsFactors"</span>]</span>
<span id="cb15-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $stringsAsFactors</span></span>
<span id="cb15-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] FALSE</span></span>
<span id="cb15-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formals</span>(expand.grid)[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"stringsAsFactors"</span>]</span>
<span id="cb15-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $stringsAsFactors</span></span>
<span id="cb15-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] TRUE</span></span></code></pre></div></div>
</div>
<p>You can of course change that by being explicit about not wanting factors, or use <code>tidyr::expand_grid</code> alternative instead:</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;">expand.grid</span>(</span>
<span id="cb16-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</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;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>),</span>
<span id="cb16-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">version =</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;">"cs"</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ss"</span>), </span>
<span id="cb16-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">choice_rule =</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;">"simple"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"softmax"</span>),</span>
<span id="cb16-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">stringsAsFactors =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span></span>
<span id="cb16-6">) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">give.attr =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb16-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; 'data.frame':    8 obs. of  3 variables:</span></span>
<span id="cb16-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ value      : num  1 100 1 100 1 100 1 100</span></span>
<span id="cb16-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ version    : chr  "cs" "cs" "ss" "ss" ...</span></span>
<span id="cb16-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ choice_rule: chr  "simple" "simple" "simple" "simple" ...</span></span>
<span id="cb16-11"></span>
<span id="cb16-12">tidyr<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;">expand_grid</span>(</span>
<span id="cb16-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">value =</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;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>),</span>
<span id="cb16-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">version =</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;">"cs"</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ss"</span>), </span>
<span id="cb16-15">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">choice_rule =</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;">"simple"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"softmax"</span>)</span>
<span id="cb16-16">) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">str</span>()</span>
<span id="cb16-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; tibble [8 × 3] (S3: tbl_df/tbl/data.frame)</span></span>
<span id="cb16-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ value      : num [1:8] 1 1 1 1 100 100 100 100</span></span>
<span id="cb16-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ version    : chr [1:8] "cs" "cs" "ss" "ss" ...</span></span>
<span id="cb16-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  $ choice_rule: chr [1:8] "simple" "softmax" "simple" "softmax" ...</span></span></code></pre></div></div>
</div>
</section>
<section id="consistency-is-important" class="level2">
<h2 class="anchored" data-anchor-id="consistency-is-important">Consistency is important</h2>
<p>Consistency is a loaded term. Whether two things are consistent or not necessarily depends on context and goals. One could go on and on about consistent naming styles, consistent argument order, consistent default behaviors, etc. At its core the problem with inconsistent design choices in a programming language is that it makes it very difficult, if not impossible, to build an accurate mental model of how the language works. It’s more than a bit ironic when programming languages, which are supposed to be the precise counterpart of natural languages, become a similar tangled mess of exceptions you need to learn by heart, just like irregular verbs in English.</p>
<p>The main example of this post concerns the concept of <a href="https://www.google.com/search?q=control+of+flow&amp;sourceid=chrome&amp;ie=UTF-8">control flow</a> in programming. Both if/else statements and case/switch statements aim to accomplish the same goal - to execute different parts of a program depending on a prespecified condition. One would expect that logical comparisons would work the same between those two constructs, but as this example has illustrated, they do not in R. Sometimes complex examples obscure the core problems, so let’s present it at it’s simplest form:</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">x <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="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Peace"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">levels =</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;">"Violence"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Peace"</span>))</span>
<span id="cb17-2"></span>
<span id="cb17-3">x <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Peace"</span></span>
<span id="cb17-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] TRUE</span></span>
<span id="cb17-5">x <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="cb17-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] FALSE</span></span>
<span id="cb17-7">x <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2"</span></span>
<span id="cb17-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] FALSE</span></span>
<span id="cb17-9"></span>
<span id="cb17-10"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">switch</span>(x, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Peace =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I choose peace"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">Violence =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"I choose violence"</span>)</span>
<span id="cb17-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Warning in switch(x, Peace = "I choose peace", Violence = "I choose violence"): EXPR is a "factor", treated as integer.</span></span>
<span id="cb17-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt;  Consider using 'switch(as.character( * ), ...)' instead.</span></span>
<span id="cb17-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "I choose violence"</span></span></code></pre></div></div>
</div>
<div style="text-align: center">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://venpopov.com/posts/2025/r-love-you-r-hate-you/i-choose-violence.gif" class="img-fluid figure-img"></p>
<figcaption>Was Cercei mislead by R? We’ll never know.</figcaption>
</figure>
</div>
</div>
<p>Logical comparisons treat factors as character vectors, switch comparisons treat them as integer values. This is not ok. Sure, you can learn that this is the case, but you shouldn’t have to.</p>
<p>These inconsistencies in base R have led to various attempts at reform, most notably through the tidyverse ecosystem. While the tidyverse strongly emphasizes consistency, it presents its own challenges. Breaking changes are frequent, forcing regular code updates and relearning of functionality. While useful for analysis code, the packages’ interdependence and heavy footprint make them difficult to justify in lightweight, stable package development. Many tidyverse functions on the surface appear to simply wrap basic R operations, unnecessarily expanding the language’s complexity. The tidyverse’s impact on the R community remains contentious, sparking numerous debates about its influence (see discussions <a href="https://github.com/matloff/TidyverseSkeptic">here</a>, <a href="https://www.tinyverse.org/">here</a>, <a href="https://www.reddit.com/r/rprogramming/comments/rd4ksl/i_am_concerned_about_the_tidyverse_and_its_impact/">here</a>, <a href="https://forum.posit.co/t/should-tidyeval-be-abandoned/2238/12">here</a>, <a href="https://blog.thecoatlessprofessor.com/programming/r/woes-of-the-rlang-enabled-tidyverse/index.html">here</a>). My own perspective has evolved from enthusiasm to skepticism, though I still use tidyverse tools when appropriate. I don’t want to throw away the baby along with the bathwater though - despite its drawbacks, the tidyverse gets one thing right: function APIs should be consistent and avoid “magic” behavior. R along with its predecessor S, is an old language, and a lot can be forgiven due to its legacy.</p>
<p>This situation mirrors broader patterns in programming language evolution. Consider C++: despite being a modern, widely-used language, it carries significant legacy baggage due to its strict backwards compatibility requirements. Newer languages like Rust, free from such constraints, can implement better defaults and more consistent behavior from the start. The tidyverse represents a curious case of attempting to fix a language’s shortcomings ‘from within’ rather than through dialect separation or replacement. While this approach maintains ecosystem cohesion, it creates a complex divide within the R community.</p>
<p>Python offers an instructive contrast in language evolution. Through semantic versioning, Python made major breaking changes between versions 2 and 3, prioritizing language improvement over backwards compatibility. While the transition wasn’t painless—Python 2 still claimed 25% usage a decade after Python 3’s 2008 release—by 2023, Python 2 usage had <a href="https://lp.jetbrains.com/python-developers-survey-2023/?_gl=1tz6z12_gcl_auNjAwNzE4MDIwLjE3NDAxNTU0NjI.FPAUNjAwNzE4MDIwLjE3NDAxNTU0NjI._gaMTAyNzAzMTQwOC4xNzQwMTU1NDYx_ga_9J976DJZ68*MTc0MDE1NTQ2MC4xLjEuMTc0MDE1NTQ5Ni4yNi4wLjA.#python-versions">dropped to just 6%</a> (and the transition plans were announced well in advance - PEP 3000 was published in 2006). R, conversely, maintains strong API stability between major versions. Many of the tidyverse’s consistency-oriented features could serve as excellent candidates for a base R rewrite if the community embraced the possibility of meaningful breaking changes in major versions. Yes, such transitions can be challenging—Python’s experience demonstrates this—but they also show that systematic language improvement is achievable with proper planning and community support.”</p>
</section>
<section id="what-now" class="level2">
<h2 class="anchored" data-anchor-id="what-now">What now?</h2>
<p>First, of course, is for me to sit down and do the annoying grunt work of going through all user-facing functions and ensuring that we test and validate every input. There’s a ton of ways to do it, either with vanilla R and custom functions (<code>stopifnot</code> and <code>match.arg</code> are your friends), or with the help of some validation packages I listed earlier. Then add more unit tests about edge cases to make sure things like this don’t happen. And so on… We have done this for a lot of our existing code, but as this example taught me, it’s easy to forget - and sometimes easy to not know.</p>
<p>Long-term, however, I’m becoming more and more interested in programming languages that use a strong static type system. I’ve long taught that static typing is simply annoying - as a <a href="../../2024/reproducibility-is-hard">self-taught</a> programmer, I haven’t had the benefit of learning things “the right way”. Over the last few months I’ve started digging deeper into programming as a core skill, exploring various languages and resources. I’m growing more and more attuned to the virtues of good type systems, for many other reasons. I’m sure another post on this topic is incoming at some time. In the meantime:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb18-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (language <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"R"</span>) {</span>
<span id="cb18-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stopifnot</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">inputs_are_carefully_validated</span>())</span>
<span id="cb18-3">}</span></code></pre></div></div>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2025,
  author = {Popov, Vencislav},
  title = {R Love You, {R} Hate You},
  date = {2025-02-21},
  url = {https://venpopov.com/posts/2025/r-love-you-r-hate-you/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2025" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2025. <span>“R Love You, R Hate You.”</span> February
21, 2025. <a href="https://venpopov.com/posts/2025/r-love-you-r-hate-you/">https://venpopov.com/posts/2025/r-love-you-r-hate-you/</a>.
</div></div></section></div> ]]></description>
  <category>R</category>
  <category>programming</category>
  <category>2025</category>
  <guid>https://venpopov.com/posts/2025/r-love-you-r-hate-you/</guid>
  <pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2025/r-love-you-r-hate-you/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Reduce friction for creating Quarto blog posts</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2024/reduce-friction-for-creating-quarto-blog-posts/</link>
  <description><![CDATA[ 






<p>My relationship with blogging is complicated. I made my first blog about 15 years ago, fresh out of high school. WordPress was the dominant platform at the time, and my blog wasn’t anything particularly interesting - I shared some vapid attempts at poetry and short story writing, some travel logs, and a mish-mash of other topics<sup>1</sup>.</p>
<p>I have never liked WordPress, for mostly the same reasons that I don’t like most software - I found every action incredibly cumbersome. Shortly thereafter I joined Facebook and Twitter, two platforms I no longer use, and most of my writing took on the “micro-blogging” nature that social media lends itself to so easily. I’ve always suspected that one of the reasons social media blew up was that it made it so easy to write, share, and follow content. At some point during my PhD (circa 2017), I created another WordPress blog, out of a desire to write longer pieces on more technical topics. That blog was again short-lived and the reason was simple - I wanted to write about programming, data analysis, and R, to share computational insights, but WordPress was a poor medium for it. At the time I was just starting to get enamored with literate programming (Jupyter notebooks were hot off the press), and the ease with which I could create computational writing contrasted starkly with the difficulty of sharing such writing on the internet.</p>
<p>Last year I discovered Quarto and I immediately became excited by the possibilities. I rebuilt my personal website with it and set up this blog to go with it. I expected to post a lot more often than I have done in reality. There are many reasons for this - from starting a new tenured position, to being a typical overworked, overextended, and overcommitted academic.</p>
<p>While I can’t do anything about that, I realized that one mental barrier arose every time I considered making a new post. Despite how easy Quarto makes it to publish technical material, there are a number of steps, at least in the way I have currently set up my system, that are just busywork, which I have to do any time I want to create a new post:</p>
<ul>
<li>navigate to my website directory on my computer</li>
<li>make a new sub-folder in the <code>posts</code> directory to hold the files for the blog post</li>
<li>create an <code>index.qmd</code> file in the new folder with a specific front matter boilerplate</li>
<li>write the post</li>
<li>execute <code>quarto render</code> to view it</li>
<li>commit and push my changes to update the website</li>
</ul>
<p>For some reason, the first three steps, as small as they appear, were a mental barrier that stopped me from even beginning new posts. Ideally, I want this to be as simple as possible. So I decided to write a bash script that automates it.</p>
<section id="a-bash-script-for-automating-post-creation" class="level2">
<h2 class="anchored" data-anchor-id="a-bash-script-for-automating-post-creation">A bash script for automating post creation</h2>
<p>The problem was that aside from some basic commands, my bash knowledge is quite limited. So I figured this was an opportunity to put Github Copilot to the test. I use Visual Studio Code as my IDE, and have setup my website repository as a workspace. I already had a markdown document that describes in plain language the steps I need to do when creating new blog posts<sup>2</sup>. So could the built-in Copilot in VSCode write me a bash script just by being told to implement the steps I’ve described in my little instruction manual?</p>
<p>It worked flawlessly, and this code was the result after a few iterations:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#!/bin/bash</span></span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Prompt for post details</span></span>
<span id="cb1-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">read</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-p</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Enter post title: "</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">title</span></span>
<span id="cb1-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">read</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-p</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Enter post subtitle: "</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">subtitle</span></span>
<span id="cb1-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">read</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-p</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Enter post categories (comma-separated): "</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">categories</span></span>
<span id="cb1-7"></span>
<span id="cb1-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Convert title to a slug for the folder name</span></span>
<span id="cb1-9"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">slug</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$(</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$title</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tr</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'[:upper:]'</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'[:lower:]'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tr</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-cd</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'[:alnum:] '</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tr</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;">'-'</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb1-10"></span>
<span id="cb1-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create the new post directory</span></span>
<span id="cb1-12"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">year</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$(</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">date</span> +%Y<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb1-13"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">post_dir</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$HOME</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">/venpopov.com/posts/</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$year</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">/</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$slug</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-14"></span>
<span id="cb1-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Check if the directory already exists</span></span>
<span id="cb1-16"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">[</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">-d</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$post_dir</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">]</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">;</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb1-17">  <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Error: The directory </span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$post_dir</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> already exists."</span></span>
<span id="cb1-18">  <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">exit</span> 1</span>
<span id="cb1-19"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">fi</span></span>
<span id="cb1-20"></span>
<span id="cb1-21"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mkdir</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-p</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$post_dir</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-22"></span>
<span id="cb1-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Get today's date</span></span>
<span id="cb1-24"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">date</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$(</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">date</span> +%Y-%m-%d<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb1-25"></span>
<span id="cb1-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Append the current year to the categories list</span></span>
<span id="cb1-27"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">categories</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$categories</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">, </span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$year</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-28"></span>
<span id="cb1-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create the .qmd file with the provided metadata</span></span>
<span id="cb1-30"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;&lt;EOF</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$post_dir</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">/index.qmd"</span></span>
<span id="cb1-31"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb1-32"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">title: "</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$title</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-33"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">subtitle: "</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$subtitle</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-34"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">categories: [</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$(</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$categories</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sed</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'s/,/, /g'</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">)</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb1-35"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">date: "</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$date</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-36"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb1-37"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">EOF</span></span>
<span id="cb1-38"></span>
<span id="cb1-39"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"New post created at </span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$post_dir</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">/index.qmd"</span></span>
<span id="cb1-40"></span>
<span id="cb1-41"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Open the new post in the default editor</span></span>
<span id="cb1-42"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">code</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$post_dir</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">/index.qmd"</span></span></code></pre></div></div>
<p>I saved this code in a file <code>new_blog_post</code> and Copilot instructed me that I can make it executable from the command line by first setting file permission via:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">chmod</span> +x new_blog_post</span></code></pre></div></div>
<p>I saved this file in a directory on my path, so now when I want to create a new blog post, I simply open a terminal and type</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">new_blog_post</span></span></code></pre></div></div>
<p>This prompts me to enter a title for the post, a subtitle and some tags, then creates all the necessary boilerplate and opens the file in VSCode for editing. In fact, I used it to make this very post, and for example, here is the terminal output from it</p>
<p><img src="https://venpopov.com/posts/2024/reduce-friction-for-creating-quarto-blog-posts/images/paste-1.png" class="img-fluid"></p>
<p>Neat. Will this help me post more often? Time will tell.</p>
</section>
<section id="postscript-ai-and-learning-to-code" class="level2">
<h2 class="anchored" data-anchor-id="postscript-ai-and-learning-to-code">Postscript: AI and learning to code</h2>
<p>I tell my students that they can use AI tools when working on projects, but I urge them that it is still important to learn how to code for many reasons. They need to understand what the code is doing, at the very least, and know how to fix it. Did I follow my own advice here? Ugh, maybe not, depending on the perspective. I still have no idea about the core bash syntax and how to write a script from scratch. But I do have a “lifetime” of experience with programming, and I could understand what the script was doing, and I could modify it to suit my needs. I think that’s the important part. I don’t need to know everything about bash scripting, but I need to know enough to be able to use it effectively. In any case, I have a tendency to fall into rabbit holes when learning new things, and this is the last thing I want to do right now while trying to optimize my workflow! So, I have a working script, I asked Copilot to add some reasonable checks that occurred to me, and I know what each line does, even if I don’t know how to write it from scratch. I think that’s good enough for now.</p>


</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>To my huge surprise, the <a href="https://addic7ed2life.wordpress.com/">blog</a> still exists! The last entry is from 2013, and I hadn’t looked at it probably since then. I only rediscovered it now while writing this post.↩︎</p></li>
<li id="fn2"><p>I write these types of “Notes to self” instruction manuals for many things, because my memory is very poor for procedural operations↩︎</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><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2024,
  author = {Popov, Vencislav},
  title = {Reduce Friction for Creating {Quarto} Blog Posts},
  date = {2024-11-17},
  url = {https://venpopov.com/posts/2024/reduce-friction-for-creating-quarto-blog-posts/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2024" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2024. <span>“Reduce Friction for Creating Quarto Blog
Posts.”</span> November 17, 2024. <a href="https://venpopov.com/posts/2024/reduce-friction-for-creating-quarto-blog-posts/">https://venpopov.com/posts/2024/reduce-friction-for-creating-quarto-blog-posts/</a>.
</div></div></section></div> ]]></description>
  <category>workflow</category>
  <category>shell</category>
  <category>2024</category>
  <guid>https://venpopov.com/posts/2024/reduce-friction-for-creating-quarto-blog-posts/</guid>
  <pubDate>Sun, 17 Nov 2024 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2024/reduce-friction-for-creating-quarto-blog-posts/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Rethinking my approach to computational projects and reproducibility</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2024/reproducibility-is-hard/</link>
  <description><![CDATA[ 






<p>I have to finally admit it to myself - my research workflows have been breaking down and it’s time to get serious about change. I’ve been trying to incorporate better coding practices, modern reporting pipelines, and reproducible workflows into my research projects, but it’s been a struggle. The complexity of the tools, the sheer number of moving parts, and the interactions between them have been overwhelming. I’ve spent more time debugging my workflow than actually doing research. There has to be a better way.</p>
<section id="the-comfort-of-old-habits" class="level2">
<h2 class="anchored" data-anchor-id="the-comfort-of-old-habits">The Comfort of Old Habits</h2>
<p>For many years I had relatively stable workflows for project organization and version control of my research projects. They were never perfect, and given the state of reproducibility in academic psychology, likely much better than the status quo. <a href="https://github.com/venpopov/109-relational-lures">Since 2014</a>, I’ve been using systematic project template folders, project-based organization, programmatic data wrangling, version control with Git and GitHub, and I’ve dabbled in literate programming tools such as Jupyter Notebooks and R Markdown. <a href="https://github.com/venpopov/DataSciencePsychUZH">I’ve taugth these tools in graduate courses</a>. I knew my way around a command line, and on the rare occasions I needed it, I could run demanding analyses on the Carnegie Mellon computing cluster. I was able to get things done efficiently while maintaining a somewhat decent level of reproducibility.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://venpopov.com/posts/2024/reproducibility-is-hard/folder-structure.png" class="img-fluid figure-img"></p>
<figcaption>I’ve been using the same project structure for 10+ years</figcaption>
</figure>
</div>
<p>My comfortable routine started changing last year, gradually at first, as I began working on my <a href="../introducing-bmm/">first R package</a>. What began as a side project quickly evolved into something that preoccupied me for months. This was partly because I had <a href="https://www.gfrischkorn.org/">a fantastic collaborator</a>, working with whom was an exhilarating, idea-bootstrapping experience. But collaborating also meant I had to up my game — I couldn’t rely on the shortcuts and hacks I’d developed over the years. For the first time, I had to learn about proper documentation, testing, and collaborative workflows.</p>
<p>When you’re working on a package, you’re not just writing code for yourself—you’re writing code for others to use. This means writing clear and concise documentation, creating tests to ensure your code works as expected, and developing code that is modular and easy to understand. And you have to do it in a way that makes collaboration less painful.</p>
</section>
<section id="the-pitfalls-of-self-taught-coding" class="level2">
<h2 class="anchored" data-anchor-id="the-pitfalls-of-self-taught-coding">The Pitfalls of Self-Taught Coding</h2>
<p>The painful truth is that proper computational skills are rarely taught in academic programs (at least in psychology). Many of us are self-taught, each with our own quirky ways of doing things. Tools and processes that are standard fare in software development are often foreign to us. So, we muddle through, doing the best we can with what we have. My use of GitHub was a glorified Dropbox, and my coding practices, if you could call them that, were a mishmash of concepts I picked up from various tutorials and blog posts. It mostly worked, but even now I am barely able to reproduce my own work from a few years ago. Broken package dependencies, uncertain code order, and the utter lack of systematic documentation have made my old projects a nightmare to revisit.</p>
<p><img src="https://venpopov.com/posts/2024/reproducibility-is-hard/gandalf.png" class="img-fluid"></p>
<p>Maybe I’m exaggerating a bit, but the reality is that as my career has progressed, my projects have grown in number and complexity, and it’s become more and more frustrating to keep track of everything. My existing workflow sat squarely in the middle of <a href="https://raps-with-r.dev/part2_intro.html#the-reproducibility-iceberg">“the reproducibility iceberg”</a> - better than most, but I was starting to feel cold.</p>
<p>Thankfully, while formal education in this area still lags behind, the online landscape is now rich with <a href="https://github.com/venpopov/reproducible-research">resources</a>. <a href="https://venpopov.github.io/ltm-time-benefit/">Over the last year</a>, I’ve been trying to incorporate <a href="https://adv-r.hadley.nz/fp.html">better coding practices</a>, <a href="https://quarto.org/">Quarto</a> websites reporting for projects, and <a href="https://rstudio.github.io/renv/">renv</a> for package management. It’s been an uphill battle, consuming a lot of time and energy.</p>
</section>
<section id="the-tension-between-efficiency-and-reproducibility" class="level2">
<h2 class="anchored" data-anchor-id="the-tension-between-efficiency-and-reproducibility">The Tension Between Efficiency and Reproducibility</h2>
<p>The hardest part is not learning new tools but unlearning old habits and deconstructing my mental models of code, data, and reporting. One of the reasons I love coding in R is the incredibly quick iteration cycle and feedback loop. The ability to have an idea and, within minutes, simulate, visualize, and analyze it often feels like a superpower.</p>
<p>The problem is that this same superpower also makes it so easy to be sloppy. Who has time and patience for carefully curating a reproducible workflow when that puts a delay between your idea and its realization?</p>
<p>The thing is, I am no longer a grad student chasing down any odd idea that comes my way. As fun as the wild west of coding can be, it’s not sustainable for a long-term research program, especially when other people depend on you. And let’s be honest, I also need to be kinder to my future self.</p>
</section>
<section id="what-a-mess" class="level2">
<h2 class="anchored" data-anchor-id="what-a-mess">What a Mess</h2>
<p>Case in point: I just resumed work on a simulation project that I last touched in April. Despite all my best intentions, I was shocked to find that I couldn’t reproduce a set of figures I had sent to my collaborator at the time. Even worse, it took me a full day to even figure out what I was doing back then.</p>
<p>Why did this happen? There are many culprits, but part of it is that while I coded all the simulation scripts locally, I ran the simulations on a cluster because they were very computationally demanding. At the time, I had no established workflow for sharing intermediate data objects between my remote and local codebases. My attempts to reconstruct what had happened have driven me to the brink of madness. Trust me when I say that I’ve spent more time trying to figure out what I did than it would have taken me to redo everything from scratch.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://venpopov.com/posts/2024/reproducibility-is-hard/what-a-mess.png" class="img-fluid figure-img"></p>
<figcaption>What a mess…</figcaption>
</figure>
</div>
<p>To avoid scenarios exactly like this, in the last six months I’ve been experimenting with <a href="https://books.ropensci.org/targets/">the targets package</a>. Targets is a pipeline toolkit for R that helps you manage the dependencies between your scripts and data objects. It’s a bit like <a href="https://en.wikipedia.org/wiki/Make_(software)">make</a> for R, but with a lot of bells and whistles. I even implemented it for <a href="https://venpopov.github.io/ltm-time-benefit/">a couple of other projects</a>.</p>
<p>When combined with <code>renv</code> for package management, and <code>Quarto</code> for reporting, it comes close to what I imagine as a nearly ideal scenario: A self-contained research website, with all the code, data, and results in one place, and a clear, reproducible workflow that can be run on any machine. A modern reporting pipeline that is both transparent and efficient.</p>
</section>
<section id="the-complexity-conundrum" class="level2">
<h2 class="anchored" data-anchor-id="the-complexity-conundrum">The Complexity Conundrum</h2>
<p><em>When it works.</em> Sigh.</p>
<p>Maybe I’m just getting old, but putting together all these pieces has proven to be a lot more challenging than I anticipated. The learning curve is steep. These are all <em>fantastic</em> tools, with good documentation, and in some respects I feel lucky to be working in a time when such tools are becoming widely available, and there are so many people voluntarily developing them. The open source community is truly a marvel. But the sheer number of moving parts, and the complexity of the interactions between them, is overwhelming.</p>
<p>Git + Github + renv + Quarto + targets + crew + deployment + credential management + testing + documentation + collaboration + teaching + writing + thinking + living. It’s a lot.</p>
<p>Ok, I got carried away with the list, but even when just considering the computational parts, each of them comes with lengthy manuals, quirks, and a dizzying amount of options and configurations, and they don’t always play nice with each other. I feel like I’m constantly fighting fires, and my self-help tutorials are getting longer and longer. These past 6 months, I’ve spent more time debugging my workflow than actually doing research.</p>
<p>I’m not giving up, though. This <em>is</em> a path worth walking. I just can’t help but feel there must be a better way. I’ve been trying to code up a personal library of tools to automate some of that complexity, but other things keep getting in the way. I just can’t help but compare this state of affairs to the relatively much smoother package development workflows, largely thanks to <code>devtools</code> and <code>usethis</code> packages, as well as <a href="https://r-pkgs.org/">the consistent framework around it</a>. Package development is not any less complex in scope, but the community has managed to converge on an integrated consistent workflow that just works.</p>
<p>Part of this is the nature of the beast - research projects are by definition more diverse and less predictable than package development. Every project is a new adventure, with its own unique challenges and requirements, and formats vary so widely between disciplines or even subfields. Part of it is also that these tools are still <em>relatively</em> new.</p>
</section>
<section id="the-signal-to-noise-ratio" class="level2">
<h2 class="anchored" data-anchor-id="the-signal-to-noise-ratio">The Signal-to-Noise Ratio</h2>
<p>But even if all the quirks were ironed out, and the process streamlined, another issue, that I rarely see discussed, is just how much more “irrelevant” artefacts are produced in the codebase. When I look <a href="https://venpopov.github.io/ltm-time-benefit/">at my best attempts to implement the full workflow</a>, I am a bit paralized by the sheer amount of files and code in my repositories that is not directly related to the substance of the research. There are layers upon layers of abstraction, scaffolding, configuration files, and helper functions that are just necessary for the workflow to function.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://venpopov.com/posts/2024/reproducibility-is-hard/its-a-lot.png" class="img-fluid figure-img"></p>
<figcaption>It’s a lot…</figcaption>
</figure>
</div>
<p>Targets itself, for all its truly wonderful functionality, suffers (or at least I do!) from 1) an incredible level of syntax verbosity and 2) too many ways to do the same thing, both of which make it hard to read and understand the code. Flexibility is a double-edged sword.</p>
<p>Even in an ideal world in which I finally learn all the ins and outs of these packages, I can’t help but wonder: does the improved reproducibility and transparency pay off if barely anyone else can understand what’s going on? If I share the code with expert colleagues, I doubt many of them would be able to make sense of it given the signal-to-noise ratio in the codebase. It might as well be written in a different programming language given the layers of abstraction. This perhaps is a temporary problem, as the tools mature and the community converges on best practices, but it’s a real one.</p>
<p>This is not a critique of the tools themselves, but rather a reflection on the complexity of the research process. The tools are a reflection of the complexity of the problem they are trying to solve. But there has to be a better way.</p>
<p><em>PS: This post took an unexpected direction. I was planning to write a short introduction to my frustrations, and a detailed guide to targets as I figured out a how to apply it to a new project. I learn best by writing and teaching, and I was hoping that by writing a tutorial I would solidify my understanding of the package. As often happens in writing though, our thoughts, especially those bottled up frustrations, tend to take a life of their own. I guess I might still write the more technical post I was imagining later</em></p>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2024,
  author = {Popov, Vencislav},
  title = {Rethinking My Approach to Computational Projects and
    Reproducibility},
  date = {2024-11-17},
  url = {https://venpopov.com/posts/2024/reproducibility-is-hard/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2024" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2024. <span>“Rethinking My Approach to Computational
Projects and Reproducibility.”</span> November 17, 2024. <a href="https://venpopov.com/posts/2024/reproducibility-is-hard/">https://venpopov.com/posts/2024/reproducibility-is-hard/</a>.
</div></div></section></div> ]]></description>
  <category>workflow</category>
  <category>reproducibility</category>
  <category>R</category>
  <category>git</category>
  <category>targets</category>
  <category>project management</category>
  <category>2024</category>
  <guid>https://venpopov.com/posts/2024/reproducibility-is-hard/</guid>
  <pubDate>Sun, 17 Nov 2024 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2024/reproducibility-is-hard/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Taking a stand on open peer review</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2024/taking-a-stand-on-open-peer-review/</link>
  <description><![CDATA[ 






<p>I have spent the last few months thinking about academic publishing and the peer review system. The whole thing is rotten and no sane person would design the system we have today. Now that I find myself in a professional position where I don’t have to worry about my future, I realize I can take actions to help dismantle this system. In other words, I can finally afford to act on my principles (as absurd as that sounds).</p>
<p>I am not yet ready to share the full extent of what this entails, and I plan a longer post about the future of academic publishing. But last week was the first time I took action consistent with these principles, specifically my belief that all peer review should be open, published alongside the work it reviews. I received a standard review request from a respected APA journal, and this was my response:</p>
<blockquote class="blockquote">
<p>Dear XXX,</p>
<p>Thank you for inviting me to review this submission. I would be glad to review the manuscript if my review and the subsequent author responses are published alongside the paper, should it be accepted.</p>
<p>I understand this is not the current practice at XXX, but I firmly believe that open review is the future of academic publishing. It is a responsibility we owe to the public that funds our research. This is not about receiving credit for my review work but about promoting transparency and accountability in the peer review process. Many modern publishing platforms already support this practice.</p>
<p>I appreciate that finding reviewers is challenging and that this request might make your job as an editor more difficult.</p>
<p>Best regards,<br>
Ven</p>
</blockquote>
<p>Will this have any effect? I doubt it. The system is too rigid. I expect that the editor would ask me to reconsider, and move on to other reviewers. Which is unfortunate, because I really don’t want to add more burden to a difficult job. But change has to start from somewhere.</p>
<p>Update: As I suspected, the Editor sent me a polite email saying that the journal currently does not allow this, but that they will keep reviewing their policies as practices in the field change.</p>



<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2024,
  author = {Popov, Vencislav},
  title = {Taking a Stand on Open Peer Review},
  date = {2024-08-04},
  url = {https://venpopov.com/posts/2024/taking-a-stand-on-open-peer-review/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2024" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2024. <span>“Taking a Stand on Open Peer
Review.”</span> August 4, 2024. <a href="https://venpopov.com/posts/2024/taking-a-stand-on-open-peer-review/">https://venpopov.com/posts/2024/taking-a-stand-on-open-peer-review/</a>.
</div></div></section></div> ]]></description>
  <category>academic publishing</category>
  <category>peer review</category>
  <category>open science</category>
  <category>2024</category>
  <guid>https://venpopov.com/posts/2024/taking-a-stand-on-open-peer-review/</guid>
  <pubDate>Sun, 04 Aug 2024 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2024/taking-a-stand-on-open-peer-review/images/venpopov.png" medium="image" type="image/png"/>
</item>
<item>
  <title>Introducing the Bayesian Measurement Modeling R Package (bmm)</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2024/introducing-bmm/</link>
  <description><![CDATA[ 






<p>I am excited to announce the first official release of the <a href="https://cran.r-project.org/web/packages/bmm/index.html">Bayesian Measurement Modeling R package</a>! <code>bmm</code> makes Bayesian measurement modeling in psychology accessible to all. Over the past two years, <a href="https://www.gfrischkorn.org">Gidon Frischkorn</a> and I worked closely together on this package and we are thrilled to finally share it with the world.</p>
<p>Aside from the final result, this collaboration was the most fun I’ve had doing research in a long time. I stopped counting how many days we started a random unplanned coffee chat and to suddenly realize that it’s been three hours of intense problem-solving, only because one of us was actually late for a real meeting. <code>bmm</code> is truly both of our baby (only slightly older that Gidon’s soon-to-be second flesh-and-blood daughter!) and we hope it will make fitting Bayesian measurement models easier, more reliable, and more efficient for everyone.</p>
<p>You can find detailed documentation, tutorials and examples on the <a href="https://venpopov.github.io/bmm/index.html">package website</a>. You can install <code>bmm</code> from CRAN as follows:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" 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;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bmm"</span>)</span></code></pre></div></div>
<section id="what-is-bmm-for" class="level2">
<h2 class="anchored" data-anchor-id="what-is-bmm-for">What is bmm for?</h2>
<p><code>bmm</code> provides a simple and intuitive interface for fitting measurement models in psychology using Bayesian methods. It is designed to be accessible to researchers with little to no experience with Bayesian statistics or cognitive modeling. If you know how to fit a mixed-effects regression model in R, you already know nearly everything you need to fit a measurement model with <code>bmm</code>.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://venpopov.com/posts/2024/introducing-bmm/bmmLogic.jpg" class="img-fluid quarto-figure quarto-figure-center figure-img"></p>
</figure>
</div>
<p>But first things first: <em>what are measurement models and why do we need them?</em></p>
<p>If you are a meteorologist, you can measure atmospheric temperature with a thermometer. And while thermometers are <em>technically</em> indirect measures, the relationship between the volume of a liquid and its temperature is so strong and so well understood that we take it for granted.</p>
<p>If you are a psychologist, things get a bit more complicated.</p>
<p>Behavioral and psychological data are messy and only rough proxies for what they are supposed to measure. We rarely care about how much time it takes someone to press a button after a flashing light, how precisely they can remember a specific shade of blue, or how similar they judge two badly drawn alien animals to be. Such data are only interesting insofar as they tell us something about the underlying psychological processes that govern attention, perception, decision making, memory and categorization.</p>
<p>To make matters even more difficult, usually multiple distinct cognitive processes contribute to behavior. Combined, these issues often make it difficult to draw clear conclusions from behavioral data alone. Running t.tests on averaged raw behavioral data can only get you so far. Measurement models are an important tool to bridge the gap between latent psychological constructs and observable behavioral data.</p>
<p>Such measurement models are nowadays used in many different areas of psychology. Some of the more popular models include drift diffusion models in decision making, signal detection theory models in perception and memory, and mixture models in visual working memory. The basic idea uniting these approaches is that we can decompose one or more observed measures (e.g., reaction times, accuracy rates, angular deviation, confidence ratings) into distinct theoretically meaningful parameters that reflect latent psychological processes (e.g., decision thresholds, different memory strength signals, the quality or precision of representations). These derived parameters can then be used to test hypotheses about the underlying cognitive processes that govern behavior.</p>
</section>
<section id="the-challenges-of-measurement-modeling" class="level2">
<h2 class="anchored" data-anchor-id="the-challenges-of-measurement-modeling">The challenges of measurement modeling</h2>
<div class="quarto-layout-panel" data-layout="[[70,30],[100]]">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 70.0%;justify-content: flex-start;">
<p>Unfortunately, technical challenges have prevented widespread adoption of measurement models for data analysis in psychology. And even when researchers do use these models, many (*<em>cough</em> *) rely on ad-hoc custom implementations that are not well-documented, not well-tested, not well-understood, not easily portable to new experiments, and not using optimal inference methods. This is where <code>bmm</code> comes in.</p>
</div>
<div class="quarto-layout-cell" style="flex-basis: 30.0%;justify-content: center;">
<p><img src="https://venpopov.com/posts/2024/introducing-bmm/i_mean_me.gif" class="img-fluid" width="200"></p>
</div>
</div>
</div>
<p>Traditionally, to fit a measurement model in psychology, you had to build it yourself. The simple reality is, however, that most researchers don’t have the time, resources, or expertise to build and fit these models from scratch. This is doubly true for Bayesian hierarchical implementations, which require a different set of tools and skills than traditional maximum likelihood estimation. And even for those who do have the skills, the process can be time-consuming and error-prone.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://venpopov.com/posts/2024/introducing-bmm/confused.gif" class="img-fluid quarto-figure quarto-figure-center figure-img"></p>
</figure>
</div>
<p>This is why we started working on the <code>bmm</code> package in the first place - we were tired of doing the same thing over and over again for every new project. What started as a personal project to make our own daily work easier has now turned into a fully-fledged R package. What we have built is a package that allows you to fit a wide range of measurement models with just a few lines of code.</p>
<p>You no longer need to:</p>
<ul>
<li>copy-paste custom Jags or Stan code from one project to the next (or worse, try to decipher someone else’s code!) and painstakingly adjust it for your new experiment</li>
<li>worry about whether you have correctly specified your priors or likelihoods</li>
<li>worry about whether you have correctly implemented your model</li>
<li>worry about how to adjust the script for your new experimental design and to spend hours debugging it</li>
</ul>
<p>The <code>bmm</code> package takes care of all of that for you.</p>
<p>While there exist tools for some measurement models, such as for drift diffusion models (via the wiener distribution in <code>brms</code> for <code>R</code> or via the HDDM package for <code>Python</code>), this is not the case for the vast majority of measurement models used in psychology. The <code>bmm</code> package aims to fill this gap by providing a general framework that can be extended and continuously improved by the community.</p>
</section>
<section id="example---the-interference-measurement-model" class="level2 page-columns page-full">
<h2 class="anchored" data-anchor-id="example---the-interference-measurement-model">Example - the Interference Measurement Model</h2>
<p>To give you a sense of how easy it is to fit a model using the <code>bmm</code> package, let’s walk through an example. Let’s say you have collected data from <a href="https://venpopov.github.io/bmm/articles/bmm_vwm_crt.html">a continuous reproduction</a> visual working memory task (see the margin for a visual representation of the task and the distribution of the data).</p>

<div class="no-row-height column-margin column-container"><div class="">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="vwm-crt.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Participants study 1 to 8 colors in different locations. After a delay, they reproduce one of the colors as precisely as they can on a color wheel. Click image to enlarge."><img src="https://venpopov.com/posts/2024/introducing-bmm/vwm-crt.png" class="img-fluid figure-img" alt="Participants study 1 to 8 colors in different locations. After a delay, they reproduce one of the colors as precisely as they can on a color wheel. Click image to enlarge."></a></p>
<figcaption>Participants study 1 to 8 colors in different locations. After a delay, they reproduce one of the colors as precisely as they can on a color wheel. <em>Click image to enlarge.</em></figcaption>
</figure>
</div>
<div class="cell">
<div class="cell-output-display">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="index_files/figure-html/error_plot-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Density plots of the deviation from the target color in radians, split by set size. Click image to enlarge."><img src="https://venpopov.com/posts/2024/introducing-bmm/index_files/figure-html/error_plot-1.png" class="img-fluid figure-img" width="240" alt="Density plots of the deviation from the target color in radians, split by set size. Click image to enlarge."></a></p>
<figcaption>Density plots of the deviation from the target color in radians, split by set size. <em>Click image to enlarge.</em></figcaption>
</figure>
</div>
</div>
</div>
</div></div><p>Assume you want to fit the <a href="https://venpopov.github.io/bmm/articles/bmm_imm.html">Interference Measurement Model (IMM)</a><span class="citation" data-cites="oberauer2017">(Oberauer et al. 2017)</span> to your data. For convenience, we have included the data from this study in the <code>bmm</code> package, so we can use it directly and fit the model to it. The IMM model attributes errors to different sources of activation in memory: target features, non-target features, the spacial distance between them, and random noise<sup>1</sup>.</p>
<p>Before we go into the different parts, here is the entire code that you would need to fit the IMM model to the data. Yes, it’s that simple:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(bmm)</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># load the data</span></span>
<span id="cb2-4">my_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> oberauer_lin_2017</span>
<span id="cb2-5"></span>
<span id="cb2-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># inform the model about the data structure</span></span>
<span id="cb2-7">imm_model <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;">imm</span>(</span>
<span id="cb2-8">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">resp_error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dev_rad"</span>,</span>
<span id="cb2-9">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nt_features =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"col_nt"</span>,</span>
<span id="cb2-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nt_distances =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dist_nt"</span>,</span>
<span id="cb2-11">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">set_size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"set_size"</span>,</span>
<span id="cb2-12">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">version =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"full"</span>,</span>
<span id="cb2-13">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">regex =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb2-14">)</span>
<span id="cb2-15"></span>
<span id="cb2-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># specify the regression formula for the model parameters</span></span>
<span id="cb2-17">imm_formula <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;">bmmformula</span>(</span>
<span id="cb2-18">  c <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID),</span>
<span id="cb2-19">  a <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID),</span>
<span id="cb2-20">  s <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID),</span>
<span id="cb2-21">  kappa <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID)</span>
<span id="cb2-22">)</span>
<span id="cb2-23"></span>
<span id="cb2-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># fit the model via `brms` and `Stan`</span></span>
<span id="cb2-25">imm_fit <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;">bmm</span>(</span>
<span id="cb2-26">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">formula =</span> imm_formula,</span>
<span id="cb2-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> my_data,</span>
<span id="cb2-28">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> imm_model,</span>
<span id="cb2-29">  <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="cb2-30">)</span></code></pre></div></div>
<p>Let’s take a brief look at the data we are working with. In this case, the dependent variable is <code>dev_rad</code> - the deviation of the response from the target color (in radians). <code>col_nt1</code> to <code>col_nt7</code>are the colors of the non-target items which were studied but are not being tested (coded relative to the target). Finally, <code>dist_nt1</code> to <code>dist_nt7</code> are the spatial distances of the non-target items from the target item, and <code>set_size</code> is the number of items in the display.</p>
<div class="cell">
<div class="cell-output-display">
<table class="caption-top table table-sm table-striped small">
<colgroup>
<col style="width: 1%">
<col style="width: 4%">
<col style="width: 3%">
<col style="width: 5%">
<col style="width: 6%">
<col style="width: 5%">
<col style="width: 6%">
<col style="width: 6%">
<col style="width: 5%">
<col style="width: 6%">
<col style="width: 5%">
<col style="width: 4%">
<col style="width: 5%">
<col style="width: 5%">
<col style="width: 5%">
<col style="width: 5%">
<col style="width: 5%">
<col style="width: 5%">
<col style="width: 5%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: right;">ID</th>
<th style="text-align: left;">session</th>
<th style="text-align: right;">trial</th>
<th style="text-align: left;">set_size</th>
<th style="text-align: right;">dev_rad</th>
<th style="text-align: right;">col_nt1</th>
<th style="text-align: right;">col_nt2</th>
<th style="text-align: right;">col_nt3</th>
<th style="text-align: right;">col_nt4</th>
<th style="text-align: right;">col_nt5</th>
<th style="text-align: right;">col_nt6</th>
<th style="text-align: right;">col_nt7</th>
<th style="text-align: right;">dist_nt1</th>
<th style="text-align: right;">dist_nt2</th>
<th style="text-align: right;">dist_nt3</th>
<th style="text-align: right;">dist_nt4</th>
<th style="text-align: right;">dist_nt5</th>
<th style="text-align: right;">dist_nt6</th>
<th style="text-align: right;">dist_nt7</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;">1</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">1</td>
<td style="text-align: left;">7</td>
<td style="text-align: right;">0.3839724</td>
<td style="text-align: right;">0.8726646</td>
<td style="text-align: right;">0.8552113</td>
<td style="text-align: right;">2.7750735</td>
<td style="text-align: right;">2.094395</td>
<td style="text-align: right;">1.2566371</td>
<td style="text-align: right;">0.0698132</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">1.9332878</td>
<td style="text-align: right;">2.416610</td>
<td style="text-align: right;">1.9332878</td>
<td style="text-align: right;">0.4833219</td>
<td style="text-align: right;">0.9666439</td>
<td style="text-align: right;">2.899932</td>
<td style="text-align: right;">NA</td>
</tr>
<tr class="even">
<td style="text-align: right;">1</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">2</td>
<td style="text-align: left;">3</td>
<td style="text-align: right;">-0.4537856</td>
<td style="text-align: right;">0.8552113</td>
<td style="text-align: right;">1.9547688</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">1.9332878</td>
<td style="text-align: right;">2.416610</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
</tr>
<tr class="odd">
<td style="text-align: right;">1</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">3</td>
<td style="text-align: left;">5</td>
<td style="text-align: right;">-0.0872665</td>
<td style="text-align: right;">0.7155850</td>
<td style="text-align: right;">-2.6179939</td>
<td style="text-align: right;">-1.0297443</td>
<td style="text-align: right;">1.378810</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">0.9666439</td>
<td style="text-align: right;">2.899932</td>
<td style="text-align: right;">2.4166097</td>
<td style="text-align: right;">1.9332878</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
</tr>
<tr class="even">
<td style="text-align: right;">1</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">4</td>
<td style="text-align: left;">6</td>
<td style="text-align: right;">0.3665191</td>
<td style="text-align: right;">0.2617994</td>
<td style="text-align: right;">2.0420352</td>
<td style="text-align: right;">0.1047198</td>
<td style="text-align: right;">1.099557</td>
<td style="text-align: right;">-0.9250245</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">0.4833219</td>
<td style="text-align: right;">2.899932</td>
<td style="text-align: right;">0.9666439</td>
<td style="text-align: right;">0.9666439</td>
<td style="text-align: right;">1.9332878</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
</tr>
<tr class="odd">
<td style="text-align: right;">1</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">5</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">-0.0349066</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
</tr>
<tr class="even">
<td style="text-align: right;">1</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">6</td>
<td style="text-align: left;">1</td>
<td style="text-align: right;">0.1396263</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
<td style="text-align: right;">NA</td>
</tr>
</tbody>
</table>
</div>
</div>
<p><br></p>
<p>To fit the IMM model to this data, we need to follow a few simple steps:</p>
<section id="specify-the-model" class="level3">
<h3 class="anchored" data-anchor-id="specify-the-model">1) Specify the model</h3>
<p>First, set up the <code>bmmodel</code> object to specify which variables in your data contain information about the identity of the target and non-target features, the distances between them, and the set size of the display. For the IMM model, this would look like this:</p>
<div class="cell">
<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">imm_model <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;">imm</span>(</span>
<span id="cb3-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">resp_error =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dev_rad"</span>,</span>
<span id="cb3-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nt_features =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"col_nt"</span>,</span>
<span id="cb3-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">nt_distances =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dist_nt"</span>,</span>
<span id="cb3-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">set_size =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"set_size"</span>,</span>
<span id="cb3-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">version =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"full"</span>,</span>
<span id="cb3-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">regex =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span></span>
<span id="cb3-8">)</span></code></pre></div></div>
</div>
<p>Here we used the <code>regex</code> option to specify that columns which begin with <code>col_nt</code> and <code>dist_nt</code> should be treated as non-target features and distances, respectively. This is useful when you have multiple non-target features and distances in your data (instead of enumerating them all).</p>
</section>
<section id="specify-the-regression-formula-for-the-model-parameters" class="level3">
<h3 class="anchored" data-anchor-id="specify-the-regression-formula-for-the-model-parameters">2) Specify the regression formula for the model parameters</h3>
<p>Second, specify how the parameters of the model should vary over different conditions. A list of all model parameters and their meaning is saved in the model object and can be accessed using the <code>parameters</code> element.</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-overflow-wrap code-with-copy"><code class="sourceCode r"><span id="cb4-1">imm_model<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>parameters</span>
<span id="cb4-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $mu1</span></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Location parameter of the von Mises distribution for memory responses (in radians). Fixed internally to 0 by default.</span></span>
<span id="cb4-4"><span class="co" 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;">#&gt; $kappa</span></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "Concentration parameter of the von Mises distribution"</span></span>
<span id="cb4-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb4-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $a</span></span>
<span id="cb4-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "General activation of memory items"</span></span>
<span id="cb4-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb4-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $c</span></span>
<span id="cb4-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "Context activation"</span></span>
<span id="cb4-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb4-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; $s</span></span>
<span id="cb4-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "Spatial similarity gradient"</span></span></code></pre></div></div>
</div>
<p>Using this information, we can set up the <code>bmmformula</code> for the different model parameters. Let’s say we want to first get an idea how all parameters vary across set_size:</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1">imm_formula <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;">bmmformula</span>(</span>
<span id="cb5-2">  c <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID),</span>
<span id="cb5-3">  a <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID),</span>
<span id="cb5-4">  s <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID),</span>
<span id="cb5-5">  kappa <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <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;">0</span> <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> set_size <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> ID)</span>
<span id="cb5-6">)</span></code></pre></div></div>
</div>
<p>The <code>bmmformula</code> object is closely aligned with the <code>brmsformula</code> syntax and allows for an easy specification of grouped effects. In this case, we assume random effects for all parameters over the <code>ID</code> variable, thus implementing a hierarchical model estimating individual differences in all parameters across the different set sizes.</p>
<div class="callout callout-style-simple callout-tip 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">Tip</span>Take a closer look at the formula object.
</div>
</div>
<div class="callout-body-container callout-body">
<p>This is where the power and flexibility of this approach comes from. You can specify any combination of fixed and random effects for any parameter in the model. This allows you to test a wide range of hypotheses about how the parameters of the model vary across different experimental conditions. In essence, fitting a hierarchical measurement model is not much different from fitting a mixed-effects regression model. You can find a more comprehensive tutorial on the <code>bmmformula</code> syntax and features in <a href="https://venpopov.github.io/bmm/articles/bmm_bmmformula.html">the online vignette</a></p>
</div>
</div>
</section>
<section id="fit-the-model" class="level3">
<h3 class="anchored" data-anchor-id="fit-the-model">3) Fit the model</h3>
<p>Finally, we only need to call the <code>bmm</code> function to estimate the model. For this, we pass the data the specified <code>bmmodel</code> and <code>bmmformula</code> to the function. In addition, we can pass additional options to the function to customize sampling (<code>warmup</code>, <code>iter</code>, <code>chains</code>, <code>cores</code>), save the fitted model object (<code>file</code>), or choose the backend the model should be estimated with.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb6-1">imm_fit <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;">bmm</span>(</span>
<span id="cb6-2">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># required inputs</span></span>
<span id="cb6-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> my_data,</span>
<span id="cb6-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">model =</span> imm_model,</span>
<span id="cb6-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">formula =</span> imm_formula,</span>
<span id="cb6-6">  </span>
<span id="cb6-7">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># customize sampler settings</span></span>
<span id="cb6-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="cb6-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="cb6-10">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">chains =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>,</span>
<span id="cb6-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="cb6-12">  </span>
<span id="cb6-13">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># save fitted model object</span></span>
<span id="cb6-14">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">file =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"imm_fit"</span></span>
<span id="cb6-15">)</span></code></pre></div></div>
<p>The <code>bmm</code> package is closely integrated with <code>brms</code>, the leading R package for Bayesian Regression Models. This allows you to use almost any post-processing and inference method implemented for <code>brms</code> models with the measurement models implemented in <code>bmm</code></p>
<p>You can find more detailed introductions into the different model currently implemented in <code>bmm</code> on the <a href="https://venpopov.github.io/bmm/articles/index.html">package website</a>. And we have also written a <a href="https://osf.io/preprints/psyarxiv/umt57">tutorial paper</a> that explains more details about the implementation of several measurement models, and how to specify <code>bmmodels</code> in different experimental settings.</p>
</section>
</section>
<section id="design-principles" class="level2">
<h2 class="anchored" data-anchor-id="design-principles">Design principles</h2>
<p>The <code>bmm</code> package is built on a few key design principles, some of which were strongly inspired by the <code>brms</code> package for Bayesian regression modeling:</p>
<section id="simplicity" class="level3">
<h3 class="anchored" data-anchor-id="simplicity">Simplicity</h3>
<p>Fitting a cognitive measurement model should be as simple as fitting a linear, logistic or a poisson regression model. You select the model you want to fit, specify a formula to predict how each parameter of the model varies across different experimental conditions, and hit go</p>
</section>
<section id="flexibility" class="level3">
<h3 class="anchored" data-anchor-id="flexibility">Flexibility</h3>
<p>the package should be able to handle a wide range of measurement models and experimental designs. Any model for which you can write down a likelihood function should be able to be fit with <code>bmm</code>. And every parameter in the model should be able to vary across any combination of continuous and categorical predictors. This is a big departure from many existing techniques, which fit models separately to each experimental condition, forcing all parameters to vary across the same set of conditions, and preventing you from including continuous predictors. You should also be able to fix some parameters to specific values, or to impose complex constraints on them</p>
</section>
<section id="hierarhical-estimation-and-one-step-inference" class="level3">
<h3 class="anchored" data-anchor-id="hierarhical-estimation-and-one-step-inference">Hierarhical estimation and one step inference</h3>
<p>Rather than fitting each model separately to each participant, the package should allow you to estimate all parameters of the model simultaneously, while accounting for individual differences and benefit from shrinkage and sharing of information. As we detail in our <a href="https://osf.io/preprints/psyarxiv/umt57">tutorial paper</a>, this has a number of advantages, including more stable estimates of population parameters, better generalization to new data, and more reliable estimates of individual differences (especially when there are few trials per participant)</p>
</section>
<section id="reliability-and-documentation" class="level3">
<h3 class="anchored" data-anchor-id="reliability-and-documentation">Reliability and documentation</h3>
<p>All models should be thoroughly tested and documented, so you can be confident that the model you are fitting is the model you think you are fitting. Each model should come with references to the relevant literature, so you can understand the theoretical background of the model and how it is implemented in <code>bmm</code>. The package should also provide detailed information on how to specify the model, including which parameters are available and how they can be varied across different experimental conditions</p>
</section>
<section id="parameter-recovery" class="level3">
<h3 class="anchored" data-anchor-id="parameter-recovery">Parameter recovery</h3>
<p>Related to the previous point, the package should be able to recover the parameters of the model from simulated data. These parameter recovery studies should be available as online vignettes and be fully reproducible, rather than being buried in the supplementary materials of academic papers <a href="https://github.com/GidonFrischkorn/Tutorial-MixtureModel-VWM/issues/16">(currently work in progress)</a>. Any trade-offs in the parameter recovery should be clearly documented, so you can understand the limitations of the model and how to interpret the results.</p>
</section>
<section id="efficiency-and-future-proofing" class="level3">
<h3 class="anchored" data-anchor-id="efficiency-and-future-proofing">Efficiency and future-proofing</h3>
<p>Use state-of-the-art sampling algorithms and optimization techniques to ensure that the models can be fit quickly and accurately. <code>bmm</code> is built on top of the <code>brms</code> package, which itself is an interface to the <code>Stan</code> probabilistic programming language. This means that the models are fit using the No-U-Turn Sampler (NUTS) algorithm, which is a state-of-the-art Hamiltonian Monte Carlo algorithm. These are packages that are actively maintained, used and supported by a large community of researchers, so that any future advances in the field of Bayesian statistics can be easily incorporated into the package as alternative backends or sampling algorithms. Furthermore, the package should be designed in a modular way, so that new models can be easily added by the community without having to change the core codebase. Finally, we have been working actively with the core developers of <code>brms</code> and <code>Stan</code> to <a href="https://github.com/stan-dev/math/issues/3008">improve sampling speed</a> and <a href="https://github.com/stan-dev/math/issues/3035">stability</a> of existing distributions used by our models.</p>
</section>
</section>
<section id="currently-supported-models" class="level2">
<h2 class="anchored" data-anchor-id="currently-supported-models">Currently supported models</h2>
<p>We currently have the following models implemented:</p>
<p><strong>Visual working memory</strong></p>
<ul>
<li>Interference measurement model by Oberauer and Lin (2017).</li>
<li>Two-parameter mixture model by Zhang and Luck (2008).</li>
<li>Three-parameter mixture model by Bays et al (2009).</li>
<li>Signal Discrimination Model (SDM) by Oberauer (2023)</li>
</ul>
<p>However, the setup of the <code>bmm</code> package provides the foundation for the implementation of a broad range of cognitive measurement models. In fact, we are already working on implementing additional models, such as:</p>
<ul>
<li>Signal-Detection Models</li>
<li>Evidence Accumulation Models</li>
<li>Memory Models for categorical response</li>
</ul>
<p>If you have suggestions for models that should be added to the package, feel free to create an issue on <a href="https://github.com/venpopov/bmm/">GitHub</a>. Ideally this should describe the model, point towards literature that gives details on the model, and if possible link to code that has already implemented the model.</p>
<p>Given the dynamic nature the <code>bmm</code> package is currently in, you can always view the latest list of supported models by running:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb7-1">bmm<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;">supported_models</span>()</span></code></pre></div></div>
<p>So stay tuned for updates and new models! We hope you will find the <code>bmm</code> package useful and will try fitting one of the already available models to your data. We appreciate all feedback and hope that the <code>bmm</code> package will make the use of measurement models easier for everybody.</p>
<!-- ::: {.callout-tip appearance="simple"} -->



</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0">
<div id="ref-oberauer2017" class="csl-entry">
Oberauer, Klaus, Colin Stoneking, Dominik Wabersich, and Hsuan-Yu Lin. 2017. <span>“Hierarchical Bayesian Measurement Models for Continuous Reproduction of Visual Features from Working Memory.”</span> <em>Journal of Vision</em> 17 (5): 11. <a href="https://doi.org/10.1167/17.5.11">https://doi.org/10.1167/17.5.11</a>.
</div>
</div></section><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>We won’t go into much detail here - you can find a detailed explanation of the IMM model and results of the model fitting in the <a href="https://venpopov.github.io/bmm/articles/bmm_imm.html">package documentation</a>.↩︎</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><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2024,
  author = {Popov, Vencislav},
  title = {Introducing the {Bayesian} {Measurement} {Modeling} {R}
    {Package} (Bmm)},
  date = {2024-06-13},
  url = {https://venpopov.com/posts/2024/introducing-bmm/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2024" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2024. <span>“Introducing the Bayesian Measurement
Modeling R Package (Bmm).”</span> June 13, 2024. <a href="https://venpopov.com/posts/2024/introducing-bmm/">https://venpopov.com/posts/2024/introducing-bmm/</a>.
</div></div></section></div> ]]></description>
  <category>R</category>
  <category>modeling</category>
  <category>bayesian</category>
  <category>R package</category>
  <category>2024</category>
  <guid>https://venpopov.com/posts/2024/introducing-bmm/</guid>
  <pubDate>Thu, 13 Jun 2024 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2024/introducing-bmm/microscope.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Locally Ignoring Git Files Without Affecting Others’ .gitignore</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2024/git-local-ignore/</link>
  <description><![CDATA[ 






<section id="the-problem" class="level2">
<h2 class="anchored" data-anchor-id="the-problem">The Problem</h2>
<p>I often collaborate on git projects and I find that I want to have a folder or some files stored locally in the repo but that I don’t want to be tracked by git. Obviously I could add them to .gitignore, but then I have two options:</p>
<ul>
<li>commit the .gitignore file and push it to the repo. For public projects to which I’m contributing small changes, this is not ideal as it clutters the repo with my personal configuration (and it’s not very polite to the repo owner)</li>
<li>not commit the .gitignore file and keep it only locally. This is not ideal either, as I have to remember to not commit it every time I make a change to the repo</li>
</ul>
<p>A specific example is when I collaborate on R packages. There are several .Rprofile files which R uses to load some settings at startup. I have a bunch of convenience configurations in my user .Rprofile which helps me with my workflow. The problem is that if there is an .Rprofile file in the project root, R will use that one instead of my user .Rprofile. A workaround is to <a href="https://github.com/klmr/rprofile">add some lines to the project .Rprofile to source my user .Rprofile</a>, but I don’t want to commit these lines to the project .Rprofile.</p>
<p>There is an easy solution to <a href="https://stackoverflow.com/questions/1753070/how-do-i-configure-git-to-ignore-some-files-locally">this</a>, but I always forget the syntax and after the 4th time I had to look it up, I decided to write it down in a blog post.</p>
</section>
<section id="the-solution" class="level2">
<h2 class="anchored" data-anchor-id="the-solution">The Solution</h2>
<p>The solution depends on the state of the file.</p>
<section id="if-the-file-is-not-yet-tracked-by-git-new-file" class="level3">
<h3 class="anchored" data-anchor-id="if-the-file-is-not-yet-tracked-by-git-new-file">If the file is not yet tracked by git (new file)</h3>
<p>If this is a new file that is yet untracked by git, you can just add it to the local <code>.git/info/exclude file</code>. This file is not tracked by git and is specific to your local repo. You can add the file to this file and it will be ignored by git. This follows the same syntax as the <code>.gitignore</code> file. You can do this manually by opening the file and adding the file path to it, or you can do it with the following command:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;file&gt;"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;&gt;</span> .git/info/exclude</span></code></pre></div></div>
<p>where <code>&lt;file&gt;</code> is the path to the file you want to exclude.</p>
</section>
<section id="if-the-file-is-already-tracked-by-git" class="level3">
<h3 class="anchored" data-anchor-id="if-the-file-is-already-tracked-by-git">If the file is already tracked by git</h3>
<p>In addition to adding the file to the local <code>.git/info/exclude</code> file, you also need to remove the file from the git index. This can be done with the following command:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> update-index <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--skip-worktree</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>file<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div></div>
<p>if you change your mind and want to track this file, you can do so with the following command:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> update-index <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--no-skip-worktree</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>file<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div></div>
</section>
</section>
<section id="define-an-alias-for-easy-access" class="level2">
<h2 class="anchored" data-anchor-id="define-an-alias-for-easy-access">Define an alias for easy access</h2>
<p>I find that I use this command often enough to warrant an alias. You can run the <a href="https://stackoverflow.com/a/39086325/3556519">following commands to add an alias to your git configuration</a>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> config <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--global</span> alias.ignore <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'update-index --skip-worktree'</span></span>
<span id="cb4-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> config <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--global</span> alias.unignore <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'update-index --no-skip-worktree'</span></span>
<span id="cb4-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> config <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--global</span> alias.ignored <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'git ls-files -v | grep "^S"'</span></span></code></pre></div></div>
<p>and then you can use the following commands to ignore and unignore files:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> ignore <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>file<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> unignore <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>file<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div></div>
</section>
<section id="putting-it-all-together-an-example" class="level2">
<h2 class="anchored" data-anchor-id="putting-it-all-together-an-example">Putting it all together (an example)</h2>
<p>Let’s say I want to contribute code to an R package which is developed on GitHub. I can fork the repo and clone it to my local machine. The package has an .Rprofile file which overwrites my user configuration. I have a bunch of convenience configurations in my user .Rprofile which I want to use when working on this project. I can add the following lines to the project .Rprofile to source my user .Rprofile:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" 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;">try</span>(rprofile<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;">load</span>())</span></code></pre></div></div>
<p>I can then add the project .Rprofile to the local <code>.git/info/exclude</code> file:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".Rprofile"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;&gt;</span> .git/info/exclude</span></code></pre></div></div>
<p>and finally tell git to ignore the file locally (assuming I already have the alias defined):</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> ignore .Rprofile</span></code></pre></div></div>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2024,
  author = {Popov, Vencislav},
  title = {Locally {Ignoring} {Git} {Files} {Without} {Affecting}
    {Others’} .gitignore},
  date = {2024-05-24},
  url = {https://venpopov.com/posts/2024/git-local-ignore/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2024" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2024. <span>“Locally Ignoring Git Files Without
Affecting Others’ .gitignore.”</span> May 24, 2024. <a href="https://venpopov.com/posts/2024/git-local-ignore/">https://venpopov.com/posts/2024/git-local-ignore/</a>.
</div></div></section></div> ]]></description>
  <category>git</category>
  <category>GitHub</category>
  <category>reproducibility</category>
  <category>workflow</category>
  <category>collaboration</category>
  <category>2024</category>
  <guid>https://venpopov.com/posts/2024/git-local-ignore/</guid>
  <pubDate>Fri, 24 May 2024 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2024/git-local-ignore/dontshare.png" medium="image" type="image/png" height="144" width="144"/>
</item>
<item>
  <title>Working with multiple versions of an R package</title>
  <dc:creator>Vencislav Popov</dc:creator>
  <link>https://venpopov.com/posts/2024/r-multiple-package-versions/</link>
  <description><![CDATA[ 






<section id="the-problem" class="level2">
<h2 class="anchored" data-anchor-id="the-problem">The Problem</h2>
<p>Have you ever wanted to test whether your code works with multiple versions of an R package? Or compare how the behavior of certain functions has changed? There are several ways to do that, each involving a lot of setup.</p>
<section id="base-r" class="level3">
<h3 class="anchored" data-anchor-id="base-r">Base R</h3>
<p>The way to work with different package versions in base R requires you to manually specify a folder in which to install each version. Let’s assume your current version of the package <code>stringr</code> is 1.4.0, and you want to install separately the latest version (as of the time of this writing, 1.5.1). The following will install <code>stringr</code> in a folder <code>stringr-new</code> in your user home folder:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" 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;">dir.create</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'~/stringr-new'</span>)</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'stringr'</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lib =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"~/stringr-new"</span>)</span></code></pre></div></div>
<p>Afterwards, you can load the default package with</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(stringr)</span></code></pre></div></div>
<p>and the new version with</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(stringr, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lib.loc =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"~/stringr-new"</span>)</span></code></pre></div></div>
<p>A few issues with this approach:</p>
<ul>
<li>You have to manually specify the folder for each version. And remember what it was when you want to use it again.</li>
<li>You have to remember to specify the <code>lib.loc</code> argument every time you want to use the new version.</li>
<li>You have to remember to detach the old version before loading the new one.</li>
<li>You cannot install a specific version of a package from CRAN. You have to download the tarball from CRAN, extract it, and install it from the extracted folder.</li>
<li>You have to do this one by one for each package you want to test.</li>
</ul>
</section>
<section id="remotesinstall_version" class="level3">
<h3 class="anchored" data-anchor-id="remotesinstall_version">remotes::install_version</h3>
<p>We can augment the base R approach with the <code>remotes</code> package, which provides the <code>install_version</code> function. This function allows you to install a specific version of a package from CRAN. The following will install <code>stringr</code> version 1.5.1 in a folder <code>stringr-new</code> in your user home folder:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dir.create</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'~/stringr-new'</span>)</span>
<span id="cb4-2">remotes<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;">install_version</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'stringr'</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">version =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'1.5.1'</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">lib =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"~/stringr-new"</span>)</span></code></pre></div></div>
<p>Loading the packages is the same as before. This approach solves the issue of having to download and install a specific version of a package from CRAN. However, it does not solve the other issues.</p>
</section>
<section id="renv" class="level3">
<h3 class="anchored" data-anchor-id="renv">renv</h3>
<p>The <code>renv</code> package is a package manager for R. It allows you to create a project-specific library, and to specify the versions of packages you want to use in a <code>renv.lock</code> file. It allows for a completely reproducible environment, and is the best solution for that purpose. However, it can be an overkill if you just want to test a few versions of a package. For an introduction to <code>renv</code>, see <a href="https://rstudio.github.io/renv/articles/renv.html">this blog post</a>.</p>
</section>
</section>
<section id="introducing-vmisc-pkg_vload" class="level2">
<h2 class="anchored" data-anchor-id="introducing-vmisc-pkg_vload">Introducing <code>Vmisc</code>: pkg_vload()</h2>
<p>I was dissatisfied with the existing solutions, so I wrote a package to do that. The <code>Vmisc</code> package provides the <code>pkg_vload</code> function, which allows you to load a specific version of a package, or to install it if it is not already installed. You can start by installing and loading the package:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">install.packages</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Vmisc'</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">repos =</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;">'https://popov-lab.r-universe.dev'</span>))</span>
<span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(Vmisc)</span></code></pre></div></div>
<p>The function <code>pkg_vload</code> combines the functionality of library(), remotes::install_version(), and dir.create(), and it also allows you to list as many packages as you want. The simplest option, for everyday use, is to specify just the package names, as you would with library():</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" 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;">pkg_vload</span>(stringr, dplyr, ggplot2)</span></code></pre></div></div>
<p>If you already have the package installed, it will load the default version. If you don’t, it will install the latest version from CRAN in the default library. This use case is identical to <code>xfun::pkg_load()</code>, but there is some added functionality for handling different versions of a package.</p>
<p>To load a specific version, you can specify the version argument:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" 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;">pkg_vload</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stringr</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'1.5.1'</span>), dplyr, ggplot2)</span></code></pre></div></div>
<p>The function expects a call to the package name, followed by the version in parentheses. It will also recognize if the version you specified is already installed. For example, if you already have <code>stringr</code> version 1.5.1 installed the good old way, it will load it from the default library. And you will see the following output:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb8-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Loading required package: stringr</span></span>
<span id="cb8-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Loading required package: dplyr</span></span>
<span id="cb8-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Attaching package: ‘dplyr’</span></span>
<span id="cb8-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Attaching package: ‘ggplot2’</span></span></code></pre></div></div>
<p>But let’s say we want to install version 1.0.0 of stringr. We can do that with the following:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" 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;">pkg_vload</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stringr</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'1.0.0'</span>))</span></code></pre></div></div>
<p>which results in</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb10-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Downloading package from url: https://cran.rstudio.com//src/contrib/Archive/stringr/stringr_1.0.0.tar.gz</span></span>
<span id="cb10-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; * installing *source* package 'stringr' ...</span></span>
<span id="cb10-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ** package 'stringr' successfully unpacked and MD5 sums checked</span></span>
<span id="cb10-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ** using staged installation</span></span>
<span id="cb10-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; ** ## output omitted ##</span></span>
<span id="cb10-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; * DONE (stringr)</span></span>
<span id="cb10-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Loading required package: stringr</span></span></code></pre></div></div>
<p>pkg_vload has created a folder <code>stringr-1.0.0</code> in the default library path, installed the package there, and loaded it from there. The following two folders now coexist:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb11-1">dirs <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">list.dirs</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.libPaths</span>(), <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">recursive =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">FALSE</span>)</span>
<span id="cb11-2">dirs[<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">grepl</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'stringr'</span>, dirs)]</span>
<span id="cb11-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [1] "C:/Users/vepopo/AppData/Local/R/win-library/4.3/stringr"</span></span>
<span id="cb11-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; [2] "C:/Users/vepopo/AppData/Local/R/win-library/4.3/stringr-1.0.0"</span></span></code></pre></div></div>
<p><code>pkg_vload(stringr('1.0.0'))</code> not only installed the package but also loaded it. If you restart your session, you can load either versions by simply using <code>pkg_vload(stringr)</code> or <code>pkg_vload(stringr('1.0.0'))</code>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb12-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pkg_vload</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">stringr</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'1.0.0'</span>)) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for version 1.0.0</span></span>
<span id="cb12-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pkg_vload</span>(stringr) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># for the default version</span></span></code></pre></div></div>
<p>Benefits of using <code>pkg_vload</code>:</p>
<ul>
<li>vectorized: you can load multiple packages at once</li>
<li>if a package exists, it will be loaded, otherwise it will be installed and loaded</li>
<li>you can specify the version of the package you want to load/install, without having to specify the library path (although you can)</li>
<li>you can install as many versions of a package as you want, and they will coexist in the same library path</li>
<li>you can switch which version will be the default with another function from the package, <code>pkg_switch_default</code></li>
</ul>
</section>
<section id="switching-the-default-version-of-a-package" class="level2">
<h2 class="anchored" data-anchor-id="switching-the-default-version-of-a-package">Switching the default version of a package</h2>
<p>The <code>Vmisc</code> package also provides a function to switch the default version of a package. For example, if you have versions 1.0.0 and 1.5.1 of <code>stringr</code> installed, and you want to make 1.0.0 the default, you can do that with the following:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb13-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pkg_switch_default</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'stringr'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'1.0.0'</span>)</span>
<span id="cb13-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; The default version of stringr has been switched to 1.0.0. The previous default version has been renamed to stringr-1.5.1</span></span>
<span id="cb13-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Please restart R to complete the process.</span></span></code></pre></div></div>
<p>What this will do is rename the folder <code>stringr</code> to <code>stringr-1.5.1</code> and <code>stringr-1.0.0</code> to <code>stringr</code>. After you restart your session, <code>pkg_vload(stringr)</code> or even just <code>library(stringr)</code> will load version 1.0.0. You can also switch back to the default version with:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode r code-with-copy"><code class="sourceCode r"><span id="cb14-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pkg_switch_default</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'stringr'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'1.5.1'</span>)</span>
<span id="cb14-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; The default version of stringr has been switched to 1.5.1. The previous default version has been renamed to stringr-1.0.0</span></span>
<span id="cb14-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Please restart R to complete the process.</span></span></code></pre></div></div>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>Although not a replacement for <code>renv</code> for reproducible environments, <code>Vmisc</code> provides a simple way to work with multiple versions of an R package. It is especially useful for testing and comparing different versions of a package. The package is available on my <a href="https://popov-lab.r-universe.dev">R-universe</a> and on <a href="https://github.com/venpopov/Vmisc">GitHub</a>. It was inspired by the <code>xfun</code> package, and contains other functions that I found useful in my everyday work. I hope you find it useful too.</p>
</section>
<section id="bonus-finding-all-global-options-used-by-a-package" class="level2">
<h2 class="anchored" data-anchor-id="bonus-finding-all-global-options-used-by-a-package">Bonus: finding all global options used by a package</h2>
<p>The <code>Vmisc</code> package also provides a function to find all global options used by a package. These are options you can set via <code>options()</code>, but they are rarely well documented in the package documentation. The function <code>packageOptions</code> will list all global options used by a package, and their default values. For example, to find all global options used by the <code>brms</code> package, you can use the following:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" 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;">packageOptions</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'brms'</span>)</span>
<span id="cb15-2"></span>
<span id="cb15-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; Package brms current options:</span></span>
<span id="cb15-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; </span></span>
<span id="cb15-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.save_pars       :  NULL </span></span>
<span id="cb15-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; mc.cores             :  1 </span></span>
<span id="cb15-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.threads         :  NULL </span></span>
<span id="cb15-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.opencl          :  NULL </span></span>
<span id="cb15-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.normalize       :  TRUE </span></span>
<span id="cb15-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.algorithm       :  "sampling" </span></span>
<span id="cb15-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.backend         :  "rstan" </span></span>
<span id="cb15-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; future               :  FALSE </span></span>
<span id="cb15-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.file_refit      :  "never" </span></span>
<span id="cb15-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; wiener_backend       :  "Rwiener" </span></span>
<span id="cb15-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.verbose         :  FALSE </span></span>
<span id="cb15-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; shinystan.rstudio    :  FALSE </span></span>
<span id="cb15-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.plot_points     :  FALSE </span></span>
<span id="cb15-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.plot_rug        :  FALSE </span></span>
<span id="cb15-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; brms.short_summary   :  FALSE </span></span>
<span id="cb15-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#&gt; .brmsfit_version     :  NULL </span></span></code></pre></div></div>
<p>The function is experimental - it scrapes the source code of the package to find mentions of getOption(‘something’, default = something). It also does not provide documentation. But I found it useful for reminding myself of the options I can set for a package, and their default values.</p>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@online{popov2024,
  author = {Popov, Vencislav},
  title = {Working with Multiple Versions of an {R} Package},
  date = {2024-03-03},
  url = {https://venpopov.com/posts/2024/r-multiple-package-versions/},
  langid = {en}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-popov2024" class="csl-entry quarto-appendix-citeas">
Popov, Vencislav. 2024. <span>“Working with Multiple Versions of an R
Package.”</span> March 3, 2024. <a href="https://venpopov.com/posts/2024/r-multiple-package-versions/">https://venpopov.com/posts/2024/r-multiple-package-versions/</a>.
</div></div></section></div> ]]></description>
  <category>R</category>
  <category>reproducibility</category>
  <category>package management</category>
  <category>R package</category>
  <category>2024</category>
  <guid>https://venpopov.com/posts/2024/r-multiple-package-versions/</guid>
  <pubDate>Sun, 03 Mar 2024 00:00:00 GMT</pubDate>
  <media:content url="https://venpopov.com/posts/2024/r-multiple-package-versions/juggling.png" medium="image" type="image/png" height="144" width="144"/>
</item>
</channel>
</rss>
