<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>R-bloggers</title>
	<atom:link href="https://www.r-bloggers.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.r-bloggers.com</link>
	<description>R news and tutorials contributed by hundreds of R bloggers</description>
	<lastBuildDate>Fri, 28 Aug 2026 00:00:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.5.20</generator>

<image>
	<url>https://i0.wp.com/www.r-bloggers.com/wp-content/uploads/2016/08/cropped-R_single_01-200.png?fit=32%2C32&#038;ssl=1</url>
	<title>R-bloggers</title>
	<link>https://www.r-bloggers.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">11524731</site>	<item>
		<title>rOpenSci News Digest, August 2026</title>
		<link>https://www.r-bloggers.com/2026/08/ropensci-news-digest-august-2026/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/08/28/news-august-2026/</guid>

					<description><![CDATA[<p>Dear rOpenSci friends, it’s time for our monthly news roundup!  You can read this post on our blog. Now let’s dive into the activity at and around rOpenSci!</p>
<p>rOpenSci HQ</p>
<p>Champions Program update<br />
Our Champions are making great progress! 🌟...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/ropensci-news-digest-august-2026/">rOpenSci News Digest, August 2026</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://ropensci.org/blog/2026/08/28/news-august-2026/"> rOpenSci - open tools for open science</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<!-- Before sending DELETE THE INDEX_CACHE and re-knit! -->
<p>Dear rOpenSci friends, it’s time for our monthly news roundup! <!-- blabla --> You can read this post <a href="https://ropensci.org/blog/2026/08/28/news-august-2026" rel="nofollow" target="_blank">on our blog</a>. Now let’s dive into the activity at and around rOpenSci!</p>
<h2>
rOpenSci HQ
</h2><h3>
Champions Program update
</h3><p>Our Champions are making great progress! <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f31f.png" alt="🌟" class="wp-smiley" style="height: 1em; max-height: 1em;" /> They’ve now completed the training on good open source software development practices, package development, and peer review, and are moving on to explore community building and communications. Meanwhile, mentoring is underway with monthly meetings helping Champions move their projects forward. We’re already starting to see some exciting results: first versions of packages are taking shape, and some Champions will soon be sharing their work at LatinR! Stay tuned for more updates as their projects continue to grow! <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3>
R-Universe updates
</h3><p><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f4aa.png" alt="💪" class="wp-smiley" style="height: 1em; max-height: 1em;" /> R-Universe has started building and checking packages for Windows ARM64. Read more in our <a href="https://ropensci.org/blog/2026/08/06/r-universe-winarm/" rel="nofollow" target="_blank">tech note</a>.</p>
<p><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f9ec.png" alt="🧬" class="wp-smiley" style="height: 1em; max-height: 1em;" /> R-Universe is now part of the infrastructure of the <a href="https://blog.bioconductor.org/posts/2026-06-15-new-submission-process-with-Runiverse/" rel="nofollow" target="_blank">Bioconductor submission process</a>.</p>
<h2>
We’re still celebrating our 15th anniversary! <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f389.png" alt="🎉" class="wp-smiley" style="height: 1em; max-height: 1em;" />
</h2><p>In July, we started to share stories from members of our community about their experiences with rOpenSci. Our first story features <a href="https://ropensci.org/author/eunseop-kim/" rel="nofollow" target="_blank">Eunseop Kim</a> and his connection with rOpenSci. Read it on our blog: <a href="https://ropensci.org/blog/2026/07/14/15yo-eunseop-kim/" rel="nofollow" target="_blank">From Peer Review to Mentorship: My rOpenSci Story</a> Stay tuned for more stories from our community as we continue celebrating 15 years of rOpenSci!</p>
<h3>
Coworking
</h3><p>Read <a href="https://ropensci.org/blog/2023/06/21/coworking/" rel="nofollow" target="_blank">all about coworking</a>!</p>
<ul>
<li>
<p>Tuesday September 1st, 14:00 Europe Central (12:00 UTC) <a href="https://ropensci.org/events/coworking-2026-09/" rel="nofollow" target="_blank">“Getting to Know SORTEE”</a>, with <a href="https://ropensci.org/author/steffi-lazerte" rel="nofollow" target="_blank">Steffi LaZerte</a> and co-host <a href="https://ropensci.org/author/ed-ivimey-cook/" rel="nofollow" target="_blank">Ed Ivimey-Cook</a>.</p>
<ul>
<li>Visit <a href="https://sortee.org/" rel="nofollow" target="_blank">SORTEE</a> (Society for Open, Reliable, and Transparent Ecology and Evolutionary Biology).</li>
<li>Meet co-host, Ed Ivimey-Cook, and learn more about SORTEE and how you might get involved.</li>
</ul>
</li>
<li>
<p>Tuesday October 6th, 09:00 Americas Pacific (16:00 UTC) <a href="https://ropensci.org/events/" rel="nofollow" target="_blank">“Writing Tests &#038; Testing in R”</a>, with <a href="https://ropensci.org/author/yanina-bellini-saibene" rel="nofollow" target="_blank">Yanina Bellini Saibene</a> and co-host Olivier Leroy.</p>
<ul>
<li>Explore how to write tests for R and add some tests to your work or packages</li>
<li>Meet co-host, Olivier Leroy, and chat about testing</li>
</ul>
</li>
<li>
<p>Tuesday November 3rd, 09:00 Australia Western (01:00 UTC) <a href="https://ropensci.org/events/" rel="nofollow" target="_blank">TBA</a>, with <a href="https://ropensci.org/author/steffi-lazerte" rel="nofollow" target="_blank">Steffi LaZerte</a> and co-host TBA.</p>
</li>
<li>
<p>Tuesday December 8th, 14:00 Europe Central (12:00 UTC) <a href="https://ropensci.org/events/" rel="nofollow" target="_blank">“Code Linting in R”</a>, with <a href="https://ropensci.org/author/steffi-lazerte" rel="nofollow" target="_blank">Steffi LaZerte</a> and co-host <a href="https://ropensci.org/author/etienne-bacher/" rel="nofollow" target="_blank">Etienne Bacher</a>.</p>
<ul>
<li>Read up on Code Linting and apply some linters to your R code</li>
<li>Meet co-host, Etienne Bacher, and discuss code linting in general, or flir and Jarl in particular * Note that December coworking is a week later than usual</li>
</ul>
</li>
</ul>
<p>And remember, you can always cowork independently on work related to R, work on packages that tend to be neglected, or work on what ever you need to get done!</p>
<h2>
Software <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f4e6.png" alt="📦" class="wp-smiley" style="height: 1em; max-height: 1em;" />
</h2><p>The following four packages recently became a part of our software suite:</p>
<ul>
<li>
<p><a href="https://docs.ropensci.org/lakefetch" rel="nofollow" target="_blank">lakefetch</a>, developed by Jeremy Lynch Farrell: Calculates fetch (open water distance) and wave exposure metrics for lake sampling points. Downloads lake boundaries from OpenStreetMap, calculates directional fetch using a ray-casting approach, and optionally integrates National Hydrography Dataset (NHD) data <a href="https://www.usgs.gov/national-hydrography" rel="nofollow" target="_blank">https://www.usgs.gov/national-hydrography</a> for hydrological context including outlet and inlet locations. Can estimate lake depth from surface area using empirical relationships, and integrate historical weather data for cumulative wave energy calculations. Includes an optional interactive shiny application for visualization. It has been <a href="https://github.com/ropensci/software-review/issues/762" rel="nofollow" target="_blank">reviewed</a> by Jorrit Mesman and Kelly Hondula.</p>
</li>
<li>
<p><a href="https://docs.ropensci.org/galamm" rel="nofollow" target="_blank">galamm</a>, developed by Øystein Sørensen: Estimates generalized additive latent and mixed models using maximum marginal likelihood, as defined in Sorensen et al. (2023) <a href="https://doi.org/10.1007/s11336-023-09910-z" rel="nofollow" target="_blank">https://doi.org/10.1007/s11336-023-09910-z</a>, which is an extension of Rabe-Hesketh and Skrondal (2004)s unifying framework for multilevel latent variable modeling <a href="https://doi.org/10.1007/BF02295939" rel="nofollow" target="_blank">https://doi.org/10.1007/BF02295939</a>. Efficient computation is done using sparse matrix methods, Laplace approximation, and automatic differentiation. The framework includes generalized multilevel models with heteroscedastic residuals, mixed response types, factor loadings, smoothing splines, crossed random effects, and combinations thereof. Syntax for model formulation is close to lme4 (Bates et al. (2015) <a href="https://doi.org/10.18637/jss.v067.i01" rel="nofollow" target="_blank">https://doi.org/10.18637/jss.v067.i01</a>) and PLmixed’ (Rockwood and Jeon (2019) <a href="https://doi.org/10.1080/00273171.2018.1516541" rel="nofollow" target="_blank">https://doi.org/10.1080/00273171.2018.1516541</a>). It has been <a href="https://github.com/ropensci/software-review/issues/615" rel="nofollow" target="_blank">reviewed</a> by Nicholas Clark and David Lawrence Miller.</p>
</li>
<li>
<p><a href="https://docs.ropensci.org/EpiStrainDynamics" rel="nofollow" target="_blank">EpiStrainDynamics</a>, developed by Saras Windecker together with Oliver Eales, James McCaw, and Freya Shearer: EpiStrainDynamics is a statistical framework developed for inferring temporal trends of multiple pathogens from routinely collected surveillance data. It has been <a href="https://github.com/ropensci/software-review/issues/763" rel="nofollow" target="_blank">reviewed</a> by Sangeeta Bhatia and Joshua Lambert.</p>
</li>
<li>
<p><a href="https://docs.ropensci.org/RAMEN" rel="nofollow" target="_blank">RAMEN</a>, developed by Erick I. Navarro-Delgado together with Keegan Korthauer and Michael S. Kobor: Using population data, RAMEN identifies which genetic (G), environmental (E), additive (G+E) or interaction (GxE) model better explains DNA methylation levels in genome-wide locations with high DNA methylation variability. It has been <a href="https://github.com/ropensci/software-review/issues/743" rel="nofollow" target="_blank">reviewed</a> by Lluís Revilla Sancho and Ulduz Vafadarshamasbi.</p>
</li>
</ul>
<p>Discover <a href="https://ropensci.org/packages" rel="nofollow" target="_blank">more packages</a>, read more about <a href="https://ropensci.org/software-review" rel="nofollow" target="_blank">Software Peer Review</a>.</p>
<h3>
New versions
</h3><p>The following twenty-five packages have had an update since the last newsletter: <a href="https://docs.ropensci.org/DataSpaceR" title="Interface to the CAVD DataSpace" rel="nofollow" target="_blank">DataSpaceR</a> (<a href="https://github.com/ropensci/DataSpaceR/releases/tag/v1.0.2" rel="nofollow" target="_blank"><code>v1.0.2</code></a>), <a href="https://docs.ropensci.org/RAMEN" title="RAMEN: Regional Association of Methylome variability with the Exposome and geNome" rel="nofollow" target="_blank">RAMEN</a> (<a href="https://github.com/ropensci/RAMEN/releases/tag/v2.1.1" rel="nofollow" target="_blank"><code>v2.1.1</code></a>), <a href="https://docs.ropensci.org/frictionless" title="Read and Write Frictionless Data Packages" rel="nofollow" target="_blank">frictionless</a> (<a href="https://github.com/frictionlessdata/frictionless-r/releases/tag/v1.3.0" rel="nofollow" target="_blank"><code>v1.3.0</code></a>), <a href="https://docs.ropensci.org/cffr" title="Generate Citation File Format (CFF) Metadata" rel="nofollow" target="_blank">cffr</a> (<a href="https://github.com/ropensci/cffr/releases/tag/v1.4.2" rel="nofollow" target="_blank"><code>v1.4.2</code></a>), <a href="https://docs.ropensci.org/nodbi" title="Document NoSQL Database DBI Connector" rel="nofollow" target="_blank">nodbi</a> (<a href="https://github.com/ropensci/nodbi/releases/tag/v0.15.0" rel="nofollow" target="_blank"><code>v0.15.0</code></a>), <a href="https://docs.ropensci.org/reviser" title="Analyzing Revisions in Real-Time Time Series Vintages" rel="nofollow" target="_blank">reviser</a> (<a href="https://github.com/ropensci/reviser/releases/tag/v0.2.0" rel="nofollow" target="_blank"><code>v0.2.0</code></a>), <a href="https://docs.ropensci.org/writexl" title="Export Data Frames to Excel xlsx Format" rel="nofollow" target="_blank">writexl</a> (<a href="https://github.com/ropensci/writexl/releases/tag/v2.0.1" rel="nofollow" target="_blank"><code>v2.0.1</code></a>), <a href="https://docs.ropensci.org/rangr" title="Mechanistic Simulation of Species Range Dynamics" rel="nofollow" target="_blank">rangr</a> (<a href="https://github.com/ropensci/rangr/releases/tag/v1.0.10" rel="nofollow" target="_blank"><code>v1.0.10</code></a>), <a href="https://docs.ropensci.org/lightr" title="Read Spectrometric Data and Metadata" rel="nofollow" target="_blank">lightr</a> (<a href="https://github.com/ropensci/lightr/releases/tag/v2.1.0" rel="nofollow" target="_blank"><code>v2.1.0</code></a>), <a href="https://docs.ropensci.org/gert" title="Simple Git Client for R" rel="nofollow" target="_blank">gert</a> (<a href="https://github.com/r-lib/gert/releases/tag/v2.4.1" rel="nofollow" target="_blank"><code>v2.4.1</code></a>), <a href="https://docs.ropensci.org/c14bazAAR" title="Download and Prepare C14 Dates from Different Source Databases" rel="nofollow" target="_blank">c14bazAAR</a> (<a href="https://github.com/ropensci/c14bazAAR/releases/tag/5.3.0" rel="nofollow" target="_blank"><code>5.3.0</code></a>), <a href="https://docs.ropensci.org/landscapetools" title="Landscape Utility Toolbox" rel="nofollow" target="_blank">landscapetools</a> (<a href="https://github.com/ropensci/landscapetools/releases/tag/v0.6.3" rel="nofollow" target="_blank"><code>v0.6.3</code></a>), <a href="https://docs.ropensci.org/comtradr" title="Interface with the United Nations Comtrade API" rel="nofollow" target="_blank">comtradr</a> (<a href="https://github.com/ropensci/comtradr/releases/tag/v1.0.6" rel="nofollow" target="_blank"><code>v1.0.6</code></a>), <a href="https://docs.ropensci.org/lingtypology" title="Linguistic Typology and Mapping" rel="nofollow" target="_blank">lingtypology</a> (<a href="https://github.com/ropensci/lingtypology/releases/tag/v1.1.26" rel="nofollow" target="_blank"><code>v1.1.26</code></a>), <a href="https://docs.ropensci.org/textreuse" title="Detect Text Reuse and Document Similarity" rel="nofollow" target="_blank">textreuse</a> (<a href="https://github.com/ropensci/textreuse/releases/tag/v1.0.2" rel="nofollow" target="_blank"><code>v1.0.2</code></a>), <a href="https://docs.ropensci.org/sofa" title="Connector to CouchDB" rel="nofollow" target="_blank">sofa</a> (<a href="https://github.com/ropensci/sofa/releases/tag/v0.4.2" rel="nofollow" target="_blank"><code>v0.4.2</code></a>), <a href="https://docs.ropensci.org/pkgstats" title="Metrics of R Packages" rel="nofollow" target="_blank">pkgstats</a> (<a href="https://github.com/ropensci-review-tools/pkgstats/releases/tag/v0.2.4" rel="nofollow" target="_blank"><code>v0.2.4</code></a>), <a href="https://docs.ropensci.org/refsplitr" title="author name disambiguation, author georeferencing, and mapping of coauthorship networks with Web of Science data" rel="nofollow" target="_blank">refsplitr</a> (<a href="https://github.com/ropensci/refsplitr/releases/tag/v1.2.3" rel="nofollow" target="_blank"><code>v1.2.3</code></a>), <a href="https://docs.ropensci.org/npi" title="Access the U.S. National Provider Identifier Registry API" rel="nofollow" target="_blank">npi</a> (<a href="https://github.com/ropensci/npi/releases/tag/v0.3.0" rel="nofollow" target="_blank"><code>v0.3.0</code></a>), <a href="https://docs.ropensci.org/rerddap" title="General Purpose Client for ERDDAP&#x2122; Servers" rel="nofollow" target="_blank">rerddap</a> (<a href="https://github.com/ropensci/rerddap/releases/tag/v1.3.0" rel="nofollow" target="_blank"><code>v1.3.0</code></a>), <a href="https://docs.ropensci.org/stantargets" title="Targets for Stan Workflows" rel="nofollow" target="_blank">stantargets</a> (<a href="https://github.com/ropensci/stantargets/releases/tag/0.1.3" rel="nofollow" target="_blank"><code>0.1.3</code></a>), <a href="https://docs.ropensci.org/openalexR" title="Getting Bibliographic Records from OpenAlex Database Using DSL API" rel="nofollow" target="_blank">openalexR</a> (<a href="https://github.com/ropensci/openalexR/releases/tag/v3.1.0" rel="nofollow" target="_blank"><code>v3.1.0</code></a>), <a href="https://docs.ropensci.org/ernest" title="A Toolkit for Nested Sampling" rel="nofollow" target="_blank">ernest</a> (<a href="https://github.com/ropensci/ernest/releases/tag/v1.2.5" rel="nofollow" target="_blank"><code>v1.2.5</code></a>), <a href="https://docs.ropensci.org/mregions2" title="Access Data from Marineregions.org: Gazetteer &#038; Data Products" rel="nofollow" target="_blank">mregions2</a> (<a href="https://github.com/ropensci/mregions2/releases/tag/v1.1.3" rel="nofollow" target="_blank"><code>v1.1.3</code></a>), and <a href="https://docs.ropensci.org/spiro" title="Manage Data from Cardiopulmonary Exercise Testing" rel="nofollow" target="_blank">spiro</a> (<a href="https://github.com/ropensci/spiro/releases/tag/v0.2.4" rel="nofollow" target="_blank"><code>v0.2.4</code></a>).</p>
<h2>
Software Peer Review
</h2><p>There are eighteen recently closed and active submissions and 5 submissions on hold. Issues are at different stages:</p>
<ul>
<li>
<p>Five at <a href="https://github.com/ropensci/software-review/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%226/approved%22" rel="nofollow" target="_blank">‘6/approved’</a>:</p>
<ul>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/763" rel="nofollow" target="_blank">EpiStrainDynamics</a>, Infer temporal trends of multiple pathogens. Submitted by <a href="https://www.smwindecker.com/" rel="nofollow" target="_blank">Saras Windecker</a>. (Stats).</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/762" rel="nofollow" target="_blank">lakefetch</a>, Calculate Fetch and Wave Exposure for Lake Sampling Points. Submitted by <a href="https://github.com/jeremylfarrell" rel="nofollow" target="_blank">jeremylfarrell</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/743" rel="nofollow" target="_blank">RAMEN</a>, RAMEN: Regional Association of Methylome variability with the Exposome and geNome. Submitted by <a href="https://erick-navarrodelgado.netlify.app/" rel="nofollow" target="_blank">Erick Navarro-Delgado</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/704" rel="nofollow" target="_blank">priorsense</a>, Prior Diagnostics and Sensitivity Analysis. Submitted by <a href="https://github.com/n-kall" rel="nofollow" target="_blank">Noa Kallioinen</a>. (Stats).</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/615" rel="nofollow" target="_blank">galamm</a>, Generalized Additive Latent and Mixed Models. Submitted by <a href="https://osorensen.no/" rel="nofollow" target="_blank">Øystein Sørensen</a>. (Stats).</p>
</li>
</ul>
</li>
<li>
<p>Two at <a href="https://github.com/ropensci/software-review/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%225/awaiting-reviewer(s)-response%22" rel="nofollow" target="_blank">‘5/awaiting-reviewer(s)-response’</a>:</p>
<ul>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/744" rel="nofollow" target="_blank">RAQSAPI</a>, A Simple Interface to the US EPA Air Quality System Data Mart API. Submitted by <a href="https://github.com/mccroweyclinton-EPA" rel="nofollow" target="_blank">mccroweyclinton-EPA</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/765" rel="nofollow" target="_blank">ciecl</a>, International Classification of Diseases ICD-10/ICD-11 for Chile. Submitted by <a href="https://github.com/Rodotasso" rel="nofollow" target="_blank">Rodolfo Tasso</a>.</p>
</li>
</ul>
</li>
<li>
<p>Two at <a href="https://github.com/ropensci/software-review/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%224/review(s)-in-awaiting-changes%22" rel="nofollow" target="_blank">‘4/review(s)-in-awaiting-changes’</a>:</p>
<ul>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/718" rel="nofollow" target="_blank">rcrisp</a>, Automate the Delineation of Urban River Spaces. Submitted by <a href="https://github.com/cforgaci" rel="nofollow" target="_blank">Claudiu Forgaci</a>. (Stats).</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/717" rel="nofollow" target="_blank">coevolve</a>, Fit Bayesian Generalized Dynamic Phylogenetic Models using Stan. Submitted by <a href="https://scottclaessens.github.io/" rel="nofollow" target="_blank">Scott Claessens</a>. (Stats).</p>
</li>
</ul>
</li>
<li>
<p>Two at <a href="https://github.com/ropensci/software-review/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%223/reviewer(s)-assigned%22" rel="nofollow" target="_blank">‘3/reviewer(s)-assigned’</a>:</p>
<ul>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/792" rel="nofollow" target="_blank">brapiR2</a>, A Tidyverse-Native Client for the BrAPI v2 (Breeding API) Specification. Submitted by <a href="https://orcid.org/0009-0007-1642-0172" rel="nofollow" target="_blank">Ayo</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/787" rel="nofollow" target="_blank">ibger</a>, Access the IBGE Aggregate Data API from R. Submitted by <a href="https://castlab.org/" rel="nofollow" target="_blank">Andre Leite Wanderley</a>.</p>
</li>
</ul>
</li>
<li>
<p>Three at <a href="https://github.com/ropensci/software-review/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%222/seeking-reviewer(s)%22" rel="nofollow" target="_blank">‘2/seeking-reviewer(s)’</a>:</p>
<ul>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/775" rel="nofollow" target="_blank">grumpy</a>, Read NumPy .npy and .npz Files. Submitted by <a href="https://hugogruson.fr/" rel="nofollow" target="_blank">Hugo Gruson</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/774" rel="nofollow" target="_blank">tezr</a>, Access Thesis Metadata from Turkiye’s National Thesis Center. Submitted by <a href="https://emraher.com/" rel="nofollow" target="_blank">Emrah Er</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/769" rel="nofollow" target="_blank">rfastlowess</a>, High-Performance LOWESS Smoothing for R. Submitted by <a href="https://github.com/thisisamirv" rel="nofollow" target="_blank">Amir Valizadeh</a>. (Stats).</p>
</li>
</ul>
</li>
<li>
<p>Four at <a href="https://github.com/ropensci/software-review/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%221/editor-checks%22" rel="nofollow" target="_blank">‘1/editor-checks’</a>:</p>
<ul>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/799" rel="nofollow" target="_blank">camtrapReport</a>, Camera-Trap Report Generator. Submitted by <a href="https://www.uu.nl/staff/EEbrahimi" rel="nofollow" target="_blank">Elham Ebrahimi</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/785" rel="nofollow" target="_blank">nert</a>, Curated Access to TERN Environmental Raster Data. Submitted by <a href="https://scholar.google.com.au/citations?user=zG1uKrcAAAAJ&#038;hl=en" rel="nofollow" target="_blank">Max Moldovan</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/777" rel="nofollow" target="_blank">OptSurvCutR</a>, Optimal Survival Cut-Point Discovery for Time-to-Event Analysis with OptSurvCutR. Submitted by <a href="https://github.com/paytonyau" rel="nofollow" target="_blank">Payton Yau</a>. (Stats).</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/766" rel="nofollow" target="_blank">HydraR</a>, Stateful Agentic Orchestration for Scientific Reproducibility. Submitted by <a href="https://www.mq.edu.au/research/research-centres-groups-and-facilities/facilities/australian-proteome-analysis-facility" rel="nofollow" target="_blank">Ignatius Pang</a>.</p>
</li>
</ul>
</li>
</ul>
<p>Find out more about <a href="https://ropensci.org/software-review" rel="nofollow" target="_blank">Software Peer Review</a> and how to get involved.</p>
<h2>
On the blog
</h2><!-- Do not forget to rebase your branch! -->
<h3>
Software Review
</h3><ul>
<li>
<p><a href="https://ropensci.org/blog/2026/07/14/15yo-eunseop-kim" rel="nofollow" target="_blank">From Peer Review to Mentorship: My rOpenSci Story</a> by Eunseop Kim. From submitting a package, to reviewing one, to mentoring a Champion: my path with rOpenSci.</p>
</li>
<li>
<p><a href="https://ropensci.org/blog/2026/07/02/editor-tools" rel="nofollow" target="_blank">FOSS Tools for Lazy Editors</a> by Steffi LaZerte. How we streamlined the editing of our blog posts using 4 open-source tools that you could adopt too.</p>
</li>
<li>
<p><a href="https://ropensci.org/blog/2026/08/06/the-journey-of-nycopendata-from-classroom-to-community" rel="nofollow" target="_blank">The Journey of {nycOpenData}: From Classroom to Community</a> by Christian Martinez.</p>
</li>
<li>
<p><a href="https://ropensci.org/blog/2026/08/10/analisis-demografico-con-arcenso" rel="nofollow" target="_blank">From Census Data to Demographic Analysis with ARcenso: A Reproducible Workflow in R</a> by Andrea Gomez Vargas and Emanuel Ciardullo. How to Access and Process the 1970 and 1980 Argentine Censuses Using R. Other languages: <a href='https://ropensci.org/es/blog/2026/08/10/analisis-demografico-con-arcenso' lang='es' rel="nofollow" target="_blank">De datos censales a análisis demográficos con ARcenso: un flujo de trabajo reproducible en R (es)</a>.</p>
</li>
</ul>
<figure class="center"><img src="https://i2.wp.com/ropensci.org/es/blog/2026/08/10/analisis-demografico-con-arcenso/portada-blog.es.png?w=400&#038;ssl=1"
alt="Hex logo de ARcenso sobre documentos históricos de censos argentinos de 1970 y 1980"  data-recalc-dims="1">
</figure>
<h3>
Tech Notes
</h3><ul>
<li>
<p><a href="https://ropensci.org/blog/2026/07/08/r-universe-apis-use-cases" rel="nofollow" target="_blank">An API for Everything There Is to Know About Packages</a> by Maëlle Salmon. Use cases of the R-Universe APIs.</p>
</li>
<li>
<p><a href="https://ropensci.org/blog/2026/08/06/r-universe-winarm" rel="nofollow" target="_blank">Windows ARM64 comes to R-universe</a> by Jeroen Ooms.</p>
</li>
</ul>
<h2>
Calls for contributions
</h2><h3>
Calls for maintainers
</h3><p>If you’re interested in maintaining any of the R packages below, you might enjoy reading our blog post <a href="https://ropensci.org/blog/2023/02/07/what-does-it-mean-to-maintain-a-package/" rel="nofollow" target="_blank">What Does It Mean to Maintain a Package?</a>.</p>
<ul>
<li><a href="https://docs.ropensci.org/charlatan" rel="nofollow" target="_blank">charlatan</a>, create fake data in R. <a href="https://github.com/ropensci/charlatan/issues/150" rel="nofollow" target="_blank">Issue for volunteering</a>.</li>
</ul>
<h3>
Calls for contributions
</h3><p>Refer to our <a href="https://ropensci.org/help-wanted/" rel="nofollow" target="_blank">help wanted page</a> – before opening a PR, we recommend asking in the issue whether help is still needed.</p>
<h2>
Package development corner
</h2><p>Some useful information for R package developers. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f440.png" alt="👀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3>
From Rd files to Quarto
</h3><p>Edgar Ruiz from Posit released <a href="https://opensource.posit.co/blog/2026-06-18_pkgsite-0-1-0/" rel="nofollow" target="_blank">pkgsite</a>, a package for converting your package’s <code>.Rd</code> files to Quarto. It creates qmd files that you can integrate as you want in a Quarto website.</p>
<h3>
Mutation testing, fuzzy testing
</h3><p>First of all, a reminder in case you confuse the two concepts…</p>
<p><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f47d.png" alt="👽" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Mutation testing: you run tests on mutated version of the <em>code</em>.</p>
<p><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f6ae.png" alt="🚮" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Fuzzy testing: you run tests with all sorts of <em>inputs</em> to the code.</p>
<p>At useR! 2026, both topics were covered:</p>
<ul>
<li>mutation testing in <a href="https://docs.google.com/presentation/d/1T5iK0gRBFF869Q6eirtmYVV-5ABD4EN-WBJI5gl5EUY/edit?slide=id.g3efebd0f55a_0_99#slide=id.g3efebd0f55a_0_99" rel="nofollow" target="_blank">Beyond Code Coverage: Mutation Testing in R with mutator</a> by Assanali Amandykov, Pierre Donat-Bouillud.</li>
<li>fuzzy testing in <a href="https://events.digital-research.academy/event/109/contributions/430/attachments/187/407/slides-v2.pdf" rel="nofollow" target="_blank">Fuzz-testing R-based research software for robustness</a> by Marco Colombo.</li>
</ul>
<h3>
roxyreqs
</h3><p>Also at useR!, Moritz Lang and collaborators introduced <a href="https://github.com/mnlang/roxyreqs" rel="nofollow" target="_blank">roxyreqs</a>, a package for adding roxygen2-like documentation to testthat. <a href="https://events.digital-research.academy/event/109/contributions/471/attachments/69/201/roxyreqs-user2026.pdf" rel="nofollow" target="_blank">Slides</a>.</p>
<h3>
checktor, a new helper for CRAN submissions
</h3><p>If you want to submit your package to CRAN, you can get help through the <a href="https://contributor.r-project.org/cran-cookbook/" rel="nofollow" target="_blank">CRAN cookbook</a>, the <a href="https://github.com/ThinkR-open/prepare-for-cran" rel="nofollow" target="_blank">collaborative list maintained by ThinkR</a> and now a new package, checktor by James Balamuta! Read more in <a href="https://blog.thecoatlessprofessor.com/programming/r/the-check-passed-the-reviewer-didnt/" rel="nofollow" target="_blank">James’ post</a>.</p>
<h3>
Interesting AI reads
</h3><ul>
<li><a href="https://sfconservancy.org/llm-gen-ai/llm-backed-generative-ai-recommendations.html" rel="nofollow" target="_blank">Recommendations When Using LLM-backed Generative AI Systems for FOSS Contributions</a> by Software Freedom Conservancy, shared by Will Gearty.</li>
<li><a href="https://www.ft.com/content/cec8df9e-b43b-4cd1-8feb-c07e804e8d33" rel="nofollow" target="_blank">Who cleans up after the vibe-coding party?</a> by Sam Learner in the Financial Times.</li>
<li><a href="https://niccrane.com/posts/ai-tooling-open-source/" rel="nofollow" target="_blank">AI Tooling and Open Source</a> in which Nic Crane discusses “how AI tooling affects open source, the actions maintainers have been taking to address the less positive aspects, and emerging policies that open source projects are implementing around the topic of AI-generated pull requests”.</li>
</ul>
<h2>
Last words
</h2><p>Thanks for reading! If you want to get involved with rOpenSci, check out our <a href="https://contributing.ropensci.org/" rel="nofollow" target="_blank">Contributing Guide</a>. This guide will help direct you to the right place, whether you want to make code contributions, non-code contributions, or contribute in other ways such as through sharing use cases. You can also support our work through <a href="https://ropensci.org/donate" rel="nofollow" target="_blank">donations</a>.</p>
<p>If you haven’t subscribed to our newsletter yet, you can <a href="https://ropensci.org/news/" rel="nofollow" target="_blank">do so though our signup form</a>. Until it’s time for our next newsletter, you can keep in touch with us through our <a href="https://ropensci.org/" rel="nofollow" target="_blank">website</a>, <a href="https://hachyderm.io/@rOpenSci" rel="nofollow" target="_blank">Mastodon</a>, or <a href="https://www.linkedin.com/company/ropensci/" rel="nofollow" target="_blank">LinkedIn</a>. See you soon!</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://ropensci.org/blog/2026/08/28/news-august-2026/"> rOpenSci - open tools for open science</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/ropensci-news-digest-august-2026/">rOpenSci News Digest, August 2026</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403357</post-id>	</item>
		<item>
		<title>wbstats 1.2.0 is now on CRAN</title>
		<link>https://www.r-bloggers.com/2026/08/wbstats-1-2-0-is-now-on-cran/</link>
		
		<dc:creator><![CDATA[https://pacha.dev/blog]]></dc:creator>
		<pubDate>Thu, 27 Aug 2026 23:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pacha.dev/blog/2026/08/28/wbstats/index.html</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Programmatic Access to Data and Statistics from the World Bank API</div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/wbstats-1-2-0-is-now-on-cran/">wbstats 1.2.0 is now on CRAN</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://pacha.dev/blog/2026/08/28/wbstats/index.html"> https://pacha.dev/blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p><em>Before the main content: I am creating an R Community on Google Groups. You can join the group using this <a href="https://docs.google.com/forms/d/e/1FAIpQLSdMAj4adRAT4Gyuwt_9dPvxRvOUPml9AD59vuI7qS7XDlp48g/viewform?usp=dialog" rel="nofollow" target="_blank">form</a>.</em></p>

<h1>Changes</h1>
<ul>
<li>Removed the <code>dplyr</code>, <code>tidyr</code>, <code>readr</code>, <code>tidyselect</code>, <code>tibble</code>, <code>rlang</code>, and <code>magrittr</code> dependencies in favor of <code>data.table</code> (for row-binding and reshaping data) and base R.</li>
<li>Removed the <code>stringr</code> and <code>lubridate</code> dependencies in favor of base R string and date handling.</li>
<li>Functions now return <code>data.table</code> objects instead of <code>tibble</code>s.</li>
<li>I added <code>wb_country_coverage()</code> to summarize the available data before downloading the series.</li>
<li>I removed all the retired indicators and those that return empty tables from <code>wb_cachelist</code>.</li>
</ul>

<h1>Installation</h1>
<p>You can install the latest release version from CRAN with</p>
<pre>install.packages(&quot;wbstats&quot;)</pre>
<p>which is the same as the latest development version from GitHub this as of 2026-08-28</p>
<pre>remotes::install_github(&quot;pachadotdev/wbstats&quot;)</pre>

<h1>Downloading data from the World Bank</h1>
<pre>library(wbstats)

# Population for every country from 1960 until present
d &lt;- wb_data(&quot;SP.POP.TOTL&quot;)
    
head(d)

#&gt; # A tibble: 6 × 9
#&gt;   iso2c iso3c country    date SP.POP.TOTL unit  obs_status footnote last_updated
#&gt;   &lt;chr&gt; &lt;chr&gt; &lt;chr&gt;     &lt;dbl&gt;       &lt;dbl&gt; &lt;chr&gt; &lt;chr&gt;      &lt;chr&gt;    &lt;date&gt;      
#&gt; 1 AF    AFG   Afghanis…  2024    42647492 &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;     2025-07-01  
#&gt; 2 AF    AFG   Afghanis…  2023    41454761 &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;     2025-07-01  
#&gt; 3 AF    AFG   Afghanis…  2022    40578842 &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;     2025-07-01  
#&gt; 4 AF    AFG   Afghanis…  2021    40000412 &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;     2025-07-01  
#&gt; 5 AF    AFG   Afghanis…  2020    39068979 &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;     2025-07-01  
#&gt; 6 AF    AFG   Afghanis…  2019    37856121 &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;     2025-07-01</pre>
<p>The current World Bank API does not provide summaries of data availability. I added the <code>wb_country_coverage()</code> function to supply that, which reads pre-computed summaries from GitHub.</p>
<pre>d &lt;- wb_country_coverage(&quot;gross domestic product&quot;, c(&quot;Mexico&quot;, &quot;Chile&quot;), 2010, 2020)
d

#      iso2c  iso3c country pct_complete  from    to  nobs         indicator
#     &lt;char&gt; &lt;char&gt;  &lt;char&gt;        &lt;num&gt; &lt;int&gt; &lt;int&gt; &lt;int&gt;            &lt;char&gt;
#  1:     CL    CHL   Chile         56.5  1960  2100    52     CC.EG.INTS.KW
#  2:     MX    MEX  Mexico         56.5  1960  2100    52     CC.EG.INTS.KW
#  3:     CL    CHL   Chile         34.8  1960  2025    23 EG.EGY.PRIM.PP.KD
#  4:     MX    MEX  Mexico         34.8  1960  2025    23 EG.EGY.PRIM.PP.KD
#  5:     CL    CHL   Chile         53.0  1960  2025    35 EG.GDP.PUSE.KO.PP
# ---                                                                       
# 82:     MX    MEX  Mexico         54.5  1960  2025    36    PA.NUS.PRVT.PP
# 83:     CL    CHL   Chile         53.0  1960  2025    35 SL.GDP.PCAP.EM.KD
# 84:     MX    MEX  Mexico         53.0  1960  2025    35 SL.GDP.PCAP.EM.KD
# 85:     CL    CHL   Chile        100.0  2004  2023    20   SPI.D5.2.5.HOUS
# 86:     MX    MEX  Mexico         40.0  2004  2023     8   SPI.D5.2.5.HOUS

# countries with less than 15% coverage for any variable
d[pct_complete &lt; 15, ]

#     iso2c  iso3c country pct_complete  from    to  nobs                 indicator
#    &lt;char&gt; &lt;char&gt;  &lt;char&gt;        &lt;num&gt; &lt;int&gt; &lt;int&gt; &lt;int&gt;                    &lt;char&gt;
# 1:     MX    MEX  Mexico         12.9  1960  2100    13 UIS.XUNIT.GDPCAP.02.FSGOV</pre>

<h2>Hans Rosling’s Gapminder using <code>wbstats</code></h2>
<pre>library(wbstats)
library(data.table)
library(tinyplot)

my_indicators &lt;- c(
  life_exp = &quot;SP.DYN.LE00.IN&quot;,
  gdp_capita =&quot;NY.GDP.PCAP.CD&quot;,
  pop = &quot;SP.POP.TOTL&quot;
)

d &lt;- wb_data(my_indicators, start_date = 2016)

d &lt;- merge(d, wb_countries(), &quot;iso3c&quot;)
d &lt;- na.omit(d)

png(file=&quot;man/figures/readme-gdppc-vs-lifexp.png&quot;, width = 900, height = 600)
tinyplot(
  life_exp ~ gdp_capita | region,
  data = d,
  cex = d$pop,
  pch = 19,
  alpha = 0.7,
  palette = &quot;tableau&quot;,
  log = &quot;x&quot;,
  xaxl = &quot;$&quot;,
  main = &quot;An Example of Hans Rosling's Gapminder using wbstats&quot;,
  xlab = &quot;GDP per Capita (log scale)&quot;,
  ylab = &quot;Life Expectancy at Birth&quot;,
  cap = &quot;Source: World Bank&quot;
)
dev.off()</pre>
<p><img src="https://i2.wp.com/pacha.dev/blog/2026/08/28/wbstats/gdppc-vs-lifexp.png?w=578&#038;ssl=1" class="img-fluid" data-recalc-dims="1"></p>

<h1>How I create the package data</h1>
<p><em>Just in case this is useful.</em></p>
<p>What worked for me to query the data was to use the <code>format</code> and <code>per_page</code> arguments. You <em>do not</em> need this to work with the package.</p>
<p>Endpoints used for the package data:</p>
<ul>
<li>https://api.worldbank.org/v2/indicators?format=json&per_page=30000</li>
<li>https://api.worldbank.org/v2/source?format=json&per_page=100</li>
<li>https://api.worldbank.org/v2/topics?format=json&per_page=100</li>
<li>https://api.worldbank.org/v2/regions?format=json&per_page=100</li>
<li>https://api.worldbank.org/v2/incomelevel?format=json&per_page=10</li>
<li>https://api.worldbank.org/v2/lendingtypes?format=json&per_page=10</li>
<li>https://api.worldbank.org/v2/languages?format=json&per_page=100</li>
</ul>
<p>Parts of this API endpoints description comes from https://dlthub.com/context/source/world-bank-indicators-api and other were just testing things like “language” and “languages” tp update <code>wbstats</code>. I did not create this package, I just assumed its maintenance as it is a very valuable resoure.</p>
<table>
<colgroup>
<col style="width: 15%">
<col style="width: 42%">
<col style="width: 6%">
<col style="width: 35%">
</colgroup>
<thead>
<tr>
<th>Resource</th>
<th>Endpoint</th>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>indicators</td>
<td>/v2/indicator</td>
<td>GET</td>
<td>Access to nearly 28,000 series indicators</td>
</tr>
<tr>
<td>countries</td>
<td>/v2/country/all</td>
<td>GET</td>
<td>All countries</td>
</tr>
<tr>
<td>country_indicator</td>
<td>/v2/country/{country_id}/indicator/{indicator_id}</td>
<td>GET</td>
<td>Specific indicator for a country</td>
</tr>
<tr>
<td>sources</td>
<td>/v2/source</td>
<td>GET</td>
<td>All data sources</td>
</tr>
<tr>
<td>source_indicators</td>
<td>/v2/source/{source_id}/indicators</td>
<td>GET</td>
<td>Indicators for a specific source</td>
</tr>
<tr>
<td>topics</td>
<td>/v2/topics</td>
<td>GET</td>
<td>Metadata about indicator topics</td>
</tr>
<tr>
<td>regions</td>
<td>/v2/country/all</td>
<td>GET</td>
<td>All regions</td>
</tr>
<tr>
<td>income_levels</td>
<td>/v2/country/income_levels</td>
<td>GET</td>
<td>All income levels</td>
</tr>
<tr>
<td>lending_types</td>
<td>/v2/country/lending_types</td>
<td>GET</td>
<td>All lending types</td>
</tr>
<tr>
<td>languages</td>
<td>/v2/country/languages</td>
<td>GET</td>
<td>All languages</td>
</tr>
</tbody>
</table>
<p>I added this because I did not find much information in the official documentation.</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://pacha.dev/blog/2026/08/28/wbstats/index.html"> https://pacha.dev/blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/wbstats-1-2-0-is-now-on-cran/">wbstats 1.2.0 is now on CRAN</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403355</post-id>	</item>
		<item>
		<title>Reading notes on Naming Things by Tom Benner</title>
		<link>https://www.r-bloggers.com/2026/08/reading-notes-on-naming-things-by-tom-benner/</link>
		
		<dc:creator><![CDATA[Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website]]></dc:creator>
		<pubDate>Thu, 27 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://masalmon.eu/2026/08/27/naming-things-reading-notes/</guid>

					<description><![CDATA[<p>Naming Things by Tom Benner a tiny but neat book about the naming of identifies in code (variables, classes, methods, so not packages or libraries).<br />
It had entered my to-read list a few years ago, when I read the blog post Naming Things by Vicki Boykis...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/reading-notes-on-naming-things-by-tom-benner/">Reading notes on Naming Things by Tom Benner</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://masalmon.eu/2026/08/27/naming-things-reading-notes/"> Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p><a href="https://www.namingthings.co/" rel="nofollow" target="_blank">Naming Things by Tom Benner</a> a tiny but neat book about the naming of identifies in code (variables, classes, methods, so not packages or libraries).
It had entered my to-read list a few years ago, when I read the blog post <a href="https://vickiboykis.com/2023/06/29/naming-things/" rel="nofollow" target="_blank">Naming Things by Vicki Boykis</a>.
Tom Benner’s book is self published and available from Amazon (e-book and paperback) and <a href="https://leanpub.com/naming-things" rel="nofollow" target="_blank">Leanpub</a> (e-book).
I rarely buy stuff on Amazon, and prefer to read on paper, so I patiently waited for a copy to appear on my favorite second-hand book website.</p>
<h2 id="why-read-about-naming-things-yet-again">Why read about naming things yet again?</h2>
<p>At this point, I’ve been exposed to much advice on naming things, in <a href="https://www.oreilly.com/library/view/the-art-of/9781449318482/" rel="nofollow" target="_blank">The Art of Readable Code</a>, <a href="https://masalmon.eu/2023/10/19/reading-notes-philosophy-software-design/" rel="nofollow" target="_blank">A Philosophy of Software Design</a>, <a href="https://masalmon.eu/2026/08/21/the-programmer-s-brain-reading-notes/" rel="nofollow" target="_blank">The Programmer’s Brain</a>…
So why bother read yet another source of information on the topic?
Well, I trusted Vicki Boykis’ recommendation, and since the book is so short – less than 100 pages, it wasn’t a dangerous bet.</p>
<p>The book is well organized, easy to read, and feels exhaustive.
It explains why naming is important, why it is difficult, and presents 4 principles for naming: understandability, conciseness, consistency, distinguishability.</p>
<p>Here are some of my highlights…</p>
<h2 id="bad-names-bad-look">Bad names, bad look</h2>
<p>Among the numerous reasons why bad names are harmful for a project, this one caught my attention:</p>
<blockquote>
<p>“[A] newcomer may develop a poor perception of the project and in the worst case, a poor perception of the team.”</p>
</blockquote>
<h2 id="what-is-an-understandable-name">What is an understandable name?</h2>
<blockquote>
<p>“An understandable name has high comprehension (it can be understood quickly) and high recall (it can be remembered easily).”</p>
</blockquote>
<p>It reminds me of The Programmer’s Brain.</p>
<p>The book also recommends to avoid cleverness or irrelevant concepts: calling things based on some obscure joke or musical reference.</p>
<h2 id="the-ladder-of-abstraction">The ladder of abstraction</h2>
<p>The book advises to use the “appropriate level of abstraction”.</p>
<blockquote>
<p>“Do not use a name that’s so specific that you’re providing information that’s irrelevant to the audience, and do not use a name that’s so generic that it provides little or no relevant information to them.”</p>
</blockquote>
<p>The book then discusses 4 names for a function that removes leading and trailing whitespace<sup id="fnref:1"><a href="https://masalmon.eu/2026/08/27/naming-things-reading-notes/#fn:1" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">1</a></sup> from a phone number: <code>process()</code>, <code>format()</code>, <code>trim_whitespace()</code>, <code>strip()</code>.
The right choice is explained to be <code>format()</code>: it shows the intent of the function without disclosing details that might be irrelevant or subject to change.</p>
<h2 id="booleans">Booleans</h2>
<p>The book recommends to always add <code>is_</code> in the name of Booleans, e.g. <code>is_valid</code>.</p>
<p>It also states that they should be stated in the positive, with an example that I’m adapting to R below:</p>
<pre># Bad
if (!user_is_invalid) {
  save(user)
}

# Good

if (user_is_valid) {
  save(user)
}

</pre><p>This example resonated with me because it happens often to me to create a Boolean, use it with an <code>if</code> only to realize I should define the contrary of that Boolean instead.</p>
<p>And it reminds me, beyond naming, of negation-related rules in linters such as Jarl: <a href="https://jarl.etiennebacher.com/rules/comparison_negation" rel="nofollow" target="_blank"><code>comparison_negation</code></a>, <a href="https://jarl.etiennebacher.com/rules/outer_negation" rel="nofollow" target="_blank"><code>outer_negation</code></a>.</p>
<h2 id="the-cost-of-renames">The cost of renames</h2>
<p>The book discusses the costs of a bad name (that add up over time: slow comprehension, low recall) and of a rename (one-time cost).
It made me think of the renaming we did and do in igraph, including the batch renaming of functions with dots in them to snake-case equivalent (along with the correct <a href="https://lifecycle.r-lib.org/articles/communicate.html" rel="nofollow" target="_blank">lifecycle harness</a> <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f607.png" alt="😇" class="wp-smiley" style="height: 1em; max-height: 1em;" />): work for us but also for maintainers of reverse dependencies and direct users of the package.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Naming Things is a useful short read.
After reading it, I feel I pay even more attention to names in the code I was writing of reviewing. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f638.png" alt="😸" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Do you know about the base R <code>trimws()</code> function? Very handy. <a href="https://masalmon.eu/2026/08/27/naming-things-reading-notes/#fnref:1" class="footnote-backref" role="doc-backlink" rel="nofollow" target="_blank"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></p>
</li>
</ol>
</section>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://masalmon.eu/2026/08/27/naming-things-reading-notes/"> Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/reading-notes-on-naming-things-by-tom-benner/">Reading notes on Naming Things by Tom Benner</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403341</post-id>	</item>
		<item>
		<title>Do you need a custom R package or Shiny app? I can build it for you</title>
		<link>https://www.r-bloggers.com/2026/08/do-you-need-a-custom-r-package-or-shiny-app-i-can-build-it-for-you/</link>
		
		<dc:creator><![CDATA[https://pacha.dev/blog]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 23:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pacha.dev/blog/2026/08/27/r-shiny-build/index.html</guid>

					<description><![CDATA[<p>I offer turning a set of requirements into a maintainable package or dashboard</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/do-you-need-a-custom-r-package-or-shiny-app-i-can-build-it-for-you/">Do you need a custom R package or Shiny app? I can build it for you</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://pacha.dev/blog/2026/08/27/r-shiny-build/index.html"> https://pacha.dev/blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p><em>Before the main content: I am creating an R Community on Google Groups. You can join the group using this <a href="https://docs.google.com/forms/d/e/1FAIpQLSdMAj4adRAT4Gyuwt_9dPvxRvOUPml9AD59vuI7qS7XDlp48g/viewform?usp=dialog" rel="nofollow" target="_blank">form</a>.</em></p>
<p>I build custom R packages and Shiny/Tabler apps, and I’m currently taking on new clients. You can find my profile on <a href="https://www.fiverr.com/msepulveda3/buying?source=avatar_menu_profile" rel="nofollow" target="_blank">Fiverr</a>.</p>
<p>Before starting my second master’s and my PhD, I ran a statistics consulting practice for nearly ten years. Most of that work fell into three buckets: designing and implementing SQL databases to streamline data analysis, building tailored R packages to simplify data access and reporting for teams, and building Shiny dashboards to summarise information through plots and KPIs.</p>
<p>With the rise of AI, this is a slightly harder pitch to make than it was a couple of years ago: sketching out a function or a small app is easier than ever. Where I think I still add real value is in the parts AI doesn’t do well on its own – turning a set of requirements into a maintainable package, designing something that your team will actually enjoy using, and making sure the whole thing is tested, documented, and installable by people who aren’t R experts. Besides it, I put a focus on writing everything with a minimal approach, meaning that I make an active effort in keeping the code logic simple and thinking about its long-term maintenance.</p>
<p>I’m also in the final year of my PhD, and between a still-unresolved pending payment from my previous university and a side project – a small guitar pedal business – that I started to cope with that financial emergency but that is not very profitable nor aligned with what I study.</p>
<p>I’m looking to pick up this kind of work again alongside my research. I also have a mobility disability (I get around with a cane, due to arthritis), which has made the usual part-time options like coffee shops or restaurants impractical, so consulting work I can do from a laptop is genuinely the best fit for me right now.</p>
<p>If your organization uses R, there are clear benefits to having an internal R package, whether you have a single R user or dozens. A package built around your organization’s specific needs opens up easier data access, shared functions for transformation and analysis, and a consistent look and feel across reports and dashboards.</p>
<p>Getting that first internal package off the ground can still feel daunting: what functions belong in it, how colleagues will install it and get updates, and how to keep quality consistent as more people contribute. This is exactly the kind of problem I like helping with – planning the package around your team’s actual workflow, fitting it into your existing infrastructure, and building out the core functions for data access, analysis, and reporting so the project has a solid foundation from day one.</p>
<p>Something similar can be said about dashboards, and I can help you to build something informative that keep quality consistent as more users help to improve it.</p>
<p>If any of this sounds useful, feel free to reach out through <a href="https://www.fiverr.com/msepulveda3/buying?source=avatar_menu_profile" rel="nofollow" target="_blank">Fiverr</a> or take a look at the packages above to get a sense of my work.</p>
<p>Below is a sample of the packages I’ve built over the years.</p>

<h3>Data visualization</h3>
<ul>
<li><a href="https://cran.r-project.org/web/packages/d3po/index.html" rel="nofollow" target="_blank">d3po</a>: A set of opinionated templates for quick data visualization using D3.js and R. It is fully compatible with RMarkdown and Shiny, and it is available under the Apache 2.0 license for use in commercial and non-commercial projects.</li>
<li><a href="https://github.com/pachadotdev/tabler" rel="nofollow" target="_blank">tabler</a>: A fully open-source alternative to Shiny worth considering if you need a multi-session, multi-user dashboard.</li>
</ul>

<h3>International Trade</h3>
<ul>
<li><a href="https://cran.r-project.org/web/packages/tradestatistics/index.html" rel="nofollow" target="_blank">tradestatistics</a>: Open trade Statistics API wrapper and utility program.</li>
<li><a href="https://cran.r-project.org/web/packages/wbstats/index.html" rel="nofollow" target="_blank">wbstats</a>: An R package for searching and downloading data from the World Bank API.</li>
</ul>

<h3>Econometrics</h3>
<ul>
<li><a href="https://cran.r-project.org/web/packages/capybara/index.html" rel="nofollow" target="_blank">capybara</a>: Fast and memory efficient fitting of linear models with high-dimensional fixed effects.</li>
<li><a href="https://cran.r-project.org/web/packages/gravity/index.html" rel="nofollow" target="_blank">gravity</a>: Estimation methods for gravity models.</li>
</ul>

<h3>R and C++ bindings</h3>
<ul>
<li><a href="https://cran.r-project.org/web/packages/cpp4r/index.html" rel="nofollow" target="_blank">cpp4r</a>: Header-Only ‘C++’ and ‘R’ interface</li>
</ul>

<h3>Linear algebra</h3>
<ul>
<li><a href="https://cran.r-project.org/web/packages/armadillo4r/index.html" rel="nofollow" target="_blank">armadillo4r</a>: Provides function declarations and inline function definitions that facilitate communication between R and the Armadillo C++ library for linear algebra and scientific computing.</li>
</ul>

<h3>REDATAM format</h3>
<ul>
<li><a href="https://pacha.dev/blog/2026/08/27/r-shiny-build/github.com/pachadotdev/open-redatam" rel="nofollow" target="_blank">Open REDATAM (C++)</a>: Open Redatam is an open source software for extracting raw information from REDATAM databases. It was created to recover information of REDATAM databases for statistical analysis using standard tools such as SPSS, STATA, R, etc. It currently has both <a href="https://cran.r-project.org/web/packages/redatam/index.html" rel="nofollow" target="_blank">R</a> and Python wrappers.</li>
</ul>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://pacha.dev/blog/2026/08/27/r-shiny-build/index.html"> https://pacha.dev/blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/do-you-need-a-custom-r-package-or-shiny-app-i-can-build-it-for-you/">Do you need a custom R package or Shiny app? I can build it for you</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403351</post-id>	</item>
		<item>
		<title>Cochran&#8217;s Q test in R, or the extension of McNemar&#8217;s test for more than two groups</title>
		<link>https://www.r-bloggers.com/2026/08/cochrans-q-test-in-r-or-the-extension-of-mcnemars-test-for-more-than-two-groups/</link>
		
		<dc:creator><![CDATA[R on Stats and R]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://statsandr.com/blog/cochrans-q-test-in-r/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Introduction<br />
In previous articles, we showed how to test whether two qualitative variables are related thanks to the Chi-square test of independence in R (and how to compute it by hand). Both articles insist on one important limitation: this test requires independent observations. If you have dependent observations (paired samples), ...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/cochrans-q-test-in-r-or-the-extension-of-mcnemars-test-for-more-than-two-groups/">Cochran’s Q test in R, or the extension of McNemar’s test for more than two groups</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://statsandr.com/blog/cochrans-q-test-in-r/"> R on Stats and R</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>



<p><img src="https://i0.wp.com/statsandr.com/blog/cochrans-q-test-in-r/images/cochrans-q-test-in-r.jpg?w=578&#038;ssl=1" style="width:100.0%" data-recalc-dims="1" /></p>
<div id="introduction" class="section level1">
<h1>Introduction</h1>
<p>In previous articles, we showed how to test whether two <a href="https://statsandr.com/blog/variable-types-and-examples/#qualitative" rel="nofollow" target="_blank">qualitative variables</a> are related thanks to the <a href="https://statsandr.com/blog/chi-square-test-of-independence-in-r/" rel="nofollow" target="_blank">Chi-square test of independence in R</a> (and how to compute it <a href="https://statsandr.com/blog/chi-square-test-of-independence-by-hand/" rel="nofollow" target="_blank">by hand</a>). Both articles insist on one important limitation: this test requires <strong>independent</strong> observations. If you have dependent observations (paired samples), that is, if the measurements have been collected on the <em>same</em> subjects, the McNemar’s or Cochran’s Q tests should be used instead, the Cochran’s Q test being an extension of the McNemar’s test when we have more than two related measures.</p>
<p>The case of exactly two related measurements has already been covered in the article about the <a href="https://statsandr.com/blog/mcnemars-test-in-r/" rel="nofollow" target="_blank">McNemar’s test in R</a>. The present article is dedicated to its generalization: the <strong>Cochran’s Q test</strong>, used to compare three or more related proportions, that is, the same binary outcome measured on the same subjects under <span class="math inline">\(k \geq 3\)</span> conditions or time points.</p>
<p>It can therefore be seen as the dependent-samples counterpart of the Chi-square test of independence for a binary outcome: instead of comparing several <em>independent</em> groups, it compares several <em>repeated</em> measurements collected on the same individuals. It also follows the same logic as the other tests comparing three groups or more presented on this blog, such as the <a href="https://statsandr.com/blog/kruskal-wallis-test-nonparametric-version-anova/" rel="nofollow" target="_blank">Kruskal-Wallis test</a> (used for a quantitative variable and independent samples): a first test tells us whether at least one group differs from the others, and post-hoc tests, together with an adjustment of the <span class="math inline">\(p\)</span>-values for multiple comparisons, then tell us which groups actually differ. If you are unsure about which test is appropriate for your own data, see this <a href="https://statsandr.com/blog/what-statistical-test-should-i-do/" rel="nofollow" target="_blank">overview of the most common statistical tests</a>.</p>
<p>In the remaining of the article, we present the data, the aim, the hypotheses and the assumptions of the test, and finally how to perform it in R, how to complement it with post-hoc tests and how to interpret the results.</p>
</div>
<div id="data" class="section level1">
<h1>Data</h1>
<p>As already mentioned in the article about the McNemar’s test, a dataset with a repeated binary structure is not so easy to find among the datasets shipped with R, so we simulate our own data.</p>
<p>Suppose that we ask 200 randomly selected citizens whether they are in favor of a new policy in their city (answer “Yes” or “No”), and that we ask them exactly the same question at three different points in time:</p>
<ol style="list-style-type: decimal">
<li><strong>before</strong> a public debate on this policy,</li>
<li>right <strong>after</strong> this debate, and</li>
<li>one month later (<strong>follow_up</strong>), in order to see whether the effect of the debate persists over time.</li>
</ol>
<pre># number of respondents
n &lt;- 200

# opinion before the debate
before &lt;- sample(c(&quot;Yes&quot;, &quot;No&quot;),
  size = n,
  replace = TRUE,
  prob = c(0.4, 0.6)
)

# opinion right after the debate (respondents who were in favor
# tend to keep their opinion, while those who were against
# are more likely to change their mind)
after &lt;- ifelse(before == &quot;Yes&quot;,
  sample(c(&quot;Yes&quot;, &quot;No&quot;), size = n, replace = TRUE, prob = c(0.9, 0.1)),
  sample(c(&quot;Yes&quot;, &quot;No&quot;), size = n, replace = TRUE, prob = c(0.4, 0.6))
)

# opinion one month later (some of the respondents
# convinced by the debate go back to their initial opinion)
follow_up &lt;- ifelse(after == &quot;Yes&quot;,
  sample(c(&quot;Yes&quot;, &quot;No&quot;), size = n, replace = TRUE, prob = c(0.85, 0.15)),
  sample(c(&quot;Yes&quot;, &quot;No&quot;), size = n, replace = TRUE, prob = c(0.1, 0.9))
)

# dataset
dat &lt;- data.frame(
  respondent = factor(1:n),
  before = factor(before, levels = c(&quot;No&quot;, &quot;Yes&quot;)),
  after = factor(after, levels = c(&quot;No&quot;, &quot;Yes&quot;)),
  follow_up = factor(follow_up, levels = c(&quot;No&quot;, &quot;Yes&quot;))
)

head(dat)
##   respondent before after follow_up
## 1          1    Yes   Yes       Yes
## 2          2    Yes   Yes       Yes
## 3          3     No   Yes       Yes
## 4          4    Yes   Yes       Yes
## 5          5    Yes   Yes       Yes
## 6          6     No    No        No</pre>
<p>(Note that a seed has been set in the background with <code>set.seed(42)</code>, so the simulated data and all the results presented below are reproducible.)</p>
<p>The data are stored in the <strong>wide format</strong>: one row per respondent, and one column per measurement. This is exactly the structure the Cochran’s Q test is designed for, with each respondent playing the role of a <em>block</em> inside which the three answers are related.</p>
<p>As always, it is a good practice to start with some <a href="https://statsandr.com/blog/descriptive-statistics-in-r/" rel="nofollow" target="_blank">descriptive statistics</a>, here the proportion of respondents in favor of the policy at each of the three points in time:</p>
<pre># install.packages(&quot;dplyr&quot;)
library(dplyr)

dat %&gt;%
  summarise(across(before:follow_up, ~ mean(.x == &quot;Yes&quot;)))
##   before after follow_up
## 1   0.46  0.61      0.54</pre>
<p>These proportions are easier to compare on a plot:</p>
<pre># install.packages(&quot;ggplot2&quot;)
library(ggplot2)

# install.packages(&quot;tidyr&quot;)
library(tidyr)

# from the wide format to the long format
dat_long &lt;- dat %&gt;%
  pivot_longer(
    cols = c(before, after, follow_up),
    names_to = &quot;time&quot;,
    values_to = &quot;opinion&quot;
  ) %&gt;%
  mutate(time = factor(time, levels = c(&quot;before&quot;, &quot;after&quot;, &quot;follow_up&quot;)))

dat_long %&gt;%
  group_by(time) %&gt;%
  summarise(prop_yes = mean(opinion == &quot;Yes&quot;)) %&gt;%
  ggplot() +
  aes(x = time, y = prop_yes) +
  geom_col(fill = &quot;steelblue&quot;) +
  labs(
    x = &quot;Moment of the survey&quot;,
    y = &quot;Proportion in favor of the policy&quot;
  )</pre>
<p><img src="https://i1.wp.com/statsandr.com/blog/cochrans-q-test-in-r/index_files/figure-html/unnamed-chunk-3-1.png?w=450&#038;ssl=1" alt="" style="display: block; margin: auto;" data-recalc-dims="1" /></p>
<p>In our <a href="https://statsandr.com/blog/what-is-the-difference-between-population-and-sample/" rel="nofollow" target="_blank">sample</a>, the proportion of citizens in favor of the policy increased from 46% before the debate to 61% right after it, and then decreased to 54% one month later. The question is whether these differences are large enough to be generalized to the <a href="https://statsandr.com/blog/what-is-the-difference-between-population-and-sample/" rel="nofollow" target="_blank">population</a>, or whether they could be explained by sampling fluctuations alone.</p>
<p>Note that the three proportions are computed on the same people, so comparing them as if they came from three independent groups would ignore the fact that the measurements are repeated. Taking this dependency into account is precisely the purpose of the Cochran’s Q test. Note also that, in the code above, we created a long format version of the dataset (<code>dat_long</code>), with one row per respondent <em>and</em> per measurement, since this is the format expected by the functions used in the rest of the article:</p>
<pre>head(dat_long)
## # A tibble: 6 × 3
##   respondent time      opinion
##   &lt;fct&gt;      &lt;fct&gt;     &lt;fct&gt;  
## 1 1          before    Yes    
## 2 1          after     Yes    
## 3 1          follow_up Yes    
## 4 2          before    Yes    
## 5 2          after     Yes    
## 6 2          follow_up Yes</pre>
</div>
<div id="cochrans-q-test" class="section level1">
<h1>Cochran’s Q test</h1>
<div id="aim-and-hypotheses" class="section level2">
<h2>Aim and hypotheses</h2>
<p>The Cochran’s Q test is used to compare <span class="math inline">\(k \geq 3\)</span> related proportions, so it allows to determine whether the proportion of subjects belonging to a given category (the “successes”) changes across several dependent measurements.</p>
<p>The null and alternative hypotheses of the Cochran’s Q test are:</p>
<ul>
<li><span class="math inline">\(H_0\)</span>: the proportion of successes is the same in all <span class="math inline">\(k\)</span> related conditions, that is, <span class="math inline">\(p_1 = p_2 = \dots = p_k\)</span></li>
<li><span class="math inline">\(H_1\)</span>: at least one condition is different from the others in terms of proportion of successes</li>
</ul>
<p>Be careful that, as for the <a href="https://statsandr.com/blog/anova-in-r/" rel="nofollow" target="_blank">ANOVA</a> or the <a href="https://statsandr.com/blog/kruskal-wallis-test-nonparametric-version-anova/" rel="nofollow" target="_blank">Kruskal-Wallis test</a>, the alternative hypothesis is <strong><em>not</em></strong> that all conditions are different from each other. The opposite of all proportions being equal (<span class="math inline">\(H_0\)</span>) is that <em>at least</em> one proportion is different from the others (<span class="math inline">\(H_1\)</span>). So if the null hypothesis is rejected, we only know that at least one condition differs, and post-hoc tests (covered later in this article) must be performed to know which ones actually differ.</p>
<p>In the context of our example, the Cochran’s Q test helps us to answer the following question: “Is the proportion of citizens in favor of the new policy the same before the debate, right after the debate and one month later?”.</p>
<p>For the interested reader, denoting by <span class="math inline">\(G_j\)</span> the number of successes in condition <span class="math inline">\(j\)</span>, by <span class="math inline">\(\bar{G}\)</span> the mean of the <span class="math inline">\(G_j\)</span> and by <span class="math inline">\(L_i\)</span> the number of successes for subject <span class="math inline">\(i\)</span>, the test statistic is:</p>
<p><span class="math display">\[Q = \frac{k(k-1) \sum_{j=1}^{k} \left( G_j - \bar{G} \right)^2}{k \sum_{i=1}^{n} L_i - \sum_{i=1}^{n} L_i^2}\]</span></p>
<p>Under the null hypothesis, <span class="math inline">\(Q\)</span> approximately follows a Chi-square distribution with <span class="math inline">\(k - 1\)</span> degrees of freedom. Notice that the numerator compares the number of successes between the conditions, while a subject who gives the same answer in all conditions contributes nothing to the denominator, exactly like the concordant pairs which bring no information in the McNemar’s test.</p>
</div>
<div id="assumptions" class="section level2">
<h2>Assumptions</h2>
<p>For the results of the Cochran’s Q test to be valid, the following assumptions must be met:</p>
<ol style="list-style-type: decimal">
<li><strong>One binary dependent variable, measured <span class="math inline">\(k \geq 3\)</span> times on the same subjects.</strong> The variable of interest must be <a href="https://statsandr.com/blog/variable-types-and-examples/#qualitative" rel="nofollow" target="_blank">qualitative</a> with exactly two levels (“Yes”/“No”, success/failure, present/absent, etc.), and the <span class="math inline">\(k\)</span> measurements must be collected on the same subjects (or on matched blocks of subjects), so we are in a within-subjects design. If the <span class="math inline">\(k\)</span> samples are independent instead of related, the <a href="https://statsandr.com/blog/chi-square-test-of-independence-in-r/" rel="nofollow" target="_blank">Chi-square test of independence</a> should be used instead.</li>
<li><strong>Subjects (blocks) are independent of each other.</strong> <em>Within</em> a subject, the <span class="math inline">\(k\)</span> answers are of course dependent, and this is exactly what the test accounts for. <em>Between</em> subjects, however, independence is required: the answers of one respondent must not influence those of another respondent. As for many statistical tests, this assumption is verified based on the design of the experiment rather than via a formal test, and a random sample of respondents answering individually is generally sufficient, which is the case in our example.</li>
<li><strong>A large enough sample.</strong> The <span class="math inline">\(p\)</span>-value of the test is based on a Chi-square approximation, which is reliable only if the sample is reasonably large. A common rule of thumb is that the number of subjects multiplied by the number of conditions (<span class="math inline">\(n \times k\)</span>) should be at least 24, which is largely the case here since <span class="math inline">\(n \times k =\)</span> 600. For smaller samples, exact or permutation versions of the test are preferable (they are available, among others, in the <code>{coin}</code> package).</li>
<li><strong>The link with the McNemar’s test.</strong> Last but not least, the Cochran’s Q test reduces mathematically to the <a href="https://statsandr.com/blog/mcnemars-test-in-r/" rel="nofollow" target="_blank">McNemar’s test</a> when <span class="math inline">\(k = 2\)</span>. This is the reason why the McNemar’s test is used for exactly two related measurements, and the Cochran’s Q test for more than two. This equivalence is illustrated on our data in the next section.</li>
</ol>
</div>
<div id="in-r" class="section level2">
<h2>In R</h2>
<p>Base R has no built-in function for the Cochran’s Q test, but the <code>cochran_qtest()</code> function from the <code>{rstatix}</code> package does the job. It expects the data in the long format, and a formula of the form <code>outcome ~ condition | subject</code>:</p>
<pre># install.packages(&quot;rstatix&quot;)
library(rstatix)

dat_long %&gt;%
  cochran_qtest(opinion ~ time | respondent)
## # A tibble: 1 × 6
##   .y.         n statistic    df        p method          
## * &lt;chr&gt;   &lt;int&gt;     &lt;dbl&gt; &lt;dbl&gt;    &lt;dbl&gt; &lt;chr&gt;           
## 1 opinion   200      18.3     2 0.000108 Cochran&#39;s Q test</pre>
<p>The output shows:</p>
<ul>
<li>the variable of interest (<code>.y.</code>),</li>
<li>the number of subjects (<code>n</code>),</li>
<li>the value of the test statistic <span class="math inline">\(Q\)</span> (<code>statistic</code>),</li>
<li>the degrees of freedom (<code>df</code>), equal to <span class="math inline">\(k - 1 = 2\)</span> in our case since we compare 3 measurements,</li>
<li>the <span class="math inline">\(p\)</span>-value (<code>p</code>) and</li>
<li>the name of the test which has been performed (<code>method</code>).</li>
</ul>
<p>Note that the <code>CochranQTest()</code> function from the <code>{DescTools}</code> package is an alternative to perform this test.</p>
<p>As mentioned in the previous section, the Cochran’s Q test reduces to the McNemar’s test when only two related measurements are compared. This is easily verified on our data by keeping only the first two points in time:</p>
<pre># Cochran&#39;s Q test on the first 2 measurements only
dat_long %&gt;%
  filter(time %in% c(&quot;before&quot;, &quot;after&quot;)) %&gt;%
  mutate(time = droplevels(time)) %&gt;%
  cochran_qtest(opinion ~ time | respondent)
## # A tibble: 1 × 6
##   .y.         n statistic    df         p method          
## * &lt;chr&gt;   &lt;int&gt;     &lt;dbl&gt; &lt;dbl&gt;     &lt;dbl&gt; &lt;chr&gt;           
## 1 opinion   200      17.3     1 0.0000318 Cochran&#39;s Q test
# McNemar&#39;s test on the same 2 measurements
mcnemar.test(table(dat$before, dat$after),
  correct = FALSE
)
## 
## 	McNemar&#39;s Chi-squared test
## 
## data:  table(dat$before, dat$after)
## McNemar&#39;s chi-squared = 17.308, df = 1, p-value = 3.179e-05</pre>
<p>The two test statistics (and the two <span class="math inline">\(p\)</span>-values) are identical.<a href="https://statsandr.com/blog/cochrans-q-test-in-r/#fn1" class="footnote-ref" id="fnref1" rel="nofollow" target="_blank"><sup>1</sup></a></p>
<p>It is the <span class="math inline">\(p\)</span>-value which is of interest to conclude the test. If you are not familiar with <span class="math inline">\(p\)</span>-values, I invite you to read this <a href="https://statsandr.com/blog/student-s-t-test-in-r-and-by-hand-how-to-compare-two-groups-under-different-scenarios/#a-note-on-p-value-and-significance-level-alpha" rel="nofollow" target="_blank">section</a>.</p>
</div>
<div id="interpretations" class="section level2">
<h2>Interpretations</h2>
<p>Based on the Cochran’s Q test, we reject the null hypothesis at the significance level <span class="math inline">\(\alpha = 0.05\)</span> and we conclude that the proportion of citizens in favor of the new policy is not the same at the three points in time (<span class="math inline">\(p\)</span>-value < 0.001).</p>
<p>(<em>For the sake of illustration</em>, if the <span class="math inline">\(p\)</span>-value had been larger than the significance level <span class="math inline">\(\alpha = 0.05\)</span>: we could not have rejected the null hypothesis, so we could not have concluded that the proportion of citizens in favor of the policy changed over time.)</p>
<p>Note also that the test does not indicate the direction of the change, which must be read from the proportions and the plot presented in the section about the data.</p>
</div>
</div>
<div id="post-hoc-tests" class="section level1">
<h1>Post-hoc tests</h1>
<p>We have just showed that the proportion of citizens in favor of the policy is not stable over time. Nonetheless, here comes the limitation of the test: it does not say which measurement(s) differ(s) from the others.</p>
<p>To know this, we need post-hoc tests (in Latin, “after this”, so after obtaining significant results for the Cochran’s Q test), also referred as multiple pairwise-comparison tests. The logic is the same as the one presented for the <a href="https://statsandr.com/blog/kruskal-wallis-test-nonparametric-version-anova/#post-hoc-tests" rel="nofollow" target="_blank">Kruskal-Wallis test</a>: we compare the groups two by two, and we adjust the <span class="math inline">\(p\)</span>-values because performing several tests on the same data increases the risk of finding a significant difference by chance alone.</p>
<p>Here, the natural post-hoc test is simply the <a href="https://statsandr.com/blog/mcnemars-test-in-r/" rel="nofollow" target="_blank">McNemar’s test</a> applied to each pair of measurements. The post-hoc step is thus literally a repeated application of the test presented in the article dedicated to the McNemar’s test, which makes sense given that the Cochran’s Q test is nothing more than its extension to more than two related measurements.</p>
<p>With 3 measurements, there are 3 pairs to compare. This is done with the <code>pairwise_mcnemar_test()</code> function of the <code>{rstatix}</code> package, with the Holm method to adjust the <span class="math inline">\(p\)</span>-values:<a href="https://statsandr.com/blog/cochrans-q-test-in-r/#fn2" class="footnote-ref" id="fnref2" rel="nofollow" target="_blank"><sup>2</sup></a></p>
<pre>dat_long %&gt;%
  pairwise_mcnemar_test(opinion ~ time | respondent,
    p.adjust.method = &quot;holm&quot;
  )
## # A tibble: 3 × 8
##   group1 group2    statistic    df         p    p.adj p.adj.signif method      
## * &lt;chr&gt;  &lt;chr&gt;         &lt;dbl&gt; &lt;dbl&gt;     &lt;dbl&gt;    &lt;dbl&gt; &lt;chr&gt;        &lt;chr&gt;       
## 1 before after         16.2      1 0.0000578 0.000173 ***          McNemar test
## 2 before follow_up      3.31     1 0.0689    0.0689   ns           McNemar test
## 3 after  follow_up      6.04     1 0.0140    0.0280   *            McNemar test</pre>
<p>It is the <code>p.adj</code> column (the <span class="math inline">\(p\)</span>-values adjusted for multiple comparisons) which is of interest, and not the <code>p</code> column (the <em>un</em>adjusted <span class="math inline">\(p\)</span>-values). These adjusted <span class="math inline">\(p\)</span>-values must be compared to the desired significance level (usually 5%).</p>
<p>Based on the output, we conclude that:</p>
<ul>
<li>the proportion of citizens in favor of the policy differs significantly between before and right after the debate (<span class="math inline">\(p\)</span>-value < 0.001),</li>
<li>it differs significantly between right after the debate and one month later (<span class="math inline">\(p\)</span>-value = 0.028), and</li>
<li>it does not differ significantly between before the debate and one month later (<span class="math inline">\(p\)</span>-value = 0.069).</li>
</ul>
<p>Combined with the proportions computed earlier, these post-hoc tests give a much more precise picture than the Cochran’s Q test alone: the debate significantly increased the support for the policy in the short run (from 46% to 61%), but this increase did not last. One month later, the support had significantly decreased compared to the level observed right after the debate, and it was back to a level no longer significantly different from the initial one.</p>
</div>
<div id="summary" class="section level1">
<h1>Summary</h1>
<p>In this article, we reviewed the aim, the hypotheses and the assumptions of the Cochran’s Q test, used to compare three or more related proportions. We then showed how to perform it in R with the <code>cochran_qtest()</code> function of the <code>{rstatix}</code> package, how to interpret its results by comparing the <span class="math inline">\(p\)</span>-value with the significance level <span class="math inline">\(\alpha\)</span>, and, since a significant result only indicates that at least one measurement differs from the others, how to identify which ones thanks to pairwise McNemar’s tests with adjusted <span class="math inline">\(p\)</span>-values. Remember, last but not least, that the <a href="https://statsandr.com/blog/mcnemars-test-in-r/" rel="nofollow" target="_blank">McNemar’s test</a> is the special case of the Cochran’s Q test for exactly two related measurements, and that with independent samples the <a href="https://statsandr.com/blog/chi-square-test-of-independence-in-r/" rel="nofollow" target="_blank">Chi-square test of independence</a> should be preferred.</p>
<p>Thanks for reading.</p>
<p>I hope this article helped you to understand the Cochran’s Q test and how to perform it in R.</p>
<p>As always, if you have a question or a suggestion related to the topic covered in this article, please add it as a comment so other readers can benefit from the discussion.</p>
</div>
<div class="footnotes footnotes-end-of-document">
<hr />
<ol>
<li id="fn1"><p>The continuity correction must be removed with <code>correct = FALSE</code> for the equality to hold, since the Cochran’s Q test does not apply such a correction.<a href="https://statsandr.com/blog/cochrans-q-test-in-r/#fnref1" class="footnote-back" rel="nofollow" target="_blank"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></p></li>
<li id="fn2"><p>The Holm adjustment is less conservative than the Bonferroni one, which is the default in this function. See <code>?p.adjust</code> for the other available methods.<a href="https://statsandr.com/blog/cochrans-q-test-in-r/#fnref2" class="footnote-back" rel="nofollow" target="_blank"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></p></li>
</ol>
</div>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://statsandr.com/blog/cochrans-q-test-in-r/"> R on Stats and R</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/cochrans-q-test-in-r-or-the-extension-of-mcnemars-test-for-more-than-two-groups/">Cochran’s Q test in R, or the extension of McNemar’s test for more than two groups</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403318</post-id>	</item>
		<item>
		<title>On the Expectation-Maximization (EM) algorithm and regression models</title>
		<link>https://www.r-bloggers.com/2026/08/on-the-expectation-maximization-em-algorithm-and-regression-models/</link>
		
		<dc:creator><![CDATA[https://pacha.dev/blog]]></dc:creator>
		<pubDate>Mon, 24 Aug 2026 23:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pacha.dev/blog/2026/08/25/em-algorithm/index.html</guid>

					<description><![CDATA[<p>Using an iterative optimization framework to rethink regression models</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/on-the-expectation-maximization-em-algorithm-and-regression-models/">On the Expectation-Maximization (EM) algorithm and regression models</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://pacha.dev/blog/2026/08/25/em-algorithm/index.html"> https://pacha.dev/blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p><em>Before the main content: I am creating an R Community on Google Groups. You can join the group using this <a href="https://docs.google.com/forms/d/e/1FAIpQLSdMAj4adRAT4Gyuwt_9dPvxRvOUPml9AD59vuI7qS7XDlp48g/viewform?usp=dialog" rel="nofollow" target="_blank">form</a>.</em></p>

<h1>Linear Regression</h1>
<p>The Expectation-Maximization (EM) algorithm is an iterative optimization framework used to find <strong>maximum likelihood</strong> estimates of parameters when a model depends on unobserved, latent variables.</p>
<p>For linear regression, the EM algorithm is unnecessary because Ordinary Least Squares (OLS) yields a closed-form analytical solution, which is \(\hat{\beta} = (X^T X)^{-1} (X^T y)\). However, framing linear regression through the EM algorithm is a quite clarifying exercise before jumping to Poisson (or binomial/logit) regression, models where data is missing (e.g., Tobit), or where the dataset is generated by a Mixture of Linear Regressions (multiple hidden lines).</p>
<p>To understand how EM applies, consider a dataset with \(n\) observations and \(p < n\) variables (or features). Each observation \(i\) corresponds to a vector \(x_i = (x_{i1}, x_{i2}, \ldots, x_{ip})^T
\in \mathbb{R}^p\), a scalar response \(y_i\), and the model is</p>
<p>\[
y_i = \sum_{j = 1}^p \beta_j x_{ij} + e_i,\quad e_i \sim N(0, \sigma^2),
\]</p>
<p>where \(\beta = (\beta_1, \beta_2, \ldots, \beta_p)^T \in \mathbb{R}^p\) are the weights to be estimated.</p>
<p>Stacking all \(n\) observations gives the compact matrix form \(y = X\beta + e\), where</p>
<p>\[
X = \begin{pmatrix}
x_{11} &#038; x_{12} &#038; \ldots &#038; x_{1p} \\
x_{21} &#038; x_{22} &#038; \ldots &#038; x_{2p} \\
\vdots &#038; \ddots &#038;        &#038;        \\
x_{n1} &#038; x_{n2} &#038; \ldots &#038; x_{np}
\end{pmatrix} \in \mathbb{R}^{n \times p},
\quad
y = \begin{pmatrix} y_1 \\ y_2 \\ \vdots \\ y_n \end{pmatrix} \in \mathbb{R}^n,
\quad
e = \begin{pmatrix} e_1 \\ e_2 \\ \vdots \\ e_n \end{pmatrix} \sim N(0, \sigma^2 I_n).
\]</p>
<p>In the EM framing we treat the unobserved (latent) variables \(z_i = \sum_{j = 1}^p \beta_j x_{ij}\) as the complete data, even though we can compute their distribution exactly. The point is to establish the machinery that generalises to other models.</p>

<h2>Setup: complete-data log-likelihood</h2>
<p>If we observed both \(y_i\) and \(z_i\), the complete-data log-likelihood for \(\beta\) and \(\sigma^2\) would be</p>
<p>\[
\ell_c(\beta, \sigma^2) = -\frac{n}{2}\log(2\pi\sigma^2) &#8211; \frac{1}{2\sigma^2}\sum_{i=1}^n (y_i
  &#8211; z_i)^2.
\]</p>
<p>because \(y_i \mid z_i \sim N(z_i, \sigma^2)\) (the noise model), and \(z_i\) is deterministic given \(\beta\).</p>

<h2>Expectation step</h2>
<p>Given current parameter estimates \(\beta^{(t)}\) and \(\sigma^{2(t)}\), the E-step computes the expected complete-data log-likelihood with respect to the conditional distribution \(p(z \mid y, \beta^{(t)}, \sigma^{2(t)})\).</p>
<p>For linear regression the latent variable \(z_i\) is fully determined by \(\beta\). There is no uncertainty once \(\beta\) is fixed, so the conditional expectation is just the current fitted value</p>
<p>\[
\mathbb{E}\left[z_i \mid y_i, \beta^{(t)}\right] = \sum_{j = 1}^p \beta_j^{(t)} x_{ij}.
\]</p>
<p>In matrix notation this is the \(i\)-th entry of \(X \beta^{(t)}\).</p>
<p>Substituting into the complete-data log-likelihood yields the Q-function</p>
<p>\[
Q(\beta, \sigma^2 \mid \beta^{(t)}, \sigma^{2(t)})
  = -\frac{n}{2}\log(2\pi\sigma^2)
  &#8211; \frac{1}{2\sigma^2}\sum_{i=1}^n \left(y_i &#8211; \sum_{j = 1}^p \beta_j^{(t)} x_{ij}\right)^2.
\]</p>
<p>Or equivalently, using vector notation for the residual vector \(y &#8211; X \beta^{(t)}\),</p>
<p>\[
Q(\beta, \sigma^2 \mid \beta^{(t)}, \sigma^{2(t)})
  = -\frac{n}{2}\log(2\pi\sigma^2)
  &#8211; \frac{1}{2\sigma^2} \| y &#8211; X \beta^{(t)} \|^2.
\]</p>
<p>The E-step collapses to plugging in the current fitted values. No integration is needed.</p>

<h2>Maximization step</h2>
<p>The M-step updates the parameters by maximising \(Q\) with respect to \(\beta\) (and \(\sigma^2\)).</p>
<p><strong>Updating \(\beta\)</strong>. The only term in \(Q\) that depends on \(\beta\) is the sum of squared residuals. Applying the chain rule to \(Q\) with respect to \(\beta_j\) gives</p>
<p>\[
\frac{\partial Q}{\partial \beta_j}
  = -\frac{1}{2\sigma^2} \sum_{i=1}^n 2\!\left(y_i &#8211; \sum_{k=1}^p \beta_k x_{ik}\right)(-x_{ij})
  = \frac{1}{\sigma^2} \sum_{i=1}^n x_{ij}\!\left(y_i &#8211; \sum_{k=1}^p \beta_k x_{ik}\right).
\]</p>
<p>Setting this to zero and multiplying through by \(\sigma^2\):</p>
<p>\[
\sum_{i=1}^n x_{ij} y_i = \sum_{i=1}^n x_{ij} \sum_{k=1}^p \beta_k x_{ik}
  = \sum_{k=1}^p \beta_k \underbrace{\sum_{i=1}^n x_{ij} x_{ik}}_{(X^T X)_{jk}},
  \quad j = 1, \ldots, p.
\]</p>
<p>The left-hand side is the \(j\)-th entry of \(X^T y\), since \((X^T y)_j = \sum_i x_{ij} y_i\). The right-hand side is the \(j\)-th entry of \(X^T X \beta\), since the \((j,k)\) entry of \(X^T X\) is exactly \(\sum_i x_{ij} x_{ik}\). Stacking all \(p\) equations (\(j = 1, \ldots, p\)) into a single matrix equation:</p>
<p>\[
X^T X \beta = X^T y.
\]</p>
<p>where \(X^T X \in \mathbb{R}^{p \times p}\) is a symmetric positive-definite matrix (assuming the columns of \(X\) are linearly independent) and \(X^T y \in \mathbb{R}^p\) is a vector of inner products between each feature and the response. For illustration with \(p = 3\), these look like:</p>
<p>\[
\underbrace{\begin{pmatrix}
\sum x_{i1}^2     &#038; \sum x_{i1}x_{i2} &#038; \sum x_{i1}x_{i3} \\
\sum x_{i1}x_{i2} &#038; \sum x_{i2}^2     &#038; \sum x_{i2}x_{i3} \\
\sum x_{i1}x_{i3} &#038; \sum x_{i2}x_{i3} &#038; \sum x_{i3}^2
\end{pmatrix}}_{X^T X}
\begin{pmatrix} \beta_1 \\ \beta_2 \\ \beta_3 \end{pmatrix}
=
\underbrace{\begin{pmatrix} \sum x_{i1} y_i \\ \sum x_{i2} y_i \\ \sum x_{i3} y_i
  \end{pmatrix}}_{X^T y}.
\]</p>
<p>Inverting \(X^T X\) gives the OLS formula</p>
<p>\[
\beta^{(t+1)} = (X^T X)^{-1} X^T y.
\]</p>
<p>Note that this solution is independent of the current iterate \(\beta^{(t)}\), so the algorithm converges in a single M-step regardless of the initialisation. This is consistent with the fact that OLS has a closed-form solution.</p>
<p><strong>Updating \(\sigma^2\).</strong> Setting \(\partial Q / \partial \sigma^2 = 0\):</p>
<p>\[
\sigma^{2(t+1)} = \frac{1}{n} \sum_{i=1}^n  \left(y_i &#8211; \sum_{j=1}^p \beta_j^{(t+1)} x_{ij}\right)^2
  = \frac{1}{n}\|y &#8211; X \beta^{(t+1)}\|^2.
\]</p>
<p>which is the mean squared residual at the updated weights.</p>

<h2>Convergence</h2>
<p>Because the M-step yields the global maximum of the Q-function in closed form and that maximum does not depend on \(\beta^{(t)}\), the sequence \(\{\beta^{(t)}\}\) reaches \(\hat{\beta} = (X^T X)^{-1} X^T y\) after exactly one iteration. This is the standard EM convergence property specialised to the case where the complete-data problem is a convex problem with a unique global maximum.</p>

<h1>Poisson Regression</h1>
<p>Poisson regression models count data. As an aside, international trade models rely on Poisson pseudo maximum likelihood (PPML) and a continuous variable such as exports (or imports) does not follow a discrete Poisson distribution, which is why the PPML and not PML name. The PPML estimator is consistent if the conditional mean of the variate of interest is correctly specified. More on that on <a href="https://personal.lse.ac.uk/tenreyro/lgw.html" rel="nofollow" target="_blank">The Log of Gravity page</a>.</p>
<p>The response \(y_i \in \{0, 1, 2, \ldots\}\) is assumed to follow a Poisson distribution whose mean depends on the covariates through a log-link:</p>
<p>\[
y_i \sim \text{Poisson}(\mu_i), \quad \mu_i = \exp\!\left(\sum_{j=1}^p \beta_j x_{ij}\right) =
  \exp(x_i^T \beta).
\]</p>
<p>Unlike linear regression there is no closed-form solution for \(\beta\), so we need an iterative method. The EM algorithm provides one by introducing latent variables that make the complete-data problem tractable.</p>

<h2>Latent-variable construction</h2>
<p>Write the Poisson mean as \(\mu_i = \exp(x_i^T \beta)\) and introduce \(m\) latent binary indicators \(z_{i1}, \ldots, z_{im}\). This is one for each of \(m\) hypothetical sub-processes that together generate \(y_i\). Specifically, partition \(\mu_i\) into \(m\) equal parts \(\lambda = \mu_i / m\) and let</p>
<p>\[
z_{il} \sim \text{Bernoulli}(\lambda / (1 + \lambda)), \quad l = 1, \ldots, m,
\]</p>
<p>so that \(y_i = \sum_{l=1}^m z_{il}\) in the limit \(m \to \infty\).</p>
<p>In practice the standard EM formulation for Poisson regression avoids this explicit construction and instead treats the complete data as the pair \((y_i, \eta_i)\), where \(\eta_i = x_i^T \beta\) is the linear predictor, and exploits the exponential-family properties that apply to Poisson log-likelihood.</p>
<p>For more on the exponential family and statistical sufficiency, you can check <a href="https://www.routledge.com/Statistical-Inference/Casella-Berger/p/book/9781032593036" rel="nofollow" target="_blank">Casella and Berger</a>. It is one of my favourite books (unlike others that put elegance over clarity).</p>

<h2>Setup: complete-data log-likelihood</h2>
<p>The Poisson log-likelihood for a single observation is</p>
<p>\[
\log p(y_i \mid \beta) = y_i \log \mu_i &#8211; \mu_i &#8211; \log(y_i!)
  = y_i (x_i^T \beta) &#8211; \exp(x_i^T \beta) &#8211; \log(y_i!).
\]</p>
<p>Summing over all \(n\) observations gives the complete-data log-likelihood (dropping the constant \(\sum_i \log(y_i!)\)):</p>
<p>\[
\ell(\beta) \propto \sum_{i=1}^n \left[ y_i (x_i^T \beta) &#8211; \exp(x_i^T \beta) \right]
  = y^T X \beta &#8211; \mathbf{1}^T \exp(X\beta),
\]</p>
<p>where \(\exp(X\beta)\) denotes element-wise exponentiation and \(\mathbf{1}\) is a vector of ones.</p>

<h2>Expectation step</h2>
<p>Unlike linear regression, the Poisson log-likelihood is not quadratic in \(\beta\), so the E-step does not collapse trivially. The standard approach is to construct a working quadratic surrogate (the Q-function) at the current iterate \(\beta^{(t)}\) using a second-order Taylor expansion of \(\exp(x_i^T \beta)\) around \(\eta_i^{(t)} = x_i^T \beta^{(t)}\):</p>
<p>\[
\exp(x_i^T \beta) \approx \exp(\eta_i^{(t)}) + \exp(\eta_i^{(t)})(x_i^T \beta &#8211; \eta_i^{(t)})
  + \frac{1}{2}\exp(\eta_i^{(t)})(x_i^T \beta &#8211; \eta_i^{(t)})^2.
\]</p>
<p>As an aside, Taylor expansions provide the foundation for Newton’s Method. Both are used heavily in industry, and a famous example is Quake’s III <a href="https://www.youtube.com/watch?v=p8u_k2LIZyo" rel="nofollow" target="_blank">Fast Inverse Square Root</a>.</p>
<p>Substituting into \(\ell(\beta)\) and keeping only terms that depend on \(\beta\) gives the Q-function</p>
<p>\[
Q(\beta \mid \beta^{(t)}) \propto -\frac{1}{2} \sum_{i=1}^n \mu_i^{(t)} \!\left(x_i^T \beta &#8211; \eta_i^{(t)}
  &#8211; \frac{y_i &#8211; \mu_i^{(t)}}{\mu_i^{(t)}}\right)^{\!2},
\]</p>
<p>where \(\mu_i^{(t)} = \exp(\eta_i^{(t)})\).</p>
<p>Defining the working response</p>
<p>\[
\tilde{y}_i^{(t)} = \eta_i^{(t)} + \frac{y_i &#8211; \mu_i^{(t)}}{\mu_i^{(t)}}
\]</p>
<p>and the weight \(\mu_i^{(t)}\), the Q-function becomes</p>
<p>\[
Q(\beta \mid \beta^{(t)}) \propto -\frac{1}{2} \sum_{i=1}^n \mu_i^{(t)} \!\left(\tilde{y}_i^{(t)} &#8211;
  x_i^T \beta\right)^2.
\]</p>
<p>This is exactly a weighted least-squares objective. It is the same structure as the linear regression log-likelihood with complete data, but with observation-specific weights \(\mu_i^{(t)}\).</p>

<h2>Maximization step</h2>
<p>Maximising the weighted-least-squares Q-function with respect to \(\beta\) is the same calculation as in the linear regression M-step, but with a diagonal weight matrix \(W^{(t)} = \text{diag}(\mu_1^{(t)}, \ldots, \mu_n^{(t)}) \in \mathbb{R}^{n \times n}\).</p>
<p><strong>Updating \(\beta\).</strong> The weighted normal equations are obtained by the same entry-wise gradient argument as before. For each \(j = 1, \ldots, p\):</p>
<p>\[
\frac{\partial Q}{\partial \beta_j} = \sum_{i=1}^n \mu_i^{(t)} x_{ij}\!\left(\tilde{y}_i^{(t)} &#8211;
  x_i^T \beta\right) = 0,
\]</p>
<p>which in matrix form is</p>
<p>\[
X^T W^{(t)} X \beta = X^T W^{(t)} \tilde{y}^{(t)}.
\]</p>
<p>Here \(X^T W^{(t)} X\) is symmetric positive-definite (it is the weighted Gram matrix), and \(X^T W^{(t)} \tilde{y}^{(t)} \in \mathbb{R}^p\). Solving gives</p>
<p>\[
\beta^{(t+1)} = \left(X^T W^{(t)} X\right)^{-1} X^T W^{(t)} \tilde{y}^{(t)}.
\]</p>
<p>This is the Iteratively Reweighted Least Squares (IRLS) update, which is the standard algorithm for fitting Poisson (and other GLM) models. Each M-step is a weighted OLS problem with the same structure as the linear-regression result, but the weights \(W^{(t)}\) and working responses \(\tilde{y}^{(t)}\) change at every iteration as \(\mu_i^{(t)}\) is updated.</p>

<h2>Convergence</h2>
<p>Because the Poisson log-likelihood \(\ell(\beta)\) is strictly concave in \(\beta\) (the Hessian \(-X^T W X\) is negative-definite if \(X\) has full column rank), the sequence \(\{\beta^{(t)}\}\) converges to the unique maximum-likelihood estimate \(\hat{\beta}\). Unlike linear regression, convergence requires multiple iterations. The algorithm terminates when \(\|\beta^{(t+1)} &#8211; \beta^{(t)}\| < \varepsilon\) for a chosen tolerance \(\varepsilon\).</p>

<h2>More about Poisson and other models</h2>
<p>Check <a href="https://www.routledge.com/Generalized-Linear-Models/McCullagh-Nelder/p/book/9780412317606" rel="nofollow" target="_blank">McCullagh and Nelder</a>. It covers Poisson, Logit, and Generalized Linear Models with a very detailed treatment. This is another of my favourite books.</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://pacha.dev/blog/2026/08/25/em-algorithm/index.html"> https://pacha.dev/blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/on-the-expectation-maximization-em-algorithm-and-regression-models/">On the Expectation-Maximization (EM) algorithm and regression models</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403314</post-id>	</item>
		<item>
		<title>Does state life expectancy correlate with political party voting?</title>
		<link>https://www.r-bloggers.com/2026/08/does-state-life-expectancy-correlate-with-political-party-voting/</link>
		
		<dc:creator><![CDATA[Jerry Tuttle]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 18:19:07 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://www.r-bloggers.com/?guid=7e5aa54c49a9ebbb1cf1a06ef6eb3ee8</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Overview</p>
<p>     <br />
Do people in red states live shorter lives? </p>
<p>     <br />
This project examines whether state level life expectancy is statistically associated with each state's political climate in t...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/does-state-life-expectancy-correlate-with-political-party-voting/">Does state life expectancy correlate with political party voting?</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://onlinecollegemathteacher.blogspot.com/2026/08/does-state-life-expectancy-correlate.html"> Online College Math Teacher</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<font size = 3>
  
<h3>Overview</h3>
  
       
Do people in red states live shorter lives? <p>

     
This project examines whether state level life expectancy is statistically associated with each state&#8217;s political climate in the 2024 presidential election.  Political climate is measured using the popular vote margin, defined as (Trump votes &#8211; Harris votes) / (Trump votes + Harris votes). A positive value indicates a Republican advantage, and a negative value indicates a Democratic advantage.
<p>
  
      
I&#8217;m not predicting elections, and I&#8217;m not making a claim of causality.  There are many reasons why two states differ in life expectancies &#8211; differences in average income, availability of medical care, occupations with differing job hazards, etc. I&#8217;m just asking whether these two measurable state‑level quantities move together. <p>
  
      
A scatterplot of life expectancy versus vote margin shows a clearly downward trend: states with higher Republican vote margins tend to have lower life expectancy, and states with higher Democratic margins tend to have higher life expectancy.  <p>
  
<div class="separator" style="clear: both;"><a href="https://lh3.google.com/u/0/d/1PvRU68DbXvmoABYdxZnUT10ZqXLlpmKv=s980?auditContext=thumbnail" style="display: block; padding: 1em 0; text-align: center; " rel="nofollow" target="_blank"><img alt="" border="0" width="400" data-original-height="690" data-original-width="450" src="https://lh3.google.com/u/0/d/1PvRU68DbXvmoABYdxZnUT10ZqXLlpmKv=s400?auditContext=thumbnail"/></a></div>
  
     
The correlation coefficient r is -0.50.  This represents a <b>moderate negative relationship</b> &#8211; neither weak nor strong, but unmistakenly present.  A significance test yields t = -4.041, p = .00019. With 51 observations, this correlation is statistically significant at the .05 level.<p>
  
  
<h3>Details</h3>

      
I obtained 2024 presidential percentage popular vote data from the <a href="https://onlinecollegemathteacher.blogspot.com/2026/08/www.fec.gov/documents/5645/2024presgeresullts.xlsx" rel="nofollow" target="_blank"> Federal Election Commission</a>.  Trump won 31 of 51 (50 plus DC) states in 2024. To visualize the distribution of states, I grouped the vote margin varaible into four bins:  
  
  <ul>
  <li>Dem Majority: margin ≤ -10% </li>
  <li>Dem Narrow:  -10% < margin ≤ 0 </li>
  <li>Rep Narrow:  0 < margin ≤ 10% </li>
   <li>Rep Majority: margin > 10% </li>
</ul>  
  
24 of Trump&#8217;s 31 states were by margins greater than 10%.  A bar chart and a US map show how many states fall into each group and where they are located geographically.<p>
  
<div class="separator" style="clear: both;"><a href="https://lh3.google.com/u/0/d/1jnVe2Tv_QnpLxNMrL6UB2a-yccF_Wypl=s1462?auditContext=thumbnail" style="display: block; padding: 1em 0; text-align: center; " rel="nofollow" target="_blank"><img alt="" border="0" width="400" data-original-height="683" data-original-width="450" src="https://lh3.google.com/u/0/d/1jnVe2Tv_QnpLxNMrL6UB2a-yccF_Wypl=s400?auditContext=thumbnail"/></a></div> <p>
  
  <div class="separator" style="clear: both;"><a href="https://lh3.google.com/u/0/d/1RB24IoZ7Cy4UCKH019zWVDTxNgG7lJPs=s778?auditContext=thumbnail" style="display: block; padding: 1em 0; text-align: center; " rel="nofollow" target="_blank"><img alt="" border="0" width="400" data-original-height="687" data-original-width="450" src="https://lh3.google.com/u/0/d/1RB24IoZ7Cy4UCKH019zWVDTxNgG7lJPs=s400?auditContext=thumbnail"/></a></div>  <p>
  
       
The CDC (Centers for Disease Control and Prevention) publishes 
  <a href="https://www.cdc.gov/nchs/data/nvsr/nvsr74/nvsr74-12.pdf" rel="nofollow" target="_blank">life expectancy </a>tables by state.
These are period life tables, showing the life expectancy of a newborn under today’s mortality rates, assuming the age‑specific death rates observed in that year (e.g., 2022) stay fixed for the newborn’s entire lifetime. Hawaii has the highest life expectancy at 80.0 years, and Mississippi has the lowest at 70.9 years. <p>
  
      
The following map shows life expectancies by state.  I allocated the states by quartile (shortest life expectancy, shorter, longer, longest).  I believe there is a relationship especially with southern states having short life expectancies in this map, compared with Republican margins in the prior map. <p>

 <div class="separator" style="clear: both;"><a href="https://lh3.google.com/u/0/d/19r0PiTjO4zfFIx7-wzxoGCmxRrcBFwFU=s766?auditContext=thumbnail" style="display: block; padding: 1em 0; text-align: center; " rel="nofollow" target="_blank"><img alt="" border="0" width="400" data-original-height="683" data-original-width="450" src="https://lh3.google.com/u/0/d/19r0PiTjO4zfFIx7-wzxoGCmxRrcBFwFU=s400?auditContext=thumbnail"/></a></div> <p>
  
     
  The actual correlation coefficient is r = -0.50, which is moderate, but statistically significant. <p>

  
      
  Incidentally, I had a little challenge drawing the maps with R library usmap.  That library includes Puerto Rico which was not in the voter or life expectancy data, and the map would show Puerto Rico as an NA until I excluded it in the plot_usmap statement. <p>
  
 
<h3>R code</h3>
<pre>
library(readxl)
pres &lt;- read_excel(&quot;C:/Users/Jerry/Desktop/R_files/2024presgeresults.xlsx&quot;, n_max=51)
pres$TRUMP_PERCENT &lt;- pres$TRUMP/(pres$TRUMP + pres$HARRIS)   # ratio of popular votes
pres$HARRIS_PERCENT &lt;- pres$HARRIS/(pres$TRUMP + pres$HARRIS)
pres$TRUMP_MARGIN &lt;- round(pres$TRUMP_PERCENT - pres$HARRIS_PERCENT,3)
pres &lt;- pres[, c(&quot;STATE&quot;, &quot;TRUMP_MARGIN&quot;)]   # states are 2 letter abbrevs
colnames(pres)[1] &lt;- &quot;state&quot;  # usmap requires state Column Name to be lowercase &quot;state&quot;

# CDC life expectancies by state:   https://www.cdc.gov/nchs/data/nvsr/nvsr74/nvsr74-12.pdf

library(pdftools)   # extract text from pdf file
library(tidyverse)
raw_text &lt;- pdf_text(&quot;C:/Users/Jerry/Desktop/R_files/nvsr74-12.pdf&quot;)
page_text &lt;- raw_text[3]   # page 3 only
lines &lt;- read_lines(page_text)

# data cleaning of life expectancy file:
clean_lines &lt;- lines %&gt;%
  str_trim() %&gt;%                      # Remove leading/trailing spaces
  keep(~ .x != &quot;&quot;)                    # Drop empty rows

# Extract column headers (row 6 contains headers):
headers &lt;- str_split(clean_lines[6], &quot;\\s{2,}&quot;)[[1]]  # these are partial headers

# Process the data rows (Rows 7 to the end):
data_rows &lt;- clean_lines[7:(length(clean_lines)-3)]   # delete footnotes

# Convert text lines into data frame:
life_exp &lt;- data_rows %&gt;%
  # Split columns whenever there are 2 or more spaces
  str_split_fixed(&quot;\\s{2,}&quot;, n = length(headers)) %&gt;%
  as_tibble(.name_repair = &quot;minimal&quot;)

colnames(life_exp) &lt;- c(&quot;State&quot;, &quot;Tot_Rank&quot;, &quot;Tot_LE&quot;, &quot;Tot_SE&quot;, &quot;Male_Rank&quot;, &quot;Male_LE&quot;, &quot;Male_SE&quot;,
     &quot;Fem_Rank&quot;, &quot;Fem_LE&quot;, &quot;Fem_SE&quot;)
life_exp$State &lt;- gsub(&quot;\\.&quot;, &quot;&quot;, life_exp$State)    # delete periods
life_exp$State &lt;- sub(&quot;\\s+$&quot;, &quot;&quot;, life_exp$State)   # delete spaces after last char 
life_exp &lt;- subset(life_exp, State != &quot;United States&quot;)
# convert states from names to abbreviations; District of Columbia will be NA without next line:
life_exp$State &lt;- c(state.abb, &quot;DC&quot;)[match(life_exp$State, c(state.name, &quot;District of Columbia&quot;))]   # Convert full name to 2-letter abbreviation
life_exp &lt;- life_exp %&gt;%
  mutate(across(where(is.character) & -1, as.numeric))   # converts all character columns in a data frame into numeric columns, except for the very first column
life_exp &lt;- life_exp[, c(&quot;State&quot;, &quot;Tot_LE&quot;)]   # states are 2 letter abbrevs
colnames(life_exp)[1] &lt;- &quot;state&quot;  # usmap requires state Column Name to be lowercase &quot;state&quot;
print(life_exp)

df &lt;- merge(pres, life_exp, by = &quot;state&quot;)

########  Display summaries:  ########

library(ggplot2)

common_theme &lt;- theme(
        plot.title = element_text(size=15, face=&quot;bold&quot;),
        plot.subtitle = element_text(size=12.5, face=&quot;bold&quot;),
        axis.title = element_text(size=15, face=&quot;bold&quot;),
        axis.text = element_text(size=15, face=&quot;bold&quot;),
        legend.title = element_text(size=15, face=&quot;bold&quot;),
        legend.text = element_text(size=15, face=&quot;bold&quot;))

df &lt;- df %&gt;%
  mutate(TRUMP_MARGIN_RANGE = case_when(  
    TRUMP_MARGIN  -.10 & TRUMP_MARGIN  0 & TRUMP_MARGIN  .10 ~ &quot;Rep Majority&quot;,
    TRUE ~ NA_character_ 
  )
)

percent_colors &lt;- c(&quot;Dem Majority&quot; = &quot;#883068&quot;, &quot;Dem Narrow&quot; = &quot;#4292C6&quot;, 
                    &quot;Rep Narrow&quot; = &quot;#FB6A4A&quot;, &quot;Rep Majority&quot; = &quot;#CB181D&quot;)
df$TRUMP_MARGIN_RANGE &lt;- factor(
  df$TRUMP_MARGIN_RANGE, 
  levels = c(&quot;Dem Majority&quot;, &quot;Dem Narrow&quot;, &quot;Rep Narrow&quot;, &quot;Rep Majority&quot;)
)

ggplot(df, aes(x = TRUMP_MARGIN_RANGE)) +
  geom_bar(fill = percent_colors) +
  geom_text(
    stat = &quot;count&quot;, 
    aes(label = after_stat(count)),
    fontface = &quot;bold&quot;, 
    vjust = -0.5
  ) +  
  labs(title=&quot;2024 Presidential Election Results by Vote Margin&quot;,
       y = &quot;Number of States&quot;, x = &quot;% Popular Vote Margin&quot;) +
  guides(fill = guide_legend(title = NULL)) + 
  scale_x_discrete(
    labels = c(
      &quot;Dem Majority&quot; = &quot;Dem + 10% or more&quot;,
      &quot;Dem Narrow&quot; = &quot;Dem 0 - 10%&quot;,
      &quot;Rep Narrow&quot; = &quot;Rep 0 - 10%&quot;,
      &quot;Rep Majority&quot; = &quot;Rep + 10% or more&quot;)) +
  common_theme

colSums(is.na(df))  # 0
colnames(df)[1] &lt;- &quot;state&quot;   # usmap requires state Column Name to be lowercase &quot;state&quot;
length(df$state)   # 51
df$state &lt;- trimws(toupper(df$state))   #51 states including DC

library(usmap)
unique(usmap::us_map(regions = &quot;states&quot;)$full)  # Includes Puerto Rico
plot_usmap(data = df, , regions = &quot;states&quot;, values = &quot;TRUMP_MARGIN_RANGE&quot;, exclude = &quot;Puerto Rico&quot;) +
  labs(title=&quot;2024 Presidential Election Results by Vote Margin&quot;) +
  scale_fill_manual(values=percent_colors,
    guide = guide_legend(title = NULL, direction=&quot;vertical&quot;),
    labels = c(
      &quot;Dem Majority&quot; = &quot;Dem + 10% or more&quot;,
      &quot;Dem Narrow&quot; = &quot;Dem 0 - 10%&quot;,
      &quot;Rep Narrow&quot; = &quot;Rep 0 - 10%&quot;,
      &quot;Rep Majority&quot; = &quot;Rep + 10% or more&quot;)
    ) +
  theme(
    legend.position = &quot;bottom&quot;,
    legend.box = &quot;vertical&quot;,
    plot.title = element_text(size=15, face=&quot;bold&quot;),
    legend.title = element_text(size=12, face=&quot;bold&quot;),
    legend.text = element_text(size=12, face=&quot;bold&quot;)
  )

df &lt;- df %&gt;%
  mutate(
    LE_RANGE = case_when(
      ntile(Tot_LE, 4) == 1 ~ &quot;Shortest LE&quot;,
      ntile(Tot_LE, 4) == 2 ~ &quot;Shorter LE&quot;,
      ntile(Tot_LE, 4) == 3 ~ &quot;Longer LE&quot;,
      ntile(Tot_LE, 4) == 4 ~ &quot;Longest LE&quot;
    )
  )

table(df$LE_RANGE)

LE_colors &lt;- c(
  &quot;Shortest LE&quot; = &quot;#D1E5F0&quot;,
  &quot;Shorter LE&quot;    = &quot;#92C5DE&quot;,
  &quot;Longer LE&quot;     = &quot;#4393C3&quot;,
  &quot;Longest LE&quot;  = &quot;#8B3C59&quot;
)

df$LE_RANGE &lt;- factor(
  df$LE_RANGE, 
  levels = c(&quot;Shortest LE&quot;, &quot;Shorter LE&quot;, &quot;Longer LE&quot;, &quot;Longest LE&quot;)
)

plot_usmap(data = df, regions = &quot;states&quot;, values = &quot;LE_RANGE&quot;, exclude = &quot;Puerto Rico&quot;) +
  labs(title=&quot;Life Expectancies by State&quot;) +
  scale_fill_manual(values=LE_colors,
     guide = guide_legend(title = NULL, direction=&quot;vertical&quot;)) +
  theme(
    legend.position = &quot;bottom&quot;,
    legend.box = &quot;vertical&quot;,
    plot.title = element_text(size=15, face=&quot;bold&quot;),
    legend.title = element_text(size=12, face=&quot;bold&quot;),
    legend.text = element_text(size=12, face=&quot;bold&quot;)
  )


########  Corr coeff:  ########

library(ggrepel)
ggplot(data = df, mapping = aes(x = TRUMP_MARGIN, y = Tot_LE)) +
  geom_point(color = &quot;#4D4D4D&quot;) +
  geom_smooth(method = &quot;lm&quot;, color = &quot;steelblue&quot;, se = FALSE, linewidth = 1) +
  geom_text_repel(
    data = df[df$state %in% c(&quot;HI&quot;, &quot;WV&quot;), ],
    aes(x = TRUMP_MARGIN, y = Tot_LE, label = state),
    size = 4, color = &quot;black&quot;, fontface = &quot;bold&quot;, nudge_y = 0.25
  ) +
  ggtitle(&quot;Life Expectancy vs % Popular Vote Margin by State&quot;) +
  xlab(&quot;Pop Vote Margin %:  Rep +, Dem - &quot;) +
  ylab(&quot;Life Expectancy&quot;) + 
  annotate(
    &quot;text&quot;,
    x = min(df$TRUMP_MARGIN) + .02,
    y = max(df$Tot_LE) - .02,
    label = &quot;r = -0.50&quot;,
    hjust = 0, vjust = 1,
    color = &quot;gray20&quot;, fontface = &quot;bold&quot;, size = 4) +
  common_theme

r &lt;- round(cor(df[, c(&quot;Tot_LE&quot;, &quot;TRUMP_MARGIN&quot;)])[1, 2], 3)  # -.50 is moderate (neither weak nor strong)

# statistical significance of correlation coefficient:
n &lt;- nrow(df)
dof &lt;- n - 2
t &lt;- round(r*sqrt(n - 2) / sqrt(1 - r^2),3)   # - 4.041
alpha &lt;- .05
p_value &lt;- round(2 * pt(abs(t), df = dof, lower.tail = FALSE), 5)    # .00019
stat_signif &lt;- if(p_value &lt; alpha, &quot;is statistically significant&quot;, &quot;is not statistically significant&quot;)
cat(&quot;r = &quot;, r, &quot;p-value = &quot;, p_value, stat_signif)

</pre><p>
  
End
</font>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://onlinecollegemathteacher.blogspot.com/2026/08/does-state-life-expectancy-correlate.html"> Online College Math Teacher</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/does-state-life-expectancy-correlate-with-political-party-voting/">Does state life expectancy correlate with political party voting?</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403276</post-id>	</item>
		<item>
		<title>Using GitHub Actions to deploy to Posit Connect Cloud</title>
		<link>https://www.r-bloggers.com/2026/08/using-github-actions-to-deploy-to-posit-connect-cloud/</link>
		
		<dc:creator><![CDATA[The Jumping Rivers Blog]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 23:59:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>We’ve previously extolled the virtues of automating the repetitive chores we encounter, allowing us to focus on the tasks that matter.<br />
Three years ago, we posted an example of how to automate the deployment of a Shiny application to shinyapps.io from a GitHub Actions.<br />
When a new commit ...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/using-github-actions-to-deploy-to-posit-connect-cloud/">Using GitHub Actions to deploy to Posit Connect Cloud</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/"> The Jumping Rivers Blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<p>
<a href = "https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/">
<img src="https://i2.wp.com/www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/featured.png?w=400&#038;ssl=1" style="width:400px" class="image-center" style="display: block; margin: auto;" data-recalc-dims="1" />
</a>
</p>
<p>We’ve previously extolled the virtues of automating the repetitive chores we encounter, allowing us to focus on the tasks that matter.
Three years ago, we posted an example of <a href="https://www.jumpingrivers.com/blog/who-shiny-covid-maintenance-github-actions/" rel="nofollow" target="_blank">how to automate the deployment of a Shiny application to shinyapps.io from a GitHub Actions</a>.
When a new commit was pushed to the GitHub repository, it triggered an automated pipeline to bundle the Shiny application source code and send it to shinyapps.io. You could relax, knowing that the production deployment was always up-to-date.</p>
<p>But in January 2026, Posit announced that <a href="https://forum.posit.co/t/important-update-shinyapps-io-is-moving-to-connect-cloud/209804" rel="nofollow" target="_blank">shinyapps.io would be closing to new apps at the end of 2026</a>, with all users moving to Posit Connect Cloud.
Existing content on shinyapps.io will continue to work before automatically migrating across in early 2027.
But if you followed our previous methodology for automating that deployment from a GitHub Actions workflow, you’ll need to adjust your deployment strategy.</p>
<h2 id="why-move-to-posit-connect-cloud">Why move to Posit Connect Cloud?</h2>
<p>shinyapps.io has provided a faithful service to the Shiny community for a long time.
You make your Shiny application, you click “Deploy” in RStudio, <small>some magic happens,</small> then your work appears online for others to access.
You didn’t have to think too hard about R packages, build a Docker container, or set up a cloud compute instance to grant access to your app.
It was perhaps the simplicity of the deployment process and availability of a free-tier that made it so popular.</p>
<p><a href="https://connect.posit.cloud/" rel="nofollow" target="_blank">Posit Connect Cloud</a> takes what made shinyapps.io so popular, and stacks more features and convenience on top.
You’re no longer restricted to just hosting Shiny applications—Posit Connect Cloud can also support Streamlit, Bokeh, Jupyter Notebooks, and all plans allow unlimited hosting for rendered Quarto and R Markdown documents<sup id="fnref:1"><a href="https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/#fn:1" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">1</a></sup>.
You also get more functionality: the ability to set secret variables, regenerate content on a schedule, higher maximum compute limits, and SSL certificates when using custom domains.</p>
<p>A free-tier of Posit Connect Cloud also remains.
And while Posit Connect Cloud supports more types of content beyond just Shiny applications, you will find that benefit is reflected in the higher prices on paid-tiers over their nearest shinyapps.io equivalents.
Perhaps the biggest winners are users who mainly just needed a custom domain: This required the highest $349/month “Professional” tier on shinyapps.io, but is now available (with SSL certificate) on the $59/month<sup id="fnref:2"><a href="https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/#fn:2" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">2</a></sup> “Enhanced” tier and above on Posit Connect Cloud.</p>
<h2 id="deploying-content-to-posit-connect-cloud">Deploying content to Posit Connect Cloud</h2>
<p>The existing deployment methods used for shinyapps.io still work with Posit Connect Cloud, but there are some new options too:</p>
<ul>
<li>
<p>The <a href="https://quarto.org/docs/publishing/posit-connect-cloud.html" rel="nofollow" target="_blank">Quarto CLI can deploy to Posit Connect Cloud</a> using the <code>quarto publish</code> command.</p>
</li>
<li>
<p>You can grant <a href="https://docs.posit.co/connect-cloud/user/publish/github.html" rel="nofollow" target="_blank">Posit Connect Cloud access to your GitHub account</a> to perform a <em>Git-backed deployment</em>, where it will monitor the code for changes and automatically re-deploy when the target branch is updated.
The only extra step you need to do is to commit and push a <em>manifest.json</em> file, which is often as simple as running the following in R:</p>
<pre>rsconnect::writeManifest()
</pre></li>
</ul>
<p>The <em>Git-backed deployment</em> may be a very useful replacement to those who have previously deployed content to shinyapps.io from GitHub Actions; The deployment work is now handled by Posit Connect Cloud rather than using up your GitHub Actions allowance.
If that method works for you, it’s what we’d now recommend in most cases.
But there are some circumstances where you may still want to automate deployment from your own CI/CD process:</p>
<ol>
<li>You only want deployment to happen after earlier pipeline checks are successful.</li>
<li>You have sensitive code elsewhere in your GitHub account, and don’t feel comfortable or aren’t allowed to grant access to your GitHub repositories to a third-party tool.</li>
<li>You’re on the free-tier of Posit Connect and have code in a Private GitHub repository.</li>
<li>You want to include extra resources that aren’t stored in the GitHub repository, such as a moderately-sized read-only dataset that rarely updates. Here you might want to use the GitHub Actions workflow to pull external resources together and create a fully self-contained application bundle of source code and static data. This can reduce data export costs on busy applications.</li>
</ol>
<aside class="advert">
<p>
Do you require help building a Shiny app? Would you like someone to take over the maintenance burden? If so, check out our <a href="https://www.jumpingrivers.com/consultancy/shiny-dash-flask-dashboard-consultancy/?utm_source=blog&#038;utm_medium=banner&#038;utm_campaign=2026-github-actions-deployment-to-posit-connect-cloud" rel="nofollow" target="_blank">Shiny and Dash</a> services.
</p>
</aside>
<h3 id="deployment-to-posit-connect-cloud-using-github-actions">Deployment to Posit Connect Cloud using GitHub Actions</h3>
<p>So you might want to automate deployment, but not be able to use the standard Git-backed deployment methods.
Let’s discuss how to make it work.</p>
<h4 id="obtain-a-content-id">Obtain a Content ID</h4>
<p>The first thing you’ll want to do is perform an initial deployment of the application so that we have a <em>Content ID</em>.
The easiest way to do this is using the <a href="https://docs.posit.co/connect-cloud/user/publish/ide.html" rel="nofollow" target="_blank">one-click deployment method from RStudio, or through the Posit Publisher extension in Positron or VS Code</a>.
Log in to your Posit Connect Cloud account and find the content in your list. In the Settings menu, go to URL and look at the “Default URL”.
It should contain a <a href="https://developer.mozilla.org/en-US/docs/Glossary/UUID" rel="nofollow" target="_blank"><abbr title="universally unique identifier">UUID</abbr></a>-like section after the <code>https://</code> and before the <code>.share.connect.posit.cloud</code> parts—we want to make a note of this for later.</p>
<p><img loading="lazy" alt="The URL section of the Posit Connect Cloud settings menu, showing the “Default URL” for a piece of content." height="auto" id="h-rh-i-0" src="https://i1.wp.com/www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/default-content-uuid.png?w=450&#038;ssl=1"  data-recalc-dims="1"></p>
<p>The “Default URL” in the settings menu for your content contains the Content ID.
In this example, the default URL is <code>https://019eb78b-0c21-3b55-3fe6-38ae4d03dee4.share.connect.posit.cloud</code>, so the Content ID is <code>019eb78b-0c21-3b55-3fe6-38ae4d03dee4</code>.</p>
<p>This manual initial deployment is also a good opportunity to ensure that the deployed application is working in the first place—if your app doesn’t work when deployed from your <abbr title="Integrated Development Environment">IDE</abbr>, then it’s unlikely to work when the same stages are performed in a GitHub Actions workflow.</p>
<h4 id="add-an-renvlock-file">Add an <em>renv.lock</em> file</h4>
<p>You’ll also want to <a href="https://rstudio.github.io/renv/articles/renv.html" rel="nofollow" target="_blank">maintain an {renv} lockfile</a> to record which packages were used during development.
These matching packages will be used in the deployed version of the application for maximum compatibility.
We’ll <a href="https://rstudio.github.io/renv/reference/config.html#renv-config-pak-enabled" rel="nofollow" target="_blank">ask {renv} to use {pak}</a> when restoring these R packages during the GitHub Actions workflow—<a href="https://pak.r-lib.org/" rel="nofollow" target="_blank">{pak}</a> is generally faster at package installation and can automatically install all the system dependencies needed for the packages.
Remember to commit and push the <em>renv.lock</em> and other relevant {renv}-related files to the Git remote.</p>
<h4 id="create-a-posit-connect-cloud-token">Create a Posit Connect Cloud token</h4>
<p>Your Posit Connect Cloud account is part of your larger Posit Cloud account.
In Posit Cloud, you can access a list of your “Credentials”, which are access tokens.
These can be found at <a href="https://login.posit.cloud/identity/credentials" rel="nofollow" target="_blank">https://login.posit.cloud/identity/credentials</a>.</p>
<p>You’ll have the option to create “New Credentials”.</p>
<p><img loading="lazy" alt="The “New Client Credentials” dialog in Posit Cloud, showing a “Name” field and a “Use with” option set to “Connect Cloud”." height="auto" id="h-rh-i-1" src="https://i0.wp.com/www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/new-client-credentials.png?w=450&#038;ssl=1"  data-recalc-dims="1"></p>
<p>Provide a name for the new token that helps identify where it will be used, then for the “Use with” option, select “Connect Cloud”.
Click “OK” to generate a token.</p>
<p>You’ll be presented with a block of R code containing the credentials you can use to log in.
These should be kept secret; Anyone with these details is able to impersonate you.
It should resemble this:</p>
<pre>rsconnect::connectCloudClientCredentials(
 clientId=&quot;01234567-89a1-b2c3-d4e5-f60123456789&quot;,
 clientSecret=&quot;SuPeR/SeCrEt/VeRy/LoNg/CoDe&quot;,
 account=&quot;&lt;YOUR_ACCOUNT_HERE&gt;&quot;
)
</pre><p>We’ll need these when we come to set GitHub Actions variables and secrets later.</p>
<h4 id="write-a-github-actions-workflow">Write a GitHub Actions Workflow</h4>
<p>We’ll be creating a GitHub Actions workflow with a number of stages.
In the root of our Git repository, we’ll make a file at <em>.github/workflows/deploy.yml</em>.</p>
<pre># .github/workflows/deploy.yml
name: Deploy to Posit Connect Cloud

on:
 push:
 branches:
 - main
 - master
 workflow_dispatch:

jobs:
 deploy:
 runs-on: ubuntu-latest

 steps:
 - name: Checkout repository
 uses: actions/checkout@v4

 - name: Setup R
 uses: r-lib/actions/setup-r@v2
 with:
 r-version: &quot;4.6.1&quot;

 - name: Install pak
 run: |
 UBUNTU_CODENAME=$(lsb_release -cs)
 Rscript -e &quot;
 install.packages(&#39;pak&#39;, repos = &#39;https://packagemanager.posit.co/cran/__linux__/${UBUNTU_CODENAME}/latest&#39;);
 &quot;

 - name: Instruct renv to use pak in .Rprofile
 run: |
 echo &#39;options(renv.config.pak.enabled = TRUE)&#39; &gt;&gt; .Rprofile

 - name: Restore packages from renv.lock file
 uses: r-lib/actions/setup-renv@v2

 - name: Install rsconnect if not present in renv.lock
 run: |
 Rscript -e &quot;if (!requireNamespace(&#39;rsconnect&#39;, quietly = TRUE)) pak::pak(&#39;rstudio/rsconnect&#39;)&quot;

 - name: Authenticate with rsconnect
 run: |
 Rscript -e &#39;
 rsconnect::connectCloudClientCredentials(
 clientId = Sys.getenv(&quot;RSCONNECT_CLIENT_ID&quot;),
 clientSecret = Sys.getenv(&quot;RSCONNECT_CLIENT_SECRET&quot;),
 accountName = Sys.getenv(&quot;RSCONNECT_USERNAME&quot;),
 name = NULL
 )
 &#39;
 env:
 RSCONNECT_CLIENT_ID: ${{ secrets.RSCONNECT_CLIENT_ID }}
 RSCONNECT_CLIENT_SECRET: ${{ secrets.RSCONNECT_CLIENT_SECRET }}
 RSCONNECT_USERNAME: ${{ vars.RSCONNECT_USERNAME }}

 - name: Add Posit Connect deployment config file
 run: |
 mkdir -p &quot;rsconnect/${SERVER}/${RSCONNECT_USERNAME}&quot;
 cat &gt; &quot;rsconnect/${SERVER}/${RSCONNECT_USERNAME}/${APP_NAME}.dcf&quot; &lt;&lt;EOF
 name: ${APP_NAME}
 title: ${APP_TITLE}
 username: ${RSCONNECT_USERNAME}
 account: ${RSCONNECT_USERNAME}
 server: ${SERVER}
 hostUrl: https://api.${SERVER}/v1
 appId: ${CONNECT_CONTENT_ID}
 EOF
 env:
 SERVER: connect.posit.cloud
 APP_NAME: ${{ vars.APP_NAME }}
 APP_TITLE: ${{ vars.APP_TITLE }}
 CONNECT_CONTENT_ID: ${{ vars.CONNECT_CONTENT_ID }}
 RSCONNECT_USERNAME: ${{ vars.RSCONNECT_USERNAME }}

 - name: Deploy to Posit Connect Cloud
 run: |
 Rscript -e &#39;
 rsconnect::deployApp(
 appDir = &quot;.&quot;,
 appId = Sys.getenv(&quot;CONNECT_CONTENT_ID&quot;),
 appTitle = Sys.getenv(&quot;APP_TITLE&quot;),
 logLevel = &quot;verbose&quot;,
 account = Sys.getenv(&quot;RSCONNECT_USERNAME&quot;),
 forceUpdate = TRUE
 )
 &#39;
 env:
 CONNECT_CONTENT_ID: ${{ vars.CONNECT_CONTENT_ID }}
 APP_TITLE: ${{ vars.APP_TITLE }}
 RSCONNECT_USERNAME: ${{ vars.RSCONNECT_USERNAME }}

 - name: Clean up account details
 run: |
 Rscript -e &#39;
 rsconnect::removeAccount(
 name = Sys.getenv(&quot;RSCONNECT_USERNAME&quot;)
 )
 &#39;
 env:
 RSCONNECT_USERNAME: ${{ vars.RSCONNECT_USERNAME }}
</pre><p>As an aside, you may notice there’s a stage named “Add Posit Connect deployment config file”.
What’s that needed for?
When you deploy content the first time using the {rsconnect} package, it will keep a record of some metadata of where it was deployed to in a <code>.dcf</code> file.
If you re-deploy the content, {rsconnect} will try to overwrite the existing deployment, by identifying the target by the unique Content ID.
Without knowing the Content ID, {rsconnect} has to assume that it’s not safe to overwrite any existing content, and new content must be created instead.
Creating a <code>.dcf</code> file and populating it with some details on where the content was previously installed to convinces {rsconnect} that it is safe to overwrite the existing deployment.</p>
<h4 id="set-github-actions-variables-and-secrets">Set GitHub Actions variables and secrets</h4>
<p>The <em>deploy.yml</em> file requires a number of secrets and variables to be configured in the GitHub Actions workflow.
Remember that secrets will be censored in log messages, while variables will be visible.</p>
<p>Head to the “Settings” page for your GitHub repository, and in the side menu go to “Secrets and variables”, then “Actions”.</p>
<p><img loading="lazy" alt="The GitHub repository settings side menu, with “Secrets and variables” expanded and “Actions” selected." height="auto" id="h-rh-i-2" src="https://i1.wp.com/www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/secrets-and-variables-actions.png?w=408&#038;ssl=1"  data-recalc-dims="1"></p>
<p>There are two secrets to set:</p>
<ul>
<li><code>RSCONNECT_CLIENT_ID</code>: The <code>clientId</code> value from your Posit Cloud credentials.</li>
<li><code>RSCONNECT_CLIENT_SECRET</code>: The <code>clientSecret</code> from the Posit Cloud credentials.</li>
</ul>
<p>Followed by four variables:</p>
<ul>
<li><code>RSCONNECT_USERNAME</code>: Your Posit Connect Cloud username, which you set when you created an account. If you have forgotten this, look at the URL once you have logged in to Posit Connect Cloud; The URL will take the format <code>https://connect.posit.cloud/&lt;your-username&gt;</code>.</li>
<li><code>APP_TITLE</code>: A display title for your content. This is the title that will appear in your list of deployed content when logged into Posit Connect Cloud.</li>
<li><code>APP_NAME</code>: An internal application name. For simplicity, you could set this to your <code>APP_TITLE</code> but with spaces and punctuation replaced with hyphens. For example, “My useful application” becomes “my-useful-application”.</li>
<li><code>CONNECT_CONTENT_ID</code>: The Content ID we obtained after the initial deployment to Posit Connect Cloud.</li>
</ul>
<h3 id="extending-to-dev-deployments">Extending to dev deployments</h3>
<p>The example we’ve shown above is designed to deploy when you push to your <code>main</code> or <code>master</code> branch.
But if you want a separate deployment for development applications, you can simply extend this action to deploy when changes are pushed to a <code>dev</code> branch, but remember that you’ll need to target a different Content ID, otherwise changes pushed to the <code>dev</code> branch will overwrite your deployment from the <code>main</code> branch.</p>
<p>But keep in mind that “Basic” and “Free” accounts have a limit on the number of applications and a development deployment would count as a separate application to the main deployment.
The apps on both these tiers will also be public.</p>
<h2 id="migration-and-hosting-advice">Migration and hosting advice</h2>
<p>For users with content already on shinyapps.io, Posit has <a href="https://docs.posit.co/connect-cloud/user/shinyapps-migration.html" rel="nofollow" target="_blank">provided a migration tool</a> to help move your content now, otherwise it will be automatically moved across in early 2027.
Links to your content on shinyapps.io will automatically redirect to the new content when done through this tool.</p>
<p>It’s worth using the tool as it allows you to preview and test that the application will work on Posit Connect Cloud.
Older applications that use old dependencies or private packages are most likely to encounter issues when migrating to Posit Connect Cloud.</p>
<p>At Jumping Rivers, we often encounter packages and applications that need bringing up-to-date. Our R and Python experts can provide advice and solutions for migrating your content to new hosting solutions that match your needs. Contact us at <a href="mailto:hello@jumpingrivers.com" rel="nofollow" target="_blank">hello@jumpingrivers.com</a> to see how we can help.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Features correct at the date of publication and subject to memory and processing limits. <a href="https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/#fnref:1" class="footnote-backref" role="doc-backlink" rel="nofollow" target="_blank"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></p>
</li>
<li id="fn:2">
<p>Prices are USD + tax, with 17% discounts for annual subscriptions, and prices are correct at date of publication. <a href="https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/#fnref:2" class="footnote-backref" role="doc-backlink" rel="nofollow" target="_blank"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></p>
</li>
</ol>
</div>
<p>
For updates and revisions to this article, see the <a href = "https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/">original post</a>
</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://www.jumpingrivers.com/blog/github-actions-deployment-to-posit-connect-cloud/"> The Jumping Rivers Blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/using-github-actions-to-deploy-to-posit-connect-cloud/">Using GitHub Actions to deploy to Posit Connect Cloud</a>]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">403251</post-id>	</item>
		<item>
		<title>Running local large language models not as difficult as you might think</title>
		<link>https://www.r-bloggers.com/2026/08/running-local-large-language-models-not-as-difficult-as-you-might-think/</link>
		
		<dc:creator><![CDATA[Seascapemodels]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 14:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.seascapemodels.org/posts/2026-08-22-running-local-LLMs/</guid>

					<description><![CDATA[<p>This post was originally published on our collaborative substack site. Visit the site to follow us and read more similar posts.<br />
Jointly authored by Chris Brown, Scott Spillias, Carla Sbrocchi and Luis D. Verde Arregoitia.<br />
Chris had putting off t...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/running-local-large-language-models-not-as-difficult-as-you-might-think/">Running local large language models not as difficult as you might think</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://www.seascapemodels.org/posts/2026-08-22-running-local-LLMs/"> Seascapemodels</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 





<p>This post was <a href="https://vitaexmachina.substack.com/p/running-local-large-language-models" rel="nofollow" target="_blank">originally published on our collaborative substack site</a>. Visit the site to follow us and read more similar posts.</p>
<p><em>Jointly authored by Chris Brown, Scott Spillias, Carla Sbrocchi and Luis D. Verde Arregoitia.</em></p>
<p>Chris had putting off trying local models as the setup seemed too complex for him, but Scott convinced him to try it out. Chris did and says it was easier than he had thought. This tutorial walks through the setup.</p>
<section id="what-youll-need" class="level2">
<h2 class="anchored" data-anchor-id="what-youll-need">What you’ll need</h2>
<ul>
<li><p>A desktop or laptop computer, more on hardware below.</p></li>
<li><p><strong>Download the <a href="https://ollama.com/" rel="nofollow" target="_blank">Ollama</a></strong> software</p></li>
<li><p><strong>A large language model</strong>, it’s simple to use Ollama to download one.</p></li>
<li><p><strong>Ideally, another piece of software that interacts with Ollama</strong> — this can either be, say, <a href="https://ellmer.tidyverse.org/" rel="nofollow" target="_blank">ellmer</a>, the R package, which can make calls to Ollama, or it can be an extension like <a href="https://www.continue.dev/" rel="nofollow" target="_blank">Continue</a>, which lets you do agentic coding and autocomplete.</p></li>
</ul>
</section>
<section id="ollama" class="level2">
<h2 class="anchored" data-anchor-id="ollama">Ollama</h2>
<p>The main software you need to get is Ollama. Go to <a href="https://ollama.com/" rel="nofollow" target="_blank">ollama.com</a> and download and install Ollama (you may need to seek IT approval if you are doing this on a work computer). Then you have several choices for <a href="https://ollama.com/library" rel="nofollow" target="_blank">models</a> to download directly from Ollama These are open source models, and we recommend researching their webpage for which model would be best for the particular application you want to use Ollama for. More on this below.</p>
<p>The way Ollama works is it sets up a localhost server. This is like a web server, where you would access a service from the internet, except the server runs locally on your computer. It just sits there waiting until you make a request of the LLM. Then what it’s going to do is load a large language model into memory and pass that request to a locally run large language model.</p>
</section>
<section id="the-commands-you-need" class="level2">
<h2 class="anchored" data-anchor-id="the-commands-you-need">The commands you need</h2>
<p>Ollama comes with a clickable interface, but we find it more convenient to use the terminal. Here are some of the key commands.</p>
<pre>ollama serve                  # start the server (the desktop app does this for you)
ollama pull qwen2.5-coder     # download a model
ollama run qwen2.5-coder      # chat with a model (downloads it first if you don't have it)
ollama list                   # see which models you've already downloaded
ollama ps                     # see which models are currently loaded in memory
ollama stop qwen2.5-coder     # unload a model from memory now
ollama rm qwen2.5-coder       # delete a downloaded model from disk</pre>
<p>Full list: <a href="https://docs.ollama.com/cli" rel="nofollow" target="_blank">CLI reference</a>.</p>
<p>Before starting on these, let’s look at model choice.</p>
</section>
<section id="performance-and-hardware" class="level2">
<h2 class="anchored" data-anchor-id="performance-and-hardware">Performance and hardware</h2>
<p>Now its important to understand the difference between hard-disk memory, RAM, GPUs and CPUs.</p>
<p>Hard-disk memory is where data is stored long term. You need enough of this just to download the model file. This is unlikely to be a constraint for downloading a model, unless your computers memory is really chockers.</p>
<p>Common LLM choices range from about 8GB up to terabytes. You will need at least 10GB of free hard-disk memory to download a basic coding assistant model.</p>
<p>RAM is the accessible memory where Ollama (and other programs) hold data so its ready for quick access. The local LLM needs to fit in your RAM for Ollama to do inference with it. If the LLM is using most of your RAM it may still work, but not you will find other software on your computer slows down or breaks while the model is in use, because it can’t use that RAM.</p>
<p>For a basic 7 billion parameter model (‘7B’) you will need 8GB RAM minimum. But practically you will want 16GB+ for responses to be fast enough and to allow you to use other software simultaneously.</p>
<p>Ollama only holds an LLM in RAM while it’s in use. By default Ollama unloads it after five minutes of inactivity, or use the <code>stop</code> command to get it out of RAM sooner.</p>
<p>GPUs and CPUs are what do the inference. They take your prompt and process it to produce text/images/audio. Hopefully you have a computer with a decent GPU, this is much faster. Read more on <a href="https://docs.ollama.com/gpu" rel="nofollow" target="_blank">Hardware support</a> if you are not sure.</p>
<p>GPU setups differ with different brands of computers. For instance, a mid-range Macbook will be sufficient to run basic models. For windows machines, you will want to have a performance NVidia GPU. Developers are aggressively compressing and quantizing local models to help us run decent local models on memory’-constrained machines, and hopefully good coding assistants will soon perform similarly to cloud-hosted frontier models on the consumer grade laptops most of us use.</p>
</section>
<section id="choosing-a-model" class="level2">
<h2 class="anchored" data-anchor-id="choosing-a-model">Choosing a model</h2>
<p>Some of these models are very large (use a lot of memory), and there’s a fair bit of choice that needs to go into selecting the right model. Memory size roughly correlates with the number of parameters an LLM has (e.g. 7B, 14B). LLMs with more parameters are in general smarter, but you’re going to need more RAM and a more powerful GPU to use them.</p>
<p>When he tried Ollama, Chris was surprised that there are lots of quite good, relatively small models these days that will run on most modern laptops.</p>
<p>Useful references for picking one:</p>
<ul>
<li><p><a href="https://ollama.com/search" rel="nofollow" target="_blank">Browse models</a> — filter by chat, coding, vision, embeddings and reasoning</p></li>
<li><p><a href="https://docs.ollama.com/context-length" rel="nofollow" target="_blank">Context length</a> — how much text the model can take in at once</p></li>
<li><p><a href="https://docs.ollama.com/quickstart" rel="nofollow" target="_blank">Ollama quickstart</a></p></li>
</ul>
<p>Now let’s look at a couple of applications of Ollama.</p>
</section>
<section id="autocomplete-and-agentic-coding" class="level2">
<h2 class="anchored" data-anchor-id="autocomplete-and-agentic-coding">Autocomplete and agentic coding</h2>
<p>Chris started with the <a href="https://ollama.com/library/qwen2.5-coder" rel="nofollow" target="_blank">Qwen 2.5 Coder base</a>, because he wanted to try using Ollama for autocomplete suggestions while coding.</p>
<p>To get the model he just ran <code>ollama pull qwen2.5-coder:7b-base</code>. Then it downloaded from the internet. This took a while as the file is several gigabytes.</p>
<p>He used the ‘base’ version as it seems to perform better for line completion. Other models are trained for back and forth chatting, so tend not to want to complete your sentences (which would be annoying in a chat interface!).</p>
<p>So download that model, and then you’re going to need another extension to help with the autocomplete. If you’re using Visual Studio Code, you can install the <a href="https://marketplace.visualstudio.com/items?itemName=Continue.continue" rel="nofollow" target="_blank">Continue extension</a> and then just follow <a href="https://docs.continue.dev/customize/model-providers/ollama" rel="nofollow" target="_blank">their instructions</a> for connecting Continue to Ollama. Note that Continue has been acquired by the company Cursor and the actual extension may not be around for much longer. We can also use <a href="https://kilo.ai/docs/automate/extending/local-models" rel="nofollow" target="_blank">Kilo Code</a> as an alternative extension that also supports local models.</p>
<p><a href="https://docs.continue.dev/customize/models" rel="nofollow" target="_blank">Check their list of recommended models.</a></p>
<p>Other applications you might want to try out are the chat agents option in Continue. This will make changes to your scripts. The base model we used above won’t work for this task, you will want a different model that is optimized for chat and agentic workflows. Usually these models are significantly larger.</p>
</section>
<section id="agentic-programming" class="level2">
<h2 class="anchored" data-anchor-id="agentic-programming">Agentic programming</h2>
<p>Agents write code, run code, look at the results, update the code and keep going until they decide to finish the task.</p>
<p>It is possible to do agentic coding with local LLMs, but you will need a really good consumer grade computer that can handle much bigger models than used for autocomplete or chatting.</p>
<p>See this <a href="https://simonpcouch.com/blog/2026-04-16-local-agents-2/" rel="nofollow" target="_blank">post</a> by Simon Couch on driving coding agents on a laptop using local models such as variants of Qwen 3.5 and Gemma 4.</p>
<p>Qwen 3.8 (27B) is also <a href="https://simonwillison.net/2026/Aug/16/qwen-38-27b/" rel="nofollow" target="_blank">getting great reviews for agentic coding</a>. That blogger has found it works ok with both a 128GB M5 Max MacBook Pro and an NVIDIA DGX Spark (about $16,000 and $9000 AUD respectively at post publication).</p>
<p>The good news is that clever people are finding new ways to compress and use these LLMs such that they run faster on smaller computers.</p>
<p>Note there are some cybersecurity issues with agents because they run code on your computer. You want to be careful that you know what you’re doing before you start using agents, and get permission from your work if you’re using them on your work computer.</p>
</section>
<section id="processing-files-and-literature-in-r" class="level2">
<h2 class="anchored" data-anchor-id="processing-files-and-literature-in-r">Processing files and literature in R</h2>
<p>Another application of local LLMs is processing text through the LLM. If you’re doing this in R, it’s relatively simple to set up, just use the ellmer package to chat with the model. ellmer will link to Ollama via its API (application programming interface), and then you can send text to that API to have ellmer process it through the LLM.</p>
<p>You don’t have to use R. There is software written in many languages for accessing Ollama programmatically, including python, javascript and bash.</p>
<p>We’ve written previously about using <a href="https://www.seascapemodels.org/posts/2025-03-15-LMs-in-R-with-ellmer/index.html" rel="nofollow" target="_blank">ellmer to batch process text files</a>, such as if you want to automate the extraction of meta-data from papers for a literature review. The main difference is you would use <code>chat_ollama</code>to send text to the model.</p>
<p>One thing to keep in mind when using Ollama for scientific workflows, is that the default commands provided above will download the 4 bit <a href="https://huggingface.co/docs/optimum/concept_guides/quantization" rel="nofollow" target="_blank">quantized</a> versions of the LLMs. Quantization roughly means rounding some of the numbers in the massive matrices of weights that make up an LLM’s neural networks. This saves memory, but reduces precision.</p>
<p>In our experience, for text-processing tasks the difference in different quantizations is negligible, but needs reporting when writing up results.</p>
<p>We hope this quick guide helps those who are curious about local models. We are interested to hear from readers about your experiences with local LLMs and what applications you are using them for.</p>
<hr>
<p>*Note that Ollama as a wrapper for llama.cpp is considered <a href="https://sleepingrobots.com/dreams/stop-using-ollama/" rel="nofollow" target="_blank">problematic</a> by some. In R we can use other bindings to the llama.cpp library for local inference of large language models (LLMs), such as the<a href="https://github.com/Zabis13/llamaR" rel="nofollow" target="_blank">llamaR</a> package by Yuri Baramykov.</p>


</section>

 
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://www.seascapemodels.org/posts/2026-08-22-running-local-LLMs/"> Seascapemodels</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/running-local-large-language-models-not-as-difficult-as-you-might-think/">Running local large language models not as difficult as you might think</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403274</post-id>	</item>
		<item>
		<title>socviz 2.0.0 on CRAN</title>
		<link>https://www.r-bloggers.com/2026/08/socviz-2-0-0-on-cran/</link>
		
		<dc:creator><![CDATA[R on kieranhealy.org]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 11:25:44 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://kieranhealy.org/blog/archives/2026/08/21/socviz-2.0.0-on-cran/</guid>

					<description><![CDATA[<p>In anticipation of the second edition of Data Visualization, which is coming later this year from Princeton University Press, version 2.0.0 of my socviz package is now on CRAN. The update removes some functions that aren’t needed anymore and adds...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/socviz-2-0-0-on-cran/">socviz 2.0.0 on CRAN</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://kieranhealy.org/blog/archives/2026/08/21/socviz-2.0.0-on-cran/"> R on kieranhealy.org</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p>In anticipation of the second edition of <a href="https://socviz.co/" rel="nofollow" target="_blank"><em>Data Visualization</em></a>, which is coming later this year from Princeton University Press, version 2.0.0 of my <code>socviz</code> package is now on <a href="https://cran.r-project.org/" rel="nofollow" target="_blank">CRAN</a>. The update removes some functions that aren’t needed anymore and adds a couple of sample datasets.</p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://kieranhealy.org/blog/archives/2026/08/21/socviz-2.0.0-on-cran/"> R on kieranhealy.org</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/socviz-2-0-0-on-cran/">socviz 2.0.0 on CRAN</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403256</post-id>	</item>
		<item>
		<title>The boring part first: Building a crosswalk from O*NET-SOC to ANZSCO</title>
		<link>https://www.r-bloggers.com/2026/08/the-boring-part-first-building-a-crosswalk-from-onet-soc-to-anzsco/</link>
		
		<dc:creator><![CDATA[Giles Dickenson-Jones]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 06:18:39 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.gilesd-j.com/?p=4324</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> There's no official crosswalk between the ONET's occupational taxonomy and ANZSCO, which is awkward, because Australian researchers use ONET data all the time. This post builds one, explains why you should be suspicious of relying on it and then suggests a better methodology is probably what was applied by the ...</div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/the-boring-part-first-building-a-crosswalk-from-onet-soc-to-anzsco/">The boring part first: Building a crosswalk from O*NET-SOC to ANZSCO</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://www.gilesd-j.com/2026/08/21/the-boring-part-first-building-a-crosswalk-from-onet-soc-to-anzsco/"> Data Analytics and AI Archives - Giles</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<p class="wp-block-paragraph"><strong>TLDR:</strong> <em>There’s no official crosswalk between the ONET’s occupational taxonomy and ANZSCO, which is awkward, because Australian researchers use ONET data all the time. This post builds one</em>, <em>explains why you should be suspicious of relying on it</em> <em>and then suggests a better methodology is probably <a href="https://esco.ec.europa.eu/en/about-esco/data-science-and-esco/crosswalk-between-esco-and-onet" rel="nofollow" target="_blank">what was applied by the European Commission</a>. </em></p>



<h3 class="wp-block-heading">Background</h3>



<p class="wp-block-paragraph">In 2025 I served as an adviser for a project to map occupational transition pathways in India. Although the work leveraged locally-sourced data and occupational profiles, the methodology leaned heavily on studies that use occupational profile data from the <a href="https://www.dol.gov/agencies/eta/onet" rel="nofollow" target="_blank">Occupational Information Network</a> (O*NET). With the basic idea being that the viability of a transition pathway was <em>in-part</em> determined by how similar two jobs are (conditional on geography, wage rate differentials, education etc).</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" loading="lazy" src="https://i0.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/image.png?w=450&#038;ssl=1" alt="" class="wp-image-4327" srcset_temp="https://i0.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/image.png?w=450&#038;ssl=1 565w, https://www.gilesd-j.com/wp-content/uploads/2026/08/image-300x218.png 300w" sizes="auto, (max-width: 565px) 100vw, 565px" data-recalc-dims="1" /><figcaption class="wp-element-caption"><strong>Source: </strong><a href="https://www.dol.gov/agencies/eta/onet" rel="nofollow" target="_blank">https://www.dol.gov/agencies/eta/onet</a></figcaption></figure>



<h3 class="wp-block-heading">The Boring Part</h3>



<p class="wp-block-paragraph">I plan to write more about the interesting parts of this project in the future, but I need to start with the boring parts first: developing a crosswalk table between the O*NET-SOC taxonomy and national standards. </p>



<p class="wp-block-paragraph">I’ll focus on developing a crosswalk / correspondence table between the O*NET-SOC and the Australian and New Zealand Standard Classification of Occupations (ANZSCO). This is both because I’ll use this crosswalk in a future post that uses this standard and as an “official” crosswalk doesn’t exist (despite Australian researchers frequently using the O*NET database).</p>



<p class="wp-block-paragraph">I suspect one reason official correspondence tables don’t exist already is that the OSCA is a new standard and that the O*NET SOC taxonomy doesn’t cleanly match to the ANZSCO <em>or</em> intermediate correspondence tables. In practice, this means the judgement of the analyst will be required to decide how to match one standard with the other so that it suits their use-case. For instance, if the research is on occupations in the Trucking industry it will be sensible to confirm the data you’re drawing on is being sensibly assigned.</p>



<p class="wp-block-paragraph">This problem isn’t exclusive to occupational correspondences. The same problems will often rear their head when trying to connect datasets that use different definitions for industries, administrative boundaries and/or products. In most cases the difficulty stems from each standard using a different approach for defining groups, which results in occupations being weirdly assigned at each step of creating a map between standards.</p>



<p class="wp-block-paragraph">In the case of this post, the problems reared their head at every step from SOC to ANZSCO:</p>



<ul class="wp-block-list">
<li class=""><strong>From ANZSCO to OSCA:</strong> In most cases ANZSCO occupations have been assigned to one or more OSCA group, but there are cases where the opposite occurs too, such as <em>Production Manager (Manufacturing),</em> which has been assigned more than one ANZSCO grouping.</li>



<li class=""><strong>From ISCO-08 (or ESCO) to OSCA:</strong> The bridge between the OSCA to ISCO suffers similar problems. However, because ISCO-08 groupings are only provided at the unit group level, in most cases OSCA occupations are bundled into large groups. However, the opposite also occurs too, with <em>Engineering Technologist</em> being assigned to several ISCO-08 groupings at the same time.</li>



<li class=""><strong>From SOC to ISCO-08 (or ESCO):</strong> <a href="https://www.gilesd-j.com/2026/08/21/the-boring-part-first-building-a-crosswalk-from-onet-soc-to-anzsco/#0" rel="nofollow" target="_blank">one of the best crosswalks</a> maps 3,349 occupations to 958 occupations in the O*NET. Once again, these aren’t 1:1 matches,. For example, the ISCO/ESCO job <em>Sports, recreation and cultural centre managers</em> is assigned two jobs from the O*NET. While the O*NET occupation <em>legislators</em> is assigned to more than one distinct ISCO/ESCO occupation category.</li>
</ul>



<p class="wp-block-paragraph">The reason I mention this upfront is to make it clear the process is messy. And if I wasn’t intending to replicate research that uses ANZSCO in a future post I wouldn’t bother. But, I am, so I thought I should share my process (and pain) so other people can learn from my mistakes and re-purpose the approach in a way that suits their analysis.</p>



<p class="wp-block-paragraph"><strong>Note: </strong><em>Because the Occupation Standard Classification for Australia (OSCA) is the modern successor of the ANZSCO, this crosswalk (and post) will have a short shelf-life.  </em></p>



<p class="wp-block-paragraph"><strong>Data:</strong> the correspondence tables used in this post are available <a href="https://gilesd-j.com/shared_resources/blogs/260821_anzsco_to_soc/ESCO_to_ONET-SOC.xlsx" rel="nofollow" target="_blank">here</a> for the O*NET and <a href="https://gilesd-j.com/shared_resources/blogs/260821_anzsco_to_soc/OSCA_correspondence_tables_v2.xlsx" rel="nofollow" target="_blank">here</a> for the ABS. These were originally sourced from the <a href="https://www.onetcenter.org/crosswalks.html" rel="nofollow" target="_blank">O*NET</a> and <a href="https://www.abs.gov.au/statistics/classifications/osca-occupation-standard-classification-australia/2024-version-1-0/data-downloads" rel="nofollow" target="_blank">ABS</a> on 21/8/2026.</p>



<p class="wp-block-paragraph"><strong>How I used AI in this post:</strong> Because developing the correspondence table mainly requires data cleaning and joining occupational definitions, Claude was heavily leaned on to write the code for this post. The write up is more or less untouched by AI.</p>



<h3 class="wp-block-heading">Project Setup</h3>



<p class="wp-block-paragraph">The code below sets the assumptions for importing correspondence tables and saving outputs.</p>



<pre>library(tidyverse)
library(readxl)
library(janitor)

ref_dir_data &lt;- file.path(&quot;.&quot;, &quot;Data&quot;)
ref_dir_out  &lt;- file.path(&quot;.&quot;, &quot;Outputs&quot;)

ref_file_esco_to_soc &lt;- file.path(ref_dir_data, &quot;ESCO_to_ONET-SOC.xlsx&quot;)
ref_file_osca_tables &lt;- file.path(ref_dir_data, &quot;OSCA_correspondence_tables_v2.xlsx&quot;)

# Keep a SOC link only if this share of the ANZSCO unit group's detailed
# occupations backs it. Set to 0 to keep everything.
ref_min_pct_unit_support &lt;- 50

#import the O*NET / ESCO crosstab
dta_esco_soc_raw &lt;- read_excel(ref_file_esco_to_soc, sheet = 1, skip = 3,
                               col_types = &quot;text&quot;) |&gt;
  clean_names() |&gt;
  filter(!is.na(esco_isco_code), !is.na(o_net_soc_2019_code)) |&gt;
  transmute(
    esco_code   = str_trim(esco_isco_code),          # e.g. &quot;8332.5&quot; or &quot;8332&quot;
    esco_name   = str_trim(esco_isco_title),
    isco_code   = str_trim(str_extract(esco_isco_code, &quot;^[^.]+&quot;)),
    isco_digits = str_length(isco_code),
    soc_code    = str_trim(o_net_soc_2019_code),
    soc_name    = str_trim(o_net_soc_2019_title),
    link_level  = if_else(str_detect(esco_isco_code, &quot;\\.&quot;),
                          &quot;esco_occupation&quot;, &quot;isco_unit_group&quot;)
  )

# A few rows sit at ISCO MINOR group level (3 digits, e.g. &quot;213&quot;), which is
# coarser than a unit group, so they cannot be placed and are dropped.
dta_esco_soc_unitlevel &lt;- dta_esco_soc_raw |&gt;
  filter(isco_digits == 4)

# ESCO maps some ISCO unit groups directly, and others only via the narrow
# occupations inside them. tHEPrefer the direct mapping; fall back to the
# occupation rows for the 84 groups that have none. 
lkp_isco_with_own_row &lt;- dta_esco_soc_unitlevel |&gt;
  filter(link_level == &quot;isco_unit_group&quot;) |&gt;
  pull(isco_code) |&gt;
  unique()

dta_esco_soc_kept &lt;- dta_esco_soc_unitlevel |&gt;
  filter(link_level == &quot;isco_unit_group&quot; | !(isco_code %in% lkp_isco_with_own_row))

# Table 8 is written OSCA -&gt; ISCO; we travel it ISCO -&gt; OSCA. Same pairs.
dta_osca_to_isco &lt;- read_excel(
  ref_file_osca_tables,
  sheet     = &quot;Table 8&quot;,
  col_names = c(&quot;osca_code&quot;, &quot;osca_name&quot;, &quot;isco_code&quot;, &quot;match_flag&quot;, &quot;isco_name&quot;),
  col_types = &quot;text&quot;,
  range     = cell_limits(ul = c(6L, 1L), lr = c(NA_integer_, 5L))
) |&gt;
  # The last row is an ABS copyright line, not data.
  filter(!str_detect(coalesce(osca_code, &quot;&quot;), &quot;Commonwealth&quot;)) |&gt;
  fill(osca_code, osca_name, .direction = &quot;down&quot;) |&gt;
  filter(!is.na(isco_code)) |&gt;
  # &quot;xxxxxx&quot; is the ABS marker for &quot;no counterpart exists&quot;.
  filter(osca_code != &quot;xxxxxx&quot;, isco_code != &quot;xxxxxx&quot;) |&gt;
  mutate(across(c(osca_code, isco_code), str_trim)) |&gt;
  distinct(osca_code, osca_name, isco_code, isco_name)

# Table 1 is written ANZSCO -&gt; OSCA.
dta_anzsco_to_osca &lt;- read_excel(
  ref_file_osca_tables,
  sheet     = &quot;Table 1&quot;,
  col_names = c(&quot;anzsco_code&quot;, &quot;anzsco_name&quot;, &quot;osca_code&quot;, &quot;match_flag&quot;, &quot;osca_name&quot;),
  col_types = &quot;text&quot;,
  range     = cell_limits(ul = c(6L, 1L), lr = c(NA_integer_, 5L))
) |&gt;
  filter(!str_detect(coalesce(anzsco_code, &quot;&quot;), &quot;Commonwealth&quot;)) |&gt;
  fill(anzsco_code, anzsco_name, .direction = &quot;down&quot;) |&gt;
  filter(!is.na(osca_code)) |&gt;
  filter(anzsco_code != &quot;xxxxxx&quot;, osca_code != &quot;xxxxxx&quot;) |&gt;
  mutate(across(c(anzsco_code, osca_code), str_trim)) |&gt;
  distinct(anzsco_code, anzsco_name, osca_code) |&gt;
  # ANZSCO codes are 6 digits (a detailed occupation). The first 4 are the unit
  # group, which is the level the O*NET analysis reports at.
  mutate(anzsco_unit_code = str_sub(anzsco_code, 1, 4))</pre>



<h3 class="wp-block-heading">Joins</h3>



<p class="wp-block-paragraph">The code below joins each correspondence pair sequentially. Because each mapping splits and merges occupational classifications differently, the unified crosswalk results isn’t a clean 1:1 correspondence. For this reason a better approach is likely to be matching occupational descriptions from either standard, such as was <a href="https://esco.ec.europa.eu/en/about-esco/data-science-and-esco/crosswalk-between-esco-and-onet" rel="nofollow" target="_blank">done by the European Commission for mapping the O*NET to ISCO-08</a>, but I’ve already written the code so here we are…</p>



<pre># Start at the ESCO level of detail: one row per ESCO occupation and SOC code.
dta_esco_x_soc &lt;- dta_esco_soc_kept

# COLLAPSE to the ISCO unit group. This is where the ESCO occupation codes and
# names get dropped -- they cannot be carried further, because the ABS tables
# are keyed on the ISCO unit group and not on ESCO.
dta_isco_x_soc &lt;- dta_esco_x_soc |&gt;
  distinct(isco_code, soc_code, soc_name)

dta_isco_x_soc_x_osca &lt;- dta_isco_x_soc |&gt;
  inner_join(dta_osca_to_isco, by = join_by(isco_code),
             relationship = &quot;many-to-many&quot;)

dta_isco_x_soc_x_osca_x_anzsco &lt;- dta_isco_x_soc_x_osca |&gt;
  inner_join(dta_anzsco_to_osca, by = join_by(osca_code),
             relationship = &quot;many-to-many&quot;)

dta_crosswalk_all_levels &lt;- dta_isco_x_soc_x_osca_x_anzsco |&gt;
  select(soc_code, soc_name, isco_code, isco_name, osca_code, osca_name,
         anzsco_code, anzsco_name, anzsco_unit_code) |&gt;
  arrange(soc_code, isco_code, anzsco_code)</pre>



<h3 class="wp-block-heading">Collapsing Occupations to the Unit-Group Level</h3>



<p class="wp-block-paragraph">Because the OSCA occupations are only mapped to the ISCO-08 unit-group (the first 4 digits of the code), the code below collapses the correspondence table to provide a listing of major SOC and ESCO occupations by unit group. As you’d expect, this results in a lot of granularity being lost. </p>



<pre>lkp_anzsco_unit_detail &lt;- dta_crosswalk_all_levels |&gt;
  distinct(anzsco_unit_code, anzsco_code, anzsco_name) |&gt;
  summarise(
    nmb_unit_occs = n_distinct(anzsco_code),
    # These files carry no ANZSCO unit group titles, only occupation titles, so
    # the lowest-numbered occupation stands in as the label.
    anzsco_unit_name = anzsco_name[order(anzsco_code)][1],
    .by = anzsco_unit_code
  )

# Advisory sanity check: do the ISCO major group (1st digit) and the SOC major
# group (1st 2 digits) sit in compatible broad families? Some ESCO mappings are
# simply poor, and this catches them. It has false positives, so it is reported
# as a column and never filtered on.
lkp_valid_major_group_pairs &lt;- tribble(
  ~isco_major, ~soc_majors,
  &quot;0&quot;,         &quot;55,33&quot;,                              # armed forces
  &quot;1&quot;,         &quot;11&quot;,                                 # managers
  &quot;2&quot;,         &quot;13,15,17,19,21,23,25,27,29&quot;,         # professionals
  &quot;3&quot;,         &quot;13,15,17,19,21,25,29,31,33,49&quot;,      # technicians
  &quot;4&quot;,         &quot;41,43&quot;,                              # clerical
  &quot;5&quot;,         &quot;31,33,35,37,39,41&quot;,                  # service and sales
  &quot;6&quot;,         &quot;45&quot;,                                 # agriculture
  &quot;7&quot;,         &quot;47,49,51&quot;,                           # trades
  &quot;8&quot;,         &quot;51,53&quot;,                              # plant and machine
  &quot;9&quot;,         &quot;35,37,41,45,47,53&quot;                   # elementary
) |&gt;
  separate_longer_delim(soc_majors, delim = &quot;,&quot;) |&gt;
  rename(soc_major = soc_majors) |&gt;
  mutate(broad_group_match = TRUE)

rlt_crosswalk_by_unit_group &lt;- dta_crosswalk_all_levels |&gt;
  summarise(nmb_occ_support = n_distinct(anzsco_code),
            .by = c(soc_code, soc_name, isco_code, anzsco_unit_code)) |&gt;
  left_join(lkp_anzsco_unit_detail, by = join_by(anzsco_unit_code)) |&gt;
  mutate(pct_unit_support = round(100 * nmb_occ_support / nmb_unit_occs, 1),
         isco_major = str_sub(isco_code, 1, 1),
         soc_major  = str_sub(soc_code, 1, 2)) |&gt;
  left_join(lkp_valid_major_group_pairs, by = join_by(isco_major, soc_major)) |&gt;
  mutate(broad_group_match = coalesce(broad_group_match, FALSE)) |&gt;
  select(-isco_major, -soc_major) |&gt;
  arrange(soc_code, anzsco_unit_code)</pre>



<h3 class="wp-block-heading">Filtering out Poor Matches</h3>



<p class="wp-block-paragraph">The final step drops matches that are supported by a minority of occupations after joins. Each ANZSCO group holds several occupations and the joins match each of them to a SOC group separately. So, when more ANZSCO occupations are matched to the same SOC code <em>within a unit group</em> it’s assumed the match is stronger, while weaker matches are dropped and assumed to reflect the many quirks of trying to match definitions like this.</p>



<pre>rlt_crosswalk_for_onet &lt;- rlt_crosswalk_by_unit_group |&gt;
  filter(pct_unit_support &gt;= ref_min_pct_unit_support) |&gt;
  summarise(isco_codes        = paste(sort(unique(isco_code)), collapse = &quot;; &quot;),
            pct_unit_support  = max(pct_unit_support),
            broad_group_match = any(broad_group_match),
            .by = c(anzsco_unit_code, anzsco_unit_name, soc_code, soc_name)) |&gt;
  select(anzsco_code  = anzsco_unit_code,
         label_4digit = anzsco_unit_name,
         soc          = soc_code,
         soc_label    = soc_name,
         isco_codes, pct_unit_support, broad_group_match) |&gt;
  arrange(anzsco_code, soc)

stopifnot(
  &quot;Duplicate anzsco_code x soc rows would double-weight a SOC in the O*NET average&quot; =
    nrow(rlt_crosswalk_for_onet) ==
      nrow(distinct(rlt_crosswalk_for_onet, anzsco_code, soc))
)</pre>



<h3 class="wp-block-heading">Exploratory analysis</h3>



<p class="wp-block-paragraph">Claude produced <em>a lot</em> of exploratory analysis and checks after I harangued it and quizzed its analysis, but I think the summary below is the most useful. It essentially checks how many occupations from the source correspondence files remained in the unit-group mapping.</p>



<p class="wp-block-paragraph">The TLDR: <em>most</em> of the O*NET occupations made it, despite the inconsistencies encountered along the way. On one level that’s a surprisingly good outcome, particularly given how naive the final mapping approach is. But it hides a few things that I’d worry about if using the table in my analysis: occupations being more likely to drop out in some categories more than others and SOC occupations landing in the wrong groups (which could result in drawing on incorrect data from the O*NET).</p>



<pre># The links behind the delivered table, taken at the level where isco_code is
# still a single column. This is the same set of links as rlt_crosswalk_for_onet.
dta_final_links &lt;- rlt_crosswalk_by_unit_group |&gt;
  filter(pct_unit_support &gt;= ref_min_pct_unit_support)

# Codes are lost at two different points, so both are reported. The joins are
# inner joins, so a code with no counterpart drops out silently. The support
# filter then removes more. OSCA is an intermediate hop and is not carried into
# the final table at all, so it has no final count.
rlt_codes_kept_vs_dropped &lt;- tibble(
  classification = c(&quot;O*NET-SOC&quot;, &quot;ISCO-08&quot;, &quot;OSCA&quot;, &quot;ANZSCO (unit group)&quot;),
  nmb_in_source = c(
    n_distinct(dta_esco_soc_raw$soc_code),
    n_distinct(c(dta_esco_soc_unitlevel$isco_code, dta_osca_to_isco$isco_code)),
    n_distinct(c(dta_osca_to_isco$osca_code, dta_anzsco_to_osca$osca_code)),
    n_distinct(dta_anzsco_to_osca$anzsco_unit_code)),
  nmb_after_joins = c(
    n_distinct(dta_crosswalk_all_levels$soc_code),
    n_distinct(dta_crosswalk_all_levels$isco_code),
    n_distinct(dta_crosswalk_all_levels$osca_code),
    n_distinct(dta_crosswalk_all_levels$anzsco_unit_code)),
  nmb_in_final = c(
    n_distinct(dta_final_links$soc_code),
    n_distinct(dta_final_links$isco_code),
    NA_integer_,
    n_distinct(dta_final_links$anzsco_unit_code))
) |&gt;
  mutate(nmb_dropped = nmb_in_source - nmb_in_final,
         pct_kept    = round(100 * nmb_in_final / nmb_in_source, 1))

rlt_codes_kept_vs_dropped</pre>



<h3 class="wp-block-heading">Output files</h3>



<p class="wp-block-paragraph">The final code block outputs the crosswalks.</p>



<pre>ref_stamp &lt;- format(Sys.Date(), &quot;%y%m%d&quot;)
if (!dir.exists(ref_dir_out)) dir.create(ref_dir_out)

write_csv(dta_crosswalk_all_levels,
          file.path(ref_dir_out, paste0(ref_stamp, &quot; - crosswalk_full.csv&quot;)))
write_csv(rlt_crosswalk_by_unit_group,
          file.path(ref_dir_out, paste0(ref_stamp, &quot; - crosswalk_unit.csv&quot;)))
write_csv(rlt_crosswalk_for_onet,
          file.path(ref_dir_out, paste0(ref_stamp, &quot; - crosswalk_for_onet.csv&quot;)))</pre>



<h3 class="wp-block-heading">Summing up</h3>



<p class="wp-block-paragraph">Because the main point of this post is to produce a correspondence table for a future post, I’m not going to split hairs about the assignment. However, if you’re intending to use the crosswalks for something more rigorous, I hope this post demonstrates clearly why you <em>should.</em></p>



<p class="wp-block-paragraph">A few things I’d check:</p>



<ul class="wp-block-list">
<li class="">Confirm assignments for occupations your analysis relies on. For me, it’s truck drivers as the next post replicates analysis that looks at occupational paths for that group.</li>



<li class="">Validate assignments / groupings. Particularly for above, but the approach I’ve used is <em>naive</em> insofar as I’ve just joined each table together and used a simple filtering mechanism for dropping obvious cases. However, having done this makes me even more convinced that a simple comparison of job titles or descriptions would be easier to justify (<a href="https://esco.ec.europa.eu/en/about-esco/data-science-and-esco/crosswalk-between-esco-and-onet" rel="nofollow" target="_blank">like the European Commission’s methodology</a>).</li>



<li class="">What was dropped and is it valuable to your analysis. </li>
</ul>



<p class="wp-block-paragraph">Which is perhaps something I’ll do in a future post, but for now I’d just say that if you have a better approach, or want to point out the problems with mine, <a href="https://www.gilesd-j.com/contact/" rel="nofollow" target="_blank">please get in touch</a>. I wrote this up in an afternoon: this isn’t mean to be a master work. So, I’ll happily link to better approaches and make corrections to avoid leading others astray.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://www.gilesd-j.com/2026/08/21/the-boring-part-first-building-a-crosswalk-from-onet-soc-to-anzsco/" rel="nofollow" target="_blank">The boring part first: Building a crosswalk from O*NET-SOC to ANZSCO</a> appeared first on <a href="https://www.gilesd-j.com/" rel="nofollow" target="_blank">Giles</a>.</p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://www.gilesd-j.com/2026/08/21/the-boring-part-first-building-a-crosswalk-from-onet-soc-to-anzsco/"> Data Analytics and AI Archives - Giles</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/the-boring-part-first-building-a-crosswalk-from-onet-soc-to-anzsco/">The boring part first: Building a crosswalk from O*NET-SOC to ANZSCO</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403247</post-id>	</item>
		<item>
		<title>Learning how to extract parts of a string</title>
		<link>https://www.r-bloggers.com/2026/08/learning-how-to-extract-parts-of-a-string/</link>
		
		<dc:creator><![CDATA[Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://masalmon.eu/2026/08/21/extracting-string-patterns/</guid>

					<description><![CDATA[<p>This week I took time to re-read The Programmer’s Brain by Felienne Hermans. Among the many gems one idea that stuck with me is that not learning how to do something and looking it up every time will make you less efficient. Therefore I starting ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/learning-how-to-extract-parts-of-a-string/">Learning how to extract parts of a string</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://masalmon.eu/2026/08/21/extracting-string-patterns/"> Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p>This week I took time to re-read The Programmer’s Brain by Felienne Hermans. Among the many gems one idea that stuck with me is that not learning how to do something and looking it up every time will make you less efficient. Therefore I starting feeling bad about one particular thing I never get done on my own: extracting parts of a string, even in simple cases.</p>
<h2 id="avoidance-tactics">Avoidance tactics</h2>
<p>For instance, how do you extract names from the following templated sentences?</p>
<div class="highlight">
<pre>sentences &lt;- c(
  &quot;My name is Moomin.&quot;,
  &quot;My name is Little My.&quot;,
  &quot;My name is Snork Maiden.&quot;
)</pre>
</div>
<p>I would use either one of these two tactics:</p>
<ol>
<li>Removing the final period, and the first words.</li>
</ol>
<div class="highlight">
<pre>sub(&quot;My name is &quot;, &quot;&quot;, sub(&quot;.$&quot;, &quot;&quot;, sentences))
#&gt; [1] &quot;Moomin&quot;       &quot;Little My&quot;    &quot;Snork Maiden&quot;
</pre>
</div>
<ol>
<li>Looking up the regex syntax online (maybe even dutifully reading the <a href="https://rstudio.github.io/cheatsheets/html/strings.html#look-arounds" rel="nofollow" target="_blank">stringr cheatsheet</a>) or via a LLM. This could get me code calling stringr:</li>
</ol>
<div class="highlight">
<pre>stringr::str_extract(sentences, &quot;My name is (.*).&quot;, group = 1)
#&gt; [1] &quot;Moomin&quot;       &quot;Little My&quot;    &quot;Snork Maiden&quot;
# Look arounds
stringr::str_extract(sentences, pattern = '(?&lt;=My name is ).+(?=\\.)')
#&gt; [1] &quot;Moomin&quot;       &quot;Little My&quot;    &quot;Snork Maiden&quot;
</pre>
</div>
<p>Or some base R code:</p>
<div class="highlight">
<pre>regmatches(
  sentences,
  regexpr(&quot;(?&lt;=My name is ).+(?=\\.)&quot;, sentences, perl = TRUE)
)
#&gt; [1] &quot;Moomin&quot;       &quot;Little My&quot;    &quot;Snork Maiden&quot;
</pre>
</div>
<h2 id="my-problems">My problems</h2>
<p>Really I had two problems preventing me from being really autonomous:</p>
<ul>
<li>Not knowing enough regex.</li>
<li>Not knowing where to put the regex, for whatever reason I felt I had to choose between adding a dependency on stringr or using the complicated two-step regexpr/regmatches syntax.</li>
</ul>
<h2 id="solutions">Solutions</h2>
<p>To solve the first problem (lack of regex knowledge), I need to be more intentional about remembering the look-arounds syntax for instance, or what a group is.</p>
<p>What solved my second problem (thinking I had to choose between a dependency or code distateful to me) was a very simple tip by my <a href="https://jeroen.github.io/" rel="nofollow" target="_blank">rOpenSci colleague Jeroen Ooms</a>: using <a href="https://rdrr.io/r/base/grep.html" rel="nofollow" target="_blank"><code>sub()</code></a>! The code below replaces the sentences with the names (capture groups) in them.</p>
<div class="highlight">
<pre>sub(&quot;My name is (.*).&quot;, &quot;\\1&quot;, sentences)
#&gt; [1] &quot;Moomin&quot;       &quot;Little My&quot;    &quot;Snork Maiden&quot;
</pre>
</div>
<p>This is code he seems to use <a href="https://github.com/search?q=%2Fsub.*%5C%5C1%2F+user%3Ajeroen+path%3A*.R&#038;type=code&#038;ref=advsearch" rel="nofollow" target="_blank">quite often</a><sup id="fnref:1"><a href="https://masalmon.eu/2026/08/21/extracting-string-patterns/#fn:1" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">1</a></sup>.</p>
<p>What was especially great about this tip, beside its timing when I was reading the book, is that it made me “get” groups more easily. The group is what’s between parentheses, it’s not more complicated than that (at least I don’t need to know more right now).</p>
<h2 id="conclusion">Conclusion</h2>
<p>I will try to keep mindful of not being too lazy to learn some things when I can actually learn them. And now I know that I can use something else than stringr or the not so easy base R syntax with <a href="https://rdrr.io/r/base/regmatches.html" rel="nofollow" target="_blank"><code>regmatches()</code></a>: a simple call to <a href="https://rdrr.io/r/base/grep.html" rel="nofollow" target="_blank"><code>sub()</code></a>! Watch me win seconds every time I have to extract parts of a string. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f601.png" alt="😁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Learning how to add <a href="https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax#using-regular-expressions" rel="nofollow" target="_blank">regex to code search on GitHub</a> was well worth the small effort. <a href="https://masalmon.eu/2026/08/21/extracting-string-patterns/#fnref:1" class="footnote-backref" role="doc-backlink" rel="nofollow" target="_blank"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></p>
</li>
</ol>
</section>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://masalmon.eu/2026/08/21/extracting-string-patterns/"> Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/learning-how-to-extract-parts-of-a-string/">Learning how to extract parts of a string</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403268</post-id>	</item>
		<item>
		<title>BiocJobs: declaring dispatchable jobs inside Bioconductor packages</title>
		<link>https://www.r-bloggers.com/2026/08/biocjobs-declaring-dispatchable-jobs-inside-bioconductor-packages/</link>
		
		<dc:creator><![CDATA[Alexandru Mahmoud]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://blog.bioconductor.org/posts/2026-08-21-biocjobs/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>The gap<br />
Much of what Bioconductor packages do is interactive and exploratory, and rightly belongs in an R session. But some of it is batch-shaped: a well-defined analysis with file inputs, file outputs, and a handful of parameters. Differential...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/biocjobs-declaring-dispatchable-jobs-inside-bioconductor-packages/">BiocJobs: declaring dispatchable jobs inside Bioconductor packages</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://blog.bioconductor.org/posts/2026-08-21-biocjobs/"> Bioconductor community blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 





<section id="the-gap" class="level2">
<h2 class="anchored" data-anchor-id="the-gap">The gap</h2>
<p>Much of what Bioconductor packages do is interactive and exploratory, and rightly belongs in an R session. But some of it is <em>batch-shaped</em>: a well-defined analysis with file inputs, file outputs, and a handful of parameters. Differential expression, normalisation, peak calling, amplicon denoising, quantification import. None of these need a human in the loop once the parameters are chosen, and this project targets that subset.</p>
<p>Yet every workflow system that wants to offer one of these analyses today needs a <strong>hand-written wrapper</strong>: Galaxy, Nextflow, engines for CWL (Common Workflow Language) and WDL (Workflow Description Language), cloud batch services. Those wrappers are usually maintained by someone who is <em>not</em> the package author, and they drift out of sync with the package at every release. The community’s hand-written Galaxy wrappers are excellent, but each one took expert effort to build and takes expert effort to keep current. The long tail of Bioconductor packages will never get that treatment. An earlier post on this blog, <a href="https://blog.bioconductor.org/posts/2025-07-03-bioc-to-galaxy/" rel="nofollow" target="_blank">Bringing Bioconductor to Galaxy</a>, walks through what writing one of those wrappers by hand actually involves.</p>
<p>There is an ownership problem underneath the maintenance problem. The person who knows which entry points make sense non-interactively, what the inputs mean, and which parameters actually matter is the <strong>package author</strong>.</p>
</section>
<section id="where-this-came-from" class="level2">
<h2 class="anchored" data-anchor-id="where-this-came-from">Where this came from</h2>
<p>This is not a new observation, and the framework described here is the result of a long series of conversations rather than a single design session.</p>
<p>Two of those conversations were decisive. At the <strong>ELIXIR All Hands Meeting in Lyon in early June 2026</strong>, and again at the <strong>Galaxy Community Conference in Clermont-Ferrand later that month</strong>, discussions between Bioconductor and Galaxy people kept converging on the same idea from different directions. There is real and growing appetite for automatically wrapping Bioconductor tools for Galaxy, provided it can be done in a <em>high-quality, developer-driven</em> way rather than as a lowest-common-denominator scrape of function signatures. That qualifier is the whole design constraint. A generated wrapper is only worth having if it is as good as a careful hand-written one, and the way to get there is to have the package author declare the interface deliberately, not have automation scrape it from functions.</p>
<p>The scope widened during those same discussions. Once an author has declared a job precisely enough to generate a good Galaxy tool, that same declaration should carry most of what a <em>general</em> workflow dispatcher needs. It seemed wasteful to spend the effort and get only Galaxy out of it.</p>
</section>
<section id="why-the-ga4gh-task-model-became-the-goal" class="level2">
<h2 class="anchored" data-anchor-id="why-the-ga4gh-task-model-became-the-goal">Why the GA4GH task model became the goal</h2>
<p>The design settled on the <a href="https://github.com/ga4gh/task-execution-schemas" rel="nofollow" target="_blank">GA4GH Task Execution Service (TES)</a> task model as the common denominator.</p>
<p>TES is small. A task is: some input files staged in, a short sequence of executors, each one a container image plus a command run one after another, some resource requirements, and some output files collected out. That sequence is the only structure TES has. No branching, no fan-out, no data flow between tasks; orchestration is explicitly somebody else’s job. That minimalism is what makes it a good target for package developers. If a unit of analysis can be expressed as a TES task, it can be projected onto a Galaxy tool, a Nextflow process, a WDL task, or a cloud batch submission without rewriting.</p>
<p>So BiocJobs declarations are shaped around that model, and Galaxy became one target among several rather than the only one.</p>
</section>
<section id="what-a-job-looks-like" class="level2">
<h2 class="anchored" data-anchor-id="what-a-job-looks-like">What a job looks like</h2>
<p>A package opts in by adding two files under <code>inst/biocjobs/</code>. Nothing else about the package changes: no new imports, no code changes, no build-system requirements. Packages that are inherently interactive simply do not add the directory.</p>
<p>The first file is the <strong>declaration</strong>: what the job consumes, produces, and exposes. Abridged here from the example DESeq2 spec, which declares two inputs, three outputs and nine options:</p>
<pre>biocjobs: &quot;1.0&quot;
name: deseq2-differential-expression
package: DESeq2
title: DESeq2 differential expression
description: &gt;
  Runs the canonical DESeq2 workflow on a raw count matrix: size factor and
  dispersion estimation, negative-binomial GLM fitting and a Wald test for
  one pairwise contrast.
version: &quot;1.0.0&quot;
script: scripts/deseq2-differential-expression.R
depends: [apeglm, ashr]

inputs:
  - name: counts
    format: tsv
    label: Raw count matrix
    help: &gt;
      Tab-separated matrix of raw (un-normalized) integer read counts.

outputs:
  - name: results
    format: tsv
    label: Differential expression results

options:
  - name: shrinkage
    type: choice
    choices: [apeglm, ashr, normal, none]
    default: apeglm
    label: Log2 fold change shrinkage
  - name: alpha
    type: float
    default: 0.1
    min: 0
    max: 1
    label: FDR threshold

resources:
  cpus: 1
  memory_gb: 4

citations:
  - doi: 10.1186/s13059-014-0550-8</pre>
<p>The second file is the <strong>script</strong>: plain R, around 130 lines for DESeq2, effectively the analysis script to dispatch. Its first line hands the entire interface over to the declaration:</p>
<pre>params &lt;- BiocJobs::jobParams(&quot;DESeq2&quot;, &quot;deseq2-differential-expression&quot;)</pre>
<p>That call parses the command line <em>against the declaration</em>, applying type coercion, defaults, numeric bounds, enumerated choices, required-parameter checks and output directory creation. What is notably absent from the script is any argument parsing, any type checking, and any usage message, handled by the BiocJobs framework. The rest of the file is ordinary analysis code reading <code>params$counts</code>, <code>params$alpha</code> and so on.</p>
</section>
<section id="what-comes-out" class="level2">
<h2 class="anchored" data-anchor-id="what-comes-out">What comes out</h2>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/blog.bioconductor.org/posts/2026-08-21-biocjobs/biocjobs-targets.jpg?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" alt="Diagram showing two files written by the package author, a job YAML declaration and an R analysis script under inst/biocjobs/, passing through BiocJobs, which validates them and generates four artifacts: a Galaxy tool wrapper XML, a GA4GH TES task template, a Nextflow DSL2 module and a WDL task." data-recalc-dims="1"></p>
</figure>
</div>
<p>From that one declaration, BiocJobs generates:</p>
<table class="caption-top table">
<caption>Artifacts generated from a single BiocJobs declaration</caption>
<colgroup>
<col style="width: 22%">
<col style="width: 78%">
</colgroup>
<thead>
<tr class="header">
<th>Target</th>
<th>Artifact</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Galaxy</td>
<td>tool wrapper XML, with typed params, datatypes, tests and citations</td>
</tr>
<tr class="even">
<td>GA4GH TES</td>
<td>a v1.1 task template, ready to <code>POST</code> to a TES server such as Funnel or TESK, or to a cloud endpoint</td>
</tr>
<tr class="odd">
<td>Nextflow</td>
<td>a Nextflow DSL2 module with typed inputs, named <code>emit:</code> outputs and a stub block</td>
</tr>
<tr class="even">
<td>WDL</td>
<td>a 1.0 task with <code>runtime</code> and <code>parameter_meta</code></td>
</tr>
</tbody>
</table>
<p>Every target launches the same self-locating command, so the artifacts carry no absolute paths and do not drift against the installed package:</p>
<pre>Rscript -e 'BiocJobs::execJob(&quot;DESeq2&quot;, &quot;deseq2-differential-expression&quot;)' \
    --counts counts.tsv --coldata coldata.tsv \
    --contrast_factor condition --contrast_numerator treated \
    --contrast_denominator control --alpha 0.05</pre>
</section>
<section id="first-implementation-at-the-bioc2026-hackathon" class="level2">
<h2 class="anchored" data-anchor-id="first-implementation-at-the-bioc2026-hackathon">First implementation, at the BioC2026 hackathon</h2>
<p>The first working implementation was built at the <a href="https://github.com/BiocCodingCollaborations/BiocNA2026_Hackathon" rel="nofollow" target="_blank">BioC2026 hackathon in Seattle</a> in August 2026, spearheaded by Alexandru Mahmoud, and taken far enough to get a first working example.</p>
<p><strong>DESeq2</strong> was chosen for this purpose. It is a popular package, batch-shaped, and is already used in many workflow engines, hence having something to compare against after generating the wrappers. A <a href="https://github.com/almahmoud/DESeq2" rel="nofollow" target="_blank">fork of DESeq2</a> carries the two <code>inst/biocjobs/</code> files a maintainer would add.</p>
<p>The job itself was run end to end in R against simulated data, 600 genes by 6 samples with 60 planted differentially expressed genes, recovering the planted signal; the same run through the generated command-line path produced byte-identical results. The generated artifacts were then checked with the tooling each ecosystem uses. The Nextflow module passes <code>nextflow lint</code> with zero findings and executes under <code>-stub-run</code> with correct channel and <code>emit:</code> wiring. The WDL task passes <code>miniwdl check</code>. The Galaxy wrapper validates against Galaxy’s official tool XML schema (XSD), and the TES task against the GA4GH TES 1.1 <code>tesTask</code> schema.</p>
<p>What none of that establishes is whether a generated artifact survives a real workflow run against real data, which is where the next section comes in.</p>
</section>
<section id="independent-evaluations" class="level2">
<h2 class="anchored" data-anchor-id="independent-evaluations">Independent evaluations</h2>
<p>The most useful outcome from the Hackathon collaboration was an evaluation by Nextflow and WDL users. The WDL evaluation was documented in the <a href="https://github.com/getwilds/wilds-wdl-library/pull/392" rel="nofollow" target="_blank">WILDS WDL Library</a> which added a <code>run_deseq2_biocjobs</code> task to the <code>ww-deseq2</code> module, calling <code>BiocJobs::execJob()</code> as an alternative to the module’s existing hand-written R script, and ran it against real data. It produced valid results tables, normalised counts and plots.</p>
<p>The PR was closed rather than merged, with more testing to come in the future.</p>
<p>Feedback, in the form of GitHub issues, was provided regarding the formatting of the Nextflow module files generated by BiocJobs. Topics to consider include:</p>
<ul>
<li>The extent to which we should strive for compatibility with nf-core</li>
<li>Separate input items vs inputs grouped into tuples, with the latter being useful in multi-sample processing</li>
<li>Use of the <code>tag</code> directive</li>
<li>Naming of output files</li>
</ul>
<p>As a test from the Bioconductor package developer perspective, an example job was successfully developed for the VariantAnnotation package. The job takes as inputs an indexed VCF, a BED indicating regions of interest, and a list of sample names, and produces a TSV of genotypes reformatted as alternative allele counts.</p>
</section>
<section id="a-subproject-per-package-containers" class="level2">
<h2 class="anchored" data-anchor-id="a-subproject-per-package-containers">A subproject: per-package containers</h2>
<p>Making a job dispatchable exposes a second problem immediately. A generated wrapper needs an environment containing R, the host package, and the job’s declared dependencies, and the generic Bioconductor container ships none of the analysis packages.</p>
<p>That pushed out a parallel subproject: a pipeline to <strong>automatically build and host a container per Bioconductor package</strong>, or per group of packages, or per BiocJobs script. Each image would carry one package plus everything it declares (<code>Depends</code>, <code>Imports</code>, <code>LinkingTo</code> and <code>Suggests</code>) so that vignettes, examples and the package’s own tests all run inside it.</p>
<p>This is not a replacement for the container infrastructure Bioconductor and BioContainers already provide; it builds directly on top of it. Images are layered on the existing Bioconductor base stacks, both the familiar <a href="https://bioconductor.org/help/docker/" rel="nofollow" target="_blank"><code>bioconductor_docker</code></a> images and the newer <code>bioc2u</code> stack, which installs packages as Debian binaries and so builds far faster. The distinction from what exists today is granularity. Bioconductor publishes broad base images, and <a href="https://biocontainers.pro/" rel="nofollow" target="_blank">BioContainers</a> publishes per-package images built from the Bioconda recipes; what a dispatched job wants is an image scoped to exactly one package and its full declared dependency closure, tracking the Bioconductor release directly. Longer term, the ambition is to work with BioContainers so that these images are published in their Quay repository alongside the Bioconda-derived ones, since that is where workflow authors already look. Per-package images on GHCR are simply the first target, because they can be built and iterated on without coordination.</p>
<p>The one image that exists so far, <code>ghcr.io/almahmoud/deseq2:devel</code>, was built ad hoc from the DESeq2 fork, and is what the WDL evaluation described above actually ran against. The work in progress for building all packages lives at <a href="https://github.com/almahmoud/biocpkgcontainers" rel="nofollow" target="_blank">almahmoud/biocpkgcontainers</a>.</p>
</section>
<section id="how-this-was-built" class="level2">
<h2 class="anchored" data-anchor-id="how-this-was-built">How this was built</h2>
<p>The design of the specification, meaning what a job declaration contains and what the runtime contract is, came out of the conversations described above and out of a much wider set of them over a longer period.</p>
<p>In the interest of transparency: the first implementation of the generators, and a first draft of this post, were written with substantial assistance from Claude, in order to get something runnable in front of others quickly. The result is a functioning prototype rather than a finished product, and it still needs a great deal of refinement by human hands.</p>
</section>
<section id="this-is-early-and-here-is-what-would-help-if-you-want-to-contribute" class="level2">
<h2 class="anchored" data-anchor-id="this-is-early-and-here-is-what-would-help-if-you-want-to-contribute">This is early, and here is what would help if you want to contribute</h2>
<p>BiocJobs is a <strong>work in progress</strong>. The spec is marked version 1.0 but should not be considered stable and should still be expected to change before an actual v1 release. Today it supports single-file inputs only, five option types, and one analysis command per job. Collections, multi-file inputs and a CWL generator are on the roadmap. Nothing here is set in stone, and that is deliberate, so any and all feedback is welcomed.</p>
<p>Two groups of people could help enormously right now.</p>
<p><strong>Bioconductor package developers.</strong> The single most valuable contribution is adding a job declaration to your own package. The framework has been validated on exactly one package so far, which is not enough to know whether the specification is expressive enough, whether the format vocabulary covers real use cases, or whether the runtime contract survives contact with analyses structured differently from DESeq2. Every additional package is a test of the design. If a job in your package cannot be expressed in the current spec, that is precisely the feedback needed before a first release.</p>
<p><strong>Workflow developers.</strong> If you maintain Galaxy tools, Nextflow modules, WDL tasks or <a href="https://nf-co.re/" rel="nofollow" target="_blank">nf-core</a> pipelines, generated wrappers need to hold up against the standards you already apply by hand. The WILDS evaluation above is a great model: take a generated artifact, try to use it in a real pipeline, and say plainly where it falls short.</p>
<p>Before a first version is stabilised, the aim is to have job declarations in ten or so packages of different shapes, with the generated artifacts reviewed manually to validate their correctness. If that describes you or your package, open an issue on the <a href="https://github.com/almahmoud/BiocJobs" rel="nofollow" target="_blank">BiocJobs repository</a> and say which package you have in mind!</p>
<p>There is also a good opportunity to work on this together in person or remotely. BiocJobs is one of the projects at the <a href="https://github.com/BiocCodingCollaborations/BioFAIR2026_Sprint" rel="nofollow" target="_blank"><strong>BioFAIR 2026 Workflow Interoperability Sprint</strong></a>, a hybrid event running <strong>15 to 17 September 2026</strong> in Milton Keynes, United Kingdom, which brings together developers from across the Bioconductor, Galaxy, Nextflow, nf-core and WDL ecosystems. If you would like to join, in person or remotely, the sprint repository has the details, and the <code>#biofair2026-workflow-sprint</code> channel on <a href="https://chat.bioconductor.org/" rel="nofollow" target="_blank">Bioconductor Zulip</a> is where planning happens.</p>
</section>
<section id="links" class="level2">
<h2 class="anchored" data-anchor-id="links">Links</h2>
<ul>
<li><strong><a href="https://github.com/almahmoud/BiocJobs" rel="nofollow" target="_blank">BiocJobs on GitHub</a></strong>, the first implementation, and where to leave feedback as issues</li>
<li><strong><a href="https://github.com/almahmoud/DESeq2" rel="nofollow" target="_blank">The DESeq2 fork</a></strong>, used to validate the framework on a first example</li>
<li><strong><a href="https://github.com/getwilds/wilds-wdl-library/pull/392" rel="nofollow" target="_blank">The WILDS WDL Library evaluation</a></strong> of the generated WDL</li>
</ul>
</section>
<section id="acknowledgements" class="level2">
<h2 class="anchored" data-anchor-id="acknowledgements">Acknowledgements</h2>
<p>The design of this framework came out of a large collaborative network and a great many conversations among researchers worldwide, particularly across the <strong>Bioconductor</strong>, <strong>Galaxy</strong> and <strong>OpenWDL</strong> communities. It would not exist without the people who kept raising these ideas.</p>


</section>

<p>
© 2026 Bioconductor. Content is published under <a href="https://creativecommons.org/licenses/by/4.0/" rel="nofollow" target="_blank">Creative Commons CC-BY-4.0 License</a> for the text and <a href="https://opensource.org/licenses/BSD-3-Clause" rel="nofollow" target="_blank">BSD 3-Clause License</a> for any code. | <a href="https://www.r-bloggers.com/" rel="nofollow" target="_blank">R-Bloggers</a>
</p> 
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://blog.bioconductor.org/posts/2026-08-21-biocjobs/"> Bioconductor community blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/biocjobs-declaring-dispatchable-jobs-inside-bioconductor-packages/">BiocJobs: declaring dispatchable jobs inside Bioconductor packages</a>]]></content:encoded>
					
		
		<enclosure url="https://blog.bioconductor.org/posts/2026-08-21-biocjobs/biocjobs-targets.jpg" length="0" type="image/jpeg" />

		<post-id xmlns="com-wordpress:feed-additions:1">403254</post-id>	</item>
		<item>
		<title>Reading notes on The Programmer&#8217;s Brain by Felienne Hermans</title>
		<link>https://www.r-bloggers.com/2026/08/reading-notes-on-the-programmers-brain-by-felienne-hermans/</link>
		
		<dc:creator><![CDATA[Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://masalmon.eu/2026/08/21/the-programmer-s-brain-reading-notes/</guid>

					<description><![CDATA[<p>Prompted (😉) by some AI dread, I decided to go back to some basics and re-read The Programmer’s Brain by Felienne Hermans. Felienne Hermans’ work caught my attention when she gave a keynote talk at a Posit conference years ago. The book was...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/reading-notes-on-the-programmers-brain-by-felienne-hermans/">Reading notes on The Programmer’s Brain by Felienne Hermans</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://masalmon.eu/2026/08/21/the-programmer-s-brain-reading-notes/"> Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p>Prompted (<img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" />) by some AI dread, I decided to go back to some basics and re-read <a href="https://www.manning.com/books/the-programmers-brain" rel="nofollow" target="_blank">The Programmer’s Brain by Felienne Hermans</a>. Felienne Hermans’ work caught my attention when she gave a <a href="https://resources.rstudio.com/resources/rstudioconf-2019/explicit-direct-instruction-in-programming-education/" rel="nofollow" target="_blank">keynote talk</a> at a Posit conference years ago. The book was a highlight of my week: extremely interesting, and easy to follow. Here are some notes, thanks to tiny bookmarks I added as a I read.</p>
<h2 id="the-main-characters">The main characters</h2>
<p>The main characters in the book are</p>
<ul>
<li>the long-term memory (knowledge);</li>
<li>the short-term memory (information right now);</li>
<li>the working memory (processing power).</li>
</ul>
<p>Everything is brought back to them.</p>
<h2 id="making-an-effort-pays-off">Making an effort pays off</h2>
<p>I am fascinated by the fact that a schoolteacher called Ballard found out that “when you actively try to recall information without additional study, you will remember more of what you learned”.</p>
<p>You can also strengthen your memories by actively thinking, <em>elaborating</em> around something.</p>
<h2 id="cognitive-loads">Cognitive loads</h2>
<p>Felienne Hermans summarizes the different types of cognitive load:</p>
<blockquote>
<p>Intrinsic load: how complex the problem is in itself. Extraneous load: what outside distractions add to the problem. Germane load: cognitive load created by having to store your thought to long-term memory.</p>
</blockquote>
<p>Regarding extraneous load, an example that’s given is a poorly formulated math problem.</p>
<h2 id="cognitive-refactoring">Cognitive refactoring</h2>
<p>I remembered this idea from my first read: you can refactor code to understand it better, a refactor you do only for yourself.</p>
<p>One example that’s given is replacing unfamiliar language constructs such as anonymous functions. It made me think of my overcomplicating a PR by both changing something crucial and replacing for loops with <a href="https://masalmon.eu/2023/07/26/reduce/" rel="nofollow" target="_blank">reduce</a>, that were unfamiliar to my collaborator. I should have split the two changes in two PRs.</p>
<p>A related quote from the book:</p>
<blockquote>
<p>‘“readable” is really in the eye of the beholder’</p>
</blockquote>
<h2 id="help-your-working-memory">Help your working memory</h2>
<p>When mentioning strategies for helping your working memory, such as creating state tables or diagrams, the author mentioned PythonTutor by Philip Guo, which visualizes the execution of a program. It reminded me of the boomer R package by my cynkra colleague Antoine Fabri, that lets you inspect the intermediate steps of a call.</p>
<div class="highlight">
<pre>subset(head(penguins, 2), bill_len &gt; 47) |&gt; boomer::boom()
#&gt; &#x1f4a3; subset(head(penguins, 2), bill_len &gt; 47) 
#&gt; · &#x1f4a3; &#x1f4a5; head(penguins, 2) 
#&gt; ·   species    island bill_len bill_dep flipper_len body_mass    sex year
#&gt; · 1  Adelie Torgersen     39.1     18.7         181      3750   male 2007
#&gt; · 2  Adelie Torgersen     39.5     17.4         186      3800 female 2007
#&gt; · 
#&gt; · &#x1f4a3; &#x1f4a5; bill_len &gt; 47 
#&gt; · [1] FALSE FALSE
#&gt; · 
#&gt; &#x1f4a5; subset(head(penguins, 2), bill_len &gt; 47) 
#&gt; [1] species     island      bill_len    bill_dep    flipper_len body_mass   sex         year       
#&gt; &lt;0 rows&gt; (or 0-length row.names)
#&gt; 
#&gt; [1] species     island      bill_len    bill_dep    flipper_len body_mass   sex         year       
#&gt; &lt;0 rows&gt; (or 0-length row.names)
</pre>
</div>
<p>Coupling that with <a href="https://cynkra.github.io/constructive/" rel="nofollow" target="_blank">constructive</a>, another package of Antoine’s, might help one represent code better.</p>
<h2 id="roles-of-variables">Roles of variables</h2>
<p>The book has a list (by Jorma Sajaniemi) of the eleven roles a variable can have, e.g. “fixed value” or “stepper” (i in a for loop). Interesting vocabulary! The book even features a flowchart to help us determine a role a variable plays.</p>
<h2 id="parallels-with-natural-languages">Parallels with natural languages</h2>
<p>The author explains a technique for understanding code by circling all variables, linking them, etc. It reminds me of how I’d handle Latin text I had to translate in high school. I had a color and shape code, it looked very pretty and worked well.</p>
<p>Speaking of languages, the book draws some parallels between computer and natural languages. In particular, it explains how text comprehension strategies (like questioning or summarizing) apply to code reading.</p>
<h2 id="keeping-notes">Keeping notes</h2>
<p>I will try to do better at taking notes on a piece of paper when I work. I already do in some cases, for instance when reviewing packages for rOpenSci. But the book really insists how it can support your memory, or resume work after an interruptions.</p>
<p>Beside those throwaway notes, it’s important to document/comment code to prevent future contributors to fall in some traps and to facilitate onboarding of new contributors. <a href="https://github.com/duckdb/duckdb-r/tree/main/handbook" rel="nofollow" target="_blank">Recent example</a>.</p>
<h2 id="further-programming-languages">Further programming languages</h2>
<p>IDEs:</p>
<blockquote>
<p>“transfer between two programming languages is more likely if you program two different languages in the same IDE, which is a strong argument for using one IDE for multiple languages.”</p>
</blockquote>
<p>Language choice:</p>
<blockquote>
<p>“if you set out to learn a new language to expand your way of thinking, it’s important to pick one language that’s fundamentally different from the ones you’ve already mastered.”</p>
</blockquote>
<p>The book also explains how some knowledge you have in one language means you might have to “unlearn” some syntaxes. It reminded of <em>faux amis</em> (false friends) for French-speaking learners of English, like “actually” that doesn’t mean <em>actuellement</em> (currently).</p>
<h2 id="names-are-important">Names are important…</h2>
<p>And the book explains why, gives useful tips. There’s a whole chapter on the topic.</p>
<p>I liked one of the conventions by Butler: “Identifiers should consist of words and only use abbreviations when they are more commonly used than the full words”. Recently I was very stubborn about not using “comb” for “combination” in igraph. I also enjoyed another conventions from that same list: “Identifier should not combine uppercase and lowercase character in non standard ways”, with the example <code>Page_counter</code>. I might be selectively reading the rules that I like. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f607.png" alt="😇" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The chapter conveys the perspective by Allamanis that names should be consistent across a codebase, because that helps chunking (when you parse code into meaningful bits).</p>
<p>The author underlines that you should evaluate the quality of names after coding, not during code, as it might be too much cognitive load. It made me think of Git commits: you can <a href="https://masalmon.eu/talks/2025-11-24-git-history/" rel="nofollow" target="_blank">improve them after coding</a>, when you’re coding you might not be able to create a perfect Git history.</p>
<p>Another tidbit that I found interesting is that when you improve names in your codebase, the places where you find bad names might be the places with hidden bugs for various reasons (like correlation between bad names and mistakes by a novice programmer or a programmer confused by the complexity of the problem at hand).</p>
<h2 id="automatization">Automatization</h2>
<p>Some things you know so well that you can do them without thinking much, which makes you more efficient. An argument for learning and deliberate practice.</p>
<h2 id="reading-about-code">Reading about code</h2>
<p>Sometimes if you’re writing very complex code, you don’t learn much, “your brain was so engaged it could not store the solutions”.</p>
<p>Therefore, worked examples can help you learn: collaborating with someone, reading code on GitHub, reading books or blog posts about code. You don’t only (and necessarily) learn by doing.</p>
<h2 id="curse-of-expertise">Curse of expertise</h2>
<p>I enjoyed reading again about the “curse of expertise”, that is especially relevant when teaching:</p>
<blockquote>
<p>“Once you have mastered a skill sufficiently, you will inevitably forget how hard it was to learn that skill or knowledge.”</p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>I would highly recommend reading The Programmer’s Brain by Felienne Hermans! Maybe even more than once like I did since I had clearly not committed everything to long-term memory. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f601.png" alt="😁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The epilogue mentions some further reading including <a href="https://masalmon.eu/2023/10/19/reading-notes-philosophy-software-design/" rel="nofollow" target="_blank">A Philosophy of Software Design by John Ousterhout</a> which solved a mystery for me: <em>that</em> is where I had heard of that book!</p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://masalmon.eu/2026/08/21/the-programmer-s-brain-reading-notes/"> Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/reading-notes-on-the-programmers-brain-by-felienne-hermans/">Reading notes on The Programmer’s Brain by Felienne Hermans</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403249</post-id>	</item>
		<item>
		<title>useR! 2026: Futurize &#8211; Tearing Down Parallelization Barriers in R with Transpilers</title>
		<link>https://www.r-bloggers.com/2026/08/user-2026-futurize-tearing-down-parallelization-barriers-in-r-with-transpilers/</link>
		
		<dc:creator><![CDATA[JottR on R]]></dc:creator>
		<pubDate>Thu, 20 Aug 2026 12:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.jottr.org/2026/08/20/futurize-user2026-slides/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Below are the slides for my Futurize - Tearing Down Parallelization Barriers in R with Transpilers talk that I presented at the useR! 2026 conference in Warzaw, Poland.</p>
<p>Title: Futurize - Tearing Down Parallelization Barriers in R with Transpil...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/user-2026-futurize-tearing-down-parallelization-barriers-in-r-with-transpilers/">useR! 2026: Futurize – Tearing Down Parallelization Barriers in R with Transpilers</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://www.jottr.org/2026/08/20/futurize-user2026-slides/"> JottR on R</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>


<figure style="margin-top: 3ex; border: solid 1px gray;">
<img src="https://i1.wp.com/www.jottr.org/post/BengtssonH_20260708-useR2026_futurize_slide1.png?w=578&#038;ssl=1" alt=". Event: useR! 2026, Warzaw, Poland (2026-07-08)." style="width: 100%; margin: 0;" data-recalc-dims="1"/>
</figure>

<p><img src="https://i2.wp.com/www.jottr.org/post/useR2026-logo.png?w=578&#038;ssl=1" alt="Logo for useR! 2026" style="width: 30%; float: right; margin: 2ex;" data-recalc-dims="1"/></p>

<p>Below are the slides for my <em>Futurize &#8211; Tearing Down Parallelization Barriers in R with Transpilers</em> talk that I presented at the <a href="https://user2026.r-project.org/" rel="nofollow" target="_blank">useR! 2026</a> conference in Warzaw, Poland.</p>

<p>Title: Futurize &#8211; Tearing Down Parallelization Barriers in R with Transpilers<br />
Speaker: Henrik Bengtsson<br />
Slides: <a href="https://henrikbengtsson.github.io/talk-user2026-futurize/#/" rel="nofollow" target="_blank">HTML</a> (16 slides; 18 minutes)<br />
Video: To appear</p>

<hr />

<p>The new <strong><a href="https://futurize.futureverse.org/" rel="nofollow" target="_blank">futurize</a></strong> package makes it easier than ever before to parallelize existing map-reduce calls &#8211; just pipe the call to <code>futurize()</code> and you’re done!</p>

<pre>ys &lt;- lapply(xs, fit_model) |&gt; futurize()
ys &lt;- map(xs, fit_model) |&gt; futurize()
ys &lt;- foreach(x = xs) %do% fit_model(x) |&gt; futurize()
ys &lt;- llply(xs, fit_model) |&gt; futurize()
</pre>

<p>It also works with other popular domain-specific calls, e.g.</p>

<pre>xs_smooth &lt;- stats::kernapply(xs, k = k) |&gt; futurize()
b &lt;- boot(city, ratio, R = 999) |&gt; futurize()
model &lt;- caret::train(Species ~ ., data = iris, method = &quot;rf&quot;, trControl = ctrl) |&gt; futurize()
cv &lt;- glmnet::cv.glmnet(x, y) |&gt; futurize()
m &lt;- lme4::allFit(models) |&gt; futurize()
</pre>

<p>See the <strong><a href="https://futurize.futureverse.org/" rel="nofollow" target="_blank">futurize</a></strong> package site for more examples and details.</p>

<hr />

<p>I want to thank the useR! organizers, staff, volunteers, sponsors, and everyone else who contributed to this amazing event making it possible for the R community to come together in person. Just like last year’s useR! 2025 in the US, it was fantastic to see so many first and second timers attending the useR! conference in Europe. It’s very refreshing and it clear that we are on a great track to recover from not having in-person R conferences during COVID-19 pandemic. Next year’s useR! will take place in Santiago, Chile in July 2027 &#8211; exciting!</p>

<p>/Henrik</p>

<h2 id="links">Links</h2>

<ul>
<li>useR! 2026: <a href="https://user2026.r-project.org/" rel="nofollow" target="_blank">https://user2026.r-project.org/</a></li>
<li><strong>futureverse</strong> website: <a href="https://www.futureverse.org/" rel="nofollow" target="_blank">https://www.futureverse.org/</a></li>
<li><strong>futurize</strong> package <a href="https://cran.r-project.org/package=futurize" rel="nofollow" target="_blank">CRAN</a>, <a href="https://github.com/futureverse/futurize" rel="nofollow" target="_blank">GitHub</a>, <a href="https://futurize.futureverse.org/" rel="nofollow" target="_blank">pkgdown</a></li>
</ul>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://www.jottr.org/2026/08/20/futurize-user2026-slides/"> JottR on R</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/user-2026-futurize-tearing-down-parallelization-barriers-in-r-with-transpilers/">useR! 2026: Futurize – Tearing Down Parallelization Barriers in R with Transpilers</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403229</post-id>	</item>
		<item>
		<title>How to Get Sports Betting Data in R: Free APIs, Historical Odds and Daily Updates</title>
		<link>https://www.r-bloggers.com/2026/08/how-to-get-sports-betting-data-in-r-free-apis-historical-odds-and-daily-updates/</link>
		
		<dc:creator><![CDATA[rprogrammingbooks]]></dc:creator>
		<pubDate>Wed, 19 Aug 2026 21:39:35 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://rprogrammingbooks.com/?p=2588</guid>

					<description><![CDATA[<p>Building a sports betting model in R does not begin with machine learning or a complicated statistical formula. It begins with reliable data. You need historical results, team or player statistics, bookmaker odds and a process for updating everything without manually downloading a new spreadsheet every day. Fortunately, R provides ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/how-to-get-sports-betting-data-in-r-free-apis-historical-odds-and-daily-updates/">How to Get Sports Betting Data in R: Free APIs, Historical Odds and Daily Updates</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://rprogrammingbooks.com/sports-betting-data-r-apis-historical-odds/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=sports-betting-data-r-apis-historical-odds"> Blog - R Programming Books</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<p>Building a sports betting model in R does not begin with machine learning or a complicated statistical formula. It begins with reliable data.</p>

<p>You need historical results, team or player statistics, bookmaker odds and a process for updating everything without manually downloading a new spreadsheet every day. Fortunately, R provides several packages and APIs that make it possible to build a reproducible sports betting data pipeline.</p>

<p>In this guide, you will learn how to obtain sports betting data in R, download current odds, organize historical information and prepare datasets for predictive modeling and backtesting.</p>

<h2>What Data Do You Need for a Sports Betting Model?</h2>

<p>A useful sports betting dataset normally combines two different types of information:</p>

<ul>
  <li><strong>Sports performance data:</strong> scores, schedules, team statistics, player statistics and play-by-play data.</li>
  <li><strong>Betting market data:</strong> moneylines, point spreads, totals, bookmaker prices and historical closing odds.</li>
</ul>

<p>The exact variables depend on the sport and market you want to predict. For example, an NFL point-spread model may use offensive EPA, defensive EPA, quarterback performance, home advantage, rest days and the bookmaker’s closing spread.</p>

<p>An NBA totals model could use pace, offensive rating, defensive rating, injuries, recent form and the market total.</p>

<h2>Useful R Packages for Sports Data</h2>

<p>The SportsDataverse ecosystem provides packages for several major sports:</p>

<ul>
  <li><code>nflreadr</code> and <code>nflfastR</code> for NFL data.</li>
  <li><code>hoopR</code> for NBA and NCAA basketball.</li>
  <li><code>baseballr</code> for MLB, college baseball and Statcast data.</li>
  <li><code>fastRhockey</code> for NHL and hockey data.</li>
  <li><code>wehoop</code> for WNBA and women’s college basketball.</li>
  <li><code>oddsapiR</code> for current and historical sportsbook odds.</li>
</ul>

<p>Install the core packages with:</p>

<pre>install.packages(c(
  &quot;tidyverse&quot;,
  &quot;httr2&quot;,
  &quot;jsonlite&quot;,
  &quot;lubridate&quot;,
  &quot;oddsapiR&quot;
))</pre>

<p>You do not necessarily need every sport-specific package. Install only the packages required for the leagues you intend to analyze.</p>

<h2>Getting a Sports Odds API Key</h2>

<p>One of the simplest ways to access bookmaker odds is <a href="https://the-odds-api.com/" rel="nofollow" target="_blank">The Odds API</a>. It covers many sports, leagues, bookmakers and betting markets.</p>

<p>Create an account, obtain your API key and save it in your R environment. Avoid writing a private key directly inside a script that may later be shared online.</p>

<pre>install.packages(&quot;usethis&quot;)
usethis::edit_r_environ()</pre>

<p>Add the following line to the <code>.Renviron</code> file:</p>

<pre>ODDS_API_KEY=YOUR_PRIVATE_API_KEY</pre>

<p>Save the file and restart RStudio. You can then confirm that R can find the key:</p>

<pre>Sys.getenv(&quot;ODDS_API_KEY&quot;)</pre>

<p>Do not publish the result of this command or upload your key to GitHub.</p>

<h2>Download Current Sports Betting Odds in R</h2>

<p>The following example requests current NFL moneyline, spread and total prices from US bookmakers:</p>

<pre>library(httr2)
library(jsonlite)
library(dplyr)
library(tidyr)
library(purrr)

api_key &lt;- Sys.getenv(&quot;ODDS_API_KEY&quot;)

request_url &lt;- paste0(
  &quot;https://api.the-odds-api.com/v4/sports/&quot;,
  &quot;americanfootball_nfl/odds&quot;
)

response &lt;- request(request_url) |&gt;
  req_url_query(
    apiKey = api_key,
    regions = &quot;us&quot;,
    markets = &quot;h2h,spreads,totals&quot;,
    oddsFormat = &quot;decimal&quot;,
    dateFormat = &quot;iso&quot;
  ) |&gt;
  req_perform()

odds_raw &lt;- resp_body_json(response, simplifyVector = FALSE)</pre>

<p>The API response contains nested JSON because each event can include multiple bookmakers, markets and outcomes. A nested response is useful for storage, but it usually needs to be transformed before modeling.</p>

<h2>Convert the API Response into Tidy Data</h2>

<p>The following function converts the nested response into one row per event, bookmaker, market and outcome:</p>

<pre>tidy_odds &lt;- function(events) {

  map_dfr(events, function(event) {

    map_dfr(event$bookmakers, function(bookmaker) {

      map_dfr(bookmaker$markets, function(market) {

        map_dfr(market$outcomes, function(outcome) {

          tibble(
            event_id = event$id,
            sport = event$sport_title,
            commence_time = event$commence_time,
            home_team = event$home_team,
            away_team = event$away_team,
            bookmaker = bookmaker$title,
            market = market$key,
            outcome = outcome$name,
            odds = outcome$price,
            point = if (is.null(outcome$point)) NA_real_ else outcome$point,
            last_update = bookmaker$last_update
          )
        })
      })
    })
  })
}

odds_df &lt;- tidy_odds(odds_raw)

glimpse(odds_df)</pre>

<p>The resulting table can contain columns such as:</p>

<ul>
  <li><code>home_team</code> and <code>away_team</code></li>
  <li><code>commence_time</code></li>
  <li><code>bookmaker</code></li>
  <li><code>market</code></li>
  <li><code>outcome</code></li>
  <li><code>odds</code></li>
  <li><code>point</code></li>
</ul>

<p>Convert the timestamps into a proper date-time format before analyzing them:</p>

<pre>library(lubridate)

odds_df &lt;- odds_df |&gt;
  mutate(
    commence_time = ymd_hms(commence_time),
    last_update = ymd_hms(last_update)
  )</pre>

<h2>Understanding Moneylines, Spreads and Totals</h2>

<p>The API uses different market identifiers:</p>

<ul>
  <li><code>h2h</code>: head-to-head or moneyline betting.</li>
  <li><code>spreads</code>: point-spread or handicap betting.</li>
  <li><code>totals</code>: over/under markets.</li>
</ul>

<p>You can filter the dataset to analyze a single market:</p>

<pre>spread_odds &lt;- odds_df |&gt;
  filter(market == &quot;spreads&quot;)

total_odds &lt;- odds_df |&gt;
  filter(market == &quot;totals&quot;)

moneyline_odds &lt;- odds_df |&gt;
  filter(market == &quot;h2h&quot;)</pre>

<h2>Convert Decimal Odds into Implied Probabilities</h2>

<p>Decimal odds can be converted into raw implied probability using:</p>

<pre>moneyline_odds &lt;- moneyline_odds |&gt;
  mutate(implied_probability = 1 / odds)</pre>

<p>For example, decimal odds of 2.00 represent a raw implied probability of 50%. However, bookmaker probabilities normally add up to more than 100% because the prices include a margin, also known as vig or overround.</p>

<p>A simple way to remove this margin is to normalize the probabilities within each event and bookmaker:</p>

<pre>fair_moneyline &lt;- moneyline_odds |&gt;
  group_by(event_id, bookmaker) |&gt;
  mutate(
    raw_probability = 1 / odds,
    market_total = sum(raw_probability, na.rm = TRUE),
    fair_probability = raw_probability / market_total
  ) |&gt;
  ungroup()</pre>

<p>The resulting <code>fair_probability</code> column provides a basic no-vig market estimate that can be compared with probabilities generated by your model.</p>

<h2>How to Collect Historical Betting Odds</h2>

<p>A single snapshot is not enough for serious backtesting. You need to store odds repeatedly or use a provider that offers a historical odds endpoint.</p>

<p>Historical data should ideally include:</p>

<ul>
  <li>The time when the odds were observed.</li>
  <li>The bookmaker.</li>
  <li>The opening price.</li>
  <li>Intermediate market prices.</li>
  <li>The closing price before the game started.</li>
  <li>The final score and betting result.</li>
</ul>

<p>This distinction matters because a strategy tested against closing odds may produce very different results from one tested against prices available several hours before the game.</p>

<p>When saving a current snapshot, include the collection time:</p>

<pre>odds_snapshot &lt;- odds_df |&gt;
  mutate(collected_at = Sys.time())

dir.create(&quot;data&quot;, showWarnings = FALSE)

file_name &lt;- paste0(
  &quot;data/odds_&quot;,
  format(Sys.time(), &quot;%Y%m%d_%H%M%S&quot;),
  &quot;.csv&quot;
)

readr::write_csv(odds_snapshot, file_name)</pre>

<p>This creates a new timestamped file every time the script runs. For a larger project, a database such as SQLite or PostgreSQL is more efficient than storing hundreds of CSV files.</p>

<h2>Combine Betting Odds with Sports Performance Data</h2>

<p>Bookmaker odds become more useful when combined with historical results and predictive features. For NFL analysis, for example, you can use <code>nflreadr</code> to download play-by-play data:</p>

<pre>install.packages(&quot;nflreadr&quot;)

library(nflreadr)
library(dplyr)

pbp &lt;- load_pbp(2025)

team_features &lt;- pbp |&gt;
  filter(!is.na(posteam), !is.na(epa)) |&gt;
  group_by(game_id, posteam) |&gt;
  summarise(
    offensive_epa = mean(epa, na.rm = TRUE),
    success_rate = mean(success == 1, na.rm = TRUE),
    plays = n(),
    .groups = &quot;drop&quot;
  )</pre>

<p>You can then aggregate these metrics before each game and join them to the odds table using team names, event dates or a custom event identifier.</p>

<p>For a complete introduction to NFL play-by-play data, EPA and win probability, see <a href="https://rprogrammingbooks.com/product/football-analytics-r-nflfastr-nflverse/" rel="nofollow" target="_blank"><strong>Football Analytics with R: NFL Data Science using nflfastR and nflverse</strong></a>.</p>

<h2>Sports Data Sources for NFL, NBA, MLB and NHL</h2>

<h3>NFL Data</h3>

<p>The <code>nflreadr</code> and <code>nflfastR</code> ecosystem provides schedules, rosters, player statistics and detailed play-by-play data. It is particularly useful for building features based on EPA, success rate, passing performance and win probability.</p>

<h3>NBA Data</h3>

<p>The <code>hoopR</code> package can be used to work with NBA and NCAA schedules, box scores and play-by-play information. Potential betting features include pace, offensive efficiency, defensive efficiency, shot profile and recent performance.</p>

<h3>MLB Data</h3>

<p>The <code>baseballr</code> package provides access to several baseball data sources. Useful variables may include starting pitcher performance, bullpen usage, park factors, batting metrics and Statcast information.</p>

<h3>NHL Data</h3>

<p>The <code>fastRhockey</code> ecosystem can help analysts access hockey schedules and play-by-play information. Common model features include expected goals, shot quality, goaltender performance, rest and special-teams efficiency.</p>

<h2>Build a Simple Probability Model</h2>

<p>After cleaning the data and creating features, you can begin with logistic regression. Suppose your dataset contains a binary variable called <code>home_win</code> and several pregame features:</p>

<pre>model &lt;- glm(
  home_win ~ home_rating_diff +
    rest_days_diff +
    recent_form_diff +
    market_probability,
  data = training_data,
  family = binomial()
)

test_data &lt;- test_data |&gt;
  mutate(
    predicted_probability = predict(
      model,
      newdata = test_data,
      type = &quot;response&quot;
    )
  )</pre>

<p>This is only a baseline. It is usually better to begin with an interpretable model and a clean validation process before trying Random Forest, XGBoost or neural networks.</p>

<h2>Identify Potential Value Bets</h2>

<p>A potential value bet exists when your estimated probability is higher than the break-even probability implied by the available odds.</p>

<pre>betting_candidates &lt;- test_data |&gt;
  mutate(
    break_even_probability = 1 / decimal_odds,
    expected_value = predicted_probability * decimal_odds - 1,
    model_edge = predicted_probability - break_even_probability
  ) |&gt;
  filter(expected_value &gt; 0)</pre>

<p>A positive expected value in historical data does not guarantee future profit. Your probabilities must be calibrated, the backtest must avoid data leakage and the strategy must be tested on games that were not used to train the model.</p>

<h2>Backtest the Model by Season</h2>

<p>Randomly splitting individual games can accidentally allow future information to influence past predictions. A time-based split is generally more realistic.</p>

<pre>training_data &lt;- model_data |&gt;
  filter(game_date &lt; as.Date(&quot;2025-01-01&quot;))

test_data &lt;- model_data |&gt;
  filter(game_date &gt;= as.Date(&quot;2025-01-01&quot;))</pre>

<p>A useful backtest should report more than total profit. Consider tracking:</p>

<ul>
  <li>Number of bets.</li>
  <li>Win rate.</li>
  <li>Return on investment.</li>
  <li>Maximum drawdown.</li>
  <li>Closing line value.</li>
  <li>Brier score.</li>
  <li>Log loss.</li>
  <li>Probability calibration.</li>
</ul>

<p>If you want to learn how to use Elo ratings, Monte Carlo simulation and forecasting methods, explore <a href="https://rprogrammingbooks.com/product/sports-prediction-simulation-r/" rel="nofollow" target="_blank"><strong>Sports Prediction and Simulation with R: Monte Carlo, Elo Ratings, and Forecasting</strong></a>.</p>

<h2>Using Bayesian Models for Sports Prediction</h2>

<p>Bayesian models are especially useful in sports because team strength changes over time and the amount of available information varies between teams and players.</p>

<p>A Bayesian workflow can:</p>

<ul>
  <li>Represent uncertainty with probability distributions.</li>
  <li>Update team estimates when new games are played.</li>
  <li>Use partial pooling to stabilize small samples.</li>
  <li>Estimate full predictive distributions instead of single values.</li>
  <li>Incorporate prior knowledge without treating it as certainty.</li>
</ul>

<p>For a practical introduction to priors, posteriors, hierarchical models, prediction and model validation, see <a href="https://rprogrammingbooks.com/product/bayesian-sports-analytics-r-predictive-modeling-betting-performance/" rel="nofollow" target="_blank"><strong>Bayesian Sports Analytics with R: Predictive Modeling for Betting & Performance</strong></a>.</p>

<h2>Automate Daily Sports Data Updates</h2>

<p>Once your script works, you can schedule it to run every day. A simple pipeline might perform the following steps:</p>

<ol>
  <li>Download the latest games and statistics.</li>
  <li>Request current sportsbook odds.</li>
  <li>Save a timestamped odds snapshot.</li>
  <li>Update team and player features.</li>
  <li>Generate probabilities for upcoming games.</li>
  <li>Compare model probabilities with market prices.</li>
  <li>Save a report containing potential opportunities.</li>
</ol>

<p>On Windows, you can automate an R script with Task Scheduler. On Linux or a server, you can use a cron job. GitHub Actions can also run scheduled workflows, although private API keys should always be stored as encrypted secrets.</p>

<h2>Common Sports Betting Backtesting Mistakes</h2>

<h3>Using Information That Was Not Available Before the Game</h3>

<p>Every model feature must represent information available at the time the bet would have been placed. Season averages calculated using games played after the prediction date create data leakage.</p>

<h3>Ignoring Changes in the Betting Line</h3>

<p>Opening odds, morning odds and closing odds are not interchangeable. Record the exact timestamp and price that your strategy uses.</p>

<h3>Testing Too Many Strategies</h3>

<p>If you test hundreds of filters, one strategy may appear profitable by chance. Use an out-of-sample period that was not used to select the strategy.</p>

<h3>Using Accuracy as the Only Metric</h3>

<p>A model can predict many winners correctly and still lose money if it consistently selects overpriced favorites. Calibration and expected value are more relevant than accuracy alone.</p>

<h3>Assuming a Small Positive Return Proves an Edge</h3>

<p>Sports betting returns are noisy. A strategy needs enough independent bets and should be evaluated with uncertainty intervals, drawdowns and sensitivity tests.</p>

<h2>From Raw Data to a Complete Betting System</h2>

<p>A complete sports betting workflow can be summarized as:</p>

<ol>
  <li>Collect performance data and bookmaker odds.</li>
  <li>Clean team names, dates and market identifiers.</li>
  <li>Create features using only past information.</li>
  <li>Train a probabilistic model.</li>
  <li>Evaluate calibration on unseen games.</li>
  <li>Compare predictions with no-vig market probabilities.</li>
  <li>Backtest realistic prices and betting rules.</li>
  <li>Monitor results and update the model over time.</li>
</ol>

<p>For readers who want to connect probabilities with expected value, the Kelly criterion and bankroll management, <a href="https://rprogrammingbooks.com/product/bayesian-sports-betting-with-r/" rel="nofollow" target="_blank"><strong>Bayesian Sports Betting with R: Probability, Kelly Criterion and Betting Strategies</strong></a> provides a focused guide to data-driven betting decisions in R.</p>

<div style="border: 2px solid #1f5f8b; padding: 22px; margin: 30px 0; border-radius: 8px; background-color: #f4f9fc;">
  <h2 style="margin-top: 0;">Build Your Sports Betting Models with R</h2>

  <p>Learn how to transform sports data into probabilities, evaluate potential value and test strategies using reproducible R code.</p>

  <p>
    <a href="https://rprogrammingbooks.com/product/bayesian-sports-betting-with-r/" style="display: inline-block; padding: 12px 20px; background-color: #1f5f8b; color: #ffffff; text-decoration: none; border-radius: 5px;" rel="nofollow" target="_blank"><strong>View Bayesian Sports Betting with R</strong></a>
  </p>
</div>

<h2>Frequently Asked Questions</h2>

<h3>Can I get sports betting data for free in R?</h3>

<p>Yes. Several R packages provide free sports performance data, and some odds providers offer limited free API access. Historical betting odds and frequent API requests may require a paid plan.</p>

<h3>What is the best R package for sports betting odds?</h3>

<p><code>oddsapiR</code> is a convenient option for accessing The Odds API from R. You can also call the API directly with packages such as <code>httr2</code> and process its JSON response with R.</p>

<h3>Can I obtain NFL, NBA, MLB and NHL data with R?</h3>

<p>Yes. The R sports analytics ecosystem includes packages such as <code>nflreadr</code>, <code>hoopR</code>, <code>baseballr</code> and <code>fastRhockey</code>.</p>

<h3>How many years of data do I need?</h3>

<p>There is no universal minimum. More seasons provide a larger sample, but older data may describe a different competitive or betting environment. Time weighting and rolling training windows can help balance sample size and relevance.</p>

<h3>Can a sports betting model guarantee profits?</h3>

<p>No. Predictive models estimate probabilities under uncertainty. They can be evaluated and improved, but they cannot eliminate variance, bookmaker margins, model error or financial risk.</p>

<h2>Conclusion</h2>

<p>R provides the tools needed to build a complete sports betting data pipeline: data collection, cleaning, feature engineering, probability estimation, backtesting and automated updates.</p>

<p>The most important step is not choosing the most complicated algorithm. It is creating a reliable dataset that preserves the information and odds actually available before each event. Once that foundation is correct, you can compare logistic regression, Elo ratings, Bayesian models, machine learning and simulation methods in a realistic way.</p>

<p>Start with one sport and one betting market. Save every odds snapshot, build a simple baseline and evaluate it on a future season before adding more complexity.</p>

<p><em>This article is for educational and analytical purposes only. Sports betting involves financial risk. No model or strategy can guarantee a profit.</em></p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://rprogrammingbooks.com/sports-betting-data-r-apis-historical-odds/" rel="nofollow" target="_blank">How to Get Sports Betting Data in R: Free APIs, Historical Odds and Daily Updates</a> appeared first on <a href="https://rprogrammingbooks.com/" rel="nofollow" target="_blank">R Programming Books</a>.</p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://rprogrammingbooks.com/sports-betting-data-r-apis-historical-odds/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=sports-betting-data-r-apis-historical-odds"> Blog - R Programming Books</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/how-to-get-sports-betting-data-in-r-free-apis-historical-odds-and-daily-updates/">How to Get Sports Betting Data in R: Free APIs, Historical Odds and Daily Updates</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403215</post-id>	</item>
		<item>
		<title>McNemar&#8217;s test in R</title>
		<link>https://www.r-bloggers.com/2026/08/mcnemars-test-in-r/</link>
		
		<dc:creator><![CDATA[R on Stats and R]]></dc:creator>
		<pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://statsandr.com/blog/mcnemars-test-in-r/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Introduction<br />
In a previous article, we showed how to perform the Chi-square test of independence in R in order to test whether two qualitative variables are related. As mentioned in that article (and in the one showing how to do the Chi-square tes...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/mcnemars-test-in-r/">McNemar’s test in R</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://statsandr.com/blog/mcnemars-test-in-r/"> R on Stats and R</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>



<p><img src="https://i2.wp.com/statsandr.com/blog/mcnemars-test-in-r/images/mcnemars-test-in-r.jpg?w=578&#038;ssl=1" style="width:100.0%" data-recalc-dims="1" /></p>
<div id="introduction" class="section level1">
<h1>Introduction</h1>
<p>In a previous article, we showed how to perform the <a href="https://statsandr.com/blog/chi-square-test-of-independence-in-r/" rel="nofollow" target="_blank">Chi-square test of independence in R</a> in order to test whether two qualitative variables are related. As mentioned in that article (and in the one showing how to do the <a href="https://statsandr.com/blog/chi-square-test-of-independence-by-hand/" rel="nofollow" target="_blank">Chi-square test of independence by hand</a>), this test requires that observations are <strong>independent</strong>. When observations are dependent, that is, when the two measurements are collected on the <em>same</em> subjects (paired samples), the McNemar’s or Cochran’s Q tests should be used instead.</p>
<p>This article is dedicated to the first one: the <strong>McNemar’s test</strong>. It is used to compare two related (paired) proportions measured on a <a href="https://statsandr.com/blog/variable-types-and-examples/#qualitative" rel="nofollow" target="_blank">qualitative variable</a> with only two possible levels. In practice, it is mostly used when the same subjects are measured twice (typically before and after an intervention), or when two raters or two conditions are applied to the same subjects.</p>
<p>In a way, the McNemar’s test is to two paired proportions what the <a href="https://statsandr.com/blog/student-s-t-test-in-r-and-by-hand-how-to-compare-two-groups-under-different-scenarios/" rel="nofollow" target="_blank">paired Student’s t-test</a> is to two paired means: in both cases we take advantage of the fact that the two measurements belong to the same individuals, the difference being that here the variable of interest is binary instead of quantitative.</p>
<p>Note also that the McNemar’s test is limited to exactly two related measurements. If you have more than two (for example, the same question asked at three different time points), the appropriate extension is the Cochran’s Q test, of which the McNemar’s test is the special case for two measurements. If you are unsure about which test is appropriate for your own data, see this <a href="https://statsandr.com/blog/what-statistical-test-should-i-do/" rel="nofollow" target="_blank">overview of the most common statistical tests</a>.</p>
<p>In the remaining of the article, we present the data used for the illustration, the aim, hypotheses and assumptions of the test, and finally how to perform it in R and how to interpret its results.</p>
</div>
<div id="data" class="section level1">
<h1>Data</h1>
<p>A dataset with a paired binary structure is not so easy to find among the datasets shipped with R, so we simulate our own data for this article.</p>
<p>Suppose that we ask 200 randomly selected citizens whether they are in favor of a new policy in their city (answer “Yes” or “No”), that we then have them watch a public debate on this policy, and that we ask them exactly the same question again right after the debate:</p>
<pre># number of respondents
n &lt;- 200

# opinion before the debate
before &lt;- sample(c(&quot;Yes&quot;, &quot;No&quot;),
  size = n,
  replace = TRUE,
  prob = c(0.4, 0.6)
)

# opinion after the debate (respondents who were in favor
# tend to keep their opinion, while those who were against
# are more likely to change their mind)
after &lt;- ifelse(before == &quot;Yes&quot;,
  sample(c(&quot;Yes&quot;, &quot;No&quot;), size = n, replace = TRUE, prob = c(0.9, 0.1)),
  sample(c(&quot;Yes&quot;, &quot;No&quot;), size = n, replace = TRUE, prob = c(0.4, 0.6))
)

# dataset
dat &lt;- data.frame(
  respondent = 1:n,
  before = factor(before, levels = c(&quot;Yes&quot;, &quot;No&quot;)),
  after = factor(after, levels = c(&quot;Yes&quot;, &quot;No&quot;))
)

head(dat)
##   respondent before after
## 1          1    Yes   Yes
## 2          2    Yes   Yes
## 3          3     No   Yes
## 4          4    Yes   Yes
## 5          5    Yes   Yes
## 6          6     No    No</pre>
<p>(Note that a seed has been set in the background with <code>set.seed(42)</code>, so the simulated data and all results below are reproducible.)</p>
<p>Each row corresponds to one respondent and contains two measurements of the same binary variable: the opinion before and the opinion after the debate. The two samples are thus paired, since the two answers on a given row belong to the same person.</p>
<p>As always, it is a good practice to start with some <a href="https://statsandr.com/blog/descriptive-statistics-in-r/" rel="nofollow" target="_blank">descriptive statistics</a>. Here, the proportion of respondents in favor of the policy at each of the two time points:</p>
<pre># install.packages(&quot;dplyr&quot;)
library(dplyr)

dat %&gt;%
  summarise(
    prop_before = mean(before == &quot;Yes&quot;),
    prop_after = mean(after == &quot;Yes&quot;)
  )
##   prop_before prop_after
## 1        0.46       0.61</pre>
<p>In our sample, the proportion of respondents in favor of the policy went from 46% before the debate to 61% after the debate.</p>
<p>These two proportions are computed on the same people, so comparing them as if they came from two independent groups would ignore the pairing. What matters for the McNemar’s test is the way each respondent moved (or did not move) from one answer to the other, and this information is contained in the 2 <span class="math inline">\(\times\)</span> 2 contingency table of the paired answers:</p>
<pre>tab &lt;- table(dat$before, dat$after,
  dnn = c(&quot;Before&quot;, &quot;After&quot;)
)

tab
##       After
## Before Yes No
##    Yes  81 11
##    No   41 67</pre>
<p>This table must be read pair by pair, and not cell by cell as we usually do:</p>
<ul>
<li>the two cells on the diagonal are the <strong>concordant pairs</strong>: 81 respondents answered “Yes” twice and 67 answered “No” twice, so these 148 respondents did not change their mind,</li>
<li>the two cells outside the diagonal are the <strong>discordant pairs</strong>: 11 respondents were in favor before the debate but against after, while 41 were against before but in favor after.</li>
</ul>
<p>Only the discordant pairs carry information about a change of opinion (a respondent who gave twice the same answer tells us nothing about the effect of the debate), and this is precisely what the McNemar’s test is built on.</p>
<p>The same information can be visualized with a simple barplot of the paired counts:</p>
<pre># install.packages(&quot;ggplot2&quot;)
library(ggplot2)

ggplot(dat) +
  aes(x = before, fill = after) +
  geom_bar(position = &quot;dodge&quot;) +
  labs(
    x = &quot;Opinion before the debate&quot;,
    y = &quot;Number of respondents&quot;,
    fill = &quot;Opinion after the debate&quot;
  )</pre>
<p><img src="https://i1.wp.com/statsandr.com/blog/mcnemars-test-in-r/index_files/figure-html/unnamed-chunk-4-1.png?w=450&#038;ssl=1" alt="" style="display: block; margin: auto;" data-recalc-dims="1" /></p>
<p>From the table and the plot, we see that the changes of opinion do not balance out: many more respondents switched from “No” to “Yes” than the opposite. The question is now whether this imbalance is large enough to be declared significant, or whether it could reasonably be explained by chance alone (that is, by sampling fluctuations).</p>
</div>
<div id="mcnemars-test" class="section level1">
<h1>McNemar’s test</h1>
<div id="aim-and-hypotheses" class="section level2">
<h2>Aim and hypotheses</h2>
<p>The McNemar’s test is used to compare two related proportions, so it allows to determine whether the proportion of subjects belonging to a given category changed between two dependent measurements.</p>
<p>The null and alternative hypotheses of the McNemar’s test are:</p>
<ul>
<li><span class="math inline">\(H_0\)</span>: the two related proportions are equal (marginal homogeneity, that is, there is no systematic change between the two measurements)</li>
<li><span class="math inline">\(H_1\)</span>: the two related proportions are different (there is a significant change between the two measurements)</li>
</ul>
<p>Since concordant pairs bring no information about a change, the test is based only on the two discordant cells. Denoting by <span class="math inline">\(b\)</span> the number of subjects who answered “Yes” then “No”, and by <span class="math inline">\(c\)</span> the number of subjects who answered “No” then “Yes”, the hypotheses can equivalently be written as:</p>
<ul>
<li><span class="math inline">\(H_0: p_b = p_c\)</span></li>
<li><span class="math inline">\(H_1: p_b \ne p_c\)</span></li>
</ul>
<p>where <span class="math inline">\(p_b\)</span> and <span class="math inline">\(p_c\)</span> are the probabilities of the two possible types of change. Under the null hypothesis, a change in one direction is as likely as a change in the other direction, so the test statistic</p>
<p><span class="math display">\[\chi^2 = \frac{(b - c)^2}{b + c}\]</span></p>
<p>follows a Chi-square distribution with 1 degree of freedom. By default, R applies a continuity correction (see more on this below), which replaces the numerator by <span class="math inline">\((|b - c| - 1)^2\)</span>.</p>
<p>In the context of our example, the McNemar’s test helps us to answer the following question: “Did the public debate significantly change the proportion of citizens in favor of the new policy?”.</p>
<p>Rejecting <span class="math inline">\(H_0\)</span> would mean that the proportion of citizens in favor of the policy is significantly different before and after the debate, so that the changes of opinion observed in our sample are unlikely to be due to chance only. On the contrary, not rejecting <span class="math inline">\(H_0\)</span> would mean that we do not have enough evidence to conclude that opinions changed: the switches observed in the two directions would then be compatible with random fluctuations.</p>
<p>Note that, as for many tests, the McNemar’s test does not indicate the <em>direction</em> of the change. The direction must be read from the contingency table or from the marginal proportions computed in the previous section.</p>
</div>
<div id="assumptions" class="section level2">
<h2>Assumptions</h2>
<p>For the results of the McNemar’s test to be valid, the following assumptions must be met:</p>
<ol style="list-style-type: decimal">
<li><strong>Paired measurements on a binary variable.</strong> The two measurements must be collected on the same subjects, or on matched pairs (twins, or patients matched on age and sex for instance), and the variable of interest must be qualitative with exactly two levels (“Yes”/“No”, success/failure, present/absent, etc.). If the two samples are independent instead of paired, use the <a href="https://statsandr.com/blog/chi-square-test-of-independence-in-r/" rel="nofollow" target="_blank">Chi-square test of independence</a>.</li>
<li><strong>Data organized in a 2 <span class="math inline">\(\times\)</span> 2 contingency table of the paired outcomes.</strong> Each subject contributes to one and only one cell of the table, so the sum of the four cells equals the number of subjects (200 in our case), and not twice this number.</li>
<li><strong>Pairs are independent of each other.</strong> Within a pair, the two measurements are of course dependent, and this is precisely the reason why we use this test. Between pairs, however, independence is required: one subject’s answers must not influence another subject’s answers. As for many statistical tests, this assumption is usually verified based on the design of the experiment rather than via a formal test. A random and representative <a href="https://statsandr.com/blog/what-is-the-difference-between-population-and-sample/" rel="nofollow" target="_blank">sample</a> of the <a href="https://statsandr.com/blog/what-is-the-difference-between-population-and-sample/" rel="nofollow" target="_blank">population</a> of interest is generally sufficient. In our example, respondents have been selected at random and answered the question individually, so we consider this assumption as met.</li>
<li><strong>Enough discordant pairs.</strong> The <span class="math inline">\(p\)</span>-value returned by <code>mcnemar.test()</code> is based on a Chi-square approximation, which is reliable only if the number of discordant pairs is large enough. A common rule of thumb is that <span class="math inline">\(b + c\)</span> should be at least 25. In our sample, <span class="math inline">\(b + c\)</span> = 52, so the approximation can be used safely.</li>
</ol>
<p>When the number of discordant pairs is small, it is preferable to use the exact version of the test, which is based on a binomial distribution instead of the Chi-square approximation. It boils down to testing whether, among the discordant pairs, changes in one direction are as frequent as changes in the other direction, so it can be performed in base R with the <code>binom.test()</code> function:</p>
<pre># exact version of the McNemar&#39;s test
binom.test(tab[1, 2], tab[1, 2] + tab[2, 1], p = 0.5)
## 
## 	Exact binomial test
## 
## data:  tab[1, 2] and tab[1, 2] + tab[2, 1]
## number of successes = 11, number of trials = 52, p-value = 3.589e-05
## alternative hypothesis: true probability of success is not equal to 0.5
## 95 percent confidence interval:
##  0.1106115 0.3470376
## sample estimates:
## probability of success 
##              0.2115385</pre>
<p>Note that the <code>{exact2x2}</code> package also provides a dedicated <code>mcnemar.exact()</code> function, which returns the same <span class="math inline">\(p\)</span>-value together with a confidence interval for the odds ratio.</p>
</div>
<div id="in-r" class="section level2">
<h2>In R</h2>
<p>The McNemar’s test can be performed in R with the <code>mcnemar.test()</code> function, applied on the contingency table of the paired outcomes:</p>
<pre>mcnemar.test(tab)
## 
## 	McNemar&#39;s Chi-squared test with continuity correction
## 
## data:  tab
## McNemar&#39;s chi-squared = 16.173, df = 1, p-value = 5.781e-05</pre>
<p>The test can also be applied directly on the two variables, without building the contingency table first (results are of course identical):</p>
<pre>mcnemar.test(dat$before, dat$after)
## 
## 	McNemar&#39;s Chi-squared test with continuity correction
## 
## data:  dat$before and dat$after
## McNemar&#39;s chi-squared = 16.173, df = 1, p-value = 5.781e-05</pre>
<p>The output shows:</p>
<ul>
<li>the title of the test, together with the mention that a continuity correction has been applied,</li>
<li>the data which have been used,</li>
<li>the test statistic (<code>McNemar's chi-squared</code>),</li>
<li>the degrees of freedom (always equal to 1 for a 2 <span class="math inline">\(\times\)</span> 2 table) and</li>
<li>the <span class="math inline">\(p\)</span>-value.</li>
</ul>
<p>As mentioned above, R applies a continuity correction by default. This correction makes the test slightly more conservative (that is, it gives a larger <span class="math inline">\(p\)</span>-value), and it can be removed thanks to the <code>correct = FALSE</code> argument:</p>
<pre>mcnemar.test(tab, correct = FALSE)
## 
## 	McNemar&#39;s Chi-squared test
## 
## data:  tab
## McNemar&#39;s chi-squared = 17.308, df = 1, p-value = 3.179e-05</pre>
<p>With 52 discordant pairs, both versions lead to the same conclusion. The correction really matters only when the number of discordant pairs is small, and in that case the exact version presented in the previous section is a better option anyway.</p>
<p>It is the <span class="math inline">\(p\)</span>-value which is of interest to conclude the test. If you are not familiar with <span class="math inline">\(p\)</span>-values, I invite you to read this <a href="https://statsandr.com/blog/student-s-t-test-in-r-and-by-hand-how-to-compare-two-groups-under-different-scenarios/#a-note-on-p-value-and-significance-level-alpha" rel="nofollow" target="_blank">section</a>.</p>
</div>
<div id="interpretations" class="section level2">
<h2>Interpretations</h2>
<p>Based on the McNemar’s test, we reject the null hypothesis and we conclude that the proportion of citizens in favor of the new policy is significantly different before and after the debate (<span class="math inline">\(p\)</span>-value < 0.001).</p>
<p><span class="math inline">\(\Rightarrow\)</span> In our context, rejecting the null hypothesis means that the debate is associated with a significant change of opinion. Looking at the direction of this change, the proportion of citizens in favor of the policy increased from 46% before the debate to 61% after the debate.</p>
<p>(<em>For the sake of illustration</em>, if the <span class="math inline">\(p\)</span>-value had been larger than the significance level <span class="math inline">\(\alpha = 0.05\)</span>: we could not have rejected the null hypothesis, so we could not have concluded that the proportion of citizens in favor of the policy was different before and after the debate.)</p>
<p>Contrary to the tests comparing three groups or more, no post-hoc test is required after a significant McNemar’s test: only two related measurements are compared, so a significant result already tells us which two proportions differ. Post-hoc comparisons become relevant again with more than two related measurements, in which case you should turn to the Cochran’s Q test.</p>
</div>
</div>
<div id="summary" class="section level1">
<h1>Summary</h1>
<p>In this article, we reviewed the aim and the hypotheses of the McNemar’s test, which is used to compare two related proportions measured on the same subjects, together with its underlying assumptions (paired measurements on a binary variable, independence between pairs and a sufficient number of discordant pairs). We then showed how to perform it in R with the <code>mcnemar.test()</code> function, applied either on the 2 <span class="math inline">\(\times\)</span> 2 contingency table of the paired answers or directly on the two variables, and how to interpret its results by comparing the <span class="math inline">\(p\)</span>-value with the significance level <span class="math inline">\(\alpha\)</span>. Remember that it is the special case of the Cochran’s Q test for exactly two related measurements, and that with independent samples the <a href="https://statsandr.com/blog/chi-square-test-of-independence-in-r/" rel="nofollow" target="_blank">Chi-square test of independence</a> should be preferred.</p>
<p>Thanks for reading.</p>
<p>I hope this article helped you to understand the McNemar’s test and how to perform it in R.</p>
<p>As always, if you have a question or a suggestion related to the topic covered in this article, please add it as a comment so other readers can benefit from the discussion.</p>
</div>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://statsandr.com/blog/mcnemars-test-in-r/"> R on Stats and R</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/mcnemars-test-in-r/">McNemar’s test in R</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403211</post-id>	</item>
		<item>
		<title>A Summer, Explained with R</title>
		<link>https://www.r-bloggers.com/2026/08/a-summer-explained-with-r/</link>
		
		<dc:creator><![CDATA[The Jumping Rivers Blog]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 23:59:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.jumpingrivers.com/blog/a-summer-explained-with-r/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>It’s 2:47pm, the meeting has been running for 38 minutes, and someone<br />
has just said, “Can everyone see my screen?”<br />
You’re trying to concentrate, your laptop fan sounds like it’s preparing<br />
for take-off, and somewhere outside the temperature has climbed to 34°C.<br />
You’ve already relocated once today ...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/a-summer-explained-with-r/">A Summer, Explained with R</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://www.jumpingrivers.com/blog/a-summer-explained-with-r/"> The Jumping Rivers Blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<p>
<a href = "https://www.jumpingrivers.com/blog/a-summer-explained-with-r/">
<img src="https://www.jumpingrivers.com/blog/a-summer-explained-with-r/" width="400" style="width:400px" class="image-center" style="display: block; margin: auto;" />
</a>
</p>
<p>It’s 2:47pm, the meeting has been running for 38 minutes, and someone
has just said, “Can everyone see my screen?”</p>
<p>You’re trying to concentrate, your laptop fan sounds like it’s preparing
for take-off, and somewhere outside the temperature has climbed to 34°C.
You’ve already relocated once today in search of a patch of shade under
a tree with a half-decent breeze, laptop balanced on your knees, one eye
on the battery icon. Meanwhile, your calendar still has three more
meetings in it.</p>
<p>Summer 2026 has a funny way of making us notice things like this. It’s
shaping up to be the UK’s warmest summer on record, with several
heatwaves already behind us. Rather than spending another afternoon
staring at a spreadsheet, why not give R something more interesting to
do?</p>
<h2 id="-lets-talk-about-the-weather"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2600.png" alt="☀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Let’s talk about the weather</h2>
<p>This year, the UK weather hasn’t so much been unpredictable as
relentlessly, record-breakingly hot. Which, if you’re a data person, is
still a great excuse to explore.</p>
<aside class="advert">
<p>
Whether you want to start from scratch, or improve your skills, <a href="https://www.jumpingrivers.com/training/?utm_source=blog&#038;utm_medium=banner&#038;utm_campaign=2026-a-summer-explained-with-r" rel="nofollow" target="_blank">Jumping Rivers has a training course for you</a>.
</p>
</aside>
<p>For example, imagine we have daily high temperatures for London,
Manchester and Newcastle across this summer’s headline heatwaves. We
could use R to quickly compare them, find the warmest days, and create a
chart showing just how much hotter the south has been running than the
north.</p>
<pre>library(dplyr)

# Illustrative daily highs (°C) across three of this summer&#39;s heatwave events
summer_weather &lt;- tribble(
 ~date, ~city, ~temperature,
 &quot;2026-05-24&quot;, &quot;London&quot;, 32.3,
 &quot;2026-05-24&quot;, &quot;Manchester&quot;, 25.8,
 &quot;2026-05-24&quot;, &quot;Newcastle&quot;, 21.9,
 &quot;2026-05-25&quot;, &quot;London&quot;, 34.8,
 &quot;2026-05-25&quot;, &quot;Manchester&quot;, 26.9,
 &quot;2026-05-25&quot;, &quot;Newcastle&quot;, 22.6,
 &quot;2026-05-26&quot;, &quot;London&quot;, 35.1,
 &quot;2026-05-26&quot;, &quot;Manchester&quot;, 27.4,
 &quot;2026-05-26&quot;, &quot;Newcastle&quot;, 23.1,
 &quot;2026-07-28&quot;, &quot;London&quot;, 29.6,
 &quot;2026-07-28&quot;, &quot;Manchester&quot;, 26.2,
 &quot;2026-07-28&quot;, &quot;Newcastle&quot;, 22.4,
 &quot;2026-07-29&quot;, &quot;London&quot;, 34.2,
 &quot;2026-07-29&quot;, &quot;Manchester&quot;, 28.9,
 &quot;2026-07-29&quot;, &quot;Newcastle&quot;, 23.8,
 &quot;2026-08-13&quot;, &quot;London&quot;, 37.0,
 &quot;2026-08-13&quot;, &quot;Manchester&quot;, 32.1,
 &quot;2026-08-13&quot;, &quot;Newcastle&quot;, 25.6,
 &quot;2026-08-14&quot;, &quot;London&quot;, 38.1,
 &quot;2026-08-14&quot;, &quot;Manchester&quot;, 32.8,
 &quot;2026-08-14&quot;, &quot;Newcastle&quot;, 26.3
) |&gt;
 mutate(date = as.Date(date))

library(ggplot2)

ggplot(summer_weather, aes(x = date, y = temperature, colour = city)) +
 geom_line() +
 geom_point() +
 labs(
 title = &quot;How warm has UK summer 2026 been?&quot;,
 subtitle = &quot;Daily highs across three of this summer&#39;s headline heatwaves&quot;,
 x = NULL,
 y = &quot;Temperature (°C)&quot;
 ) +
 theme_minimal()
</pre><img src="https://i2.wp.com/www.jumpingrivers.com/blog/a-summer-explained-with-r/chart.png?w=450&#038;ssl=1" alt="Line chart of illustrative daily high temperatures for London, Manchester and Newcastle across three 2026 heatwave events, showing London consistently warmest and the north-south gap widening in August." style="display: block; margin: auto;" data-recalc-dims="1" />
<p>Suddenly, that spreadsheet of numbers becomes something you can actually
explore. You can see, at a glance, just how much bigger the north-south
gap gets once a heatwave really takes hold &#8211; London hit 38.1°C in
mid-August while Newcastle stayed at 26.3°C on the same day.</p>
<p>And you don’t have to stop at temperature. You could look at rainfall
(or the lack of it), reservoir levels, ice cream sales, train delays… or
even investigate whether your team’s productivity mysteriously drops
once the office hits 25°C.</p>
<h2 id="-a-little-summer-upskilling"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f9e0.png" alt="🧠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> A little summer upskilling</h2>
<p>This is also where R becomes particularly useful.</p>
<p>Learning R isn’t just about knowing how to write code. It’s about
becoming more comfortable taking a question, finding the right data,
exploring it and turning the results into something that other people
can understand.</p>
<p>Maybe you’ve been using R for a while but keep thinking, “There must be
a better way to do this.”</p>
<p>There probably is.</p>
<p>Maybe you’re comfortable with the basics but want to get better at data
visualisation, modelling, reproducible reporting or working with larger
datasets.</p>
<p>That’s exactly where structured training can help.</p>
<p>At Jumping Rivers, we run practical training across R, Python, SQL,
Quarto, Shiny and the wider Posit ecosystem. Our courses are designed
around real-world work, so you can take what you learn straight back to
your desk — preferably somewhere with a fan, or this year, maybe even
air conditioning.</p>
<h2 id="-make-the-summer-count"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f366.png" alt="🍦" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Make the summer count</h2>
<p>The summer break can be a good time to step back from the usual routine
and invest in skills that make the rest of the year easier.</p>
<p>You could spend a few hours learning a better way to manipulate data,
finally get to grips with ggplot2, explore Quarto, or start building
your first Shiny application.</p>
<p>And if you’re not sure what training would actually be useful for you or
your team, that’s where we can help too.</p>
<p>We can look at the tools you’re currently using, the skills your team
already has and where the gaps are, then recommend a training path that
makes sense.</p>
<p>No complicated spreadsheets required.</p>
<p>Although, if you do have one &#8211; perhaps tracking this summer’s
heatwaves &#8211; we’re always happy to help you turn it into something much
more interesting.</p>
<h2 id="-ready-to-make-your-next-data-project-a-little-more-enjoyable"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2600.png" alt="☀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Ready to make your next data project a little more enjoyable?</h2>
<p>Explore our <a href="https://www.jumpingrivers.com/training/public/" rel="nofollow" target="_blank">public training
courses</a> or <a href="https://www.jumpingrivers.com/contact/" rel="nofollow" target="_blank">get in
touch with the Jumping Rivers
team</a> to talk about what would
work best for you.</p>
<p>
For updates and revisions to this article, see the <a href = "https://www.jumpingrivers.com/blog/a-summer-explained-with-r/">original post</a>
</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://www.jumpingrivers.com/blog/a-summer-explained-with-r/"> The Jumping Rivers Blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/a-summer-explained-with-r/">A Summer, Explained with R</a>]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">403177</post-id>	</item>
		<item>
		<title>Take Your R Projects on the Road:  Using R on Your Raspberry Pi, Android Device, and iPhone</title>
		<link>https://www.r-bloggers.com/2026/08/take-your-r-projects-on-the-road-using-r-on-your-raspberry-pi-android-device-and-iphone/</link>
		
		<dc:creator><![CDATA[dmwiig]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 20:11:56 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://dmwiig.net/?p=518</guid>

					<description><![CDATA[<p>This post explores using R and RStudio with Raspberry Pi, Android OS and iPhone.  A simple R programming example is provided.</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/take-your-r-projects-on-the-road-using-r-on-your-raspberry-pi-android-device-and-iphone/">Take Your R Projects on the Road:  Using R on Your Raspberry Pi, Android Device, and iPhone</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://dmwiig.net/2026/08/18/take-your-r-projects-on-the-road-using-r-on-your-raspberry-pi-adroid-device-and-iphone/"> r – R Statistics and Programming</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<p class="wp-block-paragraph">I continue to complete work on my next post on using the R <em>wordcloud</em> package.  As I normally do programming and wrting with my Lenovo desktop computer, I decided to experiment with installing R-base and RStudio on my Raspberry Pi Model 3B and tablet computer for those occasions when I desire to work while traveling.    The Raspberry Pi is running the latest version of Debian Trixie along with the Raspberry Pi desktop. The tablet is using the latest version of Android 16. My first observation relates to the availability of R for these platforms.</p>



<p class="wp-block-paragraph"><em>I. Installing R-base and RStudio on Raspberry Pi3/B,4,5</em></p>



<p class="wp-block-paragraph">R is available for a variety of UNIX, Windows, and MacOS systems. If you are running R on Windows, you are familiar with the 32- and 64-bit versions available for download and installation via an executable loader. While the R-base console has been available for the RPi platform, it has only been recently that the RStudio-server has been available for the ARM64 processor used in the RPi 3A/B, 4, and 5 models. I am currently using Debian Trixie 64-bit on my RPi 3/B. The R-base package is now available in Debian repositories so R can be installed via the RPi desktop menu rather than downloading binary builds or executable files.</p>



<p class="wp-block-paragraph">For an RPi 3B or higher I would recommend the following:<br>-Make sure your microSD card is large enough. I am using a 32 GB card.<br>-Make sure your OS is up to date. Use the command line utility to run the following commands:<br><strong>       sudo apt update (respond to prompts that follow)</strong><br><strong>       sudo apt full-upgrade (respond the prompts)</strong><br>Depending on the model of RPi you are using, the memory card size and Debian version you are using this update could take quite some time.</p>



<p class="wp-block-paragraph">Once the update is completed use the desktop menu to access the add/delete software option, search for the R package using r-base as the keyword and click on the appropriate icon to start the installation. When the installation is complete you should see the R icon in the desktop dropdown menu under the Programming or Science (or both) headings. The R-base console can now be run by clicking the menu icon, and R is now available for access by RStudio-server if it is installed.</p>



<p class="wp-block-paragraph">Because the RPi uses an ARM processor, RStudio itself cannot be installed, but RStudio-server has been successfully ported to the platform. Additional information on R downloads can be found at the Posit web site RStudio IDE User Guide RStudio User Guide, and at the link RStudio Latest Builds. If you wish to install the RStudio-server from your RPi command line utility there are several steps, but the result is a working web-based interface with full RStudio-server build. Follow the steps listed below.<br>1. When installing new software run an update using:<br>     s<strong>udo apt update</strong><br>2. The port of RStudio-server we are installing was designed for the Ubuntu OS so install dependencies needed for Debian using:<br><strong>     sudo apt install gdebi-core libssl-dev libclang-dev</strong><br>3. Get the build from the Posit Daily Builds library using </p>



<p class="wp-block-paragraph"><strong>wget <a href="https://dl.dailies.rstudio.com/server/jammy/arm64/rstudio-%C2%A0" rel="nofollow" target="_blank">https://dl.dailies.rstudio.com/server/jammy/arm64/rstudio- </a>      server-2026.06.0-242-arm64.deb</strong><br>4. Install the application using:<br><strong> sudo gdebi rstudio-server-2026.06.0-242-arm64.deb</strong><br>5. When the installation is complete use the system service command to start RStudio-server with:<br><strong>     sudo systemctl start rstudio-server (for the current  bootup)</strong>                                             and/or<br><strong>     sudo systemctl enable rstudio-server (start at all bootups)</strong><br>6. Open the Chromium or Firefox web browser from the desktop menu and access the RStudio-server by entering the URL:<br><strong>      <a href="http://<rpi/" rel="nofollow" target="_blank">http://<RPi</a> IP address on your network>:8787</strong><br>In my case I would enter <a href="http://192.168.4.115:8787/" rel="nofollow" target="_blank">http://192.168.4.115:8787</a><br>The screenshot shown below shows RStudio with the code from this article and the resulting output.</p>



<p class="wp-block-paragraph"><em><Screenshot can be viewed in the PDF version of this document></em></p>



<p class="wp-block-paragraph"><em>II. Using R on an Android Device</em></p>



<p class="wp-block-paragraph">R and RStudio will not port directly to an Android based OS, but there are a few applications that will work with varying degrees of utility. I have a tablet that runs Android 16 and am using a free application, Rlytic,. Once installed from the Play Store users sign up with a username and password. When the program starts, a code entry console is displayed. Your code can be entered directly using the on-screen keyboard provided or can be loaded from your device file storage or cloud storage. The interface is easy to use. I have included a simple program example and some screenshots below.<br>Rlytic is free to use but is restricted to having only 2 programs active at a time. An unlimited version is available for purchase. I might also add that at the time of this writing Rlytic is running on R-base v.3 so users may run into some problems with more complex projects.</p>



<p class="wp-block-paragraph"><em>III. Using R on an iPhone</em></p>



<p class="wp-block-paragraph">I currently use an iPhone 12 and was curious about any R applications that would work with it. I found an application called WebR which combines R-base 4.xxx with a text editor and browser interface. According to the program s author the application was designed for use by students in a classroom setting when learning statistics and/or R programming. It provides a highly mobile platform for Running R programs and quickly generates both text and graphics output. Once again, I will leave it to readers to engage the application s learning curve and will provide a simple example and screenshots below. The software is free and is available in the iPhone App Store.</p>



<p class="wp-block-paragraph">I<em>V. Sample Program: Raspberry Pi</em></p>



<p class="wp-block-paragraph">The following code is a simple example of how R can be used to demonstrate the Central Limit Theorem in sampling from a population. The code uses the R-base rnorm function to generate randomly selected samples from a normally distributed population of values with a given population mean and standard deviation, finds the mean of each sample generated and graphs the sampling distribution. The code is shown below.</p>



<p class="wp-block-paragraph"><strong>#population; sd=10; mean=65</strong><br><strong>#generate 25 samples of 25 observations </strong><br><strong>#calculate sample mean of each sample and plot distribution</strong><br><strong>###################################################</strong><br><strong>#code to generate samples and display all sample means</strong><br><strong>###################################################</strong><br><strong>Samples <- replicate(25, rnorm(25, mean=65, sd=10))</strong><br><strong>Samples #show the samples generated</strong><br><strong>##################################################</strong><br><strong>#code to calculate and display mean of each column of sample means</strong><br><strong>#################################################</strong><br><strong>SampleMeans <- colMeans(Samples)</strong><br><strong>SampleMeans #show the means of the samples generated</strong><br><strong>####################################################</strong><br><strong>#code to plot means of the sampling distribution</strong><br><strong>#####################################################</strong><br><strong>plot(density(SampleMeans),</strong><br><strong>main = “Density of Sample Means”,</strong><br><strong>xlab = “Sample Mean”)</strong><br>The plot of the distribution of the sample means is shown below.</p>



<p class="wp-block-paragraph"><em><Screenshot can be viewed in the PDF version of this document></em></p>



<p class="wp-block-paragraph"><em>V. Sample Program: Rlytic</em><br>Here is the same code with the plot of the results for the Rlytic app on my Android 16 tablet. For brevity I have not included all the hashtag dialog from the RPi example. The screenshot and plot are shown.<br><strong>#population; sd=10; mean=65</strong><br><strong>#generate 25 samples of 25 observations </strong><br><strong>#calculate sample mean of each sample and plot distribution</strong><br><strong>Samples <- replicate(25, rnorm(25, mean=65, sd=10))</strong><br><strong>#Samples</strong><br><strong>SampleMeans <- colMeans(Samples)</strong><br><strong>#SampleMeans</strong><br><strong>plot(density(SampleMeans),</strong><br><strong>main = “Density”,</strong><br><strong>xlab = Mean”)</strong><br>The Rlytic Screen:</p>



<p class="wp-block-paragraph"><em><Screenshot can be viewed in the PDF version of this document></em></p>



<p class="wp-block-paragraph">The Rlytic Plot: (Note Rlytic graphs are PDF format)</p>



<p class="wp-block-paragraph"><em><Screenshot can be viewed in the PDF version of this document></em></p>



<p class="wp-block-paragraph"><em>VI. Sample Program: WebR for iPhone</em><br>Here is a slightly modified version of the random sampling code entered into WebR on my iPhone 12.<br><strong>x=rnorm(25, mean=65, sd=10)</strong><br><strong>plot(density(x))</strong><br>Shown below is the resulting output. As in previous examples I did not print the output showing the randomly generated individual means.</p>



<p class="wp-block-paragraph"><em><Screenshot can be viewed in the PDF version of this document></em></p>



<p class="wp-block-paragraph">I am still working on the next part of my tutorial on using wordcloud and related packages for the analysis of large, complex text files. Please look for my next post in the not-too-distant future.<br>D.M. Wiig<br><em>R Statistics and Programming</em><br><a href="https://dmwiig.net/" rel="nofollow" target="_blank">https://dmwiig.net</a></p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://dmwiig.net/2026/08/18/take-your-r-projects-on-the-road-using-r-on-your-raspberry-pi-adroid-device-and-iphone/"> r – R Statistics and Programming</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/take-your-r-projects-on-the-road-using-r-on-your-raspberry-pi-android-device-and-iphone/">Take Your R Projects on the Road:  Using R on Your Raspberry Pi, Android Device, and iPhone</a>]]></content:encoded>
					
		
		<enclosure url="https://dmwiig.net/wp-content/uploads/2026/08/20260817_11h30m04s_grim.png" length="0" type="" />
<enclosure url="https://0.gravatar.com/avatar/05f837b5b91c1040997a17feefab84a805cfdb5570a50b6dd62e86af274e0460?s=96&#038;d=identicon&#038;r=G" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403192</post-id>	</item>
		<item>
		<title>Breaking the Python Barrier: Building a Pure R-Native DeepAR Engine with LibTorch</title>
		<link>https://www.r-bloggers.com/2026/08/breaking-the-python-barrier-building-a-pure-r-native-deepar-engine-with-libtorch/</link>
		
		<dc:creator><![CDATA[Selcuk Disci]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 13:10:49 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://datageeek.com/?p=12478</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Deep learning for time series forecasting in R has historically faced a major architectural hurdle: Python overhead. Frameworks like modeltime.gluonts provide interface wrappers around AWS GluonTS, but they rely on a complex execution chain passing through reticulate, virtual environments, Python serialization, and MXNet/PyTorch backends. To overcome the performance ...</div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/breaking-the-python-barrier-building-a-pure-r-native-deepar-engine-with-libtorch/">Breaking the Python Barrier: Building a Pure R-Native DeepAR Engine with LibTorch</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://datageeek.com/2026/08/18/breaking-the-python-barrier-building-a-pure-r-native-deepar-engine-with-libtorch/"> DataGeeek</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<p class="wp-block-paragraph">Deep learning for time series forecasting in R has historically faced a major architectural hurdle: <strong>Python overhead</strong>. Frameworks like <code>modeltime.gluonts</code> provide interface wrappers around AWS GluonTS, but they rely on a complex execution chain passing through <code>reticulate</code>, virtual environments, Python serialization, and MXNet/PyTorch backends.</p>



<p class="wp-block-paragraph">To overcome the performance bottlenecks and dependency friction of cross-language bridging, we engineered a <strong>pure R-native DeepAR forecasting engine</strong>. Powered by the C++ <code>LibTorch</code> backend via R’s <a href="https://torch.mlverse.org/" rel="nofollow" target="_blank"><code>torch</code> </a>package, this architecture offers lightweight, in-memory execution without any Python or <code>reticulate</code> dependencies.</p>



<h2 class="wp-block-heading">Architectural Comparison: Modeltime/GluonTS vs. Native R Torch</h2>



<p class="wp-block-paragraph">The architectural difference between traditional wrappers and our native C++ LibTorch binding lies in data marshalling and execution depth:</p>



<figure data-wp-context="{"imageId":"6a845a0d2a104"}" data-wp-interactive="core/image" data-wp-key="6a845a0d2a104" class="wp-block-image size-large wp-lightbox-container"><img loading="lazy" data-attachment-id="12485" data-permalink="https://datageeek.com/2026/08/18/breaking-the-python-barrier-building-a-pure-r-native-deepar-engine-with-libtorch/image-134/" data-orig-file="https://datageeek.com/wp-content/uploads/2026/08/image.png" data-orig-size="949,638" data-comments-opened="1" data-image-meta="{"aperture":"0","credit":"","camera":"","caption":"","created_timestamp":"0","copyright":"","focal_length":"0","iso":"0","shutter_speed":"0","title":"","orientation":"0","alt":""}" data-image-title="image" data-image-description="" data-image-caption="" data-large-file="https://i1.wp.com/datageeek.com/wp-content/uploads/2026/08/image.png?w=450&#038;ssl=1" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://i1.wp.com/datageeek.com/wp-content/uploads/2026/08/image.png?w=450&#038;ssl=1" alt="" class="wp-image-12485" srcset_temp="https://datageeek.com/wp-content/uploads/2026/08/image.png 949w, https://datageeek.com/wp-content/uploads/2026/08/image.png?w=150 150w, https://datageeek.com/wp-content/uploads/2026/08/image.png?w=300 300w, https://datageeek.com/wp-content/uploads/2026/08/image.png?w=768 768w" sizes="(max-width: 949px) 100vw, 949px" data-recalc-dims="1" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 class="wp-block-heading">Deep Dive into the Code Architecture</h2>



<p class="wp-block-paragraph">Our R implementation mirrors the probabilistic depth of DeepAR while maintaining computational stability and clean visual interactivity.</p>



<h3 class="wp-block-heading">1. Bounded Student-t Distribution Head</h3>



<p class="wp-block-paragraph">Financial time series, such as the <strong>SOXX ETF</strong>, exhibit heavy-tailed return distributions (“fat tails”) and sudden volatility shocks. Gaussian models often understate extreme risks or produce over-reactive prediction bands.</p>



<p class="wp-block-paragraph">We implement a <strong>3-head architecture</strong> off the LSTM hidden state:</p>



<ul class="wp-block-list">
<li><strong>Location parameter (μ):</strong> Unconstrained linear output layer.</li>



<li><strong>Scale parameter (σ):</strong> Softplus activation layer with numerical stability offset.</li>



<li><strong>Degrees of freedom parameter (ν):</strong> Bounded dynamically between 4.0 and 30.0 using a scaled sigmoid:</li>
</ul>



<div class="wp-block-math has-medium-font-size"><math display="block"><semantics><mrow><mi>ν</mi><mo>=</mo><mn>4.0</mn><mo>+</mo><mn>26.0</mn><mo>⋅</mo><mtext>sigmoid</mtext><mo form="prefix" stretchy="false">(</mo><mi>z</mi><mo form="postfix" stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\nu = 4.0 + 26.0 \cdot \text{sigmoid}(z)</annotation></semantics></math></div>



<p class="wp-block-paragraph">Bounding ν ≥ 4.0 guarantees mathematically finite variance, preventing Monte Carlo variance explosion over multi-step autoregressive horizons.</p>



<h3 class="wp-block-heading">2. Variance-Controlled Stochastic Monte Carlo Sampling</h3>



<p class="wp-block-paragraph">During the 10-day forecast horizon, we generate 100 autoregressive simulation paths. To align Monte Carlo variance with predicted σ, we scale Student-t samples by the theoretical variance factor:</p>



<p class="has-text-align-center has-medium-font-size wp-block-paragraph"><math data-latex="\text{scale\_factor} = \sqrt{\frac{\nu - 2}{\nu}}"><semantics><mrow><mtext>scale_factor</mtext><mo>=</mo><msqrt><mfrac><mrow><mi>ν</mi><mo>−</mo><mn>2</mn></mrow><mi>ν</mi></mfrac></msqrt></mrow><annotation encoding="application/x-tex">\text{scale\_factor} = \sqrt{\frac{\nu – 2}{\nu}}</annotation></semantics></math></p>



<p class="has-text-align-center has-medium-font-size wp-block-paragraph"><math data-latex="y_{t} = \mu + \sigma \cdot \text{scale\_factor} \cdot t_{\nu}"><semantics><mrow><msub><mi>y</mi><mi>t</mi></msub><mo>=</mo><mi>μ</mi><mo>+</mo><mi>σ</mi><mo>⋅</mo><mtext>scale_factor</mtext><mo>⋅</mo><msub><mi>t</mi><mi>ν</mi></msub></mrow><annotation encoding="application/x-tex">y_{t} = \mu + \sigma \cdot \text{scale\_factor} \cdot t_{\nu}</annotation></semantics></math></p>



<p class="wp-block-paragraph">This ensures the trajectory bounds remain stable across multi-step autoregressive rollouts.</p>



<h3 class="wp-block-heading">3. Granular Interactive Plotly Visualization</h3>



<p class="wp-block-paragraph">The frontend layer leverages <code>ggplot2</code>, <code>ggtext</code>, and <code>plotly</code> to deliver clean UI/UX interactivity:</p>



<ul class="wp-block-list">
<li><strong>Embedded HTML Titles:</strong> Eliminates redundant legend boxes by color-coding series names directly inside the Markdown title using <code>ggtext::element_markdown</code>.</li>



<li><strong>Invisible Boundary Anchors:</strong> Invisible hover points (<code>alpha = 0</code>) are placed along the 95% confidence bounds (<code>conf_hi</code> and <code>conf_lo</code>). Users can inspect exact upper/lower boundary prices dynamically without cluttering the plot with extra lines.</li>
</ul>



<h2 class="wp-block-heading">Complete R Script</h2>



<p class="wp-block-paragraph"></p>


<pre>
# ==============================================================================
# TITLE: Pure R-Native Torch DeepAR - Bounded Student-t Distribution Engine
# PATH: tool_nodes/forecasting/engine/evaluate_torch_deepar_student_t_bounded.R
# DEPLOYMENT TARGET: Native R Pipeline (Zero Python / Zero Reticulate Dependency)
# All code descriptions and labels are systematically maintained in English.
# ==============================================================================

if (!require(&quot;pacman&quot;)) install.packages(&quot;pacman&quot;)
pacman::p_load(tidyquant, tidyverse, timetk, torch, plotly, yardstick)

# 1. Fetch & Prepare Data from Yahoo Finance
df_dl &lt;- tq_get(&quot;SOXX&quot;) %&gt;%
  select(date, close) %&gt;%
  filter(date &gt;= last(date) - months(12)) %&gt;%
  drop_na()

# Data Normalization Parameters
mean_close &lt;- mean(df_dl$close)
sd_close   &lt;- sd(df_dl$close)
df_dl      &lt;- df_dl %&gt;% mutate(close_scaled = (close - mean_close) / sd_close)

# Configuration Parameters
lookback_length   &lt;- 20
prediction_length &lt;- 10
num_paths         &lt;- 100

train_data &lt;- head(df_dl, nrow(df_dl) - prediction_length)
test_data  &lt;- tail(df_dl, prediction_length)

# 2. Sequence Generator
create_sequences &lt;- function(data_vector, lookback) {
  num_samples &lt;- length(data_vector) - lookback
  x_mat &lt;- matrix(0, nrow = num_samples, ncol = lookback)
  y_mat &lt;- matrix(0, nrow = num_samples, ncol = 1)
  
  for (i in 1:num_samples) {
    x_mat[i, ]  &lt;- data_vector[i:(i + lookback - 1)]
    y_mat[i, 1] &lt;- data_vector[i + lookback]
  }
  
  list(
    x = torch_tensor(x_mat, dtype = torch_float())$unsqueeze(3),
    y = torch_tensor(y_mat, dtype = torch_float())
  )
}

seqs &lt;- create_sequences(train_data$close_scaled, lookback_length)

# 3. Native Torch DeepAR Architecture with Bounded Student-t Head
deepar_student_net &lt;- nn_module(
  &quot;DeepARStudentNetBounded&quot;,
  initialize = function(input_size = 1, hidden_size = 32, num_layers = 2) {
    self$lstm     &lt;- nn_lstm(input_size = input_size, hidden_size = hidden_size, 
                             num_layers = num_layers, batch_first = TRUE)
    self$fc_mu    &lt;- nn_linear(hidden_size, 1)
    self$fc_sigma &lt;- nn_linear(hidden_size, 1)
    self$fc_v     &lt;- nn_linear(hidden_size, 1)
  },
  forward = function(x) {
    out &lt;- self$lstm(x)
    last_hidden &lt;- out[[1]][, dim(out[[1]])[2], ]
    
    mu    &lt;- self$fc_mu(last_hidden)
    sigma &lt;- nnf_softplus(self$fc_sigma(last_hidden)) + 1e-4
    
    # Bound degrees of freedom v between 4.0 and 30.0 to prevent explosive tails
    v     &lt;- 4.0 + 26.0 * torch_sigmoid(self$fc_v(last_hidden))
    
    list(mu = mu, sigma = sigma, v = v)
  }
)

model     &lt;- deepar_student_net()
optimizer &lt;- optim_adam(model$parameters, lr = 0.003)

# Stable Student-t Negative Log-Likelihood Loss
student_t_nll_loss &lt;- function(mu, sigma, v, y) {
  term1 &lt;- torch_lgamma((v + 1) / 2)
  term2 &lt;- torch_lgamma(v / 2)
  term3 &lt;- 0.5 * torch_log(v * pi)
  term4 &lt;- torch_log(sigma)
  
  residual &lt;- (y - mu) / sigma
  term5 &lt;- ((v + 1) / 2) * torch_log(1 + (residual$pow(2) / v))
  
  - (term1 - term2 - term3 - term4 - term5)
}

# 4. Training Loop
model$train()
for (epoch in 1:40) {
  optimizer$zero_grad()
  preds &lt;- model(seqs$x)
  loss  &lt;- student_t_nll_loss(preds$mu, preds$sigma, preds$v, seqs$y)$mean()
  loss$backward()
  
  # Gradient clipping for numerical stability
  nn_utils_clip_grad_norm_(model$parameters, max_norm = 1.0)
  optimizer$step()
}

# 5. Stochastic Monte Carlo Trajectory Sampling (Variance Variance-Controlled)
model$eval()
price_paths &lt;- matrix(0, nrow = num_paths, ncol = prediction_length)
initial_input_seq &lt;- tail(train_data$close_scaled, lookback_length)

with_no_grad({
  for (s in 1:num_paths) {
    curr_seq &lt;- initial_input_seq
    
    for (t in 1:prediction_length) {
      curr_tensor &lt;- torch_tensor(matrix(curr_seq, nrow = 1), dtype = torch_float())$unsqueeze(3)
      pred &lt;- model(curr_tensor)
      
      mu    &lt;- as.numeric(pred$mu)
      sigma &lt;- as.numeric(pred$sigma)
      v_val &lt;- as.numeric(pred$v)
      
      # Scaled Student-t sampling to strictly align variance with sigma
      scale_factor &lt;- sqrt((v_val - 2) / v_val)
      sampled_scaled &lt;- mu + sigma * scale_factor * rt(1, df = v_val)
      
      price_paths[s, t] &lt;- sampled_scaled * sd_close + mean_close
      
      # Autoregressive slide
      curr_seq &lt;- c(curr_seq[-1], sampled_scaled)
    }
  }
})

# 6. Extract Quantiles &#038; Prepare Tidy Evaluation Data Frame
predicted_prices &lt;- colMeans(price_paths)
lower_bound      &lt;- apply(price_paths, 2, quantile, probs = 0.025)
upper_bound      &lt;- apply(price_paths, 2, quantile, probs = 0.975)

df_eval &lt;- tibble(
  date     = test_data$date,
  actual   = test_data$close,
  pred     = predicted_prices,
  conf_lo  = lower_bound,
  conf_hi  = upper_bound
)

# 7. Tidymodels / Yardstick Metric Engine
eval_metrics &lt;- metric_set(mape, rmse, rsq)

metrics_summary &lt;- df_eval %&gt;%
  eval_metrics(truth = actual, estimate = pred) %&gt;%
  select(.metric, .estimate) %&gt;%
  rename(Metric = .metric, Value = .estimate)

print(metrics_summary)

mape_val &lt;- metrics_summary %&gt;% 
  filter(Metric == &quot;mape&quot;) %&gt;% 
  pull(Value)


# 8. Modern Interactive Plotly Visualization (Clean Lines & Clear Ribbon)

if (!require(&quot;pacman&quot;)) install.packages(&quot;pacman&quot;)
pacman::p_load(tidyquant, tidyverse, plotly, scales, glue, ggtext)

# 1. Prepare Dedicated Hover Text Layers
df_plot_actual &lt;- df_eval %&gt;% 
  select(date, actual) %&gt;% 
  mutate(text_actual = glue::glue(&quot;&lt;b&gt;Actual Price:&lt;/b&gt; ${round(actual, 2)}\n&lt;b&gt;Date:&lt;/b&gt; {format(date, &#039;%b %d, %Y&#039;)}&quot;))

df_plot_pred &lt;- df_eval %&gt;% 
  select(date, pred) %&gt;% 
  mutate(text_pred = glue::glue(&quot;&lt;b&gt;DeepAR Pred:&lt;/b&gt; ${round(pred, 2)}\n&lt;b&gt;Date:&lt;/b&gt; {format(date, &#039;%b %d, %Y&#039;)}&quot;))

df_plot_hi &lt;- df_eval %&gt;% 
  select(date, conf_hi) %&gt;% 
  mutate(text_hi = glue::glue(&quot;&lt;b&gt;95% Upper Bound:&lt;/b&gt; ${round(conf_hi, 2)}\n&lt;b&gt;Date:&lt;/b&gt; {format(date, &#039;%b %d, %Y&#039;)}&quot;))

df_plot_lo &lt;- df_eval %&gt;% 
  select(date, conf_lo) %&gt;% 
  mutate(text_lo = glue::glue(&quot;&lt;b&gt;95% Lower Bound:&lt;/b&gt; ${round(conf_lo, 2)}\n&lt;b&gt;Date:&lt;/b&gt; {format(date, &#039;%b %d, %Y&#039;)}&quot;))

# 2. Build GGPlot Spec with Invisible Boundary Anchors
p &lt;- ggplot() +
  # Clean Background Ribbon
  geom_ribbon(
    data = df_eval,
    aes(x = date, ymin = conf_lo, ymax = conf_hi),
    fill  = &quot;#808080&quot;,
    alpha = 0.20
  ) +
  # Invisible Upper Bound Hover Points (No Lines, Pure Hover)
  geom_point(
    data = df_plot_hi,
    aes(x = date, y = conf_hi, text = text_hi),
    color = &quot;transparent&quot;,
    alpha = 0,
    size  = 3
  ) +
  # Invisible Lower Bound Hover Points (No Lines, Pure Hover)
  geom_point(
    data = df_plot_lo,
    aes(x = date, y = conf_lo, text = text_lo),
    color = &quot;transparent&quot;,
    alpha = 0,
    size  = 3
  ) +
  # Actual Price: Solid Dark Line &#038; Hover Points
  geom_line(
    data = df_plot_actual,
    aes(x = date, y = actual),
    color = &quot;#2c3e50&quot;,
    linewidth = 1.2
  ) +
  geom_point(
    data = df_plot_actual,
    aes(x = date, y = actual, text = text_actual),
    color = &quot;#2c3e50&quot;,
    size  = 2
  ) +
  # DeepAR Forecast: Dashed Red Line &#038; Clean Hover Points
  geom_line(
    data = df_plot_pred,
    aes(x = date, y = pred),
    color = &quot;#e74c3c&quot;,
    linetype = &quot;dashed&quot;,
    linewidth = 1.2
  ) +
  geom_point(
    data = df_plot_pred,
    aes(x = date, y = pred, text = text_pred),
    color = &quot;#e74c3c&quot;,
    size  = 2
  ) +
  # Formatting &#038; Theme
  scale_y_continuous(labels = dollar_format(accuracy = 1)) +
  labs(
    x = &quot;&quot;,
    y = &quot;&quot;,
    title = paste0(
      &quot;SOXX ETF &lt;span style = &#039;color:#2c3e50&#039;&gt;Actual Prices&lt;/span&gt; vs &quot;,
      &quot;&lt;span style = &#039;color:#e74c3c&#039;&gt;Torch DeepAR Forecast&lt;/span&gt;&lt;br&gt;&quot;,
      &quot;&lt;span style=&#039;font-size:12px; color:#555555;&#039;&gt;10-Day Horizon | MAPE: &quot;, round(mape_val, 2), &quot;%&lt;/span&gt;&quot;
    )
  ) +
  theme_minimal() +
  theme(
    plot.title = element_markdown(
      hjust = 0.5, 
      face  = &quot;bold&quot;
    ),
    plot.background  = element_rect(fill = &quot;#ffffff&quot;, color = NA),
    panel.background = element_rect(fill = &quot;#ffffff&quot;, color = NA),
    panel.grid.minor = element_blank()
  )

# 3. Render Interactive Plotly Spec
font_family &lt;- list(family = &quot;Roboto Slab, Sans-Serif&quot;, size = 16)
label_font  &lt;- list(font = list(family = &quot;Roboto Slab, Sans-Serif&quot;, size = 13))

ggplotly(p, tooltip = &quot;text&quot;) %&gt;% 
  style(hoverlabel = label_font) %&gt;% 
  layout(font = font_family) %&gt;% 
  config(displayModeBar = FALSE)
</pre>


<figure data-wp-context="{"imageId":"6a845a0d2bf52"}" data-wp-interactive="core/image" data-wp-key="6a845a0d2bf52" class="wp-block-image size-large wp-lightbox-container"><img loading="lazy" data-attachment-id="12487" data-permalink="https://datageeek.com/2026/08/18/breaking-the-python-barrier-building-a-pure-r-native-deepar-engine-with-libtorch/torch_deepar_soxx/" data-orig-file="https://datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png" data-orig-size="948,790" data-comments-opened="1" data-image-meta="{"aperture":"0","credit":"","camera":"","caption":"","created_timestamp":"0","copyright":"","focal_length":"0","iso":"0","shutter_speed":"0","title":"","orientation":"0","alt":""}" data-image-title="torch_deepar_soxx" data-image-description="" data-image-caption="" data-large-file="https://i2.wp.com/datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png?w=450&#038;ssl=1" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://i2.wp.com/datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png?w=450&#038;ssl=1" alt="" class="wp-image-12487" srcset_temp="https://datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png 948w, https://datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png?w=150 150w, https://datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png?w=300 300w, https://datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png?w=768 768w" sizes="(max-width: 948px) 100vw, 948px" data-recalc-dims="1" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">By implementing DeepAR directly in R via <code>torch</code> (LibTorch), we achieve a <strong>low-latency, zero-Python architecture</strong> that fits naturally into existing <code>tidymodels</code> workflows. The resulting pipeline delivers high-precision probabilistic predictions (achieving a <strong>MAPE of ~2.17%</strong> on a 10-day SOXX forecast horizon) with fast, in-memory performance suitable for production deployment.</p>



<p class="wp-block-paragraph"></p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://datageeek.com/2026/08/18/breaking-the-python-barrier-building-a-pure-r-native-deepar-engine-with-libtorch/"> DataGeeek</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/breaking-the-python-barrier-building-a-pure-r-native-deepar-engine-with-libtorch/">Breaking the Python Barrier: Building a Pure R-Native DeepAR Engine with LibTorch</a>]]></content:encoded>
					
		
		<enclosure url="https://datageeek.com/wp-content/uploads/2026/08/torch-1.png" length="0" type="" />
<enclosure url="https://1.gravatar.com/avatar/db5e3f9ef188ea98fe38ab05c5a3fad9fb52fe3472715a8fc02f7ea41731f77c?s=96&#038;d=identicon&#038;r=G" length="0" type="" />
<enclosure url="https://datageeek.com/wp-content/uploads/2026/08/image.png?w=949" length="0" type="" />
<enclosure url="https://datageeek.com/wp-content/uploads/2026/08/torch_deepar_soxx.png?w=948" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403179</post-id>	</item>
		<item>
		<title>PyData Berlin Talk on our new within Fixed Effect Solver</title>
		<link>https://www.r-bloggers.com/2026/08/pydata-berlin-talk-on-our-new-within-fixed-effect-solver-2/</link>
		
		<dc:creator><![CDATA[Alex Fischer]]></dc:creator>
		<pubDate>Mon, 17 Aug 2026 22:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://s3alfisc.github.io/blog/posts/pydata-berlin-within/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Yesterday I was in Berlin for the day and presented at the local PyData Meetup on our new solver for fixed effects regression. It was of course a lot of fun! The development was prompted by an issue we received via github a couple of months ago:<br />
...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/pydata-berlin-talk-on-our-new-within-fixed-effect-solver-2/">PyData Berlin Talk on our new within Fixed Effect Solver</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://s3alfisc.github.io/blog/posts/pydata-berlin-within/"> Alex Fischer</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 




<p>Yesterday I was in Berlin for the day and presented at the local PyData Meetup on our new solver for fixed effects regression. It was of course a lot of fun! The development was prompted by an issue we received via github a couple of months ago:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i0.wp.com/s3alfisc.github.io/blog/posts/pydata-berlin-within/how-it-started.png?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:85.0%" data-recalc-dims="1"></p>
</figure>
</div>
<p>Hours vs minutes! Urgh. Luckily, we’ve made some progress in between, so the issue could be closed with with</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/s3alfisc.github.io/blog/posts/pydata-berlin-within/converges.png?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:85.0%" data-recalc-dims="1"></p>
</figure>
</div>
<p>If you are curious how the new solver works, you can find the slides for the talk <a href="https://s3alfisc.github.io/blog/posts/pydata-berlin-within/pydata-berlin-within.pdf" rel="nofollow" target="_blank">here</a>, and please make sure to take a look at the <a href="https://github.com/py-econometrics/within/" rel="nofollow" target="_blank">code</a> or to even run a regression with it! We also have a longer <a href="https://github.com/py-econometrics/within-paper" rel="nofollow" target="_blank">write up</a> up in preparation, but as unfortunately is too often the case, the Pareto principle bites and we’ve been refining the last 20% for quite a while now.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i0.wp.com/s3alfisc.github.io/blog/posts/pydata-berlin-within/benchmarks-torch-cuda.png?w=578&#038;ssl=1" class="img-fluid figure-img" style="width:85.0%" data-recalc-dims="1"></p>
<figcaption>Benchmarks with Torch CUDA</figcaption>
</figure>
</div>



 
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://s3alfisc.github.io/blog/posts/pydata-berlin-within/"> Alex Fischer</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/pydata-berlin-talk-on-our-new-within-fixed-effect-solver-2/">PyData Berlin Talk on our new within Fixed Effect Solver</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403231</post-id>	</item>
		<item>
		<title>PyData Berlin Talk on our new within Fixed Effect Solver</title>
		<link>https://www.r-bloggers.com/2026/08/pydata-berlin-talk-on-our-new-within-fixed-effect-solver/</link>
		
		<dc:creator><![CDATA[Alex Fischer]]></dc:creator>
		<pubDate>Mon, 17 Aug 2026 22:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://s3alfisc.github.io/blog/posts/linear-algebra-faster-than-rust-cuda/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Yesterday I was in Berlin for the day and presented at the local PyData Meetup on our new solver for fixed effects regression. It was of course a lot of fun! The development was prompted by an issue we received via github a couple of months ago:<br />
...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/pydata-berlin-talk-on-our-new-within-fixed-effect-solver/">PyData Berlin Talk on our new within Fixed Effect Solver</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://s3alfisc.github.io/blog/posts/linear-algebra-faster-than-rust-cuda/"> Alex Fischer</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 




<p>Yesterday I was in Berlin for the day and presented at the local PyData Meetup on our new solver for fixed effects regression. It was of course a lot of fun! The development was prompted by an issue we received via github a couple of months ago:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/s3alfisc.github.io/blog/posts/linear-algebra-faster-than-rust-cuda/how-it-started.png?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:85.0%" data-recalc-dims="1"></p>
</figure>
</div>
<p>Hours vs minutes! Urgh. Luckily, we’ve made some progress in between, so the issue could be closed with with</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/s3alfisc.github.io/blog/posts/linear-algebra-faster-than-rust-cuda/converges.png?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:85.0%" data-recalc-dims="1"></p>
</figure>
</div>
<p>If you are curious how the new solver works, you can find the slides for the talk <a href="https://s3alfisc.github.io/blog/posts/linear-algebra-faster-than-rust-cuda/pydata-berlin-within.pdf" rel="nofollow" target="_blank">here</a>, and please make sure to take a look at the <a href="https://github.com/py-econometrics/within/tree/maincode" rel="nofollow" target="_blank">code</a> or to even run a regression with it! We also have a longer <a href="https://github.com/py-econometrics/within-paper" rel="nofollow" target="_blank">write up</a> up in preparation, but as unfortunately is too often the case, the Pareto principle bites and we’ve been refining the last 20% for quite a while now.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i0.wp.com/s3alfisc.github.io/blog/posts/linear-algebra-faster-than-rust-cuda/benchmarks-torch-cuda.png?w=578&#038;ssl=1" class="img-fluid figure-img" style="width:85.0%" data-recalc-dims="1"></p>
<figcaption>Benchmarks with Torch CUDA</figcaption>
</figure>
</div>



 
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://s3alfisc.github.io/blog/posts/linear-algebra-faster-than-rust-cuda/"> Alex Fischer</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/pydata-berlin-talk-on-our-new-within-fixed-effect-solver/">PyData Berlin Talk on our new within Fixed Effect Solver</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403219</post-id>	</item>
		<item>
		<title>Notes on purposeful skill maintenance/improvement/neglect</title>
		<link>https://www.r-bloggers.com/2026/08/notes-on-purposeful-skill-maintenance-improvement-neglect/</link>
		
		<dc:creator><![CDATA[Alexej Gossmann]]></dc:creator>
		<pubDate>Mon, 17 Aug 2026 04:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.alexejgossmann.com/skill-maintenance-improvement-neglect</guid>

					<description><![CDATA[<p>Over the course of my life I’ve invested a lot of time and effort into the acquisition of different skills, abilities, competencies, specialized knowledge – for simplicity I will refer to all of it as “skills” in the following. Some of those skills, I would say, have even become an ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/notes-on-purposeful-skill-maintenance-improvement-neglect/">Notes on purposeful skill maintenance/improvement/neglect</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://www.alexejgossmann.com/skill-maintenance-improvement-neglect/"> 0-fold Cross-Validation</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p>Over the course of my life I’ve invested a lot of time and effort into the acquisition of different skills, abilities, competencies, specialized knowledge – for simplicity I will refer to all of it as “skills” in the following. Some of those skills, I would say, have even become an integral part of my identity, which isn’t unusual for a human, I guess. At my current age (sadly I’m not 17 anymore) I have accumulated a skill collection sizable enough that it <em>needs to be managed</em> systematically.</p>

<p>I need to decide: What do I want maintain? Maintain fully or partially, or merely keep-warm? Where do I want to improve? What’s the minimal maintenance dose required in each case? What new skills will I invest substantial efforts in? Which of my current skills do I choose to neglect (to balance everything out)?</p>

<p>Below are some directional thoughts on skill maintenance/improvement/neglect with some vague personal examples.</p>

<ol>
  <li>Skill maintenance generally requires far less time and/or effort than skill acquisition (or meaningful improvement). Therefore it makes sense to keep a skill sharp at a minimally acceptable level rather than letting it deteriorate. Of course, there are different levels of “maintain”, requiring different levels of effort. For instance, for now I merely keep-warm my music skills by playing my instruments only a few minutes per week.</li>
  <li>Like the approach to acquiring a skill will differ between types of skills, such as motor skills, procedural skills, knowledge-based skills, etc., so will the approach to maintenance differ too. A few examples:
    <ul>
      <li>It seems that a memory-reliant skill will require active recall, e.g., spaced repetition (which I’ve been struggling with for almost 10 years now because it’s actually hard work to do the reviews). But, almost all non-trivial skills rely on memory to a significant degree.</li>
      <li>Motor skill development and (later) maintenance appears to be based on regular practice of basically the same fundamental movements; for example, practicing scales on a musical instrument, or practicing the same kicks/punches over and over in a martial art.</li>
      <li>Language skills rely primarily on regular exposure and use. Living in the US, this is something I need to actively/proactively seek out for my non-English languages. Reading books seems to work to some extent to maintain a language I already know, though it isn’t enough – at least it gives me an excuse to read fun but mediocre fiction (as in “I only read this to maintain my German/Russian!”) which I wouldn’t be reading otherwise.</li>
    </ul>
  </li>
  <li>A skill is made up of many component sub-skills that deteriorate at different rates. For example, fundamental hand and finger movements of playing an instrument are retained much better than individual music pieces or specific memorized chord progressions. Sometimes I may choose to maintain a specific sub-skill, and hope the best for the rest of the overall skill. For example, with the current AI wave, I chose to maintain only my competence in reviewing, rather than writing, code in certain my-non-main programming languages.</li>
  <li>Despite point 1 above, many skills are easily restored if forgotten. There is not enough time in the day/week/month to dedicate to everything, and I need to make a conscious decision on <em>what to neglect</em>, eventually allowing it to atrophy. However, often skills come back quickly with a little use, even if they deteriorate quickly. A silly example: I lost the ability to type in QWERTY immediately after learning the Colemak keyboard layout; then, years later, easily relearned QWERTY in 1-2 weeks, but immediately lost the ability to type in Colemak; after using QWERTY exclusively for a year, I then easily relearned Colemak and have been maintaining both layouts in muscle memory ever since.</li>
</ol>

<p>That’s the end of this blog post. So, obviously, a skill I should work on is <em>writing</em>.</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://www.alexejgossmann.com/skill-maintenance-improvement-neglect/"> 0-fold Cross-Validation</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/notes-on-purposeful-skill-maintenance-improvement-neglect/">Notes on purposeful skill maintenance/improvement/neglect</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403167</post-id>	</item>
		<item>
		<title>Creating self-contained R scripts for rendering Quarto documents using the knitr engine – courtesy of the new R package managers ir and uvr</title>
		<link>https://www.r-bloggers.com/2026/08/creating-self-contained-r-scripts-for-rendering-quarto-documents-using-the-knitr-engine-courtesy-of-the-new-r-package-managers-ir-and-uvr/</link>
		
		<dc:creator><![CDATA[R &#124; Dr Tom Palmer]]></dc:creator>
		<pubDate>Sun, 16 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://remlapmot.github.io/post/2026/self-contained-r-script-for-quarto/</guid>

					<description><![CDATA[<p>Introduction<br />
In previous posts I have described how to use the self-contained Python scripts feature in the uv Python package manager to create virtual environments to render Quarto documents using the Jupyter<br />
nbstata kernel and the<br />
python3 kernel. I...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/creating-self-contained-r-scripts-for-rendering-quarto-documents-using-the-knitr-engine-courtesy-of-the-new-r-package-managers-ir-and-uvr/">Creating self-contained R scripts for rendering Quarto documents using the knitr engine – courtesy of the new R package managers ir and uvr</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://remlapmot.github.io/post/2026/self-contained-r-script-for-quarto/"> R | Dr Tom Palmer</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<h2 id="introduction">Introduction</h2>
<p>In previous posts I have described how to use the self-contained Python scripts feature in the <strong>uv</strong> Python package manager to create virtual environments to render Quarto documents using the Jupyter 
<a href="https://remlapmot.github.io/post/2025/nbstata-uv-venv/" rel="nofollow" target="_blank">nbstata kernel</a> and the 
<a href="https://remlapmot.github.io/post/2025/self-contained-python-script-for-quarto/" rel="nofollow" target="_blank">python3 kernel</a>. In this post I describe how to do the same for R scripts to render Quarto documents running R code using the knitr engine.</p>
<p>I recently discovered that there are now three uv-inspired package managers for R; 
<a href="https://r-lib.github.io/ir/" rel="nofollow" target="_blank">ir</a>, 
<a href="https://nbafrank.github.io/uvr/" rel="nofollow" target="_blank">uvr</a>, and 
<a href="https://a2-ai.github.io/rv-docs/" rel="nofollow" target="_blank">rv</a> (… maybe there are more?). I will concentrate on the first two because they allow defining self-contained R scripts. I find self-contained scripts a fast and lightweight way to define project dependencies, and I very rarely require a record of the exact package versions.</p>
<p>In the following examples I assume we are creating an R script, <em>render.R</em>, which contains one or more calls to <code>quarto::quarto_render()</code> for a lecture or tutorial. For the dependency R packages I include the packages the document itself needs, plus the quarto and knitr packages.</p>
<h2 id="example-self-contained-r-script-using-ir">Example self-contained R script using <code>ir</code></h2>
<p>To define dependencies for <code>ir</code>, at the top of the script begin each comment line with <code>#| </code> then write a list under a <code>packages</code> key as follows – this is the list of packages I require for one of my practicals on missing data.</p>
<pre>#| packages:
#|   - gtsummary
#|   - haven
#|   - tidyverse
#|   - VIM
#|   - quarto
#|   - knitr

# Rest of R code follows ...
# ... essentially one or sometimes multiple quarto::quarto_render() calls
</pre>
<p>This script can be run with</p>
<pre>ir run render.R
</pre>
<h2 id="example-self-contained-r-script-using-uvr">Example self-contained R script using <code>uvr</code></h2>
<p><code>uvr</code> follows the same dependency syntax as <code>uv</code>. Each line begins with a <code># </code> comment, and the dependencies are defined as a TOML array of strings between <code># /// script</code> and <code># ///</code>. So the top of our <em>render.R</em> script looks as follows.</p>
<pre># /// script
# dependencies = [
#   &quot;gtsummary&quot;,
#   &quot;haven&quot;,
#   &quot;tidyverse&quot;,
#   &quot;VIM&quot;,
#   &quot;quarto&quot;,
#   &quot;knitr&quot;,
# ]
# ///

# Rest of R code follows ...
# ... essentially one or sometimes multiple quarto::quarto_render() calls
</pre>
<p>This script can be run with</p>
<pre>uvr run render.R
</pre>
<h2 id="automation-with-just-in-a-complex-directory-structure">Automation with <code>just</code> in a complex directory structure</h2>
<p>For each course I teach I have the lecture or tutorial in a subdirectory. To run each script I could run the shell commands given above. To slightly improve efficiency I find that putting the following 
<a href="https://just.systems/" rel="nofollow" target="_blank">justfile</a> at the top of the directory structure saves a bit of typing. The first recipe, <code>render</code>, uses my system R library, the others resolve packages via <code>ir</code>/<code>uvr</code>.</p>
<pre>render dir=invocation_directory():
    cd &quot;{{ dir }}&quot; && Rscript render.R

ir dir=invocation_directory():
    cd &quot;{{ dir }}&quot; && ir run render.R

uvr dir=invocation_directory():
    cd &quot;{{ dir }}&quot; && uvr run render.R
</pre>
<p>I can simply type <code>just ir</code> or <code>just uvr</code> to render the lecture/tutorial given whichever directory I’m in.</p>
<h2 id="bonus-1--example-self-contained-quarto-document-using-ir">Bonus 1 – Example self-contained Quarto document using <code>ir</code></h2>
<p><code>ir</code> cleverly allows us to alternatively define the dependencies within the YAML header of a Quarto document, under an <code>ir</code> key. In this case we can remove the quarto package as we might assume we’d render this document by clicking the <em>Render</em> button in RStudio or using <code>quarto render ...</code> in the terminal.</p>
<pre>---
title: My lecture/tutorial
ir:
  packages:
    - gtsummary
    - haven
    - tidyverse
    - VIM
    - knitr
---

Rest of Quarto document follows ...
</pre>
<p>Say this Quarto document is <em>tutorial.qmd</em> we would then render it with</p>
<pre>ir render tutorial.qmd
</pre>
<p>More details are given in the 
<a href="https://r-lib.github.io/ir/quarto.html" rel="nofollow" target="_blank">ir Quarto docs</a>.</p>
<h2 id="bonus-2--making-the-r-script-executable">Bonus 2 – Making the R script executable</h2>
<p>With both 
<a href="https://r-lib.github.io/ir/run.html" rel="nofollow" target="_blank"><code>ir</code></a> and <code>uvr</code> (and indeed 
<a href="https://docs.astral.sh/uv/guides/scripts/#using-a-shebang-to-create-an-executable-file" rel="nofollow" target="_blank"><code>uv</code></a>) we can optionally make the <em>render.R</em> script executable, say renaming to simply <em>render</em>, by adding the relevant shebang to the very top of the file.</p>
<p>For <code>ir</code> we add</p>
<pre>#!/usr/bin/env -S ir run
</pre>
<p>and for <code>uvr</code> we add</p>
<pre>#!/usr/bin/env -S uvr run
</pre>
<p>We then make the script executable</p>
<pre>chmod +x render
</pre>
<p>and run it with</p>
<pre>./render
</pre>
<h2 id="summary">Summary</h2>
<p>I have shown how to make a self-contained, and optionally executable, R script to render Quarto documents using the knitr engine which automatically manages the required R packages. This functionality is provided by both the <code>ir</code> and <code>uvr</code> R package managers. This approach would also work for RMarkdown documents (of course one would need to swap the quarto package for the rmarkdown package in the list of dependencies).</p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://remlapmot.github.io/post/2026/self-contained-r-script-for-quarto/"> R | Dr Tom Palmer</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/creating-self-contained-r-scripts-for-rendering-quarto-documents-using-the-knitr-engine-courtesy-of-the-new-r-package-managers-ir-and-uvr/">Creating self-contained R scripts for rendering Quarto documents using the knitr engine – courtesy of the new R package managers ir and uvr</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403163</post-id>	</item>
		<item>
		<title>More progress with Tabler for R</title>
		<link>https://www.r-bloggers.com/2026/08/more-progress-with-tabler-for-r/</link>
		
		<dc:creator><![CDATA[https://pacha.dev/blog]]></dc:creator>
		<pubDate>Sat, 15 Aug 2026 23:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pacha.dev/blog/2026/08/16/tabler/index.html</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> For R users that are just starting with R or that have been using it for years</div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/more-progress-with-tabler-for-r/">More progress with Tabler for R</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://pacha.dev/blog/2026/08/16/tabler/index.html"> https://pacha.dev/blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p><em>Before the main content: I am creating an R Community on Google Groups. You can join the group using this <a href="https://docs.google.com/forms/d/e/1FAIpQLSdMAj4adRAT4Gyuwt_9dPvxRvOUPml9AD59vuI7qS7XDlp48g/viewform?usp=dialog" rel="nofollow" target="_blank">form</a>.</em></p>
<p>I’ve been working on a a modern dashboard framework for R using the beautiful Tabler Bootstrap theme. Furthermore, to render Tabler apps using a server I created <a href="https://github.com/pachadotdev/tabler-server" rel="nofollow" target="_blank">Tabler Server</a> alongside the process.</p>

<h2>Installation</h2>
<p>Old version, depends on Shiny:</p>
<pre>install.packages(&quot;tabler&quot;, repos = &quot;https://cran.r-project.org&quot;)</pre>
<p>New version, does not use Shiny:</p>
<pre># using the R-Universe
install.packages(&quot;tabler&quot;, repos = &quot;https://pachadotdev.r-universe.dev&quot;)

# or using the remotes package
remotes::install_github(&quot;pachadotdev/tabler&quot;)</pre>

<h2>Quick Start</h2>

<h3>Single-script app</h3>
<p>The following example uses the “combo” layout to recreate Shiny’s geyser example. The theme options can be adjusted from the code or the theme setting icon that can be hidden. See the example <a href="https://github.com/pachadotdev/tabler/blob/main/inst/extdata/app-template/combo-layout.R" rel="nofollow" target="_blank">here</a>.</p>

<img class="my-fig figure-img" style="width:75%!important" src="https://i2.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/combo-layout-light.png?w=578&#038;ssl=1" title="Light theme + teal colour + zinc base" alt="layout-geyser" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i1.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/combo-layout-dark.png?w=578&#038;ssl=1" title="Dark theme + cyan colour + slate base" alt="layout-geyser" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i2.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/theme-selection.png?w=578&#038;ssl=1" title="Theme selection" alt="layout-geyser" data-recalc-dims="1">

<p>I added a UI-only example to cover the different input elements and their options <a href="https://github.com/pachadotdev/tabler/blob/main/inst/extdata/app-template/boxed-layout-all-ui-elements.R" rel="nofollow" target="_blank">here</a>.</p>

<img class="my-fig figure-img" style="width:75%!important" src="https://i0.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/select.png?w=578&#038;ssl=1" title="Select &#038; Multi-Select" alt="select" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i0.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/sliders.png?w=578&#038;ssl=1" title="Sliders" alt="sliders" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i1.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/dates.png?w=578&#038;ssl=1" title="Dates" alt="dates" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i1.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/radio-checkboxes.png?w=578&#038;ssl=1" title="Radio &#038; Checkboxes" alt="radio-checkboxes" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i0.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/text-numeric-buttons.png?w=578&#038;ssl=1" title="Text, Numeric &#038; buttons" alt="text-numeric-buttons" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i2.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/flags-social.png?w=578&#038;ssl=1" title="Flags &#038; social" alt="flags-social" data-recalc-dims="1">

<h3>Modular R package app</h3>
<p>Create an R package with modular components:</p>
<pre>library(tabler)

pkg_template(&quot;mydashboard&quot;)</pre>
<p>See the package skeleton <a href="https://github.com/pachadotdev/tabler/tree/main/inst/extdata/pkg-template" rel="nofollow" target="_blank">here</a>. <code>pkg_template()</code> adds a <code>DESCRIPTION</code> and other components required for an R package to work.</p>
<p>For instance, Open Trade Statistics consists in a full dashboard that uses environment variables, SQL connections, caching, and D3 plots. Its code is <a href="https://github.com/pachadotdev/tradestatistics-dashboard" rel="nofollow" target="_blank">here</a>, and the result is <a href="https://dashboard.tradestatistics.io/" rel="nofollow" target="_blank">here</a>.</p>

<h2>Loading/Progress bar</h2>
<p>I added an example with a progress bar <a href="https://github.com/pachadotdev/tabler/blob/main/inst/extdata/app-template/combo-layout-with-progress-bar.R" rel="nofollow" target="_blank">here</a>. The progress bar hides the app while the new plots or other elements are computed.</p>

<img class="my-fig figure-img" style="width:75%!important" src="https://i0.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/combo-layout-progress-bar.png?w=578&#038;ssl=1" title="Progress bar" alt="progress-bar" data-recalc-dims="1">

<h2>Login page</h2>
<p>This R package provides a login page that you can connect to a database or another system. The example <a href="https://github.com/pachadotdev/tabler/blob/main/inst/extdata/app-template/combo-layout-with-login.R" rel="nofollow" target="_blank">here</a> shows the dashboard after correctly typing the user “SpaceMariner” and password “IDDQD”. There is an example using RSQLite <a href="https://github.com/pachadotdev/tabler/blob/main/inst/extdata/app-template/combo-layout-with-login-sqlite.R" rel="nofollow" target="_blank">here</a>.</p>
<p>I was thinking about adding a Google/Outlook/GitHub account login but I have no idea how to. If you know how and would like to contribute, please comment <a href="https://github.com/pachadotdev/tabler/issues/2" rel="nofollow" target="_blank">here</a>.</p>

<img class="my-fig figure-img" style="width:75%!important" src="https://i2.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/combo-layout-sign-in.png?w=578&#038;ssl=1" title="Sign in" alt="sign-in" data-recalc-dims="1">

<img class="my-fig figure-img" style="width:75%!important" src="https://i0.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/combo-layout-sign-out.png?w=578&#038;ssl=1" title="Dashboard with sing out button" alt="sign-out" data-recalc-dims="1">

<h2>Does it run Doom?</h2>
<p>Yes. I tested the WebSocket compiling and running the Doom WASM version. See the code <a href="https://github.com/pachadotdev/tabler/blob/main/dev/doom.R" rel="nofollow" target="_blank">here</a>.</p>

<img class="my-fig figure-img" style="width:75%!important" src="https://i0.wp.com/github.com/pachadotdev/tabler/blob/main/screenshots/doom.png?w=578&#038;ssl=1" title="Doom" alt="doom" data-recalc-dims="1">

<h2>Available Layouts</h2>
<p>There are <a href="https://github.com/pachadotdev/tabler/tree/main/examples" rel="nofollow" target="_blank">additional examples</a> for each of the following layouts:</p>
<ul>
<li><strong>Boxed (Default)</strong>: Basic dashboard with top navbar and constrained width content area. This is the default layout.</li>
<li><strong>Combo</strong>: Combines vertical sidebar navigation with top header.</li>
<li><strong>Condensed</strong>: Compact layout with reduced padding/margins.</li>
<li><strong>Fluid</strong>: Full-width layout without container constraints.</li>
<li><strong>Fluid Vertical</strong>: Full-width layout with vertical sidebar.</li>
<li><strong>Horizontal</strong>: Layout with horizontal navigation menu.</li>
<li><strong>Navbar Dark</strong>: Layout with dark navbar theme.</li>
<li><strong>Navbar Overlap</strong>: Layout where content overlaps with navbar for a modern look.</li>
<li><strong>Navbar Sticky</strong>: Layout with sticky/fixed navbar that stays at the top when scrolling.</li>
<li><strong>RTL</strong>: Right-to-left layout for Hebrew/Arabic languages.</li>
<li><strong>Vertical</strong>: Vertical sidebar layout without top navbar.</li>
<li><strong>Vertical Right</strong>: Vertical sidebar positioned on the right side.</li>
<li><strong>Vertical Transparent</strong>: Vertical layout with transparent sidebar.</li>
</ul>
<p>Note: <code>tabler</code> allows to pass <code>layout = &quot;navbar&quot;</code> and <code>layout = &quot;navbar-sticky-dark&quot;</code> which are wrappers for a light theme navbar layout and a dark theme sticky navbar layour, respectively.</p>

<h2>Differences with Shiny</h2>
<ul>
<li>Static plots (base, ggplot, tinyplot, etc.) render as SVG and can be downloaded with the right click button.</li>
<li>URLs are of the form <code>my.site/myapp?year=2000&country=gbr</code> instead of <code>my.site/myapp?year=2000&country=%22gbr%22</code></li>
</ul>

<h2>License</h2>
<p>Apache License (>= 2)</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://pacha.dev/blog/2026/08/16/tabler/index.html"> https://pacha.dev/blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/more-progress-with-tabler-for-r/">More progress with Tabler for R</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403150</post-id>	</item>
	</channel>
</rss>
