<?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>Thu, 11 Jun 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.18</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>Ronny Hernandez Mora, Joel Nitta, and Nick Tierney Join rOpenSci Software Peer Review Editorial Team</title>
		<link>https://www.r-bloggers.com/2026/06/ronny-hernandez-mora-joel-nitta-and-nick-tierney-join-ropensci-software-peer-review-editorial-team/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Thu, 11 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/06/11/neweditorsq22026/</guid>

					<description><![CDATA[<p>What makes rOpenSci’s software peer review work is people who care deeply about the quality and usability of scientific software, and who give their time and expertise to help others build it better. Today, we’re pleased to announce three ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/ronny-hernandez-mora-joel-nitta-and-nick-tierney-join-ropensci-software-peer-review-editorial-team/">Ronny Hernandez Mora, Joel Nitta, and Nick Tierney Join rOpenSci Software Peer Review Editorial Team</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/06/11/neweditorsq22026/"> 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>

<p>What makes <a href="https://ropensci.org/software-review/" rel="nofollow" target="_blank">rOpenSci’s software peer review</a> work is people who care deeply about the quality and usability of scientific software, and who give their time and expertise to help others build it better. Today, we’re pleased to announce three new members of our editorial team.</p>
<p>We welcome <em>Joel H. Nitta</em>, and <em>Nicholas Tierney</em> as new editors, and formally introduce <em>Ronny Hernández Mora</em>, who joined the editorial team in August 2025. Each brings a distinct perspective shaped by their work, their communities, and their experience with open-source R development. Together, they strengthen our capacity to serve the growing number of package authors who submit their work for review, and to uphold the collaborative, friendly and transparent standards that rOpenSci software peer review is known for.</p>
<h2>
Ronny Hernández Mora
</h2><div class="float-left"><figure class="m-0"><img src="https://i1.wp.com/ropensci.org/img/team/ronny-hernandez-mora.png?w=578&#038;ssl=1"
alt="Headshot of Ronny"ZgotmplZ
ZgotmplZ
ZgotmplZ
style=" object-fit: cover; object-position: center; height: 250px; width: 200; margin-right: 15px" data-recalc-dims="1"
/>
<p>
<p>Ronny is a PhD student at the University of Alberta and a research software developer with a background in data analysis and remote sensing. His current research explores how perception-driven drone systems can support detection, monitoring, and decision making in real world settings.</p>
<p>Before starting his PhD, Ronny worked as a data developer at <a href="https://www.ixpantia.com/en/" rel="nofollow" target="_blank">ixpantia</a>, developing data tools, automation pipelines, APIs, and production analytical applications for organizations across Latin America and the United States. Alongside his PhD, he works with <a href="https://openscapes.org/" rel="nofollow" target="_blank">Openscapes</a> and contributes to opensource communities through teaching, mentorship, talks, and collaborative software projects.</p>
</p>
</figure>
</div>
<div style="clear: both;"></div>
<p>Ronny on <a href="https://github.com/ronnyhdez" rel="nofollow" target="_blank">GitHub</a>, <a href="https://ronnyale.com/" rel="nofollow" target="_blank">Website</a>.</p>
<blockquote class='blockquote text-left'>
<p class="mb-0"><p>I first heard about rOpenSci through a <a href="https://www.datalatam.com/29/" rel="nofollow" target="_blank">Data Latam podcast episode</a>, and from there I started following the organization and its work. Over time, I got to know people connected to the community, but it was not until 2024 that I became directly involved by volunteering as a package reviewer.</p>
<p>I have always liked the idea of building software collaboratively: creating tools that can be understood, reviewed, reused, and improved by others. That is one of the things I value most about rOpenSci: the way it combines technical review with openness, care, and constructive feedback. I am grateful for the opportunity to contribute as an editor and to support authors and reviewers through that process and continue learning through this community effort.</p>
</p>
<footer class="blockquote-footer">Ronny </footer>
</blockquote>
<h2>
Joel H. Nitta
</h2><div class="float-left"><figure class="m-0"><img src="https://i1.wp.com/ropensci.org/img/team/joel-nitta.png?w=578&#038;ssl=1"
alt="Headshot of Joel"ZgotmplZ
ZgotmplZ
ZgotmplZ
style=" object-fit: cover; object-position: center; height: 250px; width: 200; margin-right: 15px" data-recalc-dims="1"
/>
<p>
Joel is currently an associate professor at <a href="https://www.las.chiba-u.jp/research/faculty/nitta.html" rel="nofollow" target="_blank">Chiba University</a>, Japan, where he studies the evolution and ecology of ferns. Throughout his career as a botanist, he has also cultivated a keen interest in reproducible data analysis and has authored several R packages. Two of these are currently part of rOpenSci, <a href="https://docs.ropensci.org/canaper/" rel="nofollow" target="_blank">canaper</a> for spatial phylogenetic analysis, and <a href="https://docs.ropensci.org/dwctaxon/" rel="nofollow" target="_blank">dwctaxon</a> for validation and maintenance of taxonomic databases. He is also an official maintainer of two rOpenSci packages, <a href="https://docs.ropensci.org/rgnparser/" rel="nofollow" target="_blank">rgnparser</a> for parsing taxonomic names, and <a href="https://docs.ropensci.org/restez/" rel="nofollow" target="_blank">restez</a> for querying the GenBank DNA database. Outside of rOpenSci, Joel is active in the <a href="https://sites.google.com/view/biopackathon/" rel="nofollow" target="_blank">Bio&#8221;Pack&#8221;athon</a> community in Japan, serves on the organizing committee of the <a href="https://pteridogroup.github.io/" rel="nofollow" target="_blank">Pteridophyte Phylogeny Group</a>, and is a certified instructor for <a href="https://carpentries.org/community/instructors/" rel="nofollow" target="_blank">The Carpentries</a>. Joel’s hobbies include various forms of being active outside (hiking, running, backpacking, botanizing, cycling), playing the euphonium, and tabletop games.
</p>
</figure>
</div>
<div style="clear: both;"></div>
<p>Joel on <a href="https://github.com/joelnitta" rel="nofollow" target="_blank">GitHub</a>, <a href="https://www.joelnitta.com/" rel="nofollow" target="_blank">Website</a>.</p>
<blockquote class='blockquote text-left'>
<p class="mb-0">rOpenSci has been incredibly important in my journey with data science and R. Above all, the extremely knowledgeable and helpful rOpenSci community has provided invaluable support as I transitioned from “R package user” to “R package developer”. While it may be possible to do this on your own, it is much more enjoyable and efficient when you are in the company of like-minded people. I especially appreciate this because I did not study computer science in undergrad and I am largely self-taught when it comes to R, having first learned it out of necessity during my graduate studies. I am very excited to officially join the editorial team and have the opportunity to give back to the organization that has supported me so much.</p>
<footer class="blockquote-footer">Joel </footer>
</blockquote>
<h2>
Nicholas Tierney
</h2><div class="float-left"><figure class="m-0"><img src="https://i1.wp.com/ropensci.org/img/team/nick-tierney.jpg?w=578&#038;ssl=1"
alt="Headshot of "ZgotmplZ
ZgotmplZ
ZgotmplZ
style=" object-fit: cover; object-position: center; height: 250px; width: 200; margin-right: 15px" data-recalc-dims="1"
/>
<p>
Nicholas (Nick) Tierney is a statistician, <a href="https://researchsoftware.org/" rel="nofollow" target="_blank">Research Software Engineer</a>, and freelance consultant with a PhD in Statistics who specialises in data analytics, R package development, and teaching. Previously, he worked with <a href="https://www.thekids.org.au/contact-us/our-people/g/nick-golding/" rel="nofollow" target="_blank">Professor Nick Golding</a> at <a href="https://www.thekids.org.au/" rel="nofollow" target="_blank">The Kids Research Institute Australia</a> and was a Research Fellow at Monash University with <a href="https://www.dicook.org/" rel="nofollow" target="_blank">Professor Dianne Cook</a>, where he developed tools for exploratory data analysis including <a href="https://docs.ropensci.org/visdat/" rel="nofollow" target="_blank">visdat</a>, <a href="https://naniar.njtierney.com/" rel="nofollow" target="_blank">naniar</a>, and <a href="https://brolgar.njtierney.com/" rel="nofollow" target="_blank">brolgar</a>. Nick actively writes about R related projects at his blog, <a href="https://www.njtierney.com/" rel="nofollow" target="_blank">“credibly curious”</a>. When not coding, Nick enjoys outdoor adventures and hiked the entire Pacific Crest Trail in 2023, documenting his journey at <a href="https://njt.micro.blog/" rel="nofollow" target="_blank">njt.micro.blog</a>.
</p>
</figure>
</div>
<div style="clear: both;"></div>
<p>Nick on <a href="https://github.com/njtierney" rel="nofollow" target="_blank">GitHub</a>, <a href="https://www.njtierney.com/" rel="nofollow" target="_blank">Website</a>.</p>
<blockquote class='blockquote text-left'>
<p class="mb-0"><p>I remember seeing rOpenSci online in 2014, at their first <a href="https://unconf14.ropensci.org/" rel="nofollow" target="_blank">rOpenSci hackathon</a> in San Francisco, USA. I was inspired by not just the projects they worked on, but the collective of people who were kind and generous as much as they were brilliant. I helped run an offshoot of the rOpenSci Unconf in Australia, which ran in <a href="https://auunconf.ropensci.org/" rel="nofollow" target="_blank">2016</a>, <a href="https://ozunconf17.ropensci.org/" rel="nofollow" target="_blank">2017</a>, <a href="https://ozunconf18.ropensci.org/" rel="nofollow" target="_blank">2018</a>, and <a href="https://ozunconf19.ropensci.org/" rel="nofollow" target="_blank">2019</a>. I received amazing mentorship and support from Karthik Ram and Stefanie Butland in learning how to run these unconferences. I think these have had a lasting impact on connecting people from Australia and New Zealand.</p>
<p>I had my R package, <a href="https://docs.ropensci.org/visdat/" rel="nofollow" target="_blank">visdat</a>, reviewed by rOpenSci in 2017, and this experience was formative to my understanding of software review, and greatly improved my own day to day practice. In 2024, with <a href="https://github.com/Aariq" rel="nofollow" target="_blank">Eric Scott</a>, and <a href="https://github.com/brownag" rel="nofollow" target="_blank">Andrew Brown</a>, we submitted the <a href="https://github.com/ropensci/geotargets" rel="nofollow" target="_blank">geotargets</a> package to rOpenSci.</p>
<p>rOpenSci has always been at the forefront of building a community of practice, and their model of peer review is a gold standard. Science would be better if more journals practised such transparency and kindness. It is an enormous honour to be able to give back to a community that has been so formative in my career and my life.</p>
</p>
<footer class="blockquote-footer">Nick </footer>
</blockquote>
<h2>
About the Software Peer Review Program
</h2><p>rOpenSci’s software peer review program brings together volunteers to collaboratively review scientific and statistical software according to transparent, constructive, and open standards.
We have two different type of review: one for <a href="https://devguide.ropensci.org/" rel="nofollow" target="_blank">general research software packages</a> and another for <a href="https://stats-devguide.ropensci.org/" rel="nofollow" target="_blank">package that implement statistical methods</a>.
Editors like Ronny, Joel, and Nick are central to that process: they handle initial submission checks, identify and coordinate reviewers, and guide authors through the review until their package is ready.</p>
<h2>
Get Involved
</h2><p>Thinking about submitting a package? Start here:</p>
<ul>
<li><a href="https://ropensci.org/software-review/" rel="nofollow" target="_blank">rOpenSci Software Peer Review</a>: scope, process, and guidelines;</li>
<li><a href="https://github.com/ropensci/software-review/issues" rel="nofollow" target="_blank">rOpenSci Packages: Development, Maintenance, and Peer Review</a>: the full guide for package authors;</li>
<li><a href="https://stats-devguide.ropensci.org/" rel="nofollow" target="_blank">rOpenSci Statistical Software Peer Review</a>: the full guide for statistical software submissions;</li>
<li>Public <a href="https://github.com/ropensci/software-review/issues" rel="nofollow" target="_blank">software review threads on GitHub</a>: see software peer review in action.</li>
</ul>
<p>Would you like to contribute as a reviewer? We’d love to have you. Fill out the <a href="https://airtable.com/app8dssb6a7PG6Vwj/shrnfDI2S9uuyxtDw" rel="nofollow" target="_blank">rOpenSci Reviewer Sign-Up Form</a> and we’ll match you with packages that fit your expertise.</p>
<p>A warm welcome to <strong>Ronny, Joel, and Nick</strong>! We’re very glad to have you with us.</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/06/11/neweditorsq22026/"> 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/06/ronny-hernandez-mora-joel-nitta-and-nick-tierney-join-ropensci-software-peer-review-editorial-team/">Ronny Hernandez Mora, Joel Nitta, and Nick Tierney Join rOpenSci Software Peer Review Editorial Team</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401799</post-id>	</item>
		<item>
		<title>Eleven Latin American Voices for Open Science: The New Cohort of Champions rOpenSci 2026</title>
		<link>https://www.r-bloggers.com/2026/06/eleven-latin-american-voices-for-open-science-the-new-cohort-of-champions-ropensci-2026/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/06/09/champions-2026/</guid>

					<description><![CDATA[<p>Read it in: . We are very happy to introduce the new rOpenSci Champions.<br />
This group will experience the program and work in Spanish, allowing us to continue to strengthen the open science and research software development community in this language.<br />
W...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/eleven-latin-american-voices-for-open-science-the-new-cohort-of-champions-ropensci-2026/">Eleven Latin American Voices for Open Science: The New Cohort of Champions rOpenSci 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/06/09/champions-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>

<p><a href='https://ropensci.org/es/blog/2026/06/09/champions-2026/' rel="nofollow" target="_blank">Read it in: </a>.</p> <p>We are very happy to introduce the new rOpenSci Champions.
This group will experience the program and work in Spanish, allowing us to continue to strengthen the open science and research software development community in this language.
We are excited about the projects they will develop, which address real challenges from different disciplines and territories in Latin America.</p>
<p>We invite you to meet each of these people and the projects they will be working on throughout the program.</p>
<h2>
Bastián Olea Herrera
</h2><figure class="pull-left"><img src="https://i1.wp.com/ropensci.org/img/team/bastian-olea-herrera.jpg?w=250&#038;ssl=1"
alt="Profile photo for Bastián Olea Herrera"  data-recalc-dims="1"><figcaption>
<p><strong>Bastián Olea Herrera </br> Undersecretary of Regional and Administrative Development </br> Government of Chile</strong></p>
</figcaption>
</figure>
<p>My name is Bastián, I live in Chile, I am a sociologist by training and I have a Master’s degree in sociology.
I like to create content and tutorials about R, and recently we are organizing a community of R users in Santiago, where I live.
I am dedicated to data analysis in the public sector, where I work mainly with social data at different territorial levels.
This kind of data always needs the same types of cleaning and corrections, but at the same time they usually vary a lot in small details between each source.
That is why I applied to the program to develop a package that facilitates working with data at the communal and regional level in Chile.
I hope to meet other R users and learn together, as well as receive support from people with more experience and knowledge, so that we can create useful tools for many people!</p>
</br>
</br>
<h2>
Denisse Fierro Arcos
</h2><figure class="pull-right"><img src="https://i2.wp.com/ropensci.org/img/team/denisse-fierro-arcos.JPG?w=250&#038;ssl=1"
alt="Profile photo for Denisse Fierro Arcos"  data-recalc-dims="1"><figcaption>
<p><strong>Denisse Fierro Arcos </br> Institute for Marine and Antarctic Studies </br> University of Tasmania</strong></p>
</figcaption>
</figure>
<p>My name is Denisse Fierro Arcos, I am a marine scientist originally from Ecuador but currently living in Australia.
At the moment I am close to finishing my PhD program which focuses on developing best practices for the use of oceanographic models in marine ecosystem studies.
I am also currently working as a researcher at the University of Tasmania where I am developing a marine ecosystem model that will allow us to project the possible impacts of climate change on marine species and fisheries.
It is precisely this model that is the focus of my project with the Champions Program.
We want to publish a package in R that will allow other marine researchers to easily run our model.</p>
</br>
</br>
<h2>
Durga Valentina Linares Herrera
</h2><figure class="pull-left"><img src="https://i2.wp.com/ropensci.org/img/team/valentina-linares.jpeg?w=250&#038;ssl=1"
alt="Profile photo for Durga Valentina Linares Herrera"  data-recalc-dims="1"><figcaption>
<p><strong>Durga Valentina Linares Herrera </br> Research Center of the Universidad del Pacífico (CIUP)</strong></p>
</figcaption>
</figure>
<p>Hello!
My name is Durga Valentina Linares Herrera and I live in Lima, Peru.
I am a social scientist, with a degree in Political Science from the Universidad Antonio Ruiz de Montoya and a diploma in Data Science for Social Sciences and Public Management from the Pontificia Universidad Católica del Perú.
I work as a research assistant at the Research Center of the Universidad del Pacífico, where I explore the intersection between technology and work from a sociological perspective and with mixed methodologies.
My current projects revolve around the Peruvian labor market and its exposure to artificial intelligence, and the evolution of the strike as a social phenomenon in Peru in the last three decades.</p>
<p>For the program I presented as a project {epen}, an R package to download, process, and analyze microdata from the Permanent National Employment Survey and its predecessor, the Lima EPE.
This idea was born from my experience working directly with these databases.
I could see how difficult it can be to access public labor data in a fast, clear, and reproducible way, especially for those of us who come from the social sciences and do not always come to these tools with a previous technical background, as was also my case.
With this package, I seek to facilitate that path for researchers, students, and public sector analysts who need to build reliable labor indicators without having to start from scratch each time, something I find especially relevant in this time of so many changes.</p>
<p>In recent years, with the intention of developing new skills in a context where technology is quickly advancing, I moved towards a more quantitative approach and learned to program in a self-taught way with free resources on the internet.
That process has been important for my professional development, but it also made me want to turn that learning into something useful for other people: a tool to help reduce the entry barriers that I myself encountered when I started.
That’s how the motivation behind this package came about, and this Champions Program appeared just at the right time.
I am excited to participate in an initiative like this and to be part of a network like rOpenSci, whose commitment to a more diverse and inclusive open science seems very valuable to me.
I hope that this experience will allow me to consolidate in community a path that I once started on my own and pave the way for future packages aimed at improving access to Peruvian public data in R.</p>
<h2>
Evelia Lorena Coss Navarrete
</h2><p><figure class="pull-right"><img src="https://i2.wp.com/ropensci.org/img/team/evelia-lorena-coss-navarrete.jpg?w=250&#038;ssl=1"
alt="Profile photo for Evelia Lorena Coss Navarrete"  data-recalc-dims="1"><figcaption>
<p><strong>Evelia Lorena Coss Navarrete </br> LIIGH-UNAM</strong></p>
</figcaption>
</figure>
I am a postdoctoral researcher specialized in transcriptomics and single cell data analysis.
My research focuses on the study of transcriptomic profiles of Mexican patients with lupus, with the aim of better understanding the biological mechanisms of the disease and providing reproducible bioinformatics tools that strengthen both biomedical research and academic training in Mexico and Latin America.</p>
<p>I am a Biotechnology Engineer from the Polytechnic University of Sinaloa (UPSIN), originally from Mazatlan, and I did my Master’s and PhD in Plant Biotechnology at Cinvestav, where I studied the conservation of lncRNAs in plants.</p>
<p>My participation in the rOpenSci Champions Program seeks to strengthen communities such as VieRnes de Bioinformatics, R-Ladies Morelia and RSG-Mexico, promoting the creation of R packages with international standards, reproducible documentation and open review.
My vision is to bridge the gap between the global rOpenSci community and local initiatives in Latin America, promoting a more open, inclusive and sustainable science.</p>
</br>
</br>
<h2>
Gladys Choque Ulloa
</h2><figure class="pull-left"><img src="https://i1.wp.com/ropensci.org/img/team/gladys-choque.jpeg?w=250&#038;ssl=1"
alt="Profile photo for Gladys Choque Ulloa"  data-recalc-dims="1"><figcaption>
<p><strong>Gladys Choque Ulloa </br> University of São Paulo </br> Founder of Women in DataLab</strong></p>
</figcaption>
</figure>
<p>Hello!
My name is Gladys Choque Ulloa, I am originally from Peru and currently reside in Brazil.
I have a degree in Statistics, a Master’s in Statistics, and I am currently pursuing my PhD in Computer Science at the ICMC-USP of the University of São Paulo, Brazil. There, I am developing research in computational neuroscience focused on the automatic diagnosis of mental disorders, using Machine Learning models, Neural Networks, LLMs, Causal Inference, and Time Series.
In addition to my academic work, I am founder of the organization Women in DataLab, where I work to reduce the gender gap in technology and data science.</p>
<p>I applied to the rOpenSci Champions Program because I strongly believe in the power of open science and reproducible software to democratize knowledge.
During the program, my goal is to hone my skills in developing R tools and scientific software under global standards.
With this experience I want to strengthen my technical profile and act as a bridge for more researchers in Latin America to adopt collaborative and open practices, enhancing the impact of our scientific community internationally.</p>
</br>
</br>
<h2>
José Daniel Conejeros Pavez
</h2><figure class="pull-right"><img src="https://i1.wp.com/ropensci.org/img/team/jose-conejeros.png?w=250&#038;ssl=1"
alt="Profile photo for José Daniel Conejeros Pavez"  data-recalc-dims="1"><figcaption>
<p><strong>José Daniel Conejeros Pavez </br> Lagrange Fellow, ISI Foundation </br> Early Career Researcher, SENTINET &#8211; UC</strong></p>
</figcaption>
</figure>
<p>I am José Daniel Conejeros, MSc in Statistics from the Pontificia Universidad Católica de Chile.
I am originally from Chile and currently develop my work between Chile and Italy.
In Italy I work as a Lagrange Fellow at the ISI Foundation and as a young researcher at the SENTINET Center (Surveillance, epidemiology and new technologies for emerging infectious threats), working on issues of data science, complex systems, and public health.</p>
<p>My work is situated at the intersection of statistics, epidemiology, and computational social science to understand infectious disease dynamics and health outcomes for different populations.
Currently, I am developing spatmask, an R package oriented to the masking and anonymization of spatial data (geomasking), with the goal of enabling reproducible analyses without compromising the privacy of individuals.
This project aims to bridge the gap between the use of sensitive data for research and the ethical and regulatory restrictions that limit its access and use.</p>
<p>I decided to apply to the Champions Program because many of the challenges I face are not only technical, but also organizational and cultural.
I want to understand how to develop scientific software collaboratively, how to document it correctly and how to foster reproducible practices in contexts where open science is not yet fully installed.
I am interested in learning how to build tools that not only work, but that are understandable, auditable, and useful for research teams and Public Policy decision makers.</p>
<p>Through this program, I hope to strengthen my skills in scientific software development, open review and collaborative work.
My goal is to translate this learning into concrete transfer: training, collaboration, and community building around open science in the region.</p>
<h2>
Linda Cabrera Orellana
</h2><figure class="pull-left"><img src="https://i2.wp.com/ropensci.org/img/team/linda-cabrera-orellana.png?w=250&#038;ssl=1"
alt="Profile photo for Linda Cabrera Orellana"  data-recalc-dims="1"><figcaption>
<p><strong>Linda Cabrera Orellana </br> R-Ladies Ecuador</strong></p>
</figcaption>
</figure>
<p>Hello!
My name is Linda, I am Ecuadorian and currently reside in Granada, Spain.
I work as a Senior Data Analyst in a digital marketing agency and I share my experience as a teacher in business schools, where I teach classes on AI applied to analytics and data visualization.</p>
<p>My project consists of developing an R package with an educational approach to detect, explain, and help correct common structural errors in manually created datasets, specially designed for people with no technical background in data.
I am applying to the program because I want to turn my practical and teaching experience into a real contribution to the open scientific software ecosystem, and to strengthen my skills in R package design, documentation, and maintenance.</p>
<p>I also hope that the project will serve as an educational resource in universities in Ecuador and in the R-Ladies community, and that it will build a bridge between those who collect data and those who analyze it, promoting data literacy in Spanish.</p>
</br>
</br>
<h2>
María Florencia Tames
</h2><figure class="pull-right"><img src="https://i1.wp.com/ropensci.org/img/team/maria-tames.png?w=250&#038;ssl=1"
alt="Profile photo for María Florencia Tames"  data-recalc-dims="1"><figcaption>
<p><strong>María Florencia Tames </br> National University of Córdoba </br> Argentina</strong></p>
</figcaption>
</figure>
<p>My name is María Florencia Tames, I am a professor at the National University of Córdoba (Argentina) and I work in research in the area of air quality, exposure to air pollutants and environmental inequalities in urban contexts in Latin America.</p>
<p>With a team I developed the AirExposure R package, a tool for estimating daily exposure to air pollutants by integrating information on ambient concentrations, mobility, and daily routines.
Through the rOpenSci Champions Program, my goal is to improve and consolidate this package as an open, reproducible and accessible tool for the community.</p>
<p>I am especially interested in strengthening my skills in open software development, incorporating best practices such as documentation, testing, and peer review, and learning to build tools that can be used beyond their original research context.</p>
<p>I am also motivated by the program’s focus on community and working in Spanish, as access to programming resources and training is often limited by language.
I hope to be able to share what I have learned through training activities and contribute to the development of an open scientific software community in Latin America.</p>
</br>
</br>
<h2>
Marina Cecilia Cock
</h2><figure class="pull-left"><img src="https://i1.wp.com/ropensci.org/img/team/marina-cecilia-cock.JPG?w=250&#038;ssl=1"
alt="Profile photo for Marina Cecilia Cock"  data-recalc-dims="1"><figcaption>
<p><strong>Marina Cecilia Cock </br> INCITAP (CONICET-UNLPam) </br> National University of La Pampa</strong></p>
</figcaption>
</figure>
<p>I am from Santa Rosa, La Pampa, Argentina.
I have a degree in Natural Resources and Environment Engineering from the National University of La Pampa (UNLPam) and a PhD in Agrarian Sciences with orientation in Ecology from the University of Buenos Aires (UBA).</p>
<p>I am currently working as a research assistant at the National Council of Scientific and Technical Research (CONICET) and as a teaching assistant in Biogeography and Statistics at the National University of La Pampa.</p>
<p>I applied to the program to learn about the R package review process.
I am interested in participating because I find it especially valuable to join a community where learning is shared and collaborative.
I am looking to strengthen my knowledge in R and then be able to transmit it both in my teaching role and within the R community.</p>
</br>
</br>
<h2>
Patricia Andrea Loto
</h2><figure class="pull-right"><img src="https://i1.wp.com/ropensci.org/img/team/patricia-loto.jpg?w=250&#038;ssl=1"
alt="Profile photo for Patricia Andrea Loto"  data-recalc-dims="1"><figcaption>
<p><strong>Patricia Andrea Loto </br> FACENA, National University of the Northeast (UNNE)</strong></p>
</figcaption>
</figure>
<p>I have a degree in Information Systems and a Diploma in Data Science, Machine Learning and its Applications.
I am currently pursuing a Master’s Degree in Information Technology at the Universidad Nacional del Nordeste.
I live and work in Argentina.</p>
<p>I work as a software developer and data analyst in the public sector, and as a university teacher in the area of systems and programming.
I am a co-founder of RSE Argentina, member of the organizing committee of LatinR, and co-organizer of R-Ladies Resistencia-Corrientes.
From these spaces I work to promote open science, reproducible research software and technology inclusion in Latin America.</p>
<p>In the 2026 cohort I am participating as mentee with the guidance of Guadalupe Pascal.
My project is an R package to systematize the creation of Software and Data Management Plans through standardized templates developed with Quarto, which facilitate the documentation, preservation, and reuse of data and scientific code under international open science standards.</p>
<p>I applied to the Champions Program because I see it as a bridge: between where I am technically and where I want to be.
I hope to learn about the rOpenSci peer review process, deepen my understanding of testing, technical documentation, and everything that makes a package really useful to others.
I’m also looking to connect with a network of developers and researchers who share open science values.
Finally, I want research software developed from the Global South to have greater visibility and for our contributions to be recognized, and I think rOpenSci is the ideal platform for that.</p>
<h2>
Estefania Torrejón
</h2><figure class="pull-left"><img src="https://i1.wp.com/ropensci.org/img/team/estefania-torrejon.png?w=250&#038;ssl=1"
alt="Profile photo for Estefania Torrejón"  data-recalc-dims="1"><figcaption>
<p><strong>Estefania Torrejón </br>NOVA Medical School. Lisbon, Portugal</strong></p>
</figcaption>
</figure>
<p>I am Peruvian and a biologist graduate from the Universidad Nacional Mayor de San Marcos (UNMSM), with a Master’s degree in Biomedical Sciences from the Institute of Hygiene and Tropical Medicine (IHMT) in Lisbon, Portugal.
I currently reside in Portugal, where I work as a predoctoral researcher in bioinformatics at the Metabolic Diseases Research Lab of NOVA Medical School.
I am also director of the International Relations Department of the Peruvian Society of Bioinformatics and Computational Biology.</p>
<p>The project I am developing in the framework of the rOpenSci Champions Program consists of the preparation and submission of my R package, EV-Net, to the CRAN repository.
EV-Net is a bioinformatics tool that identifies and prioritizes molecules present in the cargo of extracellular vesicles (EVs) with high regulatory potential on a receptor tissue of interest.
EVs are structures surrounded by a lipid bilayer that carry a great diversity of active molecules.
These vesicles can move through the organism and reach specific tissues, which is why they are recognized as key mediators of cell-to-cell communication (CCC).
However, most of the bioinformatics tools available to study CCC do not consider EV-mediated communication.
To address this limitation, my collaborators, supervisors, and I developed EV-Net.</p>
<p>Being an rOpenSci Champion is an invaluable opportunity to receive the necessary training, coaching and mentoring to bring EV-Net up to the required quality standards and to be successfully incorporated into CRAN.</p>
<h2>
Next steps
</h2><p>With the presentation of this new group of Champeons, we begin the fourth edition of the program, the second in Spanish.
This group has already started the training stage and met their mentors.
They will be working for 12 months developing new packages, preparing existing packages to submit to the peer review process, and reviewing other people’s packages.</p>
<p>If you want to follow the development of their projects and where and when their dissemination and communication activities will take place, don’t miss our blog articles, news in our newsletter and social networks.</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/06/09/champions-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/06/eleven-latin-american-voices-for-open-science-the-new-cohort-of-champions-ropensci-2026/">Eleven Latin American Voices for Open Science: The New Cohort of Champions rOpenSci 2026</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401779</post-id>	</item>
		<item>
		<title>Little useless-useful R functions – Ulam Prime Spiral</title>
		<link>https://www.r-bloggers.com/2026/06/little-useless-useful-r-functions-ulam-prime-spiral/</link>
		
		<dc:creator><![CDATA[tomaztsql]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 17:00:17 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://tomaztsql.wordpress.com/?p=11162</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Stanislaw Ulam, Los Alamos, 1963 was bored in a meeting and he started dooddling integers in a spiral and circled the primes. Diagonal lines appeared. He later showed it to Martin Gardner, to Ulam surprise, Gardner published his findings in…Read more ›</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/06/little-useless-useful-r-functions-ulam-prime-spiral/">Little useless-useful R functions – Ulam Prime Spiral</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://tomaztsql.wordpress.com/2026/06/07/little-useless-useful-r-functions-ulam-prime-spiral/"> R – TomazTsql</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">Stanislaw Ulam, Los Alamos, 1963 was bored in a meeting and he started dooddling integers in a spiral and circled the primes. Diagonal lines appeared. He later showed it to Martin Gardner, to Ulam surprise, Gardner published his findings in  Scientific American. We are still confused to this day.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="https://i2.wp.com/tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png?ssl=1" rel="nofollow" target="_blank"><img loading="lazy" data-attachment-id="11169" data-permalink="https://tomaztsql.wordpress.com/2026/06/07/little-useless-useful-r-functions-ulam-prime-spiral/image-343/" data-orig-file="https://tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png" data-orig-size="558,562" 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://i2.wp.com/tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png?w=450&#038;ssl=1" src="https://i2.wp.com/tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png?w=450&#038;ssl=1" alt="" class="wp-image-11169" srcset_temp="https://tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png 558w, https://tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png?w=150 150w, https://tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png?w=298 298w" sizes="(max-width: 558px) 100vw, 558px" data-recalc-dims="1" /></a><figcaption class="wp-element-caption">Ulam prime spiral (or short <a href="https://en.wikipedia.org/wiki/Ulam_spiral" rel="nofollow" target="_blank">Ulam spiral</a>) with dimensions of 150 x 150 and total of 2547prime numbers (11.2 % coverage).</figcaption></figure>
</div>


<p class="wp-block-paragraph">But Ulam was not doodling some little houses or boats, or cars like a normal person. No. He wrote numbers in a spiral. Then circled all the prime numbers. Then stared at what he had created with the dawning horror of a man who has seen too much.</p>



<p class="wp-block-paragraph">So where does the spiral comes from? Start with 1 in the middle. Write the number in a spiral outward. And then highlight all the prime numbers. If you draw long enough diagonal lines will appear.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><a href="https://i1.wp.com/tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif?ssl=1" rel="nofollow" target="_blank"><img loading="lazy" data-attachment-id="11173" data-permalink="https://tomaztsql.wordpress.com/2026/06/07/little-useless-useful-r-functions-ulam-prime-spiral/ulam_100/" data-orig-file="https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif" data-orig-size="860,900" 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="ulam_100" data-image-description="" data-image-caption="" data-large-file="https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif?w=605" src="https://i1.wp.com/tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif?w=450&#038;ssl=1" alt="" class="wp-image-11173" style="aspect-ratio:0.9555634553857092;width:595px;height:auto" srcset_temp="https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif 860w, https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif?w=143 143w, https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif?w=287 287w, https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif?w=768 768w" sizes="(max-width: 860px) 100vw, 860px" data-recalc-dims="1" /></a></figure>
</div>


<p class="wp-block-paragraph">Primary function has initial position and directions calculated, in order to have the symmetry of the plot.</p>


<pre>
ulam_prime_spiral &lt;- function(
    n         = 51,    
    theme     = c(&quot;Cosmic&quot;,&quot;Blueish&quot;,&quot;Classy&quot;,&quot;Psycho&quot;),
    show_nums = FALSE,   # print values (n ≤ 21 only)
    animate   = FALSE,       
    speed     = 1,          
    verbose   = TRUE
) {
  
  theme &lt;- match.arg(theme)
  
  #n must be odd so integers have a unique centre cell 
  if (n %% 2 == 0) { n &lt;- n + 1L }
  if (n &lt; 5) stop(&quot;Size muste be &gt; 5.&quot;)
  total &lt;- n^2
  mid   &lt;- (n + 1L) / 2L            
  
  dr &lt;- c( 0L, -1L,  0L,  1L)   # row deltas:  E  N  W  S
  dc &lt;- c( 1L,  0L, -1L,  0L)   # col deltas:  E  N  W  S
  
  mat   &lt;- matrix(0L, n, n)     
  ord_r &lt;- integer(total)        
  ord_c &lt;- integer(total)        
  
  r &lt;- mid;  
  cc &lt;- mid  
  
  mat[r, cc] &lt;- 1L
  ord_r[1]   &lt;- r
  ord_c[1]   &lt;- cc
  
  d    &lt;- 1L  # current direction index (1=E 2=N 3=W 4=S)
  step &lt;- 1L  # current arm length
  num  &lt;- 2L                     
  
  while (num &lt;= total) {
    for (half in 1:2) {          # each dir is twice 
      for (i in seq_len(step)) {
        r  &lt;- r  + dr[d]
        cc &lt;- cc + dc[d]
        mat[r, cc] &lt;- num
        ord_r[num] &lt;- r
        ord_c[num] &lt;- cc
        num &lt;- num + 1L
        if (num &gt; total) break  
      }
      d &lt;- (d %% 4L) + 1L       # turn left: E→N→W→S→E
      if (num &gt; total) break    
    }
    step &lt;- step + 1L            
  }
  
</pre>


<p class="wp-block-paragraph">and we determine the prime:</p>


<pre>
# Sieve of Eratosthenes   
  is_prime    &lt;- rep(TRUE, total)
  is_prime[1] &lt;- FALSE
  p &lt;- 2L
  while (p * p &lt;= total) {
    if (is_prime[p])
      is_prime[seq.int(p * p, total, p)] &lt;- FALSE
    p &lt;- p + 1L
  }

  prime_mat &lt;- matrix(is_prime[mat], n, n)
  n_primes  &lt;- sum(prime_mat)
  density   &lt;- 100 * n_primes / total
</pre>


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



<p class="wp-block-paragraph">With for type of visuals (Classy, Psycho, Blueish and Cosmic), if you decide to create a grid smaller than 21×21, you can have also the numbers displayed. And of course, the diagonals are visible as well:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><a href="https://i1.wp.com/tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png?ssl=1" rel="nofollow" target="_blank"><img loading="lazy" data-attachment-id="11179" data-permalink="https://tomaztsql.wordpress.com/2026/06/07/little-useless-useful-r-functions-ulam-prime-spiral/image-344/" data-orig-file="https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png" data-orig-size="464,459" 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/tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png?w=450&#038;ssl=1" src="https://i1.wp.com/tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png?w=450&#038;ssl=1" alt="" class="wp-image-11179" style="aspect-ratio:1.0108936170212766;width:454px;height:auto" srcset_temp="https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png 464w, https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png?w=150 150w, https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png?w=300 300w" sizes="(max-width: 464px) 100vw, 464px" data-recalc-dims="1" /></a></figure>
</div>


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



<p class="wp-block-paragraph">As always, the complete code is available on GitHub in  <a href="https://github.com/tomaztk/Useless_R_functions" rel="nofollow" target="_blank">Useless_R_function repository</a>. The complete version of code is here: <a href="https://github.com/tomaztk/Useless_R_functions/blob/main/functions/ulam_spiral.R" rel="nofollow" target="_blank">https://github.com/tomaztk/Useless_R_functions/blob/main/functions/ulam_spiral.R</a></p>



<p class="wp-block-paragraph">Check the repository for future updates!</p>



<p class="wp-block-paragraph">Stay healthy and happy R-coding!</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://tomaztsql.wordpress.com/2026/06/07/little-useless-useful-r-functions-ulam-prime-spiral/"> R – TomazTsql</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/06/little-useless-useful-r-functions-ulam-prime-spiral/">Little useless-useful R functions – Ulam Prime Spiral</a>]]></content:encoded>
					
		
		<enclosure url="https://2.gravatar.com/avatar/84af3d4db7552aa6f0fc3db37b65871290b5174af7d5b84c3f185ca92ccfb237?s=96&#038;d=identicon&#038;r=G" length="0" type="" />
<enclosure url="https://tomaztsql.wordpress.com/wp-content/uploads/2026/05/image-1.png?w=558" length="0" type="" />
<enclosure url="https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/ulam_100.gif?w=860" length="0" type="" />
<enclosure url="https://tomaztsql.wordpress.com/wp-content/uploads/2026/06/image.png?w=464" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401777</post-id>	</item>
		<item>
		<title>Learning Amino Acids Part 1: Non-Polar Amino Acids, Rodrigues Rotation, and Lennard-Jones Potential</title>
		<link>https://www.r-bloggers.com/2026/06/learning-amino-acids-part-1-non-polar-amino-acids-rodrigues-rotation-and-lennard-jones-potential/</link>
		
		<dc:creator><![CDATA[r on Everyday Is A School Day]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.kenkoonwong.com/blog/aa1/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>🧬 Back to basics! Learning non-polar amino acids, what zwitterions actually are, and dipping into the applied math — Rodrigues rotation and Lennard-Jones potential. Slowly building toward optimal phi/psi!</p>
<p>Motivations</p>
<p>We’ve explored quite a bit lately in molecular dynamic simulation and then<br />
protein-protein docking as well the last time. There ...</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/06/learning-amino-acids-part-1-non-polar-amino-acids-rodrigues-rotation-and-lennard-jones-potential/">Learning Amino Acids Part 1: Non-Polar Amino Acids, Rodrigues Rotation, and Lennard-Jones Potential</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.kenkoonwong.com/blog/aa1/"> r on Everyday Is A School Day</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>
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/kePrint/kePrint.js"></script>
<link href="https://www.kenkoonwong.com/blog/aa1/index_files/lightable/lightable.css" rel="stylesheet" />
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/kePrint/kePrint.js"></script>
<link href="https://www.kenkoonwong.com/blog/aa1/index_files/lightable/lightable.css" rel="stylesheet" />
<link href="https://www.kenkoonwong.com/blog/aa1/index_files/htmltools-fill/fill.css" rel="stylesheet" />
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/htmlwidgets/htmlwidgets.js"></script>
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/plotly-binding/plotly.js"></script>
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/typedarray/typedarray.min.js"></script>
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/jquery/jquery.min.js"></script>
<link href="https://www.kenkoonwong.com/blog/aa1/index_files/crosstalk/css/crosstalk.min.css" rel="stylesheet" />
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/crosstalk/js/crosstalk.min.js"></script>
<link href="https://www.kenkoonwong.com/blog/aa1/index_files/plotly-htmlwidgets-css/plotly-htmlwidgets.css" rel="stylesheet" />
<script src="https://www.kenkoonwong.com/blog/aa1/index_files/plotly-main/plotly-latest.min.js"></script>
<blockquote>
<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;" /> Back to basics! Learning non-polar amino acids, what zwitterions actually are, and dipping into the applied math — Rodrigues rotation and Lennard-Jones potential. Slowly building toward optimal phi/psi!</p>
</blockquote>




<h2 id="motivations">Motivations
  <a href="https://www.kenkoonwong.com/blog/aa1/#motivations" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<p>We’ve explored quite a bit lately in molecular dynamic simulation and then 
<a href="https://www.kenkoonwong.com/blog/haddock/#covr" rel="nofollow" target="_blank">protein-protein docking</a> as well the last time. There is still so much to learn. I’ve decided to go back to basics, revisiting our old friends amino acids and try to understand the natural properties behind each one and see if that will make more sense in the future when we’re exploring more. While making notes for myself of all the amino acids, I’ll also try to understand some of the basic math behind the structures. Are you ready !? Lol, I’m not, but let’s go anyway! <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f923.png" alt="🤣" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>




<h2 id="objectives">Objectives:
  <a href="https://www.kenkoonwong.com/blog/aa1/#objectives" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<ul>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#aa" rel="nofollow" target="_blank">Amino Acids</a>
<ul>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#nonpolar" rel="nofollow" target="_blank">Non-polar</a></li>
</ul>
</li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#rodriguez" rel="nofollow" target="_blank">Rodriguez Rotation Formula</a></li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#lj" rel="nofollow" target="_blank">Lennard-Jone Potential Enegery</a>
<ul>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#stretch" rel="nofollow" target="_blank">Bond Stretch</a></li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#angle" rel="nofollow" target="_blank">Bond Angle</a></li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#torsion" rel="nofollow" target="_blank">Proper dihedral</a></li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#nonbond" rel="nofollow" target="_blank">Non-bonded</a></li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#calc" rel="nofollow" target="_blank">Calculating LJ</a></li>
</ul>
</li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#opportunities" rel="nofollow" target="_blank">Opportunities For Improvement</a></li>
<li>
<a href="https://www.kenkoonwong.com/blog/aa1/#lessons" rel="nofollow" target="_blank">Lessons Learnt</a></li>
</ul>




<h2 id="amino-acids">Amino Acids
  <a href="https://www.kenkoonwong.com/blog/aa1/#amino-acids" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<p>Amino acids are the building blocks of proteins, each sharing a common backbone: a central α-carbon bonded to an amino group (–NH₂), a carboxyl group (–COOH), a hydrogen atom, and a variable side chain (R group) that defines each amino acid’s identity and chemistry.</p>
<p align="center">
<img loading="lazy" src="https://i0.wp.com/www.kenkoonwong.com/blog/aa1/feature.png?w=450&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>




<h2 id="non-polar-amino-acids">Non-polar amino acids
  <a href="https://www.kenkoonwong.com/blog/aa1/#non-polar-amino-acids" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<p>Non-polar amino acids have hydrophobic side chains — they avoid water and tend to cluster in the interior of folded proteins, forming the hydrophobic core that drives protein stability. Understanding each one’s shape and bulk is directly relevant to how they pack, how they constrain backbone flexibility, and how substitutions affect enzyme active sites.</p>
<pre>library(tibble)
library(kableExtra)

aa_nonpolar &lt;- tribble(
  ~aa,  ~aa3,  ~name,           ~functional_group,  ~smiles_sidechain,    ~charge_ph7, ~mw_da, ~pka,     ~md_note,                                                                                ~main_function,
  &quot;G&quot;,  &quot;Gly&quot;, &quot;Glycine&quot;,       &quot;H (none)&quot;,         &quot;[H]&quot;,                &quot;Neutral&quot;,   75.03,  NA_real_, &quot;Minimal VDW radius; unrestricted phi/psi; near-zero excluded volume&quot;,                  &quot;Conformational flexibility; tight turns; active site geometry&quot;,
  &quot;A&quot;,  &quot;Ala&quot;, &quot;Alanine&quot;,       &quot;Methyl&quot;,           &quot;C&quot;,                  &quot;Neutral&quot;,   89.09,  NA_real_, &quot;Low steric perturbation; high alpha-helix propensity in force fields&quot;,                 &quot;Helix former; hydrophobic core; alanine-scanning mutagenesis&quot;,
  &quot;V&quot;,  &quot;Val&quot;, &quot;Valine&quot;,        &quot;Isopropyl&quot;,        &quot;CC(C)&quot;,              &quot;Neutral&quot;,   117.15, NA_real_, &quot;Beta-branching restricts psi; favors extended beta-sheet; large gamma-carbons&quot;,        &quot;Beta-sheet core; hydrophobic packing; sickle-cell HbS Glu6Val&quot;,
  &quot;L&quot;,  &quot;Leu&quot;, &quot;Leucine&quot;,       &quot;Isobutyl&quot;,         &quot;CCC(C)C&quot;,            &quot;Neutral&quot;,   131.17, NA_real_, &quot;Flexible chi2; common rotamers at -65/-65 and -65/175; high hydrophobic SASA&quot;,         &quot;Hydrophobic core; leucine zippers; most abundant non-polar in proteomes&quot;,
  &quot;I&quot;,  &quot;Ile&quot;, &quot;Isoleucine&quot;,    &quot;sec-Butyl&quot;,        &quot;CCC(C)&quot;,             &quot;Neutral&quot;,   131.17, NA_real_, &quot;Beta-branching + gamma-branch; most restricted chi1/chi2; large buried SASA&quot;,          &quot;Hydrophobic core; beta-barrel interiors; transmembrane helices&quot;,
  &quot;P&quot;,  &quot;Pro&quot;, &quot;Proline&quot;,       &quot;Pyrrolidine ring&quot;, &quot;C1CCNC1&quot;,             &quot;Neutral&quot;,   115.13, NA_real_, &quot;Fixed phi ~-60; no backbone NH donor; cis/trans isomerism at Xaa-Pro bond&quot;,            &quot;Helix breaker; beta-turns; collagen Gly-Pro-X repeats&quot;,
  &quot;F&quot;,  &quot;Phe&quot;, &quot;Phenylalanine&quot;, &quot;Benzyl&quot;,           &quot;Cc1ccccc1&quot;,          &quot;Neutral&quot;,   165.19, NA_real_, &quot;Rigid aromatic ring; pi-pi stacking and cation-pi in MD energy decomposition&quot;,         &quot;Hydrophobic core; aromatic clusters; ligand binding pockets&quot;,
  &quot;W&quot;,  &quot;Trp&quot;, &quot;Tryptophan&quot;,    &quot;Indolylmethyl&quot;,    &quot;Cc1c[nH]c2ccccc12&quot;, &quot;Neutral&quot;,   204.23, NA_real_, &quot;Indole NH can H-bond; amphipathic at membrane interface; strong 280nm absorbance&quot;,     &quot;Membrane anchoring; fluorescence probe; ligand binding; rarest standard AA&quot;,
  &quot;M&quot;,  &quot;Met&quot;, &quot;Methionine&quot;,    &quot;Thioether&quot;,        &quot;CCSC&quot;,               &quot;Neutral&quot;,   149.20, NA_real_, &quot;Flexible sulfur geometry; oxidizable to sulfoxide in long MD runs; check reactive FF&quot;, &quot;Translation initiation; hydrophobic core; redox sensing&quot;
)

aa_nonpolar |&gt;
  dplyr::select(aa:mw_da) |&gt;
  kbl()
</pre><table>
<thead>
<tr>
<th style="text-align:left;">
<p>aa</p>
</th>
<th style="text-align:left;">
<p>aa3</p>
</th>
<th style="text-align:left;">
<p>name</p>
</th>
<th style="text-align:left;">
<p>functional_group</p>
</th>
<th style="text-align:left;">
<p>smiles_sidechain</p>
</th>
<th style="text-align:left;">
<p>charge_ph7</p>
</th>
<th style="text-align:right;">
<p>mw_da</p>
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">
<p>G</p>
</td>
<td style="text-align:left;">
<p>Gly</p>
</td>
<td style="text-align:left;">
<p>Glycine</p>
</td>
<td style="text-align:left;">
<p>H (none)</p>
</td>
<td style="text-align:left;">
<p>[H]</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>75.03</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>A</p>
</td>
<td style="text-align:left;">
<p>Ala</p>
</td>
<td style="text-align:left;">
<p>Alanine</p>
</td>
<td style="text-align:left;">
<p>Methyl</p>
</td>
<td style="text-align:left;">
<p>C</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>89.09</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>V</p>
</td>
<td style="text-align:left;">
<p>Val</p>
</td>
<td style="text-align:left;">
<p>Valine</p>
</td>
<td style="text-align:left;">
<p>Isopropyl</p>
</td>
<td style="text-align:left;">
<p>CC(C)</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>117.15</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>L</p>
</td>
<td style="text-align:left;">
<p>Leu</p>
</td>
<td style="text-align:left;">
<p>Leucine</p>
</td>
<td style="text-align:left;">
<p>Isobutyl</p>
</td>
<td style="text-align:left;">
<p>CCC(C)C</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>131.17</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>I</p>
</td>
<td style="text-align:left;">
<p>Ile</p>
</td>
<td style="text-align:left;">
<p>Isoleucine</p>
</td>
<td style="text-align:left;">
<p>sec-Butyl</p>
</td>
<td style="text-align:left;">
<p>CCC(C)</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>131.17</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>P</p>
</td>
<td style="text-align:left;">
<p>Pro</p>
</td>
<td style="text-align:left;">
<p>Proline</p>
</td>
<td style="text-align:left;">
<p>Pyrrolidine ring</p>
</td>
<td style="text-align:left;">
<p>C1CCNC1</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>115.13</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>F</p>
</td>
<td style="text-align:left;">
<p>Phe</p>
</td>
<td style="text-align:left;">
<p>Phenylalanine</p>
</td>
<td style="text-align:left;">
<p>Benzyl</p>
</td>
<td style="text-align:left;">
<p>Cc1ccccc1</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>165.19</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>W</p>
</td>
<td style="text-align:left;">
<p>Trp</p>
</td>
<td style="text-align:left;">
<p>Tryptophan</p>
</td>
<td style="text-align:left;">
<p>Indolylmethyl</p>
</td>
<td style="text-align:left;">
<p>Cc1c[nH]c2ccccc12</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>204.23</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>M</p>
</td>
<td style="text-align:left;">
<p>Met</p>
</td>
<td style="text-align:left;">
<p>Methionine</p>
</td>
<td style="text-align:left;">
<p>Thioether</p>
</td>
<td style="text-align:left;">
<p>CCSC</p>
</td>
<td style="text-align:left;">
<p>Neutral</p>
</td>
<td style="text-align:right;">
<p>149.20</p>
</td>
</tr>
</tbody>
</table>
<pre>aa_nonpolar |&gt;
  dplyr::select(aa,aa3,md_note,main_function) |&gt;
  kbl()
</pre><table>
<thead>
<tr>
<th style="text-align:left;">
<p>aa</p>
</th>
<th style="text-align:left;">
<p>aa3</p>
</th>
<th style="text-align:left;">
<p>md_note</p>
</th>
<th style="text-align:left;">
<p>main_function</p>
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">
<p>G</p>
</td>
<td style="text-align:left;">
<p>Gly</p>
</td>
<td style="text-align:left;">
<p>Minimal VDW radius; unrestricted phi/psi; near-zero excluded volume</p>
</td>
<td style="text-align:left;">
<p>Conformational flexibility; tight turns; active site geometry</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>A</p>
</td>
<td style="text-align:left;">
<p>Ala</p>
</td>
<td style="text-align:left;">
<p>Low steric perturbation; high alpha-helix propensity in force fields</p>
</td>
<td style="text-align:left;">
<p>Helix former; hydrophobic core; alanine-scanning mutagenesis</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>V</p>
</td>
<td style="text-align:left;">
<p>Val</p>
</td>
<td style="text-align:left;">
<p>Beta-branching restricts psi; favors extended beta-sheet; large gamma-carbons</p>
</td>
<td style="text-align:left;">
<p>Beta-sheet core; hydrophobic packing; sickle-cell HbS Glu6Val</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>L</p>
</td>
<td style="text-align:left;">
<p>Leu</p>
</td>
<td style="text-align:left;">
<p>Flexible chi2; common rotamers at -65/-65 and -65/175; high hydrophobic SASA</p>
</td>
<td style="text-align:left;">
<p>Hydrophobic core; leucine zippers; most abundant non-polar in proteomes</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>I</p>
</td>
<td style="text-align:left;">
<p>Ile</p>
</td>
<td style="text-align:left;">
<p>Beta-branching + gamma-branch; most restricted chi1/chi2; large buried SASA</p>
</td>
<td style="text-align:left;">
<p>Hydrophobic core; beta-barrel interiors; transmembrane helices</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>P</p>
</td>
<td style="text-align:left;">
<p>Pro</p>
</td>
<td style="text-align:left;">
<p>Fixed phi ~-60; no backbone NH donor; cis/trans isomerism at Xaa-Pro bond</p>
</td>
<td style="text-align:left;">
<p>Helix breaker; beta-turns; collagen Gly-Pro-X repeats</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>F</p>
</td>
<td style="text-align:left;">
<p>Phe</p>
</td>
<td style="text-align:left;">
<p>Rigid aromatic ring; pi-pi stacking and cation-pi in MD energy decomposition</p>
</td>
<td style="text-align:left;">
<p>Hydrophobic core; aromatic clusters; ligand binding pockets</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>W</p>
</td>
<td style="text-align:left;">
<p>Trp</p>
</td>
<td style="text-align:left;">
<p>Indole NH can H-bond; amphipathic at membrane interface; strong 280nm absorbance</p>
</td>
<td style="text-align:left;">
<p>Membrane anchoring; fluorescence probe; ligand binding; rarest standard AA</p>
</td>
</tr>
<tr>
<td style="text-align:left;">
<p>M</p>
</td>
<td style="text-align:left;">
<p>Met</p>
</td>
<td style="text-align:left;">
<p>Flexible sulfur geometry; oxidizable to sulfoxide in long MD runs; check reactive FF</p>
</td>
<td style="text-align:left;">
<p>Translation initiation; hydrophobic core; redox sensing</p>
</td>
</tr>
</tbody>
</table>
<p>Claude generated most of the above information. We’ll add onto the md_note section as we encounter certain things during our MD sims.</p>




<h3 id="whats-zwitterion">What’s Zwitterion?
  <a href="https://www.kenkoonwong.com/blog/aa1/#whats-zwitterion" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h3>
<p>A zwitterion is a molecule that has both positive and negative charges but is overall electrically neutral. In amino acids, the amino group (–NH₂) can accept a proton to become positively charged (–NH₃⁺), while the carboxyl group (–COOH) can lose a proton to become negatively charged (–COO⁻). At physiological pH (~7.4), most amino acids exist as zwitterions, with the amino group protonated and the carboxyl group deprotonated. This dual charge allows amino acids to interact with both polar and non-polar environments, contributing to their solubility in water and their ability to form various interactions in proteins.</p>




<h3 id="what-does-non-polar-actually-mean">What Does Non-polar Actually Mean?
  <a href="https://www.kenkoonwong.com/blog/aa1/#what-does-non-polar-actually-mean" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h3>
<p>It is worth clarifying what “non-polar” actually refers exclusively to the side chain (R group) — specifically that it consists largely of carbon and hydrogen bonds with no net dipole and no ionizable groups, making it hydrophobic and largely indifferent to water. It says nothing about the backbone, which is the same for all amino acids and always carries polar bonds (C=O, N–H). In fact, as mentioned above, all amino acids including non-polar ones exist as zwitterions at physiological pH — a property that comes entirely from the backbone, not the side chain.</p>
<blockquote>
<p>Note to self: All amino acids’ backbones are zwitterions; the R-side chain determines polarity and hydrophobicity. Also, net charge neutral == overall charges equals zero, does not mean the molecule is non-polar.</p>
</blockquote>




<h2 id="rodriguez-rotation-formula">Rodriguez Rotation Formula
  <a href="https://www.kenkoonwong.com/blog/aa1/#rodriguez-rotation-formula" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<p>
<a href="https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula" rel="nofollow" target="_blank">Rodrigues’ rotation formula</a> is a method for rotating a 3D vector in space around a specified axis by a given angle. The formula is expressed as:</p>
<p><code>\(v_{rotation} = v.\cos(\theta) + \sin(\theta)(k \times v) + (1 - \cos(\theta))(k(k \cdot v))\)</code></p>
<p>where <code>v</code> is the original vector, <code>k</code> is the unit vector along the axis of rotation, and <code>θ</code> is the angle of rotation in radians.</p>
<p align="center">
<img loading="lazy" src="https://i1.wp.com/upload.wikimedia.org/wikipedia/commons/6/69/Rodrigues_rotation_formula_animation.gif?w=60%25&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>
<p>This formula apparently is very popular in computer graphics and robotics, but I can see how it can be useful in molecular dynamics as well when we want to rotate a molecule or a part of it around an axis. Especially when we want to estimate the least energy conformation of a molecule. The direction application of this formula in amino acid sequence would be in rearranging the atoms based on <code>phi</code> and <code>psi</code> which are the angles of rotations around the <code>N-Cα</code> and <code>Cα-C</code> bonds of the amino acid backbone, respectively. By applying Rodrigues’ rotation formula, we can calculate the new positions of the atoms in the amino acid after rotating them by the specified angles, allowing us to explore different conformations of the molecule. How I remember which angle is which is <code>Nancy Phi</code> (sounds like some detective show and also N->C) and <code>C C Psi</code> (All with S sound, also Carbon to carbon). We’ll leave the hand calculation until next time, but let’s learn how to rotate a coordinate based on an axis with Rodriguez!</p>
<p>Below I’ll write the code first, then explain. Please feel free to use your mouse to hover over the plotly object and check out the coordinates.</p>
<pre>library(plotly)
library(pracma)

#### Let&#39;s start simple
x1 &lt;- c(0,0,0)
x2 &lt;- c(1,1,1)
x3 &lt;- c(1,2,1)

rodrigues &lt;- function(v, k, theta) {
  k &lt;- k / sqrt(sum(k^2))
  cos(theta)*v + sin(theta)*pracma::cross(k, v) + (1 - cos(theta))*sum(k*v)*k
}

k &lt;- x2 - x1
v &lt;- x3 - x1

result &lt;- rodrigues(v,k,pi/2) #notice this, pi/2 == 90 degrees

pts &lt;- data.frame(
  x = c(x1[1],x2[1],x3[1]),
  y = c(x1[2],x2[2],x3[2]),
  z = c(x1[3],x2[3],x3[3]),
  label = c(&quot;x1&quot;,&quot;x2&quot;,&quot;x3&quot;)
)

pts_rs &lt;- data.frame(
  x = c(x1[1],x2[1],result[1]),
  y = c(x1[2],x2[2],result[2]),
  z = c(x1[3],x2[3],result[3]),
  label = c(&quot;x1&quot;,&quot;x2&quot;,&quot;x3_new&quot;)
)

plot_ly() |&gt;
  add_trace(data=pts, x=~x, y=~y, z=~z,
            type=&quot;scatter3d&quot;, mode=&quot;lines+markers+text&quot;,
            text=~label,
            marker=list(size=8, color=&quot;blue&quot;, opacity=0.5),
            line=list(width=4, color=&quot;blue&quot;, dash=&quot;solid&quot;)) |&gt;
  add_trace(data=pts_rs, x=~x, y=~y, z=~z,
            type=&quot;scatter3d&quot;, mode=&quot;lines+markers+text&quot;,
            text=~label,
            marker=list(size=8, color=&quot;red&quot;, opacity=0.5),
            line=list(width=4, color=&quot;red&quot;, dash=&quot;dash&quot;))
</pre><div class="plotly html-widget html-fill-item" id="htmlwidget-1" style="width:672px;height:480px;"></div>
<script type="application/json" data-for="htmlwidget-1">{"x":{"visdat":{"1559871fbb946":["function () ","plotlyVisDat"],"155984540dd20":["function () ","data"],"1559825758365":["function () ","data"]},"cur_data":"1559825758365","attrs":{"155984540dd20":{"alpha_stroke":1,"sizes":[10,100],"spans":[1,20],"x":{},"y":{},"z":{},"type":"scatter3d","mode":"lines+markers+text","text":{},"marker":{"size":8,"color":"blue","opacity":0.5},"line":{"width":4,"color":"blue","dash":"solid"},"inherit":true},"1559825758365":{"alpha_stroke":1,"sizes":[10,100],"spans":[1,20],"x":{},"y":{},"z":{},"type":"scatter3d","mode":"lines+markers+text","text":{},"marker":{"size":8,"color":"red","opacity":0.5},"line":{"width":4,"color":"red","dash":"dash"},"inherit":true}},"layout":{"margin":{"b":40,"l":60,"t":25,"r":10},"scene":{"xaxis":{"title":"x"},"yaxis":{"title":"y"},"zaxis":{"title":"z"}},"hovermode":"closest","showlegend":true},"source":"A","config":{"modeBarButtonsToAdd":["hoverclosest","hovercompare"],"showSendToCloud":false},"data":[{"x":[0,1,1],"y":[0,1,2],"z":[0,1,1],"type":"scatter3d","mode":"lines+markers+text","text":["x1","x2","x3"],"marker":{"color":"blue","size":8,"opacity":0.5,"line":{"color":"rgba(31,119,180,1)"}},"line":{"color":"blue","width":4,"dash":"solid"},"error_y":{"color":"rgba(31,119,180,1)"},"error_x":{"color":"rgba(31,119,180,1)"},"frame":null},{"x":[0,1,0.75598306414370775],"y":[0,1,1.3333333333333337],"z":[0,1,1.9106836025229594],"type":"scatter3d","mode":"lines+markers+text","text":["x1","x2","x3_new"],"marker":{"color":"red","size":8,"opacity":0.5,"line":{"color":"rgba(255,127,14,1)"}},"line":{"color":"red","width":4,"dash":"dash"},"error_y":{"color":"rgba(255,127,14,1)"},"error_x":{"color":"rgba(255,127,14,1)"},"frame":null}],"highlight":{"on":"plotly_click","persistent":false,"dynamic":false,"selectize":false,"opacityDim":0.20000000000000001,"selected":{"opacity":1},"debounce":0},"shinyEvents":["plotly_hover","plotly_click","plotly_selected","plotly_relayout","plotly_brushed","plotly_brushing","plotly_clickannotation","plotly_doubleclick","plotly_deselect","plotly_afterplot","plotly_sunburstclick"],"base_url":"https://plot.ly"},"evals":[],"jsHooks":[]}</script>
<p>So with the above, we want to start off with 3 points, x1, x2, x3. they all represent their xyz coordinates.</p>
<blockquote>
<p>Funny thing is, xyz coordinate here is different from what I learnt xyz as. I’ve always thought x is horizontal, y is vertical and z is depth. But in this case, x is depth, y is horizontal and z is vertical. I guess it depends on how you look at it.</p>
</blockquote>
<p>Then we want to rotate x3 around the axis defined by x1 and x2 by 90 degrees (pi/2 radians). The <code>rodrigues</code> function takes in the vector <code>v</code> (which is the vector from x1 to x3), the axis <code>k</code> (which is the vector from x1 to x2), and the angle <code>theta</code> (which is pi/2). It returns the new coordinates of x3 after rotation.</p>
<p>Finally, we plot the original points and the rotated point using <code>plotly</code>. The original points are in blue, and the rotated point is in red. You can hover over the points to see their coordinates.</p>
<p>Now if we were to maneuver the 3d plot and align both x1 and x2 into a dot, we can clearly see that it moved 90 degrees anti-clockwise!</p>
<p align="center">
<img loading="lazy" src="https://i0.wp.com/www.kenkoonwong.com/blog/aa1/dot.png?w=60%25&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>
<p>Now there is a pretty cool rule to know where the rotation should occur anti-clockwise vs clockwise is by using your hand !!! Remember this from high school?</p>
<p align="center">
<img loading="lazy" src="https://i2.wp.com/cdn1.byjus.com/wp-content/uploads/2021/09/Angular-Velocity.png?w=60%25&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>
<p>It would be really cool to derive the above formula. There are a lot of videos that have done this. I’m still trying to conceptualize it, let’s leave that for another blog! It sounds interesting and may be a good exercise, especially when we’re venturing into 3d spaces.</p>




<h2 id="lennard-jones-potential-energy">Lennard-Jones Potential Energy
  <a href="https://www.kenkoonwong.com/blog/aa1/#lennard-jones-potential-energy" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<p>The Lennard-Jones potential energy formula is a mathematical model used to describe the interaction between a pair of neutral atoms or molecules. It is given by the equation:</p>
<p><code>\(V(r) = 4\epsilon \left[ \left( \frac{\sigma}{r} \right)^{12} - \left( \frac{\sigma}{r} \right)^6 \right]\)</code></p>
<p>Where:</p>
<ul>
<li><code>\(V(r)\)</code> is the potential energy as a function of the distance <code>\(r\)</code> between the two particles.</li>
<li><code>\(\epsilon\)</code> is the depth of the potential well, representing the strength of the attractive interaction.</li>
<li><code>\(\sigma\)</code> is the finite distance at which the inter-particle potential is zero, representing the effective diameter of the particles.</li>
<li>The term <code>\(\left( \frac{\sigma}{r} \right)^{12}\)</code> represents the repulsive part of the potential, which dominates at short distances due to the Pauli exclusion principle.</li>
<li>The term <code>\(\left( \frac{\sigma}{r} \right)^6\)</code> represents the attractive part of the potential, which dominates at longer distances due to van der Waals forces.</li>
</ul>
<p>The Lennard-Jones potential is widely used in molecular dynamics simulations to model the interactions between non-bonded atoms or molecules, particularly in the context of van der Waals forces. It helps to predict the behavior of particles in a system, such as their equilibrium positions and the energy landscape of molecular interactions.</p>
<p>Wow there are a bunch of terms and word above! I’m getting dizzy just to keep track of what is what. Let’s push through this. To use the above formula, we’d have to have some understanding of the parameters <code>epsilon</code> and <code>sigma</code>. These parameters are typically derived from experimental data or quantum mechanical calculations and are specific to the types of atoms or molecules involved in the interaction. Where to get these parameters? 
<a href="https://raw.githubusercontent.com/openbabel/openbabel/refs/heads/master/data/gaff.dat" rel="nofollow" target="_blank">Here you go &#8211; openbabel:: gaff.dat</a></p>
<p>When you opened <code>gaff.dat</code> there are bunch of numbers! Let’s find the numbers that are meaningful for us. All the below are separated by new lines as you scroll down.</p>




<h3 id="bond-stretch">Bond Stretch
  <a href="https://www.kenkoonwong.com/blog/aa1/#bond-stretch" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h3>
<p>The column names should be: <code>type  mass(g/mol)  polarizability(Å³)  source</code></p>
<p align="center">
<img loading="lazy" src="https://i2.wp.com/www.kenkoonwong.com/blog/aa1/stretch.png?w=450&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>




<h3 id="bond-angle">Bond Angle
  <a href="https://www.kenkoonwong.com/blog/aa1/#bond-angle" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h3>
<p>The column names should be: <code>types   K       r0       source    count   rmsd</code></p>
<p align="center">
<img loading="lazy" src="https://i0.wp.com/www.kenkoonwong.com/blog/aa1/angle.png?w=450&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>




<h3 id="proper-dihedral">Proper Dihedral
  <a href="https://www.kenkoonwong.com/blog/aa1/#proper-dihedral" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h3>
<p>The column names should be: <code>types      div  barrier  phase   periodicity</code></p>
<p align="center">
<img loading="lazy" src="https://i1.wp.com/www.kenkoonwong.com/blog/aa1/torsion.png?w=450&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>




<h3 id="non-bonded">Non-bonded
  <a href="https://www.kenkoonwong.com/blog/aa1/#non-bonded" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h3>
<p>The column names should be: <code>type  R*(Å)   ε(kcal/mol)</code></p>
<p align="center">
<img loading="lazy" src="https://i2.wp.com/www.kenkoonwong.com/blog/aa1/nonbond.png?w=450&#038;ssl=1" alt="image" height="auto" data-recalc-dims="1">
</p>
<p>We are purely interested in the <code>non-bonded</code> section where R* is our <code>sigma = R* × 2 / 2^(1/6)</code> and ε is our <code>epsilon</code>. With the above parameters, we can then calculate the Lennard-Jones potential energy between any two atoms in a molecule. Let’s do a simple calculation for ethanol.</p>




<h3 id="calculating-lj">Calculating LJ
  <a href="https://www.kenkoonwong.com/blog/aa1/#calculating-lj" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h3>
<pre>library(tidyverse)
library(igraph)
# Coordinates (x, y, z) in Angstroms, according to pubchem ethanol molecule
coords &lt;- rbind(
  O   = c( -1.1712,   0.2997,   0.0000),
  C2  = c( -0.0463,  -0.5665,   0.0000),
  C1  = c(  1.2175,   0.2668,   0.0000),
  H4  = c( -0.0958,  -1.2120,   0.8819),
  H5  = c( -0.0952,  -1.1938,  -0.8946),
  H1  = c(  2.1050,  -0.3720,  -0.0177),
  H2  = c(  1.2426,   0.9307,  -0.8704),
  H3  = c(  1.2616,   0.9052,   0.8886),
  H6  = c( -1.1291,   0.8364,   0.8099)
)

# AMBER GAFF parameters (sigma Å, epsilon kcal/mol)
Rstar_to_sigma &lt;- function(Rstar) 2 * Rstar / 2^(1/6)

sigma &lt;- c(
  C1=Rstar_to_sigma(1.9080), C2=Rstar_to_sigma(1.9080),
  O=Rstar_to_sigma(1.7210),
  H1=Rstar_to_sigma(1.4870), H2=Rstar_to_sigma(1.4870),
  H3=Rstar_to_sigma(1.4870), H4=Rstar_to_sigma(1.4870),
  H5=Rstar_to_sigma(1.4870), H6=0.0000
)

epsilon &lt;- c(
  C1=0.1094, C2=0.1094, O=0.2104,
  H1=0.0157, H2=0.0157, H3=0.0157,
  H4=0.0157, H5=0.0157, H6=0.0000
)

# Bonds 
bonds &lt;- tribble(
  ~from, ~to,
  &quot;C1&quot;, &quot;C2&quot;,
  &quot;C2&quot;, &quot;O&quot;,
  &quot;O&quot;, &quot;H6&quot;,
  &quot;C1&quot;, &quot;H1&quot;,
  &quot;C1&quot;, &quot;H2&quot;,
  &quot;C1&quot;, &quot;H3&quot;,
  &quot;C2&quot;, &quot;H4&quot;,
  &quot;C2&quot;, &quot;H5&quot;
)

# count bonds between two atoms
g &lt;- graph_from_data_frame(bonds, directed = FALSE)
g_dist &lt;- distances(g)

# LJ function
lj &lt;- function(r, eps, sig) 4 * eps * ((sig/r)^12 - (sig/r)^6)

# Loop all pairs
atoms &lt;- rownames(coords)
pairs &lt;- combn(atoms, 2, simplify=FALSE) # combn so we don&#39;t repeat
total_V &lt;- vector(mode = &quot;numeric&quot;, length = length(pairs)) 
 
for (i in 1:length(pairs)) {
  
  # each pair
  p &lt;- pairs[[i]]
  from &lt;- p[1]
  to &lt;- p[2]
  num_bond &lt;- g_dist[from, to]
  
  if (num_bond &lt;= 2) next                   
  
  # params needed for LJ
  r   &lt;- sqrt(sum((coords[from,] - coords[to,])^2))
  sig &lt;- (sigma[from] + sigma[to]) / 2
  eps &lt;- sqrt(epsilon[from] * epsilon[to])
  
  # scale if num bond is 3 (4 atoms)
  scale &lt;- if (num_bond == 3) 0.5 else 1.0
  
  # LJ calc
  V &lt;- scale * lj(r, eps, sig)

  cat(from, &quot;-&quot;, to, &quot; num of bonds=&quot;, num_bond, &quot; r=&quot;, r, &quot; V=&quot;, V, &quot;\n&quot;)
  total_V[i] &lt;- V
}

## O - H1  num of bonds= 3  r= 3.344395  V= -0.02733269 
## O - H2  num of bonds= 3  r= 2.642383  V= 0.110613 
## O - H3  num of bonds= 3  r= 2.659841  V= 0.09535471 
## C1 - H6  num of bonds= 3  r= 2.546942  V= 0 
## H4 - H1  num of bonds= 3  r= 2.521587  V= 0.01461147 
## H4 - H2  num of bonds= 3  r= 3.074579  V= -0.007593085 
## H4 - H3  num of bonds= 3  r= 2.514978  V= 0.01576022 
## H4 - H6  num of bonds= 3  r= 2.295394  V= 0 
## H5 - H1  num of bonds= 3  r= 2.507028  V= 0.01720963 
## H5 - H2  num of bonds= 3  r= 2.510736  V= 0.01652426 
## H5 - H3  num of bonds= 3  r= 3.070262  V= -0.007612401 
## H5 - H6  num of bonds= 3  r= 2.845344  V= 0 
## H1 - H6  num of bonds= 4  r= 3.550289  V= 0 
## H2 - H6  num of bonds= 4  r= 2.908137  V= 0 
## H3 - H6  num of bonds= 4  r= 2.392984  V= 0

cat(&quot;\nTotal V_LJ:&quot;, sum(total_V), &quot;kcal/mol\n&quot;)

## 
## Total V_LJ: 0.2275351 kcal/mol
</pre>
<p>Alright, the above we basically were trying to calculate all pairwise atoms that is more than 3 bonds (if it’s exactly 3 bonds, we scale it by half). Alright, now that we know how to do that on simple molecular, next time we can use this to minimize on as we’re seeking optimal phi and psi!</p>




<h2 id="opportunities-for-improvement">Opportunities For Improvement
  <a href="https://www.kenkoonwong.com/blog/aa1/#opportunities-for-improvement" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<ul>
<li>Derive Rodriguez Rotation formula</li>
<li>put both Rodriguez rotation formula and LJ calculation into action to find the optimal phi and psi of amino acid sequence of a protein!</li>
<li>need to include secondary/tertiary structure interactions too</li>
</ul>




<h2 id="lessons-learnt">Lessons learnt
  <a href="https://www.kenkoonwong.com/blog/aa1/#lessons-learnt" rel="nofollow" target="_blank"><svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg">
      <path d="M0 0h24v24H0z" fill="currentColor"></path>
      <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z"></path>
    </svg></a>
</h2>
<ul>
<li>refreshed on rotation and vectors</li>
<li>learnt rodriguez rotation formula</li>
<li>learnt LJ formula</li>
<li>learnt what gaff.dat actually contains.</li>
<li>learnt about phi and psi and what they actually mean.</li>
<li>learnt about net charge == total charge; polar vs non-polar is R-chain dependent.</li>
</ul>
<p>If you like this article:</p>
<ul>
<li>please feel free to send me a 
<a href="https://www.kenkoonwong.com/blog/" rel="nofollow" target="_blank">comment or visit my other blogs</a></li>
<li>please feel free to follow me on 
<a href="https://bsky.app/profile/kenkoonwong.bsky.social" rel="nofollow" target="_blank">BlueSky</a>, 
<a href="https://twitter.com/kenkoonwong/" rel="nofollow" target="_blank">twitter</a>, 
<a href="https://github.com/kenkoonwong/" rel="nofollow" target="_blank">GitHub</a> or 
<a href="https://rstats.me/@kenkoonwong" rel="nofollow" target="_blank">Mastodon</a></li>
<li>if you would like collaborate please feel free to 
<a href="https://www.kenkoonwong.com/contact/" rel="nofollow" target="_blank">contact me</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.kenkoonwong.com/blog/aa1/"> r on Everyday Is A School Day</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/06/learning-amino-acids-part-1-non-polar-amino-acids-rodrigues-rotation-and-lennard-jones-potential/">Learning Amino Acids Part 1: Non-Polar Amino Acids, Rodrigues Rotation, and Lennard-Jones Potential</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401748</post-id>	</item>
		<item>
		<title>Five recent R-universe features you might have missed</title>
		<link>https://www.r-bloggers.com/2026/06/five-recent-r-universe-features-you-might-have-missed/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/06/07/r-universe-updates/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
One of the challenges of working on R-universe is that there is never really a release day.<br />
Unlike software projects that accumulate changes for a big launch, R-universe evolves continuously. New features, infrastructure improvements, UI tweaks, and b...</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/06/five-recent-r-universe-features-you-might-have-missed/">Five recent R-universe features you might have missed</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/06/07/r-universe-updates/"> 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>

<p>One of the challenges of working on R-universe is that there is never really a release day.</p>
<p>Unlike software projects that accumulate changes for a big launch, R-universe evolves continuously. New features, infrastructure improvements, UI tweaks, and build system enhancements are silently deployed all the time without most people noticing.</p>
<p>Every now and then, however, a few features emerge that are worth highlighting. In this technote we look at five recent additions that make R-universe a little nicer, faster, or more convenient to use.</p>
<h2>
1. Social media cards that actually look good
</h2>
<div class="box" >
<figure   >
<div class="img">
<img  src="https://ropensci.r-universe.dev/card.svg" alt="Example of preview card for organization"/>
</div>
<a href="https://ropensci.r-universe.dev/card.svg"  aria-disabled="true" rel="nofollow" target="_blank"></a>
</figure>
</div>
<p>Sharing package links on social media used to be a somewhat underwhelming experience, but not anymore! We provide beautiful preview images every package, article, and universe, for example:</p>
<ul>
<li><a href="https://ropensci.r-universe.dev/card.png" rel="nofollow" target="_blank">https://ropensci.r-universe.dev/card.png</a></li>
<li><a href="https://ropensci.r-universe.dev/card.svg" rel="nofollow" target="_blank">https://ropensci.r-universe.dev/card.svg</a></li>
<li><a href="https://ropensci.r-universe.dev/targets/card.png" rel="nofollow" target="_blank">https://ropensci.r-universe.dev/targets/card.png</a></li>
<li><a href="https://ropensci.r-universe.dev/targets/card.svg" rel="nofollow" target="_blank">https://ropensci.r-universe.dev/targets/card.svg</a></li>
</ul>
<p>Each card includes package or universe stats and is automatically exposed through the appropriate HTML headers (<code>og:image</code>, <code>og:title</code>, etc). Whenever somebody shares a package link, the preview should look a bit more polished without requiring any work from package maintainers.</p>
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:sh6yzcjqgrzfyn5ka2hut3o5/app.bsky.feed.post/3mkuf6q2xc226" data-bluesky-cid="bafyreichh7mv56ykfbnyyfstbgqlrftmpbrw5qtkhptgp5zgtrypovrmga" data-bluesky-embed-color-mode="system"><p lang="en">R-universe now generates social media preview cards for each package, like this one: ropensci.r-universe.dev/targets. You can also get the card manually from the /{package}/card.png API (or svg).<br><br><a href="https://bsky.app/profile/did:plc:sh6yzcjqgrzfyn5ka2hut3o5/post/3mkuf6q2xc226?ref_src=embed" rel="nofollow" target="_blank">[image or embed]</a></p>— Jeroen Ooms (<a href="https://bsky.app/profile/did:plc:sh6yzcjqgrzfyn5ka2hut3o5?ref_src=embed" rel="nofollow" target="_blank">@jeroenooms.bsky.social</a>) <a href="https://bsky.app/profile/did:plc:sh6yzcjqgrzfyn5ka2hut3o5/post/3mkuf6q2xc226?ref_src=embed" rel="nofollow" target="_blank">12:01 · May 2, 2026</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>
<p>When a link to a vignette article is shared, R-universe automatically extracts the title and section headings from the document to generate a more informative description. For example <a href="https://bsky.app/profile/jeroenooms.bsky.social/post/3mnprwytac22e" rel="nofollow" target="_blank">this one</a>.</p>
<p>All this won’t guarantee your package goes viral, but at least it looks cool <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2>
2. PACKAGES.rds support (or: implementing R internals in JavaScript)
</h2><p>This feature is mostly invisible, but improves performance of installing packages in R, and therefore also the workflow of building packages in R-universe:</p>
<p>Every CRAN-like repository needs an index file which lists all the content from that repo. This file may be provided in a text-based <a href="https://ropensci.r-universe.dev/src/contrib/PACKAGES" rel="nofollow" target="_blank">PACKAGES</a> format and/or a binary <a href="https://ropensci.r-universe.dev/src/contrib/PACKAGES.rds" rel="nofollow" target="_blank">PACKAGES.rds</a> format (rds is R’s internal binary serialization format, see <code>?saveRDS</code>).</p>
<p>Historically R-universe supported only the former text-based format, because all repository metadata is generated on-request in JavaScript on the server side, and emitting DCF text streams from our database is fast and easy. However, on the R side, loading RDS is a bit faster than parsing a text, which becomes noticeable for large repositories like <a href="https://bioc.r-universe.dev/" rel="nofollow" target="_blank">https://bioc.r-universe.dev/</a>.</p>
<p>So therefore we now also serve the <a href="https://ropensci.r-universe.dev/src/contrib/PACKAGES.rds" rel="nofollow" target="_blank">PACKAGES.rds</a> files. The implementation exists in this <a href="https://github.com/r-universe-org/packages-rds/blob/main/src/index.js" rel="nofollow" target="_blank">NPM package</a> which reverse engineers a subset of the R RDS serializer, so that we can easily run it in our <a href="https://github.com/r-universe-org/express-frontend" rel="nofollow" target="_blank">express stack</a>. On MacOS and Windows it defaults to the new zstd compression, which makes it even faster than CRAN.</p>
<h2>
3. Fancy sort/filter bars in the WebUI
</h2>
<div class="box" >
<figure   >
<div class="img">
<img  src="https://i2.wp.com/ropensci.org/blog/2026/06/07/r-universe-updates/cover.png?w=578&#038;ssl=1" alt="A screenshot of the new search bar" data-recalc-dims="1"/>
</div>
<a href="https://ropensci.org/blog/2026/06/07/r-universe-updates/cover.png"  aria-disabled="true" rel="nofollow" target="_blank"></a>
</figure>
</div>
<p>The styling of universe-level pages that list packages, articles, and datasets have been improved, gaining some nice interactive filter and sort capabilities. For example the <a href="https://ropensci.r-universe.dev/packages" rel="nofollow" target="_blank"><code>/packages</code></a> page now allows you to do a (fuzzy) search looking for keywords that appear in package descriptions/tags/authors/etc, and sort the packages based on their of stars / downloads / dependents / etc.</p>
<p>A similar filter bar is available on the <a href="https://ropensci.r-universe.dev/articles" rel="nofollow" target="_blank"><code>/articles</code></a> and <a href="https://ropensci.r-universe.dev/datasets" rel="nofollow" target="_blank"><code>/datasets</code></a> pages to help you search those as well.</p>
<h2>
4. For the impatient: trigger a sync manually
</h2>
<div class="box" >
<figure   >
<div class="img">
<img  src="https://i2.wp.com/ropensci.org/blog/2026/06/07/r-universe-updates/sync.png?w=578&#038;ssl=1" alt="A screenshot of the new search sync button" data-recalc-dims="1"/>
</div>
<a href="https://ropensci.org/blog/2026/06/07/r-universe-updates/sync.png"  aria-disabled="true" rel="nofollow" target="_blank"></a>
</figure>
</div>
<p>R-universe automatically checks for updates in upstream git repositories and package registries approximately once per hour. Occasionally, however, you have just pushed a commit, fixed a build issue, updated a vignette, or corrected a typo, and waiting an hour suddenly feels like a very long time.</p>
<p>To accommodate the impatient among us, a new sync button has been added to the universe sidebar. Clicking the button immediately triggers a sync to check for any updates.</p>
<h2>
5. Making check results easier to find and share
</h2>
<div class="box" >
<figure   >
<div class="img">
<img  src="https://i0.wp.com/ropensci.org/blog/2026/06/07/r-universe-updates/checks1.png?w=578&#038;ssl=1" alt="A screenshot of the checks table on the R-Universe website" data-recalc-dims="1"/>
</div>
<a href="https://ropensci.org/blog/2026/06/07/r-universe-updates/checks1.png"  aria-disabled="true" rel="nofollow" target="_blank"></a>
</figure>
</div>
<p>For some organizations, package checks are among the most important parts of R-universe. We’ve made several improvements to make check results easier to access and easier to share with collaborators.</p>
<p>First, package pages now support direct links to the check table using the <code>#checktable</code> anchor, for example: <a href="https://jeroen.r-universe.dev/curl#checktable" rel="nofollow" target="_blank">https://jeroen.r-universe.dev/curl#checktable</a>. Second, build logs and build artifacts linked in this table can now be downloaded without requiring GitHub authentication. The underlying files still live on GitHub Actions, but R-universe now proxies the download links. This means users can access logs and build artifacts directly from the package page, even if they do not have a GitHub account. So “I don’t have GitHub” is no longer available as an excuse for ignoring check failures.</p>
<div class="box" >
<figure   >
<div class="img">
<img  src="https://i2.wp.com/ropensci.org/blog/2026/06/07/r-universe-updates/checks2.png?w=578&#038;ssl=1" alt="A screenshot of the checks table as GitHub summary" data-recalc-dims="1"/>
</div>
<a href="https://ropensci.org/blog/2026/06/07/r-universe-updates/checks2.png"  aria-disabled="true" rel="nofollow" target="_blank"></a>
</figure>
</div>
<p>Finally, build runs now include a deployment summary generated via the <a href="https://github.blog/news-insights/product-news/supercharging-github-actions-with-job-summaries/" rel="nofollow" target="_blank">GitHub Actions Job Summaries feature</a>: navigate to any <a href="https://github.com/r-universe/jeroen/actions/runs/26840445416" rel="nofollow" target="_blank">build run</a> and scroll down, there you find a summary table showing exactly the data deployed to R-universe during that run, including package checks and deployment results. This makes it easier to inspect builds directly from GitHub without having to cross-reference multiple pages.</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/06/07/r-universe-updates/"> 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/06/five-recent-r-universe-features-you-might-have-missed/">Five recent R-universe features you might have missed</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401746</post-id>	</item>
		<item>
		<title>Welcome Joe Zhu</title>
		<link>https://www.r-bloggers.com/2026/06/welcome-joe-zhu/</link>
		
		<dc:creator><![CDATA[Ross Farrugia]]></dc:creator>
		<pubDate>Thu, 04 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/welcome-joe-zhu.html</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Hi pharmaverse community,<br />
I wanted to share that Joe Zhu will be taking over from me (Ross Farrugia) as the Roche/Genentech representative on our pharmaverse council.<br />
Having been one of the initial co-founders of pharmaverse back in mid-2020, ...</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/06/welcome-joe-zhu/">Welcome Joe Zhu</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://pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/welcome-joe-zhu.html"> pharmaverse 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>
 





<!--------------- typical setup ----------------->
<!--------------- post begins here ----------------->
<p>Hi pharmaverse community,</p>
<p>I wanted to share that Joe Zhu will be taking over from me (Ross Farrugia) as the Roche/Genentech representative on our pharmaverse council.</p>
<p>Having been one of the initial co-founders of pharmaverse back in mid-2020, it feels like the right time after 6 years to bring fresh perspectives and energy, and I couldn’t think of anyone better than Joe for this. I’m incredibly proud (as I hope all of our community are) of how far we’ve come doing our bit to bring countless companies and individuals together to make open source collaborations a reality across pharma.</p>
<p>In our early years, I had the privilege to present in front of a distinguished board and someone there challenged me with “good luck in getting pharmaceutical companies to share code”, in reply I shared the story of admiral and pharmaverse, and they were blown away. In all their years of experience they had never imagined something like this would ever succeed at scale. It is testament to every single one of you in our community (and thanks to supporters like PHUSE) that it has and it continues to grow!</p>
<p>I won’t be going far away, as now I get to continue as an individual contributor to pharmaverse.</p>
<p>Here’s some more details on Joe and where you can learn more about our council:</p>
<p><strong>Bio:</strong></p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/Joe.png?w=578&#038;ssl=1" class="img-fluid figure-img" data-recalc-dims="1"></p>
<figcaption>Joe Zhu</figcaption>
</figure>
</div>
<p>Joe Zhu is a Principal Data Scientist at Roche and the Chief Product Owner and Lead Engineer for the company’s NEST project. A prominent advocate for open-source software in the global pharmaceutical sector, Joe co-founded both the China Pharma R User Group (Pharmarug) and R in Pharma APAC. He is dedicated to bridging the gap between advanced statistics, genomics research, and clinical trial software.</p>
<p>To learn more about our council please check our <a href="https://pharmaverse.org/contribute/council/" rel="nofollow" target="_blank">site page</a>, and you can reach this group anytime using: pharmaverse.council@phuse.global.</p>
<!--------------- appendices go here ----------------->
<div class="cell">
<div class="cell-output-display">


</div>
</div>



<div id="quarto-appendix" class="default"><section id="last-updated" class="level2 appendix"><h2 class="anchored quarto-appendix-heading">Last updated</h2><div class="quarto-appendix-contents">

<p>2026-06-04 07:19:08.001299</p>
</div></section><section id="details" class="level2 appendix"><h2 class="anchored quarto-appendix-heading">Details</h2><div class="quarto-appendix-contents">

<p><a href="https://github.com/pharmaverse/blog/tree/main/posts/2026-06-05-welcome-joe-zhu/welcome-joe-zhu.qmd" rel="nofollow" target="_blank">Source</a>, <a href="https://pharmaverse.github.io/blog/session_info.html" rel="nofollow" target="_blank">Session info</a></p>
</div></section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="nofollow" href="https://creativecommons.org/licenses/by/4.0/" target="_blank">CC BY 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre>@online{farrugia2026,
  author = {Farrugia, Ross},
  title = {Welcome {Joe} {Zhu}},
  date = {2026-06-04},
  url = {https://pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/welcome-joe-zhu.html},
  langid = {en}
}
</pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-farrugia2026" class="csl-entry quarto-appendix-citeas">
Farrugia, Ross. 2026. <span>“Welcome Joe Zhu.”</span> June 4, 2026. <a href="https://pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/welcome-joe-zhu.html" rel="nofollow" target="_blank">https://pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/welcome-joe-zhu.html</a>.
</div></div></section></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://pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/welcome-joe-zhu.html"> pharmaverse 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/06/welcome-joe-zhu/">Welcome Joe Zhu</a>]]></content:encoded>
					
		
		<enclosure url="https://pharmaverse.github.io/blog/posts/2026-06-05-welcome-joe-zhu/Joe.png" length="0" type="image/png" />

		<post-id xmlns="com-wordpress:feed-additions:1">401683</post-id>	</item>
		<item>
		<title>Football meets machine learning: Forecasting the 2026 FIFA World Cup</title>
		<link>https://www.r-bloggers.com/2026/06/football-meets-machine-learning-forecasting-the-2026-fifa-world-cup/</link>
		
		<dc:creator><![CDATA[Achim Zeileis]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 22:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.zeileis.org/news/fifa2026/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>    Probabilistic forecasts for the 2026 FIFA World Cup are obtained by using a hybrid model that combines data, expert insights, and advanced statistical models. The favorite is Spain, closely followed by England, France, and Germany.</p>
<p>...</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/06/football-meets-machine-learning-forecasting-the-2026-fifa-world-cup/">Football meets machine learning: Forecasting the 2026 FIFA World Cup</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.zeileis.org/news/fifa2026/"> Achim Zeileis</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>Probabilistic forecasts for the 2026 FIFA World Cup are obtained by using a hybrid model that combines data, expert insights, and advanced statistical models. The favorite is Spain, closely followed by England, France, and Germany.</p>
    
    
    <div class="row t20 b20">
  <div class="small-8 medium-9 large-10 columns">
  Football fans around the world are looking forward to the kick-off of the 2026 FIFA World Cup in Canada, Mexico, and the United States next week. 48 of the best teams from all around the world will compete from 11 June to 19 July to determine the new World Champion. In anticipation of the tournament the big question is who among the teams will succeed, who will drop out, and who will eventually prevail. While it is, of course, not yet possible to give <em>definitive</em> answers to these questions, we are able to provide <em>probabilistic</em> forecasts for all possible matches using a refined machine learning algorithm. This allows us to explore the likely course of the tournament by simulation.
  </div>
  <div class="small-4 medium-3 large-2 columns">
  <a href="https://www.fifa.com/worldcup/fifaworldcup2026/" alt="2026 FIFA World Cup web page" rel="nofollow" target="_blank"><img src="https://i2.wp.com/upload.wikimedia.org/wikipedia/en/thumb/1/17/2026_FIFA_World_Cup_emblem.svg/250px-2026_FIFA_World_Cup_emblem.svg.png?w=578&#038;ssl=1" alt="2026 FIFA World Cup logo" data-recalc-dims="1" /></a>
  </div>
</div>

<h2 id="winning-probabilities">Winning probabilities</h2>

<p>The forecast is based on a machine learning algorithm that blends a variety of different sources of information: An ability estimate for every team based on historic matches; an ability estimate for every team based on odds from 24 bookmakers; average ratings of the players in each team based on their individual performances in their home clubs and national teams; the average market value of all players in each team according to a wisdom-of-the-crowd approach; further team and country covariates (e.g., FIFA and Elo ratings or GDP). A machine learning algorithm is trained on the results of all major football tournaments (Men’s World Cups and Euros) between 2006 and 2024 and then applied to current information to obtain a forecast for the 2026 FIFA World Cup. More specifically, the algorithm estimates the predicted number of goals for all possible matches between all 48 teams in the tournament. Based on the predicted goals the probabilities for each potential outcome (i.e., 0-0, 1-0, 0-1, 2-0, etc.) in each of these matches can be computed from a bivariate Poisson distribution (here: assuming independence). This allows us to simulate all matches in the group phase and which teams proceed to the knockout stage and who eventually wins. Repeating the simulation 100,000 times yields winning probabilities for each team. The results show that Spain is the favorite for the title with a winning probability of 14.5%, closely followed by England and France, both with 12.4%, and Germany with 11.2%. The winning probabilities for all teams are shown in the barchart below with more information linked in the interactive full-width version.</p>

<p><a href="https://www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_win.html" rel="nofollow" target="_blank">Interactive full-width graphic</a></p>

<p><a href="https://www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_win.html" rel="nofollow" target="_blank"><img src="https://i1.wp.com/www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_win.png?w=578&#038;ssl=1" alt="Barchart: Winning probabilities" data-recalc-dims="1" /></a></p>

<p>The study has been conducted by an international team of researchers: <a href="https://bd.statistik.tu-dortmund.de/professur/arbeitsgruppe/prof-dr-andreas-groll/" rel="nofollow" target="_blank">Andreas Groll</a>, Agamyrat Hanekov, <a href="https://home.himolde.no/hvattum/" rel="nofollow" target="_blank">Lars Magnus Hvattum</a>, <a href="https://rouvenm.github.io/" rel="nofollow" target="_blank">Rouven Michels</a>, <a href="https://www.sg.tum.de/epidemiologie/team/schauberger/" rel="nofollow" target="_blank">Gunther Schauberger</a>, <a href="https://de.linkedin.com/in/elinasukhanova" rel="nofollow" target="_blank">Elina Sukhanova</a>, Sebastian Witte, <a href="https://www.zeileis.org/" rel="nofollow" target="_blank">Achim Zeileis</a>.
The basic idea for the forecast is to proceed in two steps. In the first step, sophisticated statistical models as well as expert insights are employed to determine the strengths of all teams and their players using disparate sets of information. In the second step, a machine learning algorithm decides how to best combine the strength estimates with other information about the teams.</p>

<ul>
  <li>
    <p><em>Historic information: Match abilities.</em><br />
An ability estimate is obtained for every team based on “retrospective” data, namely all historic national matches over the last 8 years (freely curated by <a href="https://www.kaggle.com/datasets/martj42/international-football-results-from-1872-to-2017" rel="nofollow" target="_blank">Mart Jürisoo on Kaggle</a>). A <em>bivariate Poisson model</em> with team-specific fixed effects and assuming independence is fitted to the number of goals scored by both teams in each match. However, rather than equally weighting all matches to obtain <em>average</em> team abilities (or team strengths) over the entire history period, an exponential weighting scheme is employed. This assigns more weight to more recent results and thus yields an estimate of <em>current</em> team abilities. More details can be found in <a href="https://doi.org/10.1177%2F1471082X18817650" rel="nofollow" target="_blank">Ley, Van de Wiele, Van Eetvelde (2019)</a>.</p>
  </li>
  <li>
    <p><em>Future expectation: Bookmaker consensus abilities.</em><br />
Another ability estimate for every team is obtained based on “prospective” data, namely the odds of 24 international bookmakers that reflect their expert expectations for the tournament. Using the <em>bookmaker consensus model</em> of <a href="https://dx.doi.org/10.1016/j.ijforecast.2009.10.001" rel="nofollow" target="_blank">Leitner, Zeileis, Hornik (2010)</a>, the bookmaker odds are first adjusted for the bookmakers’ profit margins (“overround”) and then averaged (on a logit scale) to obtain a consensus for the winning probability of each team. To adjust for the effects of the tournament draw (that might have led to easier or harder groups for some teams), an “inverse” simulation approach is used to infer which team abilities are most likely to lead up to the consensus winning probabilities.</p>
  </li>
  <li>
    <p><em>Individual player contributions: Average player ratings.</em><br />
To infer the “contributions of individual players” in a match, the <em>plus-minus player ratings</em> of <a href="https://doi.org/10.1007/s11750-020-00584-9" rel="nofollow" target="_blank">Pantuso &#038; Hvattum (2021)</a> dissect all matches with a certain player (both on club and on national level) into segments, e.g., between substitutions. Subsequently, the goal difference achieved in these segments is linked to the presence of the individual players during that segment. This yields individual ratings for all players that can be aggregated to average player ratings for each team.</p>
  </li>
  <li>
    <p><em>Wisdom of the crowd: Average market values:</em><br />
Another way to reflect the current quality and the future potential of each player in a team is to consider their expected market value. As the real market values are unknown, the <a href="https://www.transfermarkt.co.uk/" rel="nofollow" target="_blank">Transfermarkt</a> web portal employs a “wisdom-of-the-crowd” approach to determine current expected market values for all players. These are based on discussions relying on publicly available data among the online community members of the portal and moderated and consolidated by expert community members and the portal’s employees.</p>
  </li>
  <li>
    <p><em>Combination with present status: Hybrid random forests.</em><br />
Finally, machine learning is used to combine these four highly aggregated and informative variables with a broad range of further relevant covariates reflecting the current states of the different teams and the countries they come from. Such a hybrid approach was first suggested by <a href="https://doi.org/10.1515/jqas-2018-0060" rel="nofollow" target="_blank">Groll, Ley, Schauberger, Van Eetvelde (2019)</a>. A random forest algorithm is trained to decide how to blend the different ability estimates with team-specific features that are typically less informative but still powerful enough to enhance the forecasts. The features considered comprise team-specific details (e.g., FIFA rank, Elo rating, number of Champions League players) as well as country-specifc socio-economic factors (such as GDP per capita). By combining a large ensemble of rather weakly informative regression trees in a random forest, the relative importances of all the covariates can be inferred automatically. The resulting predicted number of goals for each team can then finally be used to simulate the entire tournament 100,000 times.</p>
  </li>
</ul>

<h2 id="match-probabilities">Match probabilities</h2>

<p>Using the forecasts from the machine learning algorithm yields the predicted number of goals for both teams in each possible match. The explanatory information used for this is the difference between the two teams in each of the variables listed above, i.e., the difference in historic match abilities (on a log scale), the difference in bookmaker consensus abilities (on a log scale), difference in average player ratings of the teams, difference in log market values, etc. The predicted number of goals for the two teams in each match can then be plugged as expectations into two independent Poisson distributions, from which we can compute the probability that a certain match ends in a <em>win</em>, a <em>draw</em>, or a <em>loss</em>. The same can be repeated in overtime, if necessary, and a coin flip is used to decide penalties, if needed.</p>

<p>The following heatmap shows for each possible combination of teams the probability that one team beats the other team in a knockout match. The color scheme uses green vs. purple to signal probabilities above vs. below 50%, respectively. The tooltips for each match in the interactive version of the graphic also print the probabilities for the match to end in a <em>win</em>, <em>draw</em>, or <em>loss</em> after normal time.</p>

<p><a href="https://www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_match.html" rel="nofollow" target="_blank">Interactive full-width graphic</a></p>

<p><a href="https://www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_match.html" rel="nofollow" target="_blank"><img src="https://i0.wp.com/www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_match.png?w=578&#038;ssl=1" alt="Heatmap: Match probabilities" data-recalc-dims="1" /></a></p>

<h2 id="performance-throughout-the-tournament">Performance throughout the tournament</h2>

<p>As the goals for both teams in every single match can be simulated with the approach described above, it is also straightfoward to simulate the entire tournament (here: 100,000 times) providing “survival” probabilities for each team across the different stages.</p>

<p><a href="https://www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_surv.html" rel="nofollow" target="_blank">Interactive full-width graphic</a></p>

<p><a href="https://www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_surv.html" rel="nofollow" target="_blank"><img src="https://i1.wp.com/www.zeileis.org/assets/posts/2026-06-03-fifa2026/p_surv.png?w=578&#038;ssl=1" alt="Line plot: Survival probabilities" data-recalc-dims="1" /></a></p>

<h2 id="odds-and-ends">Odds and ends</h2>

<p>All our forecasts are probabilistic, clearly below 100%, and hence by no means certain. Although we can quantify this uncertainty in terms of probabilities from a multiverse of potential tournaments, it is far from being predetermined which of these potential tournaments we will eventually see during the actual tournament.</p>

<p>Nevertheless the probabilistic view provides us with some interesting insights: For example, compared to predictions for previous tournaments (see e.g., <a href="https://www.zeileis.org/news/fifa2018/" rel="nofollow" target="_blank">2018</a>, <a href="https://www.zeileis.org/news/fifa2022/" rel="nofollow" target="_blank">2022</a>), it is even more uncertain who will win the title as there are a number of teams with good (albeit none with very high) chances of winning the tournament. An important factor for this is the substantially increased size of the tournament with 48 teams (rather than the previous 32) and an additional knockout round. Also, the tournament draw is much more variable, because 8 of the 12 third-ranked teams proceed to the knockout stage with 495 (!) possible permutations for mapping groups to matches in the round of 32.</p>

<p>Moreover, comparing our forecasts to those based only on the bookmakers odds, it is striking that Germany is ranked 4th, closely behind the three top teams, while it is only ranked 7th by many bookmakers. Conversely, Brazil and Argentina are typically ranked higher by the bookmakers but perform worse in our machine-learning-calibrated simulation.</p>

<p>In any case, all of this means that the probabilistic forecasts leave a lot of room for surprises and excitement during the 2026 FIFA World Cup. But what is absolutely certain is that we look forward to an entertaining tournament as football fans (much more than as professional forecasters).</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.zeileis.org/news/fifa2026/"> Achim Zeileis</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/06/football-meets-machine-learning-forecasting-the-2026-fifa-world-cup/">Football meets machine learning: Forecasting the 2026 FIFA World Cup</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401648</post-id>	</item>
		<item>
		<title>A Multi-Agent DDQN Strategic Audit Engine for Silver Markets using Keras/Tensorflow</title>
		<link>https://www.r-bloggers.com/2026/06/a-multi-agent-ddqn-strategic-audit-engine-for-silver-markets-using-keras-tensorflow/</link>
		
		<dc:creator><![CDATA[Selcuk Disci]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 12:46:44 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://datageeek.com/?p=12123</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> 1. Introduction &#038; Theoretical Framework In modern electronic trading markets, algorithmic execution engines drive the vast majority of institutional order flows. Evaluating whether these independent, learning-driven trading algorithms behave competitively or tacitly coordinate has become a critical challenge for quantitative compliance, market microstructure design, and risk management. This technical article implements an ...</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/06/a-multi-agent-ddqn-strategic-audit-engine-for-silver-markets-using-keras-tensorflow/">A Multi-Agent DDQN Strategic Audit Engine for Silver Markets using Keras/Tensorflow</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/06/02/a-multi-agent-ddqn-strategic-audit-engine-for-silver-markets-using-keras-tensorflow/"> 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>

<h2 class="wp-block-heading">1. Introduction &#038; Theoretical Framework</h2>



<p class="wp-block-paragraph">In modern electronic trading markets, algorithmic execution engines drive the vast majority of institutional order flows. Evaluating whether these independent, learning-driven trading algorithms behave competitively or tacitly coordinate has become a critical challenge for quantitative compliance, market microstructure design, and risk management.</p>



<p class="wp-block-paragraph">This technical article implements an automated <strong>Strategic Audit Engine</strong> designed to evaluate algorithmic execution regimes in the Silver futures market (<code>SI=F</code>). Our framework is explicitly built upon the empirical and theoretical foundations laid out by <strong>Koulouris &#038; Campajola (2026)</strong> in their groundbreaking paper, <em>“Memory-Induced Supra-Competitive Outcomes Between Deep Reinforcement Learning Agents in Optimal Trade Execution”</em> (<a href="https://arxiv.org/html/2605.20348v1" rel="nofollow" target="_blank"><strong><em>arXiv:2605.20348v1, May 2026</em></strong></a>).</p>



<h3 class="wp-block-heading">The Core Thesis: Supra-Competitive Outcomes via Memory Paths</h3>



<p class="wp-block-paragraph">Traditional regulatory frameworks look for <em>explicit collusion</em> (active communication or cartel setups). However, Koulouris &#038; Campajola demonstrate a far more subtle phenomenon: when independent Deep Reinforcement Learning (DRL) agents are equipped with memory—meaning they learn from rolling windows of historical price trajectories—they naturally converge toward <strong>supra-competitive outcomes</strong>. These are states where joint rewards remain artificially high, or execution parameters naturally align to mimic cooperation, without any explicit information exchange.</p>



<p class="wp-block-paragraph">To audit this behavior empirically, our engine models a symmetric duopoly market interaction. It maps the actual market execution path against two fundamental game-theoretic baselines:</p>



<ul class="wp-block-list">
<li><strong>The Cooperative Boundary (TWAP / Pareto Frontier):</strong> An idealized, optimal trade execution path where volume is distributed evenly across time to minimize joint market impact and maximize long-term mutual utility.</li>



<li><strong>The Competitive Boundary (Nash Equilibrium):</strong> The aggressive, non-cooperative state where individual agents structurally undercut each other, driving <strong>execution shortfall parameters</strong> to their maximum baseline.</li>
</ul>



<h2 class="wp-block-heading">2. Technical Stack &#038; Environmental Setup</h2>



<p class="wp-block-paragraph">To build a production-grade, reproducible multi-agent simulation pipeline, we leverage a hybrid data-science and deep-learning toolkit within the R ecosystem:</p>



<ul class="wp-block-list">
<li><strong><code>tidyquant</code> &#038; <code>tidyverse</code></strong><span style="margin: 0px;padding: 0px">: Serve as our core data engineering layer, managing financial API queries, formatting continuous return matrices, and handling functional list columns</span>.</li>



<li><strong><code>keras</code> &#038; <code>tensorflow</code>:</strong> Form the algorithmic backbone, allowing us to build, train, and run simultaneous forward/backward passes on Deep Q-Networks.</li>



<li><strong><code>ggtext</code> &#038; <code>glue</code>:</strong> Empower our visualization suite to parse inline HTML canvas rendering and handle dynamic string interpolations smoothly.</li>
</ul>


<pre>
# 1. ENVIRONMENT SETUP
if (!require(&quot;pacman&quot;)) install.packages(&quot;pacman&quot;)
pacman::p_load(tidyquant, tidyverse, ggtext, glue, keras, tensorflow)
</pre>


<h2 class="wp-block-heading">3. Building the Double Deep Q-Network Topology</h2>



<p class="wp-block-paragraph">Following the paper’s thesis on symmetric duopoly interactions, we construct two structurally identical execution agents: <code>agent_A</code> and <code>agent_B</code>. Both utilize a Dense Neural Network (Multilayer Perceptron) architecture to approximate the action-value space, denoted as <strong><em>Q(s, a)</em></strong>.</p>



<p class="wp-block-paragraph">The state space contains 3 features: <strong>Price Deviation</strong>, <strong>Asset Volatility (sigma)</strong>, and <strong>Relative Time Horizon</strong>. The output layer projects to 3 discrete strategic action coordinates via a linear activation function.</p>


<pre>
# 2. SYMMETRIC AGENT ARCHITECTURE
build_strategic_agent &lt;- function(state_size = 3, action_size = 3) {
  model &lt;- keras_model_sequential() %&gt;%
    layer_dense(units = 32, activation = &quot;relu&quot;, input_shape = c(state_size)) %&gt;%
    layer_dense(units = 32, activation = &quot;relu&quot;) %&gt;%
    layer_dense(units = action_size, activation = &quot;linear&quot;)
  
  model %&gt;% compile(
    optimizer = optimizer_adam(learning_rate = 0.001),
    loss = &quot;mse&quot;
  )
  return(model)
}

# Initialize the competing agents
agent_A &lt;- build_strategic_agent()
agent_B &lt;- build_strategic_agent()
</pre>


<h2 class="wp-block-heading">4. Parameterization & Historical Replay Buffer Ingestion</h2>



<p class="wp-block-paragraph">To anchor our agents in empirical reality, we pull 2 years of continuous daily settlement prices for Silver futures (<code>SI=F</code>). We define our microstructural bounds—such as the risk aversion parameter (gamma) and the permanent market impact vector (eta)—alongside a fixed strategic execution memory window (T = 10).</p>


<pre>
# 3. STRATEGIC PARAMETERS
T_horizon &lt;- 10      # Strategic episode length (Memory window)
gamma_param &lt;- 0.0001 # Risk aversion
eta_param &lt;- 0.0005   # Market impact

# 4. HISTORICAL REPLAY DATA (2-Year Training Set)
silver_full &lt;- tq_get(&quot;SI=F&quot;, from = Sys.Date() - 730) %&gt;%
  filter(!is.na(close)) %&gt;%
  mutate(returns = close / lag(close) - 1) %&gt;%
  drop_na()

# Recent window for the final audit visualization
silver_recent &lt;- tail(silver_full, T_horizon)
</pre>


<h2 class="wp-block-heading">5. Dynamic Volatility Corridors</h2>



<p class="wp-block-paragraph">Rather than mapping market behavior against static thresholds, the audit engine computes a volatility-adaptive safety corridor. The boundaries dynamically expand and contract based on the asset’s realized standard deviation (sigma), isolating pure structural noise from intentional strategic maneuvers.</p>


<pre>
# 5. DYNAMIC SIGMA CORRIDORS
current_sigma &lt;- sd(silver_recent$returns, na.rm = TRUE)
if(is.na(current_sigma)) current_sigma &lt;- 0.01 

analysis_data &lt;- silver_recent %&gt;%
  mutate(
    twap_slope = current_sigma * 1.5, 
    nash_slope = current_sigma * 4.0,
    twap_path = first(close) * (1 - seq(0, first(twap_slope), length.out = n())),
    nash_path = first(close) * (1 - seq(0, first(nash_slope), length.out = n())),
    lower_safety_limit = nash_path * (1 - current_sigma)
  )
</pre>


<h2 class="wp-block-heading">6. The Joint Training Replay Engine & Payoff Matrix</h2>



<p class="wp-block-paragraph">This section represents the computational implementation of Koulouris & Campajola’s memory hypothesis. The two agents recursively traverse 2 years of rolling historical windows (<code>window_data</code>).</p>



<p class="wp-block-paragraph">At each node, they sample independent actions based on their weights, facing a non-cooperative game matrix:</p>



<ul class="wp-block-list">
<li><strong>Mutual Cooperation (Action 0, 0):</strong> High joint payout (+10) mimicking a stable, supra-competitive margin.</li>



<li><strong>Mutual Aggressive Competition (Action Match):</strong> Low joint rent (+1), representing the competitive Nash baseline.</li>



<li><strong>Cheating / Under-cutting:</strong> Asymmetric penalization (+5 vs -5).</li>
</ul>


<pre>
# 6. JOINT TRAINING ENGINE (Symmetric Memory Interaction)
message(&quot;Joint Training: Agent A & Agent B are learning Silver Market dynamics...&quot;)

for(i in 1:(nrow(silver_full) - T_horizon)) {
  window_data &lt;- silver_full[i:(i + T_horizon - 1), ]
  vol &lt;- sd(window_data$returns, na.rm = TRUE)
  if(is.na(vol)) vol &lt;- 0.01
  
  state_vec &lt;- matrix(c(1.0, vol, 0.5), nrow = 1)
  
  act_A &lt;- which.max(predict(agent_A, state_vec, verbose = 0)) - 1
  act_B &lt;- which.max(predict(agent_B, state_vec, verbose = 0)) - 1
  
  rewards &lt;- if(act_A == 0 &#038;&#038; act_B == 0) {
    list(A = 10, B = 10) 
  } else if(act_A == act_B) {
    list(A = 1, B = 1)   
  } else {
    if(act_A &gt; act_B) list(A = 5, B = -5) else list(A = -5, B = 5) 
  }
  
  target_A &lt;- predict(agent_A, state_vec, verbose = 0)
  target_B &lt;- predict(agent_B, state_vec, verbose = 0)
  
  target_A[1, act_A + 1] &lt;- rewards$A
  target_B[1, act_B + 1] &lt;- rewards$B
  
  agent_A %&gt;% fit(state_vec, target_A, epochs = 1, verbose = 0)
  agent_B %&gt;% fit(state_vec, target_B, epochs = 1, verbose = 0)
}
</pre>


<h2 class="wp-block-heading">7. Post-Convergence Audit Inference & Regime Selection</h2>



<p class="wp-block-paragraph">Once the networks stabilize, the engine takes the posture of an unbiased financial regulator. It extracts the neural policy configurations, evaluates the actual current execution window, and automatically determines the market regime using an automated classification layer.</p>


<pre>
# 7. FINAL AUDIT INFERENCE
analysis_data &lt;- analysis_data %&gt;%
  rowwise() %&gt;%
  mutate(
    state_v = list(matrix(c(close/twap_path, current_sigma, (T_horizon - row_number())/T_horizon), nrow = 1)),
    q_A = list(predict(agent_A, state_v[], verbose = 0)),
    q_B = list(predict(agent_B, state_v[], verbose = 0)),
    joint_action = (which.max(q_A[]) + which.max(q_B[])) / 2
  ) %&gt;% ungroup()

# 8. STATUS LOGIC (Professional Category Selection & Color Alignment)
last_row &lt;- tail(analysis_data, 1)
market_status &lt;- case_when(
  last_row$close &gt;= last_row$twap_path ~ 
    list(
      label = &quot;**COOPERATIVE:** Pareto-Efficient Alignment&quot;, 
      bg    = &quot;#E8F8F5&quot;,  
      color = &quot;#27AE60&quot;   
    ),
  
  last_row$close &lt; last_row$twap_path &#038; last_row$close &gt;= last_row$nash_path ~ 
    list(
      label = &quot;**NORMAL:** Competitive Nash Equilibrium&quot;, 
      bg    = &quot;#FEF5E7&quot;,  
      color = &quot;#E67E22&quot;   
    ),
  
  TRUE ~ 
    list(
      label = &quot;**LIQUIDITY SHOCK:** Strategic Deviation Detected&quot;, 
      bg    = &quot;#FDEDEC&quot;,  
      color = &quot;#C0392B&quot;   
    )
)
</pre>


<h2 class="wp-block-heading">8. High-Fidelity Infographic Layer</h2>



<p class="wp-block-paragraph">To generate a publication-quality static vector infographic, we map our theme directly via <code>ggplot2</code> and <code>ggtext</code>. By embedding the color palette directly into the HTML subtitle strings and forcing label formatting via <code>scales::percent</code>, we create a clean, high-contrast dashboard visualization.</p>


<pre>
# 9. GGPLOT PRODUCTION VISUALIZATION (Static Mode with ggtext Integration)
ggplot(analysis_data, aes(x = date)) +
  geom_ribbon(aes(ymin = lower_safety_limit, ymax = twap_path), fill = &quot;darkgray&quot;, alpha = 0.3) +
  
  geom_line(aes(y = twap_path, color = &quot;TWAP (Cooperative)&quot;), size = 1) +
  geom_line(aes(y = nash_path, color = &quot;Nash (Competitive)&quot;), size = 1) +
  geom_line(aes(y = close, color = &quot;Actual Price&quot;), size = 1.3) +
  scale_y_continuous(labels = scales::label_currency()) +
  
  geom_richtext(
    aes(x = median(date), y = max(close, twap_path) * 1.02, label = market_status$label),
    fill = market_status$bg, color = market_status$color, size = 4,
    family = &quot;Roboto Slab&quot; 
  ) +
  
  scale_color_manual(
    name = NULL,
    values = c(&quot;Actual Price&quot; = &quot;steelblue&quot;, &quot;TWAP (Cooperative)&quot; = &quot;#27AE60&quot;, &quot;Nash (Competitive)&quot; = &quot;#E67E22&quot;)
  ) +
  
  labs(
    title = &quot;Silver Market Strategic Audit Engine&quot;,
    subtitle = paste0(
      &quot;&lt;span style=&#039;color:#27AE60;&#039;&gt;─── **Cooperative Zone**&lt;/span&gt; | &quot;,
      &quot;&lt;span style=&#039;color:#E67E22;&#039;&gt;─── **Competitive Zone**&lt;/span&gt; | &quot;,
      &quot;&lt;span style=&#039;color:steelblue;&#039;&gt;─── **Actual Execution**&lt;/span&gt;&lt;br&gt;&lt;br&gt;&quot;,
      &quot;&lt;span style=&#039;color:darkgrey;&#039;&gt;**Strategic Corridor** (Supra-Competitive Margin Zone)&lt;/span&gt;&quot;
    ),
    x = NULL, y = NULL,
    caption = glue(&quot;Dynamic Sigma: {scales::percent(current_sigma, accuracy = 0.01)} | Shortfall: {round(actual_cost, 2)}%&quot;)
  ) +
  
  theme_minimal(base_family = &quot;Roboto Slab&quot;) +
  theme(plot.title = element_text(face = &quot;bold&quot;, size = 16),
        plot.subtitle = element_markdown(face = &quot;bold&quot;), 
        axis.text = element_text(face = &quot;bold&quot;),
        legend.position = &quot;none&quot;)
</pre>


<figure data-wp-context="{"imageId":"6a1ed21ecb192"}" data-wp-interactive="core/image" data-wp-key="6a1ed21ecb192" class="wp-block-image size-large wp-lightbox-container"><img loading="lazy" data-attachment-id="12146" data-permalink="https://datageeek.com/2026/06/02/a-multi-agent-ddqn-strategic-audit-engine-for-silver-markets-using-keras-tensorflow/strategic_silver/" data-orig-file="https://datageeek.com/wp-content/uploads/2026/06/strategic_silver.png" data-orig-size="1110,646" 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="strategic_silver" data-image-description="" data-image-caption="" data-large-file="https://i1.wp.com/datageeek.com/wp-content/uploads/2026/06/strategic_silver.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/06/strategic_silver.png?w=450&#038;ssl=1" alt="" class="wp-image-12146" srcset_temp="https://i1.wp.com/datageeek.com/wp-content/uploads/2026/06/strategic_silver.png?w=450&#038;ssl=1 1024w, https://datageeek.com/wp-content/uploads/2026/06/strategic_silver.png?w=150 150w, https://datageeek.com/wp-content/uploads/2026/06/strategic_silver.png?w=300 300w, https://datageeek.com/wp-content/uploads/2026/06/strategic_silver.png?w=768 768w, https://datageeek.com/wp-content/uploads/2026/06/strategic_silver.png 1110w" sizes="(max-width: 1024px) 100vw, 1024px" 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">9. Empirics & Compliance Conclusion</h2>



<p class="wp-block-paragraph">When we run the complete inference loop on our terminal Silver execution window, the strategic narrative clarifies perfectly: <strong>Actual Execution</strong> (the blue trajectory) tracks downward, bypassing the cooperative upper envelope and adhering directly to the competitive boundaries.</p>



<p class="wp-block-paragraph">The audit badge cleanly returns a status of <strong>NORMAL: Competitive Nash Equilibrium</strong>, with the terminal metrics computing the exact execution shortfall at <strong>1.59%</strong> as indicated in the chart above. While the agents are technically complex neural networks capable of learning memory patterns, the actual price action during this specific ten-day horizon reflects a highly competitive regime, keeping the execution within standard Nash boundaries rather than shifting into a supra-competitive zone.</p>



<p class="wp-block-paragraph">For quantitative auditors and systemic risk monitors, this approach signals a paradigm shift. Static threshold tests are blind to multi-agent learning trends. By deploying neural simulation baselines, structural compliance teams can automatically audit execution algorithms, isolating algorithmic alignment from pure market variance.</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/06/02/a-multi-agent-ddqn-strategic-audit-engine-for-silver-markets-using-keras-tensorflow/"> 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/06/a-multi-agent-ddqn-strategic-audit-engine-for-silver-markets-using-keras-tensorflow/">A Multi-Agent DDQN Strategic Audit Engine for Silver Markets using Keras/Tensorflow</a>]]></content:encoded>
					
		
		<enclosure url="https://datageeek.com/wp-content/uploads/2026/06/datageeek-6a1ecc01a4357.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/06/strategic_silver.png?w=1024" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401602</post-id>	</item>
		<item>
		<title>A New Guide: Organizing Events for First-time Contributors</title>
		<link>https://www.r-bloggers.com/2026/06/a-new-guide-organizing-events-for-first-time-contributors-2/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/06/02/ftc-guide/</guid>

					<description><![CDATA[<p>Making your first contribution to open source can be both empowering and yet very intimidating.<br />
– rOpenSci FTC Guide</p>
<p>Last year we were grateful to receive funding from NumFOCUS1 to organize a series of events designed to reduce barriers restri...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/a-new-guide-organizing-events-for-first-time-contributors-2/">A New Guide: Organizing Events for First-time Contributors</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/06/02/ftc-guide/"> 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>

<blockquote>
<p>Making your first contribution to open source can be both empowering and yet very intimidating.</p>
<p>– <a href="https://ftc-guide.ropensci.org/" rel="nofollow" target="_blank">rOpenSci FTC Guide</a></p>
</blockquote>
<p>Last year we were grateful to receive funding from NumFOCUS<sup id="fnref:1"><a href="https://ropensci.org/blog/2026/06/02/ftc-guide/#fn:1" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">1</a></sup> to organize a series of events designed to reduce barriers restricting First-Time Contributors to Free and Open Source Software (FOSS).
There are many barriers<sup id="fnref:2"><a href="https://ropensci.org/blog/2026/06/02/ftc-guide/#fn:2" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">2</a></sup> to first time contributions, but making these contributions can be an empowering experience.
To help reduce some of these barriers we hosted two types of events: <strong>mini-translathons</strong> and <strong>mini-hackathons</strong>.</p>
<p>A <strong>mini-translathon</strong> is a short, live, coworking session, focused on translation and localization contributions.
Participants review and improve translations of documentation, websites, or other resources.
They also work with guidance from mentors and editors, often collaborating in language-specific groups.
The goal is to make content accessible in multiple languages while helping newcomers learn <a href="https://ropensci.org/multilingual-publishing/" rel="nofollow" target="_blank">translation workflows and tools</a>.</p>
<!--- cSpell: language en,pt --->
<p>We paired our mini-translathon with a Portuguese Community Call (<a href="https://ropensci.org/commcalls/translation-portuguese/" rel="nofollow" target="_blank"><em>A comunidade R fala português</em></a><sup id="fnref:3"><a href="https://ropensci.org/blog/2026/06/02/ftc-guide/#fn:3" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">3</a></sup>) which preceded the <a href="https://latinr.org/en/cronograma/translaton/translaton-en.html" rel="nofollow" target="_blank">mini-translathon</a> at LatinR 2024.</p>
<figure><img src="https://i2.wp.com/ropensci.org/blog/2026/06/02/ftc-guide/Translathon-LatinR-3.png?w=578&#038;ssl=1"
alt="Screenshot of the Zoom call for the translathon, showing participants and a screenshare of the pull-request translation process." data-recalc-dims="1"><figcaption>
<p>Demonstrating the PR process during the translathon</p>
</figcaption>
</figure>
<p>Similarly, a <strong>mini-hackathon</strong> is a short (typically ~2-hour), live, online coworking session designed to help people make their first contributions to open-source software.
Participants work on small, well-prepared tasks such as fixing bugs, improving code, or updating documentation.
Maintainers and mentors are available in real time to guide them, answer questions, and support the contribution process.
The focus is on learning by doing in a collaborative and supportive environment.</p>
<p>Again, we paired our mini-hackathons with an English Community Call (<a href="https://ropensci.org/commcalls/first-time-contributor/" rel="nofollow" target="_blank">From Novice to Contributor: Making and Supporting First-Time Contributions to FOSS</a>) which was then followed by the two <a href="https://ropensci.org/events/coworking-2025-02/" rel="nofollow" target="_blank">mini-hackathons</a> to support contributors to coding projects.</p>
<figure><img src="https://i1.wp.com/ropensci.org/blog/2026/06/02/ftc-guide/Yani-JuanCruz-miniHackathon.jpg?w=578&#038;ssl=1"
alt="Photo of two participants of the mini-hackathon participating on their computers from the same location." data-recalc-dims="1"><figcaption>
<p>Yani and Juan Cruz participate in the mini-hackathon together</p>
</figcaption>
</figure>
<p>Part of the grant we received also allowed us to write up our processes and findings as a <strong>guide book</strong> “<a href="https://ftc-guide.ropensci.org/" rel="nofollow" target="_blank">From User to Contributor: Organizing Events for First-Time Contributors</a>”, which we are excited to share with you!</p>
<figure><img src="https://i2.wp.com/ropensci.org/blog/2026/06/02/ftc-guide/ftc_guide.png?w=578&#038;ssl=1"
alt="Screenshot of the introduction to the FTC Guide showing the Table of Contents including &#39;Preface&#39;, &#39;Pilot&#39;, &#39;Events Overview&#39;, &#39;Community Calls&#39;, &#39;Mini-translathons&#39;, &#39;Mini-hackathons&#39;, and &#39;Appendices&#39;" data-recalc-dims="1">
</figure>
<p>In our guide we start by discussing <a href="https://ftc-guide.ropensci.org/" rel="nofollow" target="_blank">why supporting first time contributors is important</a>.
We describe our <a href="https://ftc-guide.ropensci.org/pilot.html" rel="nofollow" target="_blank">pilot events</a>, how they went, feedback we received, and ideas for future improvement.
The main chapters of the guide then cover how to run these events in greater detail.
This includes <a href="https://ftc-guide.ropensci.org/event-overview.html" rel="nofollow" target="_blank">Timelines</a>, <a href="https://ftc-guide.ropensci.org/commcall.html" rel="nofollow" target="_blank">Community Calls</a>, <a href="https://ftc-guide.ropensci.org/translathon.html" rel="nofollow" target="_blank">Mini-translathons</a>, and <a href="https://ftc-guide.ropensci.org/hackathon.html" rel="nofollow" target="_blank">Mini-hackathons</a>, all from the perspective of supporting first time contributors.
In the <a href="https://ftc-guide.ropensci.org/resources.html" rel="nofollow" target="_blank">Appendices</a> we include communication examples and templates.</p>
<p>We hope that this guide can be useful to other communities beyond rOpenSci.
If you use this guide to create your own events to support first time contributors, we hope <a href="https://ropensci.org/usecases/" rel="nofollow" target="_blank">you’ll let us know</a>!</p>
<p>Thanks to NumFOCUS for the Small Development Grant to support this work.</p>
<!--- cSpell: ignore CSCW --->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://numfocus.org/" rel="nofollow" target="_blank">NumFocus</a> is rOpenSci’s fiscal sponsor. <a href="https://ropensci.org/blog/2026/06/02/ftc-guide/#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>Steinmacher et al.
identified 13 social barriers.
Igor Steinmacher, Tayana Conte, Marco Aurélio Gerosa, and David Redmiles.
2015.
Social Barriers Faced by Newcomers Placing Their First Contribution in Open Source Software Projects.
In Proceedings of the 18th ACM Conference on Computer Supported Cooperative Work &#038; Social Computing (CSCW ‘15).
Association for Computing Machinery, New York, NY, USA, 1379–1392.
<a href="https://doi.org/10.1145/2675133.2675215%E2%86%A9%EF%B8%8E" rel="nofollow" target="_blank">https://doi.org/10.1145/2675133.2675215<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> <a href="https://ropensci.org/blog/2026/06/02/ftc-guide/#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>
<li id="fn:3">
<p>“The R community speaks Portuguese” <a href="https://ropensci.org/blog/2026/06/02/ftc-guide/#fnref:3" 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>
<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/06/02/ftc-guide/"> 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/06/a-new-guide-organizing-events-for-first-time-contributors-2/">A New Guide: Organizing Events for First-time Contributors</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401677</post-id>	</item>
		<item>
		<title>11 Test Smells That Make Your Tests Lie to You</title>
		<link>https://www.r-bloggers.com/2026/06/11-test-smells-that-make-your-tests-lie-to-you/</link>
		
		<dc:creator><![CDATA[Jakub Sobolewski]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://jakubsobolewski.com/blog/test-smells-in-r</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Learn to recognize problems in R test code that cause your test suite to pass while hiding real bugs. Detect those issues and start writing more trustworthy tests.</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/06/11-test-smells-that-make-your-tests-lie-to-you/">11 Test Smells That Make Your Tests Lie to 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://jakubsobolewski.com/blog/test-smells-in-r"> Jakub Sobolewski</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/jakubsobolewski.com/blog/test-smells-in-r/og-image.png?w=578&#038;ssl=1" alt="11 Test Smells That Make Your Tests Lie to You" data-recalc-dims="1" /></p><p>Green tests don’t automatically mean correct code.</p>
<p>Tests can be written in ways that pass reliably, survive every <code>devtools::test()</code> run, and still tell you almost nothing about whether your code works. The software engineering community has a name for these patterns: test smells. They’re structural problems in test code — not bugs, but patterns that signal the tests are doing less or something different than they appear.</p>
<p>Have you ever reviewed a pull request and only skimmed the test code, because why bother if you see in CI they’re green?</p>
<p>The problem with test code is that:</p>
<ul>
<li>it can be written in ways that it looks fine but is actually misleading,</li>
<li>you may not review test code with the same seriousness as production code,</li>
<li>you may not know what to look for in test code.</li>
</ul>
<p>Here’s a list of problems in test code that show up regularly, and what to do about them.</p>
<h2 id="1-mystery-guest">1. Mystery Guest</h2>
<p><strong>🫣 The smell:</strong> the test relies on state that isn’t visible inside the test itself.</p>
<pre>test_that(&quot;report uses the correct currency symbol&quot;, {
  result &lt;- format_currency(1000)
  expect_equal(result, &quot;£1,000&quot;)
})</pre>
<p>This test passes on one machine and fails on another — and the reason isn’t in the test. Somewhere upstream, a <code>.Rprofile</code> file sets <code>options(currency = &quot;GBP&quot;)</code>, or maybe it’s set with an environment variable. In a fresh session without that state, <code>format_currency</code> falls back to <code>&quot;$1,000&quot;</code>.</p>
<p>The test has an invisible guest. You can’t understand it by reading it.</p>
<p>The fix is to make the dependency explicit — set it and clean it up within the test itself using <code>withr</code>:</p>
<pre>test_that(&quot;report uses the correct currency symbol&quot;, {
  withr::with_options(list(currency = &quot;GBP&quot;), {
    result &lt;- format_currency(1000)
    expect_equal(result, &quot;£1,000&quot;)
  })
})</pre>
<p>It might be also a default value in the function itself that is critical to the behavior being tested. In that case, the test should set it explicitly:</p>
<pre>test_that(&quot;report uses the correct currency symbol&quot;, {
  # Arrange
  currency &lt;- &quot;GBP&quot;
  amount &lt;- 1000

  # Act
  result &lt;- format_currency(amount, currency = currency)

  # Assert
  expect_equal(result, &quot;£1,000&quot;)
})</pre>
<p>Now the test tells its own story. Anyone reading it knows exactly what conditions it relies on.</p>
<h2 id="2-eager-test">2. Eager Test</h2>
<p><strong>🫣 The smell:</strong> one test that asserts on multiple unrelated behaviors.</p>
<pre>test_that(&quot;data pipeline works&quot;, {
  result &lt;- run_pipeline(raw_data)
  expect_equal(nrow(result), 100)
  expect_equal(ncol(result), 5)
  expect_true(all(!is.na(result$value)))
  expect_equal(result$category, expected_categories)
  expect_true(is.numeric(result$value))
})</pre>
<p>This test is checking the row count, the column count, the NA handling, the category values, and the column type — all under the title “data pipeline works.” When it fails, the output tells you <em>something</em> broke. Not <em>what</em>.</p>
<p>Split it into cases where each title describes the specific behavior:</p>
<pre>test_that(&quot;pipeline returns 100 rows&quot;, {
  # Arrange
  raw_data &lt;- ...

  # Act
  result &lt;- run_pipeline(raw_data)

  # Assert
  expect_equal(nrow(result), 100)
})

test_that(&quot;pipeline removes rows with missing values&quot;, {
  # Arrange
  raw_data &lt;- ...

  # Act
  result &lt;- run_pipeline(raw_data)

  # Assert
  expect_true(all(!is.na(result$value)))
})

test_that(&quot;pipeline produces numeric values&quot;, {
  # Arrange
  raw_data &lt;- ...

  # Act
  result &lt;- run_pipeline(raw_data)

  # Assert
  expect_true(is.numeric(result$value))
})</pre>
<p>This test suite has more lines, yes. Is it bad? No.</p>
<p>We’re not optimising memory, we’re optimising for clarity. It’s also much easier to maintain — when a requirement changes, you know exactly which test to update and why.</p>
<p>Adding <code># Arrange</code>, <code># Act</code>, and <code># Assert</code> comments is a simple habit that makes this smell hard to miss. When the <code># Assert</code> block has five <code>expect_*</code> calls testing unrelated things, the label might prompt you to notice — and question whether they all belong together. When the <code># Act</code> creates multiple results, it becomes clear that the test is doing too much.</p>
<p>The structure doesn’t fix the test, but it makes the problem easier to spot at a glance. The test is no longer a blob of code doing everything at once. It has a structure that invites you to think about what belongs in each section.</p>
<h2 id="3-over-specification">3. Over-specification</h2>
<p><strong>🫣 The smell:</strong> the test checks <em>how</em> the code does something, not <em>what</em> it produces.</p>
<pre>test_that(&quot;sends notification on upload&quot;, {
  # Arrange
  mock_notify &lt;- mockery::mock()
  mockery::stub(process_upload, &quot;notify_user&quot;, mock_notify)

  # Act
  process_upload(file = &quot;report.csv&quot;, user_id = &quot;u42&quot;)

  # Assert
  mockery::expect_called(mock_notify, n = 1)
  mockery::expect_call(
    mock_notify,
    1,
    notify_user(&quot;u42&quot;, type = &quot;upload_complete&quot;)
  )
})</pre>
<p>This test will break the moment you rename <code>notify_user</code>, change its signature, or switch to a different notification mechanism internally — even if the <em>behavior</em> (the user gets notified) stays exactly the same.</p>
<p>Over-specified tests are tightly coupled to implementation. They punish refactoring.</p>
<p>The alternative is to test from the outside:</p>
<pre>test_that(&quot;user receives notification after upload&quot;, {
  # Arrange
  notifications &lt;- list()

  # Act
  process_upload(
    file = &quot;report.csv&quot;,
    user_id = &quot;u42&quot;,
    notify_user = function(user_id, ...) {
      notifications[[length(notifications) + 1]] &lt;&lt;- user_id
    }
  )

  # Assert
  expect_length(notifications, 1)
  expect_equal(notifications[[1]], &quot;u42&quot;)
})</pre>
<p>The test no longer cares how notification happens internally — just that <code>&quot;u42&quot;</code> received one. You can refactor the internals freely.</p>
<p>Notice what changed structurally: instead of patching <code>notify_user</code> after the fact with a stub, <code>process_upload</code> now accepts it as an argument. This is dependency injection, and it’s worth understanding as a pattern in its own right — not just as a way to avoid over-specification, but as a replacement for stubs and mocks altogether.</p>
<p>When a dependency is injected, the test passes in exactly the implementation it wants. There’s no patching, no intercepting, no targeting of internal function names. The test controls the dependency directly:</p>
<pre>process_upload &lt;- function(file, user_id, notify_user = real_notify_user) {
  # ... upload logic
  notify_user(user_id, type = &quot;upload_complete&quot;)
}</pre>
<p>In production, <code>real_notify_user</code> is used. In tests, any function with the same interface can be passed in. The key word is <em>interface</em>: the argument name and what it receives. That’s the contract between <code>process_upload</code> and whatever handles notifications.</p>
<p>This changes how test failures work — in a good way. With a stub, the test fails when the internal call site changes: rename the function, move it to a different module, inline it, and the stub can no longer target it. The failure is about test plumbing, not about behavior. With dependency injection, the test only fails when the <em>interface</em> changes — when <code>notify_user</code> is expected to receive different arguments, or when it’s removed entirely. That’s a failure you want to know about. It means the contract between components changed, and the fake dependency needs updating to match.</p>
<p>Notice also that the fake <code>notify_user</code> is just a plain function — no mocking library needed. A mock that accepts any call and records nothing will let over-specification creep back in through <code>expect_called()</code> and <code>expect_call()</code>. A simple fake that does one concrete thing — appends to a list, increments a counter, writes to a local variable — can only be asserted on from the outside. It doesn’t offer the machinery to inspect internals, so the test can’t become over-specified even if you wanted it to.</p>
<h2 id="4-sensitive-equality">4. Sensitive Equality</h2>
<p><strong>🫣 The smell:</strong> the test asserts on an entire object when only part of it matters.</p>
<pre>test_that(&quot;filters inactive users&quot;, {
  # Arrange
  users &lt;- ...

  # Act
  result &lt;- filter_active_users(users)

  # Assert
  expect_equal(result, data.frame(...))
})</pre>
<p>The expectation was created when this test was first written. Since then, the <code>users</code> data frame has gained two new columns. Every test asserting against the full snapshot now fails — not because the filtering logic is wrong, but because the expected object is stale.</p>
<p>Asserting on a whole object isn’t always wrong.</p>
<p>A test that documents the full shape of the output — its columns, types, and structure — is genuinely useful. It acts as a contract: this is what <code>filter_active_users</code> returns. When that contract changes intentionally, the test tells you.</p>
<p>This matters especially in R. In a statically typed language the compiler enforces what a function returns — change the return type and the code won’t build. R has no such guarantee. A function can return a data frame today and a list tomorrow, drop a column, silently change a type, and nothing outside the tests will catch it.</p>
<p>A full-object contract test fills the loose-typing gap, giving you the kind of output verification the language doesn’t provide for free.</p>
<p>The smell appears when <em>every</em> test does this, or when the full-object assertion is used to check a behavior where most of the fields are irrelevant. If you’re testing that the filter removed inactive users, the output’s column names and data types are noise — and noise breaks tests for the wrong reasons.</p>
<p>A cleaner split is one test for the shape, separate tests for the behaviors:</p>
<pre>test_that(&quot;returns a data frame with user_id and active columns&quot;, {
  # Arrange
  users &lt;- ...

  # Act
  result &lt;- filter_active_users(users)

  # Assert
  expect_equal(result, expected_users)
})

test_that(&quot;filters inactive users&quot;, {
  # Arrange
  users &lt;- ...

  # Act
  result &lt;- filter_active_users(users)

  # Assert
  expect_equal(result$user_id, c(&quot;u01&quot;, &quot;u03&quot;, &quot;u07&quot;))
  expect_true(all(result$active))
})</pre>
<p>The first test owns the full-object assertion and will break when the output shape changes — which is the right test to break. The second test owns the filtering logic and only breaks when the filtering logic is wrong – not when an irrelevant column is added or renamed.</p>
<h2 id="5-obscure-test">5. Obscure Test</h2>
<p><strong>🫣 The smell:</strong> the setup is so long that the behavior being tested is buried inside it.</p>
<pre>test_that(&quot;calculates discount correctly&quot;, {
  user &lt;- list(
    id = &quot;u42&quot;,
    name = &quot;Alice&quot;,
    email = &quot;alice@example.com&quot;,
    created_at = as.Date(&quot;2020-01-01&quot;),
    plan = &quot;pro&quot;,
    region = &quot;EU&quot;,
    active = TRUE,
    referral_code = &quot;REF001&quot;,
    payment_method = &quot;card&quot;,
    currency = &quot;EUR&quot;
  )
  product &lt;- list(
    id = &quot;p99&quot;,
    name = &quot;Annual Subscription&quot;,
    base_price = 200,
    category = &quot;software&quot;,
    tax_rate = 0.2,
    available = TRUE
  )
  result &lt;- calculate_discount(user, product)
  expect_equal(result$discount_pct, 0.15)
})</pre>
<p>By the time you reach the assertion, you’ve waded through 20 lines of setup. Why does the user need a <code>referral_code</code>? Does <code>currency</code> affect the discount? Is <code>tax_rate</code> relevant? The intent is invisible.</p>
<p>The fix is to extract a builder that encodes sensible defaults and lets each test express only what’s relevant to that case:</p>
<pre># If only used in this test file, keep alongside the tests.
# If shared across multiple files, move to a `setup-*.R` file.
make_user &lt;- function(...) {
  defaults &lt;- list(
    id = &quot;u_default&quot;, name = &quot;Test User&quot;,
    plan = &quot;basic&quot;, region = &quot;EU&quot;, active = TRUE,
    currency = &quot;EUR&quot;, payment_method = &quot;card&quot;
  )
  modifyList(defaults, list(...))
}

test_that(&quot;pro users receive a 15% discount&quot;, {
  # Arrange
  user &lt;- make_user(plan = &quot;pro&quot;)
  product &lt;- list(
    id = &quot;p99&quot;,
    base_price = 200,
    category = &quot;software&quot;
  )

  # Act
  result &lt;- calculate_discount(user, product)

  # Assert
  expect_equal(result$discount_pct, 0.15)
})</pre>
<p>Now the test title and the setup agree: this is a test about the <code>&quot;pro&quot;</code> plan. Everything else is noise that’s been moved out of sight.</p>
<p>Using <code># Arrange</code>, <code># Act</code>, and <code># Assert</code> comments here also helps. The test is no longer a blob of code doing everything at once. It has a structure that invites you to think about what belongs in each section, and helps you understand when one secttion ends and another begins. The test is easier to read, and the intent is clearer.</p>
<h2 id="6-meaningless-title">6. Meaningless Title</h2>
<p><strong>🫣 The smell:</strong> the test name describes what the code does, not what behavior it verifies.</p>
<pre>test_that(&quot;calculate_discount works&quot;, {
  # Arrange
  user &lt;- make_user(plan = &quot;pro&quot;)

  # Act
  result &lt;- calculate_discount(user, product)

  # Assert
  expect_equal(result$discount_pct, 0.15)
})</pre>
<p>This is by far the most common test smell I see.</p>
<p>Naming things is hard. I don’t blame anyone for struggling to find a good test title. But a meaningless title is as useless as no title at all. Let’s make some effort here! If we can pause to come up with a good name for a function — we can think of a name for a test too.</p>
<p><code>&quot;calculate_discount works&quot;</code> is not a test title — it’s a function name with “works” appended. When this test fails, the output tells you that <code>calculate_discount</code> stopped working. It doesn’t tell you <em>what it’s supposed to do</em> or <em>under what conditions it broke</em>.</p>
<p>The title is the first thing you read when a test fails. It should answer two questions: <em>what behavior is expected</em>, and <em>under what circumstances</em>.</p>
<pre>test_that(&quot;pro users receive a 15% discount&quot;, {
  # Arrange
  user &lt;- make_user(plan = &quot;pro&quot;)

  # Act
  result &lt;- calculate_discount(user, product)

  # Assert
  expect_equal(result$discount_pct, 0.15)
})</pre>
<p>Now the failure output reads: <code>pro users receive a 15% discount</code> — and you immediately know whether the logic is wrong or the test is outdated.</p>
<p>This smell is easy to spot: if your test title could be swapped with any other test on the same function without anyone noticing, it’s not a title — it’s a placeholder. A good title is a specification. It should still make sense even if the implementation is completely rewritten.</p>
<p>Some patterns that signal the smell:</p>
<ul>
<li>Ends with “works”, “is correct”, “should work”, or “test”</li>
<li>Is just the function name</li>
<li>Doesn’t mention the input condition that makes this case distinct</li>
</ul>
<hr>
<p><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>A rule of thumb: write the title before you write the test body. If you can’t name what you’re testing, you don’t know what you’re testing yet.</strong></p>
<hr>
<h2 id="7-flaky-test">7. Flaky Test</h2>
<p><strong>🫣 The smell:</strong> the test sometimes passes and sometimes fails without any code change.</p>
<pre>test_that(&quot;model selects the most important features&quot;, {
  # Arrange
  training_data &lt;- ...
  top_n &lt;- 3

  # Act
  result &lt;- select_features(training_data, top_n = top_n)

  # Assert
  expect_equal(result$feature, c(&quot;age&quot;, &quot;income&quot;, &quot;region&quot;))
})</pre>
<p>This passes on the developer’s machine today. Tomorrow it returns <code>c(&quot;income&quot;, &quot;age&quot;, &quot;region&quot;)</code> — same features, different order — because two of them had near-identical importance scores and a random tie-breaking step inside the model produced a different ranking. The test fails. Nothing is broken.</p>
<p>Flaky tests are one of the most corrosive smells in a test suite. They don’t just fail — they train developers to distrust failures. Once the team learns to re-run tests until they go green, a genuinely broken build can hide for days.</p>
<p>The R-specific sources of flakiness to watch for: unset random seeds, tests with hard-coded <code>Sys.sleep()</code> waits that assume the app loads within a fixed time, tests that rely on network availability, and assertions against system time or floating-point values without a tolerance.</p>
<p>For the feature selection example, the fix is to set the random seed before the call, and to assert on what actually matters — that the right features were selected — rather than the incidental order they came back in:</p>
<pre>test_that(&quot;model selects age, income, and region as top features&quot;, {
  # Arrange
  training_data &lt;- ...
  top_n &lt;- 3
  withr::local_seed(42)

  # Act
  result &lt;- select_features(training_data, top_n = top_n)

  # Assert
  expect_equal(result$feature, c(&quot;age&quot;, &quot;income&quot;, &quot;region&quot;))
})</pre>
<p>Assuming it’s randomness that makes the test flaky, setting the seed makes any remaining randomness deterministic. The test now passes or fails for the right reason.</p>
<h2 id="8-never-failing-test">8. Never-Failing Test</h2>
<p><strong>🫣 The smell:</strong> the test will pass no matter what the code does.</p>
<pre>test_that(&quot;returns an error for invalid input&quot;, {
  expect_error(calculate_discount(user = NULL, product))
})</pre>
<p>This passes today. It will also pass if <code>calculate_discount</code> is rewritten to throw a completely different error, emit a warning instead, return <code>NA</code> silently, or crash with an unrelated message. The assertion only checks that <em>some</em> error occurred — not the right one.</p>
<p>The same trap appears with positive assertions:</p>
<pre>test_that(&quot;result is a list&quot;, {
  result &lt;- run_pipeline(data)
  expect_true(is.list(result))
})</pre>
<p>If <code>run_pipeline</code> returns <em>any</em> list — empty, malformed, missing all expected fields — this test will almost always pass. It is not testing behavior; it is testing that R’s type system is still working.</p>
<p>Always assert on the specific outcome. For errors, check the message:</p>
<pre>test_that(&quot;returns an error when user is NULL&quot;, {
  # Arrange
  user &lt;- NULL
  product &lt;- ...

  # Act, Assert
  expect_error(
    calculate_discount(user = NULL, product),
    &quot;user must not be NULL&quot;
  )
})</pre>
<p>For positive assertions, check the values that actually matter — not just the type that contains them.</p>
<h2 id="9-test-order-dependency">9. Test Order Dependency</h2>
<p><strong>🫣 The smell:</strong> tests only pass when run in a specific sequence.</p>
<pre>test_that(&quot;initialises the cache&quot;, {
  initialise_cache()
  expect_true(cache_is_ready())
})

test_that(&quot;stores a value in the cache&quot;, {
  store_in_cache(&quot;key&quot;, &quot;value&quot;)
  expect_equal(retrieve_from_cache(&quot;key&quot;), &quot;value&quot;)
})</pre>
<p>The second test silently depends on the first having run. If you run it in isolation it fails with an error about the cache not being initialized, which looks like a cache bug but is actually a test structure bug.</p>
<p>The symptom is tests that pass with <code>devtools::test()</code> and fail with <code>testthat::test_file()</code> on a single file, or tests that break when you move them to a different <code>describe()</code> block.</p>
<p>The fix is to make each test fully self-contained. Every test should set up whatever it needs and leave no state behind:</p>
<pre>test_that(&quot;stores a value in the cache&quot;, {
  # Arrange
  initialise_cache()
  withr::defer(clear_cache())
  store_in_cache(&quot;key&quot;, &quot;value&quot;)

  # Act
  value &lt;- retrieve_from_cache(&quot;key&quot;)

  # Assert
  expect_equal(value, &quot;value&quot;)
})</pre>
<p><code>withr::defer()</code> ensures the cleanup runs even if the test fails, so the next test always starts from a clean slate.</p>
<h2 id="10-conditional-test-logic">10. Conditional Test Logic</h2>
<p><strong>🫣 The smell:</strong> the test body contains <code>if</code>, <code>else</code>, or <code>tryCatch</code>.</p>
<pre>test_that(&quot;formats output correctly&quot;, {
  result &lt;- format_output(data)
  if (nrow(result) &gt; 0) {
    expect_equal(result$label[[1]], &quot;Total&quot;)
  } else {
    expect_equal(nrow(result), 0)
  }
})</pre>
<p>When a test branches, it is multiple tests in one — and only one branch executes on any given run. The other path is permanently untested. If the data changes so that <code>nrow(result)</code> is always <code>0</code>, the label check never runs again and a regression in label formatting will go undetected forever.</p>
<p>Conditional logic in tests is also a sign the test doesn’t know what it expects. A test that hedges with <code>if</code> hasn’t decided what the correct behavior is.</p>
<p>If you find yourself using a <code>tryCatch</code>, then it seems like you don’t know whether the code should throw an error or not. Tests must know if an error is expected.</p>
<p>Split the conditions into separate, unconditional tests with setups that guarantee the state each one needs:</p>
<pre>test_that(&quot;includes a Total label when data is non-empty&quot;, {
  # Arrange
  non_empty_data &lt;- ...

  # Act
  result &lt;- format_output(non_empty_data)

  # Assert
  expect_equal(result$label[[1]], &quot;Total&quot;)
})

test_that(&quot;returns empty output when data is empty&quot;, {
  # Arrange
  empty_data &lt;- ...

  # Act
  result &lt;- format_output(empty_data)

  # Assert
  expect_equal(nrow(result), 0)
})</pre>
<p>Each test now executes its assertion unconditionally, every time.</p>
<h2 id="11-test-logic-in-production">11. Test Logic in Production</h2>
<p><strong>🫣 The smell:</strong> production code contains special branches that only exist to make tests pass.</p>
<pre>process_payment &lt;- function(
  amount,
  user_id,
  env = Sys.getenv(&quot;APP_ENV&quot;)
) {
  if (env == &quot;test&quot;) {
    return(list(
      status = &quot;ok&quot;,
      transaction_id = &quot;test-txn-001&quot;
    ))
  }
  # ... real payment logic
}</pre>
<p>The test environment gets a hardcoded success response. Production gets the real code. The two paths diverge immediately, so the tests are no longer testing what ships.</p>
<p>This smell often starts as a quick fix — “I’ll just skip the real API call in tests” — and ends up as a permanent fixture that no one dares remove. The production branch accumulates changes – the test branch stays frozen. At some point the tests are validating behavior that the production code no longer exhibits.</p>
<p>The correct fix is dependency injection: pass the thing you want to replace as an argument, and substitute it in tests:</p>
<pre>process_payment &lt;- function(
  amount,
  user_id,
  payment_gateway = real_gateway
) {
  result &lt;- payment_gateway(amount, user_id)
  list(status = result$status, transaction_id = result$id)
}

test_that(&quot;returns ok status on successful payment&quot;, {
  # Arrange
  fake_gateway &lt;- function(amount, user_id) {
    list(status = &quot;ok&quot;, id = &quot;txn-001&quot;)
  }
  amount &lt;- 100
  user_id &lt;- &quot;u42&quot;

  # Act
  result &lt;- process_payment(
    amount,
    user_id,
    payment_gateway = fake_gateway
  )

  # Assert
  expect_equal(result$status, &quot;ok&quot;)
})</pre>
<p>Production code has no awareness of tests. Tests control the dependency. Both paths exercise the same logic.</p>
<h2 id="the-pattern">The Pattern</h2>
<p>These smells fall into two categories. Some make your tests untrustworthy: they pass for reasons unrelated to whether the code works. Others make your tests unreadable: they pass and fail correctly but no one can tell what they mean.</p>
<p>A test is documentation. It tells the next developer — probably future you — what the code is supposed to do and under what conditions. A smelly test fails at that job before it ever fails at catching bugs.</p>
<p>The most useful question to ask while writing a test isn’t “will this pass?” but “if this fails at 2am, will I understand what broke and why?”</p>
<p>If the answer is no, the test has a smell worth fixing.</p>
<p>Here’s a quick reference to keep nearby:</p>





















































<table><thead><tr><th>Smell</th><th>Signal</th></tr></thead><tbody><tr><td>Mystery Guest</td><td>Test relies on state set somewhere else</td></tr><tr><td>Eager Test</td><td>One block, many unrelated assertions</td></tr><tr><td>Over-specification</td><td>Breaks on refactor, not on broken behavior</td></tr><tr><td>Sensitive Equality</td><td>Full-object assertion on a partial concern</td></tr><tr><td>Obscure Test</td><td>Setup buries the point of the test</td></tr><tr><td>Meaningless Title</td><td>Name doesn’t say what behavior is expected</td></tr><tr><td>Flaky Test</td><td>Passes or fails depending on timing or environment</td></tr><tr><td>Never-Failing Test</td><td>Will pass regardless of what the code does</td></tr><tr><td>Test Order Dependency</td><td>Only passes when run after another specific test</td></tr><tr><td>Conditional Test Logic</td><td><code>if</code>/<code>else</code> inside the test body</td></tr><tr><td>Test Logic in Production</td><td>Production code has special branches for tests</td></tr></tbody></table>
<p>None of these require a major rewrite to fix. Each one has a mechanical correction: extract the state, split the block, inject the dependency, narrow the assertion, introduce a builder, rename the case. The value is in learning to <em>see</em> them first.</p>
<p>Once you can spot these smells, you can start to eliminate them, one by one — and build a better test suite that tells you the truth about your code.</p>
<h2 id="apply-this">Apply this</h2>
<p>Reading about smells is easy. Spotting them in your own suite is the hard part. The fastest way to make them stick is to point an AI agent at your real test code, have it find the smells, then fix the worst few yourself — that’s where the learning happens.</p>
<p>Open a test file in your AI coding agent (Claude Code, Cursor, Copilot Chat) and paste this prompt:</p>
<pre>You are a senior R engineer reviewing test code for &quot;test smells&quot; — structural patterns that let tests pass while hiding bugs or obscuring intent.

Scope: audit the test file(s) I've shared. Also read the production code they exercise — several smells can't be judged from the test alone. Use testthat and withr idioms in your fixes.

Detect these 11 smells. For each, here's the tell and the fix I prefer:

1. Mystery Guest — depends on options / env vars / .Rprofile set elsewhere → set and scope it inside the test with withr::with_options / local_envvar, or pass the value in as an argument.
2. Eager Test — one test_that asserts many unrelated behaviors → split to one behavior per test_that, each titled with the behavior it checks.
3. Over-specification — asserts on internal calls via mockery::stub / mock (expect_called, expect_call) → inject a fake as an argument and assert on the observable result, not the call.
4. Sensitive Equality — full-object expect_equal where only part matters → assert the specific fields. Keep ONE deliberate full-object &quot;shape contract&quot; test; that one is legitimate, not a smell.
5. Obscure Test — long setup buries the point → extract a make_*() builder with modifyList defaults, set only the field under test; move shared builders to setup-*.R.
6. Meaningless Title — title is &quot;&lt;fn&gt; works&quot; or just the function name → rewrite to state the expected behavior and the condition; it should still make sense if the implementation were rewritten.
7. Flaky Test — depends on seed / time / network / order, or asserts exact floats or incidental ordering → set withr::local_seed(), add a tolerance, assert on what matters not the order it came back in.
8. Never-Failing Test — expect_error() with no expected message, or asserts only a type (is.list, is.numeric) → assert the specific error message and the actual values.
9. Test Order Dependency — relies on state another test left behind → make it self-contained; set up what it needs and clean up with withr::defer().
10. Conditional Test Logic — if / else / tryCatch in the test body → split into unconditional tests whose setup guarantees each branch's state.
11. Test Logic in Production — `if (env == &quot;test&quot;)`-style branches in the source → remove them and use dependency injection so tests and production run one path.

Rules:
- Only flag clear instances. Don't invent smells to look thorough, and don't flag the legitimate patterns above (injected fakes, a single shape-contract test, AAA comments).
- Quote the offending lines and cite file:line for every finding.
- Never change what the production code does — only its testability. If a fix needs a signature change (dependency injection), say so and describe the new interface.

Output:
1. A triage table: smell | file:line | severity (high/med/low) | one-line why.
2. Then fix the highest-severity findings as before/after code blocks — the smallest change that removes the smell. Stop after three and ask before continuing if more remain.
3. Tell me how to re-run just these tests to confirm they still pass.</pre>
<p>Before you commit a test, run it past this checklist:</p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> The title states the behavior and the condition, and would still make sense if the implementation were rewritten.</li>
<li class="task-list-item"><input type="checkbox" disabled> Everything the test depends on is visible inside the test (or its <code>withr</code> / <code>setup-*.R</code> scaffolding).</li>
<li class="task-list-item"><input type="checkbox" disabled> It asserts on a specific outcome — not just a type, and not just “some error happened.”</li>
<li class="task-list-item"><input type="checkbox" disabled> It passes or fails for exactly one reason, and that reason lives in the test, not in the run order.</li>
</ul>
<p>Want a checklist for the whole suite, not just one file? The <a href="https://jakubsobolewski.com/get-roadmap" rel="nofollow" target="_blank">R testing roadmap</a> turns these habits into a step-by-step path you can work through.</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://jakubsobolewski.com/blog/test-smells-in-r"> Jakub Sobolewski</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/06/11-test-smells-that-make-your-tests-lie-to-you/">11 Test Smells That Make Your Tests Lie to You</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401604</post-id>	</item>
		<item>
		<title>Refactoring with Jarl: a coffee chat</title>
		<link>https://www.r-bloggers.com/2026/06/refactoring-with-jarl-a-coffee-chat/</link>
		
		<dc:creator><![CDATA[Posts on R-hub blog]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://blog.r-hub.io/2026/06/02/jarl/</guid>

					<description><![CDATA[<p>TL;DR, don’t let your friends use LLMs for finding useless code in a project! Using Jarl instead is cheaper, more reliable, and won’t kill any kitten.<br />
We (Hannah and Maëlle) share an appreciation for the unglamorous maintenance work we c...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/refactoring-with-jarl-a-coffee-chat/">Refactoring with Jarl: a coffee chat</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.r-hub.io/2026/06/02/jarl/"> Posts on R-hub 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>
<figure><img src="https://i2.wp.com/blog.r-hub.io/2026/06/02/jarl/meme.png?w=578&#038;ssl=1"
    alt="Cute kitten attacked by robots. The text says &#39;Everytime you use Claude for something a CLI can do, a kitten dies&#39;." data-recalc-dims="1">
</figure>

<p>TL;DR, don’t let your friends use LLMs for finding useless code in a project! Using <a href="https://jarl.etiennebacher.com/" rel="nofollow" target="_blank">Jarl</a> instead is cheaper, more reliable, and won’t kill any kitten.</p>
<p>We (Hannah and Maëlle) share an appreciation for the unglamorous maintenance work we call upkeep. So when Claude highlighted some <a href="https://github.com/tidymodels/tune/commit/5b4d63b58bd496e6c2809a5444b6ab119ae14f37" rel="nofollow" target="_blank">dead</a> <a href="https://github.com/tidymodels/tune/commit/16766b32ae1157425b124a70ec0bd64246e24c7f" rel="nofollow" target="_blank">code</a> in the tune package to Hannah, it was worth a mention during a chat. This led to Maëlle recommending Etienne Bacher’s <a href="https://jarl.etiennebacher.com/" rel="nofollow" target="_blank">Jarl</a>, a fast linter for R written in Rust that can, among other things, detect unused functions.</p>
<p>Motivated by this conversation among other things, Maëlle wrote a <a href="https://ropensci.org/blog/2026/04/02/tree-sitter-overview/" rel="nofollow" target="_blank">whole blog post</a> about Jarl and other tooling for R based on tree-sitter, including the above meme.</p>
<p>So naturally, when you get called out in a custom meme, you listen. <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;" /> So here’s the long(er) form of “Use Jarl!”, from the following sweep through the <a href="https://parsnip.tidymodels.org/" rel="nofollow" target="_blank">parsnip</a> package by Hannah, with commentary from Maëlle.</p>
<p><em>Many thanks to <a href="https://www.etiennebacher.com/" rel="nofollow" target="_blank">Etienne Bacher</a> for reviewing this post!</em></p>
<h2 id="how-hannah-used-jarl">How Hannah used Jarl</h2>
<p>I have the <a href="https://jarl.etiennebacher.com/howto/editors" rel="nofollow" target="_blank">Jarl extension</a> installed for my Positron but here I used the command line interface (CLI) of Jarl. I started off with running this <a href="https://jarl.etiennebacher.com/by-example#lint-a-directory" rel="nofollow" target="_blank">command</a> in the terminal:</p>
<pre>jarl check .
</pre><p>but that gave me too much output. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>It did include a helpful hint though, something like:</p>
<pre>── Summary ──────────────────────────────────────
Found 296 errors.
3 fixable with the `--fix` option.
More than 15 errors reported, use `--statistics` to get the count by rule.
</pre><p>So one more time, with the <code>--statistics</code> option, to get an overview of what Jarl all flagged. The output (here with made up numbers) looked something like</p>
<pre>jarl check . --statistics
  132 [ ] implicit_assignment
  109 [ ] internal_function
   34 [ ] vector_logic
   13 [ ] unused_function
    7 [*] numeric_leading_zero
    1 [*] any_is_na

Rules with `[*]` have an automatic fix.
</pre><p>Now that gives me a better overall picture. <em>Maëlle: I really enjoyed learning about this <code>--statistics</code> flag, and followed the same strategy when I ran Jarl on the <a href="https://github.com/igraph/rigraph/issues/2652" rel="nofollow" target="_blank">igraph R package</a>!</em></p>
<p>I could have run <code>jarl check . --fix</code> to fix everything with an automatic fix but I wanted to learn a bit more about the rules to see if I wanted to apply them. So I worked my way through one rule at a time, with one git commit at a time. You can <a href="https://jarl.etiennebacher.com/by-example#select-or-ignore-specific-rules" rel="nofollow" target="_blank">select rules</a> like so:</p>
<pre>jarl check . --select any_is_na
</pre><p>This separation of changes by rule also made it easier to review changes: one topic per commit. <em>Maëlle: in igraph, I created one <strong>PR</strong> per rule or group of rules.</em></p>
<p>I first went through the ones with automatic fixes, then the ones without. The result of the clean-up: <a href="https://github.com/tidymodels/parsnip/pull/1356" rel="nofollow" target="_blank">https://github.com/tidymodels/parsnip/pull/1356</a> <em>Maëlle: in igraph, I went through rules starting with the ones with the least violations, ending with the ones that had the most hits.</em></p>
<p>parsnip has been around the block a few times and had accumulated a few functions flagged by Jarl as unused. I spotted <code>release_bullets()</code> in the list which I know we want to keep.<sup id="fnref:1"><a href="https://blog.r-hub.io/2026/06/02/jarl/#fn:1" class="footnote-ref" role="doc-noteref" rel="nofollow" target="_blank">1</a></sup> We use it <em>for</em> parsnip in the release process but we don’t use it <em>in</em> parsnip. I wasn’t expecting Jarl to catch onto this difference so I decided to review the list of flagged functions separately and split that off into its own issue (and PR). I ended up keeping one other function that was part of a set of functions provided in a standalone file but I removed the other ones.</p>
<p>No kittens were hurt!</p>
<h2 id="why-use-jarl-for-refactoring">Why use Jarl for refactoring</h2>
<p>Obviously, because we are telling you you should! More seriously: Jarl is fast and free!</p>
<p>And here are a few more reasons beyond our success stories <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>
<h3 id="jarl-will-find-small-smelly-things">Jarl will find small smelly things</h3>
<p>For instance, the <a href="https://jarl.etiennebacher.com/rules/outer_negation" rel="nofollow" target="_blank"><code>outer_negation</code></a> rule helps improve readability: <code>!all(x)</code> is easier to understand than <code>any(!x)</code>. In igraph for example, thanks to that rule we <a href="https://github.com/igraph/rigraph/pull/2666" rel="nofollow" target="_blank">changed</a></p>
<pre>any(!names(options) %in% names(defaults))
</pre><p><em>Any names of options not in names of defaults</em> (and actually a tad worse since we do not use <code>%notin%</code> yet, new in <a href="https://cran.r-project.org/doc/manuals/r-release/NEWS.html" rel="nofollow" target="_blank">R 4.6.0</a>)</p>
<p>into</p>
<pre>!all(names(options) %in% names(defaults))
</pre><p><em>Not all names of options in names of defaults</em>.</p>
<p>See also the rule in action in <a href="https://github.com/tidymodels/parsnip/pull/1356/changes/8029bb8eb9acab8beef75975fd6675e38a70f802" rel="nofollow" target="_blank">parsnip</a>.</p>
<p>Generally, Jarl will make the codebase less <a href="https://github.com/jennybc/code-smells-and-feels" rel="nofollow" target="_blank">smelly</a>.</p>
<h3 id="jarl-will-find-unused-functions">Jarl will find unused functions</h3>
<p>It’s very easy for a package to accumulate functions that are now useless, sort of historical artefacts. And as they’re unused you might not even stumble upon them by chance. Jarl will flag them for you!</p>
<p>If Jarl notices such a function, check it is actually unused. If the function is actually useful, you might want to add an <a href="https://jarl.etiennebacher.com/howto/suppression-comments" rel="nofollow" target="_blank">exception comment</a> or some <a href="https://jarl.etiennebacher.com/reference/config-file#unused_function" rel="nofollow" target="_blank">rule-specific configuration</a>.</p>
<h3 id="jarl-will-find-duplicated-function-definitions">Jarl will find duplicated function definitions</h3>
<p>Also a problem that happens in a package that wasn’t born yesterday! In this case, you need to pick one definition. <a href="https://github.com/tidymodels/parsnip/issues/1360" rel="nofollow" target="_blank">Example in parsnip</a>. Deleting unused lines is very satisfying.</p>
<h3 id="jarl-will-detect-unreachable-code">Jarl will detect unreachable code</h3>
<p>For instance, code that comes after a <a href="https://rdrr.io/r/base/function.html" rel="nofollow" target="_blank"><code>return()</code></a> or a <a href="https://rdrr.io/r/base/stop.html" rel="nofollow" target="_blank"><code>stop()</code></a> in a function. Sometimes, you can simply delete that code (again, a feel good move!). Other times, the check is an opportunity for <a href="https://github.com/tidymodels/parsnip/issues/1359" rel="nofollow" target="_blank">targeted refactoring</a>!</p>
<h3 id="jarl-can-check-your-testthat-code">Jarl can check your testthat code</h3>
<p>As a bonus, because these rules are turned off by default, you can run</p>
<pre>jarl check . --select TESTTHAT
</pre>
<p>This will apply all rules from the <a href="https://jarl.etiennebacher.com/rules" rel="nofollow" target="_blank">testthat group</a> that help you use more specific expectations. For instance, <code>expect_equal(length(x), 2)</code> should be <code>expect_length(x, 2)</code>. All these rules come with automatic fixes!</p>
<p>Example in <a href="https://github.com/tidymodels/parsnip/pull/1379" rel="nofollow" target="_blank">parsnip</a>, example in <a href="https://github.com/igraph/rigraph/pull/2670" rel="nofollow" target="_blank">igraph</a>.</p>
<p>Should your package depend on dplyr, there’s a group of (currently two) rules for this as well!</p>
<h2 id="conclusion">Conclusion</h2>
<p>Automatic tools are extremely useful for guiding upkeep work. Thinking <em>“let me clean up this repo”</em> can be daunting <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f631.png" alt="😱" class="wp-smiley" style="height: 1em; max-height: 1em;" />, tools like Jarl provide a roadmap! They let you fix obvious problems, and at the same time, they can make you read some dusty corners of your codebase. Hannah’s work on parsnip inspired Maëlle to run Jarl on igraph (influencer influenced back!), and she too can confirm it was both useful and satisfying. Thanks Etienne for that cool tool!</p>
<p>Note that you could run Jarl checks manually as we did, or you could tell a LLM to run Jarl. To quote Hannah’s colleague <a href="https://emilhvitfeldt.com/post/ast-grep-r-claude-code/" rel="nofollow" target="_blank">Emil Hvitfeldt</a>, <em>“When coding or using coding agents, one way to improve your workflow is by using CLI tools with a very clear focus.”</em>. 🫡</p>
<p>What’s even better with Jarl is that it is actively developed, which you can confirm by skimming through the rich <a href="https://jarl.etiennebacher.com/dev/changelog" rel="nofollow" target="_blank">changelog</a>!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This inspired an <a href="https://github.com/etiennebacher/jarl/issues/497" rel="nofollow" target="_blank">issue</a>. As noted there, for this function one should add an <a href="https://jarl.etiennebacher.com/howto/suppression-comments" rel="nofollow" target="_blank">exception as a comment</a> or in the <a href="https://jarl.etiennebacher.com/reference/config-file#unused_function" rel="nofollow" target="_blank">config file</a>. <a href="https://blog.r-hub.io/2026/06/02/jarl/#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>
</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://blog.r-hub.io/2026/06/02/jarl/"> Posts on R-hub 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/06/refactoring-with-jarl-a-coffee-chat/">Refactoring with Jarl: a coffee chat</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401600</post-id>	</item>
		<item>
		<title>Our goodpractice Package Has New Superpowers</title>
		<link>https://www.r-bloggers.com/2026/05/our-goodpractice-package-has-new-superpowers/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/06/01/goodpractice/</guid>

					<description><![CDATA[<p>The goodpractice package has been recommended by rOpenSci since it was first started just over 10 years ago by Gábor Csárdi.<br />
We used to ask our editors to manually run goodpractice on all packages submitted to software peer-review, and then to ask aut...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/05/our-goodpractice-package-has-new-superpowers/">Our goodpractice Package Has New Superpowers</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/06/01/goodpractice/"> 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>

<p>The <a href="https://docs.ropensci.org/goodpractice" rel="nofollow" target="_blank">goodpractice package</a> has been recommended by rOpenSci since it was first started just over 10 years ago by <a href="https://gaborcsardi.org/" rel="nofollow" target="_blank">Gábor Csárdi</a>.
We used to ask our editors to manually run goodpractice on all packages submitted to software peer-review, and then to ask authors to fix any notable issues flagged by the package.
It is now integrated within our own <a href="https://docs.ropensci.org/pkgcheck" rel="nofollow" target="_blank">pkgcheck system</a>, and used to automatically identify any goodpractice issues with all new submissions.
The package changed maintainers <a href="https://ropensci.org/blog/2024/06/21/ropensci-news-june-2024/#ropensci-takes-over-maintenance-of-the-goodpractice-package" rel="nofollow" target="_blank">several times</a> before the previous maintainers gave the green light for us to take over maintenance of the package <a href="https://github.com/ropensci-review-tools/goodpractice/commit/c3f0b8e4c8e2cd7d88361bb670dd79284e6494d1" rel="nofollow" target="_blank">two years ago (28th May 2024)</a>.</p>
<p>We’re really pleased to share that we’ve recently rolled out a host of updates and extensions to the package.
These make it both easier to use, and more powerful.
This was a collaborative effort between new package author, Athanasia Mo Mowinckel, current maintainer, Mark Padgham, and the generative AI tool Claude.
We describe the process at the end of this tech note.</p>
<h2>
Easier control of checks
</h2><p>The goodpractice package is a convenience wrapper around several other packages including <a href="https://rcmdcheck.r-lib.org/" rel="nofollow" target="_blank">rcmdcheck</a>, <a href="https://lintr.r-lib.org/" rel="nofollow" target="_blank">lintr</a>, <a href="https://github.com/gaborcsardi/cyclocomp" rel="nofollow" target="_blank">cyclocomp</a>, and <a href="https://desc.r-lib.org/" rel="nofollow" target="_blank">desc</a>, along with a few hand-coded checks within the package itself.
The previous version of goodpractice had a total of 230 checks, generally prefixed with the name of the package which defined each check.
Checks were identifiable with the single function, <a href="https://docs.ropensci.org/goodpractice/reference/all_checks.html" rel="nofollow" target="_blank"><code>all_checks()</code></a>.
The only way to control which checks were run was to pass a <code>checks</code> parameter to the main function as a character vector of the names of checks you wanted to run.
So, for example, if you wanted to skip the <a href="https://rcmdcheck.r-lib.org/" rel="nofollow" target="_blank">rcmdcheck</a> checks, you had to do something like this:</p>
<pre>mychecks &lt;- grepv(&quot;^rcmdcheck&quot;, all_checks(), invert = TRUE)
gp(checks = mychecks)
</pre><p>That wasn’t easy!
And honestly, most people just didn’t bother.</p>
<p>This update finally makes it easier to control checks, by defining them in “groups”, which generally correspond to the separate packages that run the actual checks.
The groups can be seen with the new <a href="https://docs.ropensci.org/goodpractice/reference/all_check_groups.html" rel="nofollow" target="_blank"><code>all_check_groups()</code> function</a>, and controlled with the <a href="https://docs.ropensci.org/goodpractice/reference/checks_by_group.html" rel="nofollow" target="_blank"><code>checks_by_group()</code> function</a>.
Checks can be controlled by simply naming the check groups you want to run, like this:</p>
<pre>gp(checks = checks_by_group(&quot;description&quot;, &quot;lintr&quot;))
</pre><h2>
New check groups
</h2><p>The documentation for the <a href="https://docs.ropensci.org/goodpractice/reference/all_check_groups.html" rel="nofollow" target="_blank"><code>all_check_groups()</code> function</a> shows all the new check groups added in this update, including:</p>
<table>
<thead>
<tr>
<th>Group name</th>
<th>Run by default?</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>description</td>
<td>yes</td>
<td>Check common issues with <code>DESCRIPTION</code> files, including formatting issues with URLs, DOIs, package names, and author and contributor roles, along with other common <code>DESCRIPTION</code> issues.</td>
</tr>
<tr>
<td>rd</td>
<td>yes</td>
<td>Check whether or not <code>man/*.Rd</code> function documentation includes both example code and return values (regardless of whether or not documentation files are generated by <a href="https://roxygen2.r-lib.org/" rel="nofollow" target="_blank">roxygen2</a>).</td>
</tr>
<tr>
<td><a href="https://roxygen2.r-lib.org/" rel="nofollow" target="_blank">roxygen2</a></td>
<td>yes</td>
<td>Check for problems with Roxygen2 generated documentation.</td>
</tr>
<tr>
<td><a href="https://github.com/r-lib/revdepcheck" rel="nofollow" target="_blank">revdep</a></td>
<td>no</td>
<td>Reverse-dependency checks.</td>
</tr>
<tr>
<td>code_structure</td>
<td>yes</td>
<td>Checks for common issues like duplicated or unused functions.</td>
</tr>
<tr>
<td>package_structure</td>
<td>yes</td>
<td>Generic checks like whether a package has a README, a NEWS file, or whether all files use a <code>.R</code> extension, and not <code>.r</code>.</td>
</tr>
<tr>
<td><a href="https://docs.ropensci.org/spelling/" rel="nofollow" target="_blank">spelling</a></td>
<td>yes</td>
<td>Check spelling.</td>
</tr>
<tr>
<td><a href="https://tidyverse.org/" rel="nofollow" target="_blank">tidyverse</a></td>
<td>no</td>
<td>Check compliance with the <a href="https://style.tidyverse.org/" rel="nofollow" target="_blank"><em>Tidyverse style guide</em></a>, mostly <a href="https://lintr.r-lib.org/" rel="nofollow" target="_blank">lintr</a> checks.</td>
</tr>
<tr>
<td><a href="https://urlchecker.r-lib.org/" rel="nofollow" target="_blank">urlchecker</a></td>
<td>yes</td>
<td>Check whether all URLs are valid.</td>
</tr>
<tr>
<td>vignette</td>
<td>yes</td>
<td>Check that vignette code does not use either <code>rm()</code> or <code>setwd()</code>.</td>
</tr>
</tbody>
</table>
<p>Many of these checks are powered by <a href="https://ropensci.org/blog/2026/04/02/tree-sitter-overview/" rel="nofollow" target="_blank">treesitter</a>, the extremely efficient syntax parsing library used by GitHub — which is genuinely neat to have working in R.
The number of checks has been increased from 230 to 338, with all listed in the output of the <a href="https://docs.ropensci.org/goodpractice/reference/all_checks.html" rel="nofollow" target="_blank"><code>all_checks()</code> function</a>.</p>
<h2>
Improved check reporting
</h2><p>The goodpractice package has always been designed for console output.
This updated version now provides immediate and consistent detail on check progress while running.
The <a href="https://cli.r-lib.org/" rel="nofollow" target="_blank">cli package</a> is also used to give consistently formatted output for all check groups.</p>
<p>The large increase in checks also means that directly printing goodpractice results may fill several screens of output.
This update also adds a <code>groups</code> parameter to the <a href="https://docs.ropensci.org/goodpractice/reference/print.goodPractice.html" rel="nofollow" target="_blank"><code>print()</code> method</a>, to enable printing check results only for specified groups.
This means you can run the main <a href="https://docs.ropensci.org/goodpractice/reference/gp.html" rel="nofollow" target="_blank"><code>gp()</code> function</a> once, then step through each check group by printing results only for that group, fixing those, and then moving on to the next.
We’ve found this turns what could feel like an overwhelming wall of output into a manageable to-do list.
For example:</p>
<pre>x &lt;- gp()
print(x, &quot;description&quot;)
#&gt; Aww! Shining package! Keep up the priceless work!

print(x, &quot;namespace&quot;)
#&gt; ── It is good practice to ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
#&gt;
#&gt; &#x2716; remove or use internal functions that are defined but never called. Dead code increases maintenance burden.
#&gt;
#&gt; R/utils.R:3
#&gt; R/utils.R:73
#&gt;
#&gt; &#x2716; define exported (user-facing) functions before internal helper functions within each R source file.
#&gt;
#&gt; R/api.R:85
#&gt;
#&gt; ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
</pre><hr>
<h2>
Notes on the development process
</h2><h3>
From Mo (Athanasia)
</h3><p>I’ll be honest with you: a lot of this contribution was done with an AI assistant (Claude Code using Opus 4.5) at my elbow.
I want to say something about that, because I think the honest version is more useful than the marketing version.
The way Mark approached reviewing this work was very thoughtful and helpful.</p>
<p>Based on his feedback and how it was structured, I’ve learned a lot about what a good code review looks like, and it’s been absolutely wonderful.
As a person who has mainly worked on their own code-bases alone, I have adopted a style of working that often creates diff monsters (hundreds of files changed), which can make reviewing very hard.
Indeed, my first PR to this project was such a monster, and Mark pushed back asking for several smaller PRs he could actually manage to review.
This is also where I learned about <a href="https://git-scm.com/docs/git-worktree" rel="nofollow" target="_blank">git worktrees</a> (Thank you <a href="https://masalmon.eu/2026/04/23/git-worktree-again/" rel="nofollow" target="_blank">Maëlle</a>!) and asking Claude to split the work up into several worktrees for easier review, was actually the first substantial change to how I now approach working with an AI assistant.
Through this collaboration I learned a lot about splitting my work into better sizes and chunks.</p>
<p>The shape of it was something like this:</p>
<p>I’d locate an issue I thought was meaningful to tackle, and provide Claude Code with the issue context and possibly an idea of how to solve it.
Once it had a solution, I’d review it, push back where it felt off, and we’d iterate.
Mark would then review the PR and point out the things I’d missed.
Three-way collaboration, more or less, with the AI doing the typing and me doing the deciding.</p>
<p>What worked well was scaffolding.
Drafting a new check, wiring up the prep step that goes with it, generating the test cases for the edge paths I’d otherwise forget — that kind of work compresses really nicely with an AI.
I would often ask it to generate some tests first, then create code that would pass the tests.
This way we had a clear idea of what we wanted the new code to do, and then solve it.</p>
<p>What worked less well was anything involving judgement I hadn’t articulated yet.
The first few times Claude opened a PR for me, it left the test-plan checkboxes unticked — even when the tests already passed.
That’s not wrong exactly, but it’s misleading to a reviewer.
I had to say “no, check the box if the test exists and passes; an unchecked box reads as TODO.”
Similarly, when Mark left inline code suggestions on a PR, Claude tried to helpfully re-implement them locally — which would have stripped Mark’s attribution off his own contribution.
We had to agree: suggestions get accepted on GitHub, not retyped.</p>
<p>And then there’s the unglamorous part.
It would often forget instructions, despite having them documented in agents.md and local memory for the project.
These were mostly trivial, but at times fairly bad.
Some of the first PRs we made, Mark pointed out that the solutions were using regexp rather than AST (Abstrat Syntax Trees).
I have to admit, I didn’t really know about AST before starting this project, and I am so glad I now know of it.
Claude, despite being told very clearly that we wanted AST solutions, would often forget and implement regexp based solutions.
It would also often revert to using for-loops rather than vectorization, and creating nested for-loops into the 3rd or 4th level — which is just horrible to follow as a human.</p>
<p>If you take one thing from this aside, take this: AI-assisted contribution didn’t mean handing the package over to a machine.
It meant I could move faster on the parts I already understood, explore more confidently on the parts I didn’t, and spend my actual attention on the decisions that mattered — what to check for, how to group it, what to call it, and what to leave out.
The friction taught me where to watch closely.
The speed-ups taught me where I could trust the loop.</p>
<p>It’s a different way of working, and I’m still figuring out the shape of it, but it has enabled me to contribute to this project despite some very severe health issues making it hard for me to work as normal.</p>
<p>And one thing Claude always does better than me: writing good commit messages.
I have found myself writing my own code, but asking Claude to commit them (making it look like Claude wrote the code, but I don’t care) because the commit messages are just so much better than what I write.</p>
<h3>
From Mark
</h3><p>The entire idea for this major update was initiated and driven by Mo.
It was also my first real foray into using generative AI tools directly in rOpenSci packages, and I learnt a lot.
After I rejected the initial monster PR, things settled down into much smaller and manageable PRs that were always focussed on one specific thing.</p>
<p>From that point, Mo was effectively the coder (assisted by Claude), and I was the reviewer.
I never knew how much of the actual code was typed versus machine-generated, but was generally pleasantly surprised that I felt no need to inquire.
Nearly every PR was obviously going to improve the package, and many of them were genuinely creative ideas.
(That’s where I disagree slightly with Mo’s general description of the process: I am convinced a lot of the work behind this update came directly from her brilliant insights and experience, rather than her passive description of looking over pre-existing PRs.)</p>
<p>The process started in Feb 2026, and involved <a href="https://github.com/ropensci-review-tools/goodpractice/pulls?q=sort%3Aupdated-desc%20is%3Apr%20is%3Aclosed%20author%3Adrmowinckels" rel="nofollow" target="_blank">70 pull requests</a>.
So many of these really were independent and new ideas.
That enabled me to view each one with fresh eyes, and to think about whether I might approach anything in different ways.
Over so many PRs, there was a lot of back-and-forth, from pushing back on Claude insisting on nested loops, to figuring out whether code was best parsed and analysed as text, or as a syntax tree.
As Mo indicated, a lot of those discussions ultimately converged towards us bringing the power of treesitter into the package, and even helping to replace previous text-based code checks with more efficient and accurate AST approaches.</p>
<p>Our collaboration was very pleasant and enriching, and really felt like a direct cooperation between Mo as the ideas factory, me as the reviewer, and Claude as nothing more than a mediator of our ideas.
The key role of Claude throughout the process was in handling all of the fiddly, technical details.
We both looked carefully at every single line of code, but the use of Claude enabled our conversations to remain at higher, conceptual levels than what would have happened if we had to actually do all the technical implementation ourselves.
I think that is the aspect that I found most surprising: That the use of Claude made our collaboration feel less technical, and therefore somehow even more human.
And that gave us the ability to work though 70 pull requests representing over 100 new checks, all ready for everybody to use.</p>
<h2>
Let us know what you think
</h2><p>Like all rOpenSci packages, goodpractice is a community effort that lives through community use and feedback.
We’d love to hear what you think, via <a href="mailto:mark@ropensci.org" rel="nofollow" target="_blank">email</a>, or through issues on the <a href="https://github.com/ropensci-review-tools/goodpractice" rel="nofollow" target="_blank">GitHub repository</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://ropensci.org/blog/2026/06/01/goodpractice/"> 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/05/our-goodpractice-package-has-new-superpowers/">Our goodpractice Package Has New Superpowers</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401573</post-id>	</item>
		<item>
		<title>Techtonique dot net, the Machine Learning web API, is back online (but more like a passion project for now)</title>
		<link>https://www.r-bloggers.com/2026/05/techtonique-dot-net-the-machine-learning-web-api-is-back-online-but-more-like-a-passion-project-for-now/</link>
		
		<dc:creator><![CDATA[T. Moudiki]]></dc:creator>
		<pubDate>Sun, 31 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://thierrymoudiki.github.io//blog/2026/05/31/r/python/techtonique/techtonique-dot-net-is-back</guid>

					<description><![CDATA[<p>Techtonique dot net is back, but more like a passion project, with an API for machine learning tasks (classification, regression, survival analysis, reserving, forecasting etc.). Examples in R, Python are provided in the blog post.</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/05/techtonique-dot-net-the-machine-learning-web-api-is-back-online-but-more-like-a-passion-project-for-now/">Techtonique dot net, the Machine Learning web API, is back online (but more like a passion project for now)</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://thierrymoudiki.github.io//blog/2026/05/31/r/python/techtonique/techtonique-dot-net-is-back"> T. Moudiki's Webpage - 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><a href="https://www.techtonique.net/" rel="nofollow" target="_blank">https://www.techtonique.net</a> contains tools for <strong>Exploratory Data Analysis</strong> (EDA), <strong>editors</strong> for R and Python code, tools for <strong>data visualization</strong>, <strong>no-code web interfaces</strong> for various data science tasks, <strong>a language-agnostic API</strong> for machine learning tasks (classification, regression, survival analysis, reserving, forecasting etc.).</p>

<p>I recently noticed a <strong>spike</strong> in sign-ups (not sure how they noticed it was up again, without official announcement…), and I thought it was a good opportunity to share that <strong><a href="https://www.techtonique.net/" rel="nofollow" target="_blank">techtonique.net</a> is back</strong>. More like a passion project, or part of a portfolio (for now). The API is available and evolving. You can use it for free (with rate limiting), but please note that it is slightly slower than it used to be (indeed, I used to have Azure Credits thanks to Microsoft for Startups  => a faster server). It still serves the responses, in a reasonable time though, and I will try to optimize it as much as possible.</p>

<p>It is worth mentioning that I removed the stochastic simulation API for now, as it required (more technical) to run R in Python, through a Docker container.</p>

<p>Here are some examples of how to use the API for machine learning tasks in R and Python (there are also no-code interfaces for these tasks in the website, and the API is language-agnostic, so you can use it with any programming language that can make HTTP requests):</p>

<p>The starting point is to signup/login (if you’re facing issues, contact support@techtonique.net) and get a token from: <a href="https://www.techtonique.net/token" rel="nofollow" target="_blank">https://www.techtonique.net/token</a>. Then, you can use the token in API requests for machine learning tasks. Each response is a prediction, as envisaged by the chosen model, and based on the input data.</p>

<h1 id="1---r-example">1 &#8211; R example:</h1>

<pre>#!/usr/bin/env Rscript

# Install httr if needed: install.packages(&quot;httr&quot;)
# All you need is a token from www.techtonique.net/token, then run the script
library(httr)

BASE_URL   &lt;- &quot;https://www.techtonique.net&quot;
GITHUB_RAW &lt;- &quot;https://raw.githubusercontent.com/Techtonique/datasets/main&quot;

DATASETS &lt;- list(
  univariate     = paste0(GITHUB_RAW, &quot;/time_series/univariate/a10.csv&quot;),
  multivariate   = paste0(GITHUB_RAW, &quot;/time_series/multivariate/ice_cream_vs_heater.csv&quot;),
  classification = paste0(GITHUB_RAW, &quot;/tabular/classification/breast_cancer_dataset2.csv&quot;),
  regression     = paste0(GITHUB_RAW, &quot;/tabular/regression/boston_dataset2.csv&quot;),
  raa            = paste0(GITHUB_RAW, &quot;/tabular/triangle/raa.csv&quot;),
  abc            = paste0(GITHUB_RAW, &quot;/tabular/triangle/abc.csv&quot;),
  km             = paste0(GITHUB_RAW, &quot;/tabular/survival/kidney.csv&quot;),
  ridge_survival = paste0(GITHUB_RAW, &quot;/tabular/survival/gbsg2_2.csv&quot;)
)

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

get_token &lt;- function() {
  args &lt;- commandArgs(trailingOnly = TRUE)
  idx  &lt;- which(args == &quot;--token&quot;)
  if (length(idx) &gt; 0 && idx &lt; length(args)) return(args[idx + 1])
  cat(&quot;Please enter your JWT token: &quot;)
  readLines(con = &quot;stdin&quot;, n = 1)
}

# Download a GitHub dataset to a temp file and return its path.
# Using a real file is the only reliable way to do multipart uploads in httr.
fetch_dataset &lt;- function(key) {
  url      &lt;- DATASETS[[key]]
  filename &lt;- basename(url)
  cat(sprintf(&quot;  Fetching %s from GitHub... &quot;, filename))
  tmp &lt;- tempfile(fileext = &quot;.csv&quot;)
  resp &lt;- GET(url, write_disk(tmp, overwrite = TRUE))
  stop_for_status(resp)
  cat(&quot;OK\n&quot;)
  tmp   # return the temp file path
}

make_request &lt;- function(endpoint, token, dataset_key = NULL, params = list()) {
  url     &lt;- paste0(BASE_URL, endpoint)
  headers &lt;- add_headers(Authorization = paste(&quot;Bearer&quot;, token))

  if (!is.null(dataset_key)) {
    tmp_path &lt;- fetch_dataset(dataset_key)
    on.exit(unlink(tmp_path), add = TRUE)   # clean up temp file when done
    body &lt;- list(file = upload_file(tmp_path, type = &quot;text/csv&quot;))
    resp &lt;- POST(url, headers, query = params, body = body, encode = &quot;multipart&quot;)
  } else {
    resp &lt;- GET(url, headers, query = params)
  }

  if (http_error(resp)) {
    cat(sprintf(&quot;  ERROR %s: %s\n&quot;, status_code(resp),
                content(resp, as = &quot;text&quot;, encoding = &quot;UTF-8&quot;)))
    return(NULL)
  }
  content(resp, as = &quot;parsed&quot;, type = &quot;application/json&quot;)
}

print_response &lt;- function(resp) {
  if (is.null(resp)) return(invisible(NULL))
  for (key in names(resp)) {
    val &lt;- resp[[key]]
    if (is.list(val) || length(val) &gt; 6) {
      cat(sprintf(&quot;  %s: [%s ...]\n&quot;, key, paste(head(unlist(val), 6), collapse = &quot;, &quot;)))
    } else {
      cat(sprintf(&quot;  %s: %s\n&quot;, key, paste(val, collapse = &quot;, &quot;)))
    }
  }
}

# ---------------------------------------------------------------------------
# Test sections
# ---------------------------------------------------------------------------

test_forecasting &lt;- function(token) {
  cat(&quot;\n=== Testing Forecasting Endpoints ===\n&quot;)

  cat(&quot;\nTesting univariate forecasting...\n&quot;)
  resp &lt;- make_request(&quot;/forecasting&quot;, token,
    dataset_key = &quot;univariate&quot;,
    params = list(base_model = &quot;RidgeCV&quot;, n_hidden_features = 5,
                  lags = 25, type_pi = &quot;kde&quot;, replications = 4, h = 3))
  print_response(resp)

  cat(&quot;\nTesting multivariate forecasting...\n&quot;)
  resp &lt;- make_request(&quot;/forecasting&quot;, token,
    dataset_key = &quot;multivariate&quot;,
    params = list(base_model = &quot;RidgeCV&quot;, n_hidden_features = 5, lags = 25, h = 3))
  print_response(resp)
}

test_ml &lt;- function(token) {
  cat(&quot;\n=== Testing Machine Learning Endpoints ===\n&quot;)

  cat(&quot;\nTesting classification...\n&quot;)
  resp &lt;- make_request(&quot;/mlclassification&quot;, token,
    dataset_key = &quot;classification&quot;,
    params = list(base_model = &quot;RandomForestClassifier&quot;,
                  n_hidden_features = 5, predict_proba = TRUE))
  print_response(resp)

  cat(&quot;\nTesting regression...\n&quot;)
  resp &lt;- make_request(&quot;/mlregression&quot;, token,
    dataset_key = &quot;regression&quot;,
    params = list(base_model = &quot;RidgeCV&quot;, n_hidden_features = 5, return_pi = TRUE))
  print_response(resp)
}

test_reserving &lt;- function(token) {
  cat(&quot;\n=== Testing Reserving Endpoints ===\n&quot;)

  cat(&quot;\nTesting RidgeCV...\n&quot;)
  resp &lt;- make_request(&quot;/mlreserving&quot;, token,
    dataset_key = &quot;raa&quot;,
    params = list(method = &quot;RidgeCV&quot;))
  print_response(resp)

  cat(&quot;\nTesting LassoCV...\n&quot;)
  resp &lt;- make_request(&quot;/mlreserving&quot;, token,
    dataset_key = &quot;abc&quot;,
    params = list(method = &quot;LassoCV&quot;))
  print_response(resp)
}

test_survival &lt;- function(token) {
  cat(&quot;\n=== Testing Survival Analysis Endpoints ===\n&quot;)

  cat(&quot;\nTesting Kaplan-Meier...\n&quot;)
  resp &lt;- make_request(&quot;/survivalcurve&quot;, token,
    dataset_key = &quot;km&quot;,
    params = list(method = &quot;km&quot;))
  print_response(resp)

  cat(&quot;\nTesting Ridge survival...\n&quot;)
  resp &lt;- make_request(&quot;/survivalcurve&quot;, token,
    dataset_key = &quot;ridge_survival&quot;,
    params = list(method = &quot;RidgeCV&quot;, patient_id = 0))
  print_response(resp)
}

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

main &lt;- function() {
  token &lt;- get_token()
  test_forecasting(token)
  test_ml(token)
  test_reserving(token)
  test_survival(token)
}

main()
</pre>

<h1 id="2---python-example">2 - Python example:</h1>

<pre>import argparse
import requests
import os
import io
from typing import Dict, Any

# All you need is a token from www.techtonique.net/token, then run the script

# Base URL for API endpoints
base_url = &quot;https://www.techtonique.net&quot;

# Base URL for raw GitHub content
GITHUB_RAW = &quot;https://raw.githubusercontent.com/Techtonique/datasets/main&quot;

# Dataset paths within the GitHub repo
DATASETS = {
    &quot;univariate&quot;:       f&quot;{GITHUB_RAW}/time_series/univariate/a10.csv&quot;,
    &quot;multivariate&quot;:     f&quot;{GITHUB_RAW}/time_series/multivariate/ice_cream_vs_heater.csv&quot;,
    &quot;classification&quot;:   f&quot;{GITHUB_RAW}/tabular/classification/breast_cancer_dataset2.csv&quot;,
    &quot;regression&quot;:       f&quot;{GITHUB_RAW}/tabular/regression/boston_dataset2.csv&quot;,
    &quot;raa&quot;:              f&quot;{GITHUB_RAW}/tabular/triangle/raa.csv&quot;,
    &quot;abc&quot;:              f&quot;{GITHUB_RAW}/tabular/triangle/abc.csv&quot;,
    &quot;km&quot;:               f&quot;{GITHUB_RAW}/tabular/survival/kidney.csv&quot;,
    &quot;ridge_survival&quot;:   f&quot;{GITHUB_RAW}/tabular/survival/gbsg2_2.csv&quot;,
}


def get_token() -&gt; str:
    &quot;&quot;&quot;Get token from command line argument or prompt&quot;&quot;&quot;
    parser = argparse.ArgumentParser(description='Test API endpoints')
    parser.add_argument('--token', help='JWT token for authentication')
    args = parser.parse_args()

    if args.token:
        return args.token

    return input(&quot;Please enter your JWT token: &quot;)


def fetch_dataset(dataset_key: str) -&gt; tuple[io.BytesIO, str]:
    &quot;&quot;&quot;
    Download a dataset from GitHub and return it as an in-memory bytes buffer
    together with a filename, ready to be passed to requests as a file upload.
    &quot;&quot;&quot;
    url = DATASETS[dataset_key]
    filename = url.split(&quot;/&quot;)[-1]
    print(f&quot;  Fetching {filename} from GitHub...&quot;, end=&quot; &quot;)
    response = requests.get(url)
    response.raise_for_status()
    print(&quot;OK&quot;)
    return io.BytesIO(response.content), filename


def make_request(
    url: str,
    token: str,
    method: str = &quot;POST&quot;,
    file_tuple: tuple = None,   # (BytesIO, filename)
    params: Dict = None,
) -&gt; Dict[str, Any]:
    &quot;&quot;&quot;Make an API request and return the response.&quot;&quot;&quot;
    headers = {&quot;Authorization&quot;: f&quot;Bearer {token}&quot;}

    try:
        if method == &quot;POST&quot;:
            files = None
            if file_tuple:
                buf, filename = file_tuple
                files = {&quot;file&quot;: (filename, buf, &quot;text/csv&quot;)}
            response = requests.post(url, headers=headers, files=files, params=params)
        else:
            response = requests.get(url, headers=headers, params=params)

        response.raise_for_status()
        return response.json()

    except requests.exceptions.RequestException as e:
        print(f&quot;Error making request to {url}:&quot;)
        print(f&quot;Status code: {e.response.status_code if hasattr(e, 'response') else 'N/A'}&quot;)
        print(f&quot;Response: {e.response.text if hasattr(e, 'response') else str(e)}&quot;)
        return None


def test_forecasting(token: str):
    print(&quot;\n=== Testing Forecasting Endpoints ===&quot;)

    print(&quot;\nTesting univariate forecasting...&quot;)
    response = make_request(
        f&quot;{base_url}/forecasting&quot;, token,
        file_tuple=fetch_dataset(&quot;univariate&quot;),
        params={&quot;base_model&quot;: &quot;RidgeCV&quot;, &quot;n_hidden_features&quot;: 5,
                &quot;lags&quot;: 25, &quot;type_pi&quot;: &quot;kde&quot;, &quot;replications&quot;: 4, &quot;h&quot;: 3},
    )
    if response:
        print(&quot;Response:&quot;, response)

    print(&quot;\nTesting multivariate forecasting...&quot;)
    response = make_request(
        f&quot;{base_url}/forecasting&quot;, token,
        file_tuple=fetch_dataset(&quot;multivariate&quot;),
        params={&quot;base_model&quot;: &quot;RidgeCV&quot;, &quot;n_hidden_features&quot;: 5, &quot;lags&quot;: 25, &quot;h&quot;: 3},
    )
    if response:
        print(&quot;Response:&quot;, response)


def test_ml(token: str):
    print(&quot;\n=== Testing Machine Learning Endpoints ===&quot;)

    print(&quot;\nTesting classification...&quot;)
    response = make_request(
        f&quot;{base_url}/mlclassification&quot;, token,
        file_tuple=fetch_dataset(&quot;classification&quot;),
        params={&quot;base_model&quot;: &quot;RandomForestClassifier&quot;,
                &quot;n_hidden_features&quot;: 5, &quot;predict_proba&quot;: True},
    )
    if response:
        print(&quot;Response:&quot;, response)

    print(&quot;\nTesting regression...&quot;)
    response = make_request(
        f&quot;{base_url}/mlregression&quot;, token,
        file_tuple=fetch_dataset(&quot;regression&quot;),
        params={&quot;base_model&quot;: &quot;RidgeCV&quot;, &quot;n_hidden_features&quot;: 5, &quot;return_pi&quot;: True},
    )
    if response:
        print(&quot;Response:&quot;, response)


def test_reserving(token: str):
    print(&quot;\n=== Testing Reserving Endpoints ===&quot;)

    print(&quot;\nTesting RidgeCV...&quot;)
    response = make_request(
        f&quot;{base_url}/mlreserving&quot;, token,
        file_tuple=fetch_dataset(&quot;raa&quot;),
        params={&quot;method&quot;: &quot;RidgeCV&quot;},
    )
    if response:
        print(&quot;Response:&quot;, response)

    print(&quot;\nTesting LassoCV...&quot;)
    response = make_request(
        f&quot;{base_url}/mlreserving&quot;, token,
        file_tuple=fetch_dataset(&quot;abc&quot;),
        params={&quot;method&quot;: &quot;LassoCV&quot;},
    )
    if response:
        print(&quot;Response:&quot;, response)


def test_survival(token: str):
    print(&quot;\n=== Testing Survival Analysis Endpoints ===&quot;)

    print(&quot;\nTesting Kaplan-Meier...&quot;)
    response = make_request(
        f&quot;{base_url}/survivalcurve&quot;, token,
        file_tuple=fetch_dataset(&quot;km&quot;),
        params={&quot;method&quot;: &quot;km&quot;},
    )
    if response:
        print(&quot;Response:&quot;, response)

    print(&quot;\nTesting Ridge survival...&quot;)
    response = make_request(
        f&quot;{base_url}/survivalcurve&quot;, token,
        file_tuple=fetch_dataset(&quot;ridge_survival&quot;),
        params={&quot;method&quot;: &quot;RidgeCV&quot;, &quot;patient_id&quot;: 0},
    )
    if response:
        print(&quot;Response:&quot;, response)


def main():
    token = get_token()
    test_forecasting(token)
    test_ml(token)
    test_reserving(token)
    test_survival(token)


if __name__ == &quot;__main__&quot;:
    main()
</pre>

<p><img src="https://i2.wp.com/thierrymoudiki.github.io/images/2025-06-09/2025-06-09-image1.gif?w=578&#038;ssl=1" alt="image-title-here" class="img-responsive" data-recalc-dims="1" /></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://thierrymoudiki.github.io//blog/2026/05/31/r/python/techtonique/techtonique-dot-net-is-back"> T. Moudiki's Webpage - 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/05/techtonique-dot-net-the-machine-learning-web-api-is-back-online-but-more-like-a-passion-project-for-now/">Techtonique dot net, the Machine Learning web API, is back online (but more like a passion project for now)</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401561</post-id>	</item>
		<item>
		<title>April 2026 Top 40 New CRAN Packages</title>
		<link>https://www.r-bloggers.com/2026/05/april-2026-top-40-new-cran-packages/</link>
		
		<dc:creator><![CDATA[Joseph Rickert]]></dc:creator>
		<pubDate>Sat, 30 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://rworks.dev/posts/april-2026-top-40-new-cran-packages/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Three hundred seventy-six of the new packages submitted to CRAN in April were still there in mid-May. Here are my Top 40 picks in twenty-three categories: Actuarial Analysis, Archaeology, Biology, Causal Inference, Computational Methods, Ecology...</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/05/april-2026-top-40-new-cran-packages/">April 2026 Top 40 New CRAN 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://rworks.dev/posts/april-2026-top-40-new-cran-packages/"> R Works</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>Three hundred seventy-six of the new packages submitted to CRAN in April were still there in mid-May. Here are my Top 40 picks in twenty-three categories: Actuarial Analysis, Archaeology, Biology, Causal Inference, Computational Methods, Ecology, Economics, Environmental Studies, Epidemiology, Finance, Functional Data Analysis, Health Technology Assessment, Machine Learning, Medical Statistics, Meta Analysis, Networks, Physics, Programming, Statistics, Time Series, Utilities, and Visualization.</p>
<div class="columns">
<div class="column" style="width:45%;">
<section id="actuarial-analysis" class="level3">
<h3 class="anchored" data-anchor-id="actuarial-analysis">Actuarial Analysis</h3>
<p><a href="https://cran.r-project.org/package=mqriskR" rel="nofollow" target="_blank">mqriskR</a> v0.1.0: Provides functions for actuarial risk modeling, including survival models, life annuities, multiple-decrement models, and mortality improvement projections. The package is designed to align with standard actuarial notation and supports teaching, exam preparation, and reproducible actuarial analysis. The methods are based on standard actuarial references, including <a href="https://www.amazon.com/Models-Quantifying-Risk-Richard-London/dp/1625423470" rel="nofollow" target="_blank">Camilli, Duncan and London (2014)</a>, and <a href="https://www.amazon.com/Actuarial-Mathematics-Contingent-International-Science/dp/1108478085/ref=sr_1_1?dib=eyJ2IjoiMSJ9.o7GXBw5H5JYFDcvNeOrpM58_PZqcW-2pMEeMTdCpcmxkl8sSFgx9YEBiBMmqr-wWFWZPgpoWKfjvwnvE7ASss2GsLU84aE3P7YsnfFYbBbBBtlfbK0fTcwWnH23XKnJwV6JkxJMocNt2MQdIyCmdDsqWBHNyRILKd17PUpOZyPnMWkBsXn9kXX2qedo98iumU6HQiObaIzG8nmpm3U-RYVgnomDlUWq61OnCS37EDMc.Lb6GlPsG2422vJHe1j-GwV_1RSSDvznlhHe7cbX-g2I&#038;dib_tag=se&#038;keywords=Actuarial+Mathematics+for+Life+Contingent+Risks&#038;qid=1779844083&#038;s=books&#038;sr=1-1" rel="nofollow" target="_blank">Dickson, Hardy and Waters (2020)</a>. See the <a href="https://cran.r-project.org/web/packages/mqriskR/vignettes/getting-started.html" rel="nofollow" target="_blank">vignette</a>.</p>
</section>
<section id="archaeology" class="level3">
<h3 class="anchored" data-anchor-id="archaeology">Archaeology</h3>
<p><a href="https://cran.r-project.org/package=palimpsestr" rel="nofollow" target="_blank">palimpsestr</a> v0.10.0: Implements a probabilistic framework for the analysis of archaeological palimpsests based on the Stratigraphic Entanglement Field which integrates spatial proximity, stratigraphic depth, chronological overlap, and cultural similarity to estimate latent depositional phases via diagonal Gaussian mixture Expectation-Maximisation. Includes simulation, diagnostics, phase-count selection, publication-quality plots, and Geographic Information System export via <code>sf</code>. Methods are described in <a href="https://zenodo.org/records/19969277" rel="nofollow" target="_blank">Cocca (2026)</a>. See the <a href="https://cran.r-project.org/web/packages/palimpsestr/vignettes/introduction.html" rel="nofollow" target="_blank">vignette</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/palimpsestr.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-1" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/palimpsestr.png?w=578&#038;ssl=1" class="img-fluid" alt="Vertical Phase Profile Plot" data-recalc-dims="1"></a></p>
</section>
<section id="biology" class="level3">
<h3 class="anchored" data-anchor-id="biology">Biology</h3>
<p><a href="https://cran.r-project.org/package=bsocialv2" rel="nofollow" target="_blank">bsocialv2</a> v0.2.1: Provides an S4 class and methods for analyzing microbial social behavior in bacterial consortia. Includes growth parameter extraction, social behavior classification (cooperators/cheaters/neutrals), diversity effect analysis, consortium assembly path finding, and stability analysis via coefficient of variation. Methods are described in <a href="https://www.frontiersin.org/journals/microbiology/articles/10.3389/fmicb.2017.00919/full" rel="nofollow" target="_blank">Purswani et al. (2017)</a>. See the <a href="https://cran.r-project.org/web/packages/bsocialv2/vignettes/bsocial-workflow.html" rel="nofollow" target="_blank">vignette</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/bsocialv2.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-2" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/bsocialv2.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot showing fitness over number of generations" data-recalc-dims="1"></a></p>
</section>
<section id="causal-inference" class="level3">
<h3 class="anchored" data-anchor-id="causal-inference">Causal Inference</h3>
<p><a href="https://cran.r-project.org/package=CausalMixGPD" rel="nofollow" target="_blank">CausalMixGPD</a> v0.8.0: Implements tools for Bayesian analysis of heavy-tailed outcomes by combining Dirichlet process mixture models for the body of the distribution with optional generalized Pareto tails. The method allows for unconditional and covariate-modulated mixtures, implements MCMC estimation using <code>nimble</code>, and extends to mixtures of different arms’ outcomes with application to causal inference in the <a href="https://psycnet.apa.org/doiLanding?doi=10.1037%2Fh0037350" rel="nofollow" target="_blank">Rubin (1974)</a> framework. There are three vignettes, including an <a href="https://cran.r-project.org/web/packages/CausalMixGPD/vignettes/cmgpd_causal.html" rel="nofollow" target="_blank">Introduction</a> and <a href="https://cran.r-project.org/web/packages/CausalMixGPD/vignettes/cmgpd_one_arm.html" rel="nofollow" target="_blank">One-Arm Regression Modeling</a>.</p>
<p><a href="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/CausalMixGPD.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-3" rel="nofollow" target="_blank"><img src="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/CausalMixGPD.png?w=578&#038;ssl=1" class="img-fluid" alt="Quantile predictions with pointwise credible intervals" data-recalc-dims="1"></a></p>
</section>
<section id="computational-methods" class="level3">
<h3 class="anchored" data-anchor-id="computational-methods">Computational Methods</h3>
<p><a href="https://cran.r-project.org/package=nmfkc" rel="nofollow" target="_blank">nmfkc</a> v0.7.3: Provides functions to perform non-negative matrix factorization with kernel covariates. Given an observation matrix and kernel covariates, it optimizes both a basis matrix and a parameter matrix. Also provides NMF with random effects, which estimates a mixed-effects model combining covariate-driven scores with unit-specific random effects together with wild bootstrap inference, and NMF-based structural equation modeling. See <a href="https://arxiv.org/abs/2403.05359" rel="nofollow" target="_blank">Satoh (2025)</a> and <a href="https://link.springer.com/article/10.1007/s42081-025-00314-0" rel="nofollow" target="_blank">Satoh (2026)</a> for background. There are six vignettes, including <a href="https://cran.r-project.org/web/packages/nmfkc/vignettes/introduction-to-nmfkc.html" rel="nofollow" target="_blank">Introduction</a> and <a href="https://cran.r-project.org/web/packages/nmfkc/vignettes/topic-modeling-with-nmfkc.html" rel="nofollow" target="_blank">Topic Modeling</a>.</p>
<p><a href="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/nmfkc.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-4" rel="nofollow" target="_blank"><img src="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/nmfkc.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of topic proportions" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=symbolicr" rel="nofollow" target="_blank">symbolicr</a> v1.0.0: Provides functions to find non-linear formulas that fit input data. Users can systematically explore and memorize the possible formulas and their cross-validation performance, in an incremental fashion. Three main interoperable search functions are available: 1) <code>random.search()</code> performs a random exploration, 2) <code>genetic.search()</code> employs a genetic optimization algorithm, 3) <code>comb.search()</code> combines the best results of the first two. For more details, see <a href="https://link.springer.com/article/10.1208/s12248-026-01232-z" rel="nofollow" target="_blank">Tomasoni et al. (2026)</a>. There are three vignettes, including <a href="https://cran.r-project.org/web/packages/symbolicr/vignettes/get-started.html" rel="nofollow" target="_blank">get-started</a> and <a href="https://cran.r-project.org/web/packages/symbolicr/vignettes/formula-analysis.html" rel="nofollow" target="_blank">formula-analysis</a>.</p>
</section>
<section id="ecology" class="level3">
<h3 class="anchored" data-anchor-id="ecology">Ecology</h3>
<p><a href="https://cran.r-project.org/package=CharAnalysis" rel="nofollow" target="_blank">CharAnalysis</a> v2.0.3: Implements a program for reconstructing local fire histories from high-resolution, continuously sampled lake-sediment charcoal records. Functions decompose a charcoal record into low- and high-frequency components and use locally defined thresholds to separate fire signal from noise. See <a href="https://esajournals.onlinelibrary.wiley.com/doi/10.1890/07-2019.1" rel="nofollow" target="_blank">Higuera et al. (2009)</a> and <a href="https://connectsci.au/wf/article-abstract/19/8/996/23290/Peak-detection-in-sediment-charcoal-records?redirectedFrom=fulltext" rel="nofollow" target="_blank">Higuera et al. (2010)</a> for background and the <a href="https://cran.r-project.org/web/packages/CharAnalysis/vignettes/CharAnalysis_intro.html" rel="nofollow" target="_blank">vignette</a> to get started.</p>
<p><img src="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/CharAnalysis.png?w=578&#038;ssl=1" class="img-fluid" alt="Each panel showing the empirical Cpeak distribution within that window, the fitted noise component (Gaussian or Gaussian mixture, per threshMethod), and the resulting threshold value at the working percentile" data-recalc-dims="1"> <a href="https://cran.r-project.org/package=SQIpro" rel="nofollow" target="_blank">SQIpro</a> v0.1.0: Provides a comprehensive, modular framework for computing the Soil Quality Index (SQI) using six established methods: Linear Scoring <a href="https://acsess.onlinelibrary.wiley.com/doi/10.2136/sssaspecpub35.c1" rel="nofollow" target="_blank">Doran and Parkin, (1994)</a>. Regression-based <a href="https://link.springer.com/article/10.1007/s10661-007-9697-z" rel="nofollow" target="_blank">Masto et al. (2008)</a>, Principal Component Analysis <a href="https://acsess.onlinelibrary.wiley.com/doi/10.2136/sssaj2004.1945" rel="nofollow" target="_blank">Andrews et al. (2004)</a>, Fuzzy Logic, Entropy Weighting, TOPSIS <a href="https://link.springer.com/book/10.1007/978-3-642-48318-9" rel="nofollow" target="_blank">Hwang and Yoon (1981)</a>. See the <a href="https://cran.r-project.org/web/packages/SQIpro/vignettes/getting_started.html" rel="nofollow" target="_blank">vignette</a>.</p>
<p><a href="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/SQIpro.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-5" rel="nofollow" target="_blank"><img src="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/SQIpro.png?w=578&#038;ssl=1" class="img-fluid" alt="PCA Biplot of Soil Quality Variables" data-recalc-dims="1"></a></p>
</section>
<section id="economics" class="level3">
<h3 class="anchored" data-anchor-id="economics">Economics</h3>
<p><a href="https://cran.r-project.org/package=rescomp" rel="nofollow" target="_blank">rescomp</a> v1.0.0: Provides functions to generate, simulate, and visualize ODE models of consumer-resource interactions and competition modeling. There is an <a href="https://cran.r-project.org/web/packages/rescomp/vignettes/rescomp.html" rel="nofollow" target="_blank">Introduction</a> and a <a href="https://cran.r-project.org/web/packages/rescomp/vignettes/classic-results.html" rel="nofollow" target="_blank">vignette</a> reproducing classic results.</p>
<p><a href="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/rescomp.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-6" rel="nofollow" target="_blank"><img src="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/rescomp.png?w=578&#038;ssl=1" class="img-fluid" alt="Plots showing behavior of three 'type 1' comusers on three essential resources over time" data-recalc-dims="1"></a></p>
</section>
<section id="environmental-studies" class="level3">
<h3 class="anchored" data-anchor-id="environmental-studies">Environmental Studies</h3>
<p><a href="https://cran.r-project.org/package=gleam" rel="nofollow" target="_blank">gleam</a> v0.8.0: This official implementation of the Global Livestock Environmental Assessment Model of the Food and Agriculture Organization of the United Nations (<a href="https://www.fao.org/gleam/en/" rel="nofollow" target="_blank">GLEAM</a>) provides a modular, transparent framework for simulating livestock production systems and quantifying their environmental impacts. See <a href="https://www.sciencedirect.com/science/article/pii/S1751731117001847" rel="nofollow" target="_blank">MacLeod et al. (2017)</a> for background. There are four vignettes, including an <a href="https://cran.r-project.org/web/packages/gleam/vignettes/gleam-overview.html" rel="nofollow" target="_blank">Overview</a> and <a href="https://cran.r-project.org/web/packages/gleam/vignettes/gleam-modules-overview.html" rel="nofollow" target="_blank">Package Modules</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/gleam.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-7" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/gleam.png?w=578&#038;ssl=1" class="img-fluid" alt="Plots showing behavior of three 'type 1' comusers on three essential resources over time" data-recalc-dims="1"></a></p>
</section>
<section id="epidemiology" class="level3">
<h3 class="anchored" data-anchor-id="epidemiology">Epidemiology</h3>
<p><a href="https://cran.r-project.org/package=lineagefreq" rel="nofollow" target="_blank">lineagefreq</a> v0.2.0: Provides functions to model pathogen lineage frequency dynamics from genomic surveillance count data. Includes a unified interface for multinomial logistic regression, hierarchical partial-pooling models, the Piantham approximation for relative reproduction number estimation, and features such as rolling-origin backtesting, standardized forecast scoring. See <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1012443" rel="nofollow" target="_blank">Abousamra, Figgins, and Bedford (2024)</a> for background. There are four vignettes, including <a href="https://cran.r-project.org/web/packages/lineagefreq/vignettes/introduction.html" rel="nofollow" target="_blank">Getting Started</a> and <a href="https://cran.r-project.org/web/packages/lineagefreq/vignettes/real-data-analysis.html" rel="nofollow" target="_blank">Analyzing real CDC surveillance data</a>.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/lineagefreq.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-8" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/lineagefreq.png?w=578&#038;ssl=1" class="img-fluid" alt="Trajectories of several lineages" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=seroreconstruct" rel="nofollow" target="_blank">seroreconstruct</a> v1.1.5: Implements a Bayesian framework for inferring influenza infection status from serial antibody measurements. Jointly estimates season-specific infection probabilities, antibody boosting and waning after infection, and baseline hemagglutination inhibition titer distributions. Supports multi-season analysis and subgroup comparisons via a group_by interface. See <a href="https://www.nature.com/articles/s41467-022-29310-8" rel="nofollow" target="_blank">Tsang et al. (2022)</a> for methodological details and the two vignettes <a href="https://cran.r-project.org/web/packages/seroreconstruct/vignettes/introduction.html" rel="nofollow" target="_blank">Getting Started</a> and <a href="https://cran.r-project.org/web/packages/seroreconstruct/vignettes/methodology.html" rel="nofollow" target="_blank">Statistical Methodology</a>.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/sereoconstruct.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-9" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/sereoconstruct.png?w=578&#038;ssl=1" class="img-fluid" alt="Plots of antibody rise and waning" data-recalc-dims="1"></a></p>
</section>
<section id="finance" class="level3">
<h3 class="anchored" data-anchor-id="finance">Finance</h3>
<p><a href="https://cran.r-project.org/package=finlabR" rel="nofollow" target="_blank">finlabR</a> v1.0.0: Provides tools for portfolio construction and risk analytics, including mean-variance optimization, conditional value at risk minimization, risk parity, regime clustering, correlation analysis, Monte Carlo simulation, and option pricing. Includes utilities for portfolio evaluation, clustering, and risk reporting. Methods are based in part on <a href="https://onlinelibrary.wiley.com/doi/10.1111/j.1540-6261.1952.tb01525.x" rel="nofollow" target="_blank">Markowitz (1952)</a>, <a href="https://www.risk.net/journal-risk/2161159/optimization-conditional-value-risk" rel="nofollow" target="_blank">Rockafellar and Uryasev (2000)</a>, <a href="https://www.pm-research.com/content/iijpormgmt/36/4/60" rel="nofollow" target="_blank">Maillard et al. (2010)</a>, <a href="https://www.journals.uchicago.edu/doi/10.1086/260062" rel="nofollow" target="_blank">Black and Scholes (1973)</a>, and <a href="https://www.sciencedirect.com/science/article/abs/pii/0304405X79900151" rel="nofollow" target="_blank">Cox et al. (1979)</a>. See the vignettes <a href="https://cran.r-project.org/web/packages/finlabR/vignettes/finlabR-intro.html" rel="nofollow" target="_blank">Portfolio Analytics and Simulation</a> and <a href="https://cran.r-project.org/web/packages/finlabR/vignettes/finlabR-workflow.html" rel="nofollow" target="_blank">End-to-End Workflow</a></p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/finlabR.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-10" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/finlabR.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of Efficient Frontier" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=talib" rel="nofollow" target="_blank">talib</a> v0.9-2: Implements an interface to the <code>TA-Lib</code> (Technical Analysis Library) <code>C</code> library, providing access to 150+ indicators (e.g. Average Directional Movement Index (ADX), Moving Average Convergence Divergence (MACD), Relative Strength Index (RSI), Stochastic Oscillator, Bollinger Bands), candlestick pattern recognition, and rolling-window utilities. Core computations are implemented in <code>C</code> for fast Open-High-Low-Close-Volume time-series feature engineering and rule-based signal generation. There are three vignettes, including <a href="https://cran.r-project.org/web/packages/talib/vignettes/candlestick.html" rel="nofollow" target="_blank">Candlestick Pattern Recognition</a> and <a href="https://cran.r-project.org/web/packages/talib/vignettes/charting.html" rel="nofollow" target="_blank">Financial Charts</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/talib.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-11" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/talib.png?w=578&#038;ssl=1" class="img-fluid" alt="Stock chart with indicators" data-recalc-dims="1"></a></p>
</section>
<section id="functional-data-analysis" class="level3">
<h3 class="anchored" data-anchor-id="functional-data-analysis">Functional Data Analysis</h3>
<p><a href="https://cran.r-project.org/package=refundBayes" rel="nofollow" target="_blank">refundBayes</a> v0.6.0: Provides tools to perform Bayesian regression with functional data, including regression with scalar, survival, or functional outcomes. The package allows regression with scalar and functional predictors. Methods are described in <a href="https://onlinelibrary.wiley.com/doi/10.1002/sim.70265" rel="nofollow" target="_blank">Jiang et al. (2025)</a> <em>Tutorial on Bayesian Functional Regression Using Stan</em>. There are six vignettes, including <a href="https://cran.r-project.org/web/packages/refundBayes/vignettes/fofr_bayes_vignette.html" rel="nofollow" target="_blank">Bayesian Function-on-Function Regression</a> and <a href="https://cran.r-project.org/web/packages/refundBayes/vignettes/fpca_bayes_vignette.html" rel="nofollow" target="_blank">Bayesian Functional Principal Component Analysis (FPCA)</a>.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/refundBayes.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-12" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/refundBayes.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of predictive accuracy on held-out validation set" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=tidyfun" rel="nofollow" target="_blank">tidyfun</a> v0.1.2: Builds on the <code>tf</code> package to provide functions to represent, visualize, describe and wrangle functional data in tidy data frames as well as data types for functional observations that work as columns in data frames, enabling manipulation with <code>dplyr</code> verbs and visualization with <code>ggplot2</code> geoms designed for functional data. There are six vignettes, including <a href="https://cran.r-project.org/web/packages/tidyfun/vignettes/x01_tf_Vectors.html" rel="nofollow" target="_blank">tf Vectors and Operations</a> and <a href="https://cran.r-project.org/web/packages/tidyfun/vignettes/x04_Visualization.html" rel="nofollow" target="_blank">Visualization</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/tidyfun.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-13" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/tidyfun.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of functional variables" data-recalc-dims="1"></a></p>
</section>
<section id="genomics" class="level3">
<h3 class="anchored" data-anchor-id="genomics">Genomics</h3>
<p><a href="https://cran.r-project.org/package=AbSolution" rel="nofollow" target="_blank">AbSolution</a> v1.0.1: Implements an interactive framework as a <code>Shiny</code> Application for the exploration and analysis of adaptive immune receptor repertoire sequencing data in a manner that facilitates reproducible research. It enables large-scale computation and integrated analysis of sequence-derived features, including physicochemical properties, amino acid descriptor sets, sequence motifs, compositional patterns, and somatic hypermutation metrics. See the <a href="https://github.com/EDS-Bioinformatics-Laboratory/AbSolution" rel="nofollow" target="_blank">GitHub Repository</a> and the <a href="https://cran.r-project.org/web/packages/AbSolution/vignettes/AbSolution.html" rel="nofollow" target="_blank">vignette</a> for more details.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/ABsolution.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-14" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/ABsolution.png?w=578&#038;ssl=1" class="img-fluid" alt="Snapshot of Shiny page for Exploratory Analysis" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=IOBR" rel="nofollow" target="_blank">IOBR</a> v2.2.2: Provides six modules for tumor microenvironment (TME) analysis based on multi-omics data. These modules cover data preprocessing, TME estimation, TME infiltrating patterns, cellular interactions, genome and TME interaction, and visualization for TME-relevant features, as well as modelling based on key features. In addition to providing a way to construct gene signatures from single-cell RNA-seq data, it also provides a way to construct a reference matrix for TME deconvolution from single-cell RNA-seq data. See <a href="https://www.cell.com/cell-reports-methods/fulltext/S2667-2375(24)00300-X?_returnURL=https%3A%2F%2Flinkinghub.elsevier.com%2Fretrieve%2Fpii%2FS266723752400300X%3Fshowall%3Dtrue" rel="nofollow" target="_blank">Zeng et al. (2024)</a> and <a href="https://onlinelibrary.wiley.com/doi/10.1002/mdr2.70001" rel="nofollow" target="_blank">Fang et al. (2025)</a> for background and the <a href="https://cran.r-project.org/web/packages/IOBR/vignettes/IOBR-user-manual.html" rel="nofollow" target="_blank">vignette</a> for a detailed tutorial.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/IOBR.jpeg?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-15" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/IOBR.jpeg?w=578&#038;ssl=1" class="img-fluid" alt="Diagram of Workflow" data-recalc-dims="1"></a></p>
</section>
<section id="health-technology-assessment" class="level3">
<h3 class="anchored" data-anchor-id="health-technology-assessment">Health Technology Assessment</h3>
<p><a href="https://cran.r-project.org/package=htaBIM" rel="nofollow" target="_blank">htaBIM</a> v0.1.0: Implements a structured, reproducible framework, a Shiny Application, for budget impact modelling in health technology assessment (HTA), following the ISPOR Task Force guidelines <a href="https://www.valueinhealthjournal.com/article/S1098-3015(13)04235-6/fulltext?_returnURL=https%3A%2F%2Flinkinghub.elsevier.com%2Fretrieve%2Fpii%2FS1098301513042356%3Fshowall%3Dtrue" rel="nofollow" target="_blank">(Sullivan et al. (2014)</a> and <a href="https://www.valueinhealthjournal.com/article/S1098-3015(10)60471-8/pdf?_returnURL=https%3A%2F%2Flinkinghub.elsevier.com%2Fretrieve%2Fpii%2FS1098301510604718%3Fshowall%3Dtrue" rel="nofollow" target="_blank">Mauskopf et al. (2007)</a> that provides functions for epidemiology-driven population estimation, market share modelling with flexible uptake dynamics, per-patient cost calculation across multiple cost categories, multi-year budget projections, payer perspective analysis, deterministic sensitivity analysis, and probabilistic sensitivity analysis. Produces submission-quality outputs including ISPOR-aligned summary tables, scenario comparison tables, per-patient cost breakdowns, tornado diagrams, PSA histograms, and text and HTML reports compatible with NICE, CADTH, and EU-HTA dossier formats. See the vignettes <a href="https://cran.r-project.org/web/packages/htaBIM/vignettes/htaBIM-introduction.html" rel="nofollow" target="_blank">Introduction</a> and <a href="https://cran.r-project.org/web/packages/htaBIM/vignettes/shiny-app.html" rel="nofollow" target="_blank">Interactive Shiny Dashboard</a>.</p>
<p><a href="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/htaBMI.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-16" rel="nofollow" target="_blank"><img src="https://i2.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/htaBMI.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of Probabilistic Sensitivity Analysis" data-recalc-dims="1"></a></p>
</section>
</div><div class="column" style="width:10%;">

</div><div class="column" style="width:45%;">
<section id="machine-learning" class="level3">
<h3 class="anchored" data-anchor-id="machine-learning">Machine Learning</h3>
<p><a href="https://cran.r-project.org/package=AntsNet" rel="nofollow" target="_blank">AntsNet</a> v1.0.0: Implements the full suite of simulation, visualization, and analysis tools for exploring the mathematical isomorphisms between ant colony decision-making and three major paradigms of machine learning: random forests (Part I: variance reduction through decorrelation), boosting (Part II: bias reduction through adaptive recruitment), and neural networks (Part III: gradient-based generational learning). See Fokoué, Babbitt, and Levental (2026) <a href="https://arxiv.org/abs/2603.20328" rel="nofollow" target="_blank">Part I</a> and <a href="https://arxiv.org/abs/2604.00038" rel="nofollow" target="_blank">Part II</a> for background and <a href="https://cran.r-project.org/web/packages/AntsNet/readme/README.html" rel="nofollow" target="_blank">README</a> to get started.</p>
<p><a href="https://cran.r-project.org/package=bigKNN" rel="nofollow" target="_blank">bigKNN</a> v0.3.0: Implements exact nearest-neighbour and radius-search routines that operate directly on <code>bigmemory::big.matrix</code> objects. Functions stream row blocks through <code>BLAS</code> kernels, support self-search and external-query search, expose prepared references for repeated queries, and can build exact k-nearest-neighbour, radius, mutual k-nearest-neighbour, and shared-nearest-neighbour graphs. There are seven vignettes, including <a href="https://cran.r-project.org/web/packages/bigKNN/vignettes/bigknn-quickstart.html" rel="nofollow" target="_blank">Quick Start</a> and <a href="https://cran.r-project.org/web/packages/bigKNN/vignettes/bigknn-evaluating-approximate-search.html" rel="nofollow" target="_blank">Using bigKNN as Exact Ground Truth</a>.</p>
</section>
<section id="medical-statistics" class="level3">
<h3 class="anchored" data-anchor-id="medical-statistics">Medical Statistics</h3>
<p><a href="https://gosukehommaex.github.io/BayesianQDM/" rel="nofollow" target="_blank">BayesianQDM</a> v0.1.0: Provides comprehensive methods to calculate posterior probabilities, posterior predictive probabilities, and Go/NoGo/Gray decision probabilities for quantitative decision-making under a Bayesian paradigm in clinical trials. Supports both single and two-endpoint analyses for binary and continuous outcomes, with controlled, uncontrolled, and external designs. External designs incorporate historical data through power priors using exact conjugate representations to significantly reduce computational burden while preserving complete Bayesian rigor. See <a href="https://www.tandfonline.com/doi/full/10.1080/10543406.2026.2655410" rel="nofollow" target="_blank">Kang, Yamaguchi, and Han (2026)</a> for the methodological framework. There are five vignettes, including <a href="https://cran.r-project.org/web/packages/BayesianQDM/vignettes/overview.html" rel="nofollow" target="_blank">Overview</a> and <a href="https://cran.r-project.org/web/packages/BayesianQDM/vignettes/two-continuous.html" rel="nofollow" target="_blank">Two Continuous Endpoints</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/BayesianQDM.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-17" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/BayesianQDM.png?w=578&#038;ssl=1" class="img-fluid" alt="An Optimal Threshold Plot" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=raretrans" rel="nofollow" target="_blank">raretrans</a> v1.0.5: Provides functions to correct biased transition and fertility estimates in population projection matrices caused by small sample sizes, never observed biologically possible transmissions, or transitions estimated at 100% survival, stasis, or mortality that are biologically implausible. Implements a multinomial-Dirichlet Bayesian prior for transition probabilities and a Gamma-Poisson prior for reproduction, allowing analysts to incorporate prior biological knowledge and regularise estimates from rare or unobserved events. Methods are described in <a href="https://www.sciencedirect.com/science/article/abs/pii/S0304380021000971" rel="nofollow" target="_blank">Tremblay et al. (2021)</a>. There are six vignettes, including both a <a href="https://cran.r-project.org/web/packages/raretrans/vignettes/quick_start.html" rel="nofollow" target="_blank">Quick start</a> and an <a href="https://cran.r-project.org/web/packages/raretrans/vignettes/introduction.html" rel="nofollow" target="_blank">Introduction</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/raretrans.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-18" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/raretrans.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot showing effect of prior weight oncredible intervals" data-recalc-dims="1"></a></p>
</section>
<section id="meta-analysis" class="level3">
<h3 class="anchored" data-anchor-id="meta-analysis">Meta Analysis</h3>
<p><a href="https://cran.r-project.org/package=confMeta" rel="nofollow" target="_blank">confMeta</a> v0.1.0: Provides tools for the combination of individual study results in meta-analyses using <em>p-value</em> functions. Implements various combination methods, including those by Fisher, Stouffer, Tippett, Edgington, along with weighted generalizations. Contains functionality for the visualization and calculation of confidence curves and drapery plots to summarize evidence across studies. See the <a href="https://cran.r-project.org/web/packages/confMeta/vignettes/confMeta-usage.html" rel="nofollow" target="_blank">vignette</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/confMeta.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-19" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/confMeta.png?w=578&#038;ssl=1" class="img-fluid" alt="Combined p=value and effects plots" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=drmeta" rel="nofollow" target="_blank">drmeta</a> v0.1.0: Implements a variance-function random-effects framework in which between-study heterogeneity is modeled as a function of a study-level design robustness index, allowing heterogeneity to depend systematically on study quality or design strength rather than being treated as a single nuisance parameter. The framework nests classical fixed-effects and standard random-effects meta-analysis as special cases, making it a strict generalization of existing approaches. See the <a href="https://cran.r-project.org/web/packages/drmeta/vignettes/getting-started.html" rel="nofollow" target="_blank">Getting Started Guide</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/drmeta.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-20" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/drmeta.png?w=578&#038;ssl=1" class="img-fluid" alt="Funnel plot showing Standard error vs Effect Size" data-recalc-dims="1"></a></p>
</section>
<section id="networks" class="level3">
<h3 class="anchored" data-anchor-id="networks">Networks</h3>
<p><a href="https://cran.r-project.org/package=sparsecommunity" rel="nofollow" target="_blank">sparsecommunity</a> v0.1.1: Implements spectral clustering algorithms for community detection in sparse networks under the stochastic block model and degree-corrected stochastic block model following the methods of <a href="https://projecteuclid.org/journals/annals-of-statistics/volume-43/issue-1/Consistency-of-spectral-clustering-in-stochastic-block-models/10.1214/14-AOS1274.full" rel="nofollow" target="_blank">Lei and Rinaldo (2015)</a>. Provides a regularized normalized Laplacian embedding, spherical k-median clustering, simulation utilities, and a misclustering rate evaluation metric along with the NCAA college football network of <a href="https://www.pnas.org/doi/full/10.1073/pnas.122653799" rel="nofollow" target="_blank">Girvan and Newman (2002)</a> as a benchmark dataset, and the Bethe-Hessian community number estimator of <a href="https://www.tandfonline.com/doi/full/10.1080/01621459.2023.2223793" rel="nofollow" target="_blank">Hwang (2023)</a>. See the <a href="https://cran.r-project.org/web/packages/sparsecommunity/vignettes/sparsecommunity-intro.html" rel="nofollow" target="_blank">vignette</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/sparsecommunity.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-21" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/sparsecommunity.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of detetcted vs. true communities" data-recalc-dims="1"></a></p>
</section>
<section id="physics" class="level3">
<h3 class="anchored" data-anchor-id="physics">Physics</h3>
<p><a href="https://cran.r-project.org/package=orbitr" rel="nofollow" target="_blank">orbitr</a> v0.3.0: Provides a lightweight, fully vectorized N-body physics engine built for the R ecosystem. Simulate and visualize complex orbital mechanics, celestial trajectories, and gravitational interactions using tidy data principles. Features multiple numerical integration methods, including the energy-conserving velocity Verlet algorithm <a href="https://journals.aps.org/pr/abstract/10.1103/PhysRev.159.98" rel="nofollow" target="_blank">Verlet (1967)</a> to ensure highly stable orbital propagation. Gravitational N-body methods follow <a href="https://www.cambridge.org/us/universitypress/subjects/physics/astrophysics/gravitational-n-body-simulations-tools-and-algorithms?format=PB&#038;isbn=9780521121538" rel="nofollow" target="_blank">Aarseth (2003)</a>. There are twelve vignettes, including <a href="https://cran.r-project.org/web/packages/orbitr/vignettes/keplerian-elements.html" rel="nofollow" target="_blank">Quick Start Guide</a> and <a href="https://cran.r-project.org/web/packages/orbitr/vignettes/the-physics.html" rel="nofollow" target="_blank">The Physics</a>.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/orbitr.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-22" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/orbitr.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of planetary orbits" data-recalc-dims="1"></a></p>
</section>
<section id="programming" class="level3">
<h3 class="anchored" data-anchor-id="programming">Programming</h3>
<p><a href="https://cran.r-project.org/package=mori" rel="nofollow" target="_blank">mori</a> v0.2.0: Enables users to share <code>R</code> objects across processes on the same machine via a single copy in <code>POSIX</code> shared memory (<code>Linux</code>, <code>macOS</code>) or a <code>Win32</code> file mapping (<code>Windows</code>). Every process reads from the same physical pages through the <code>R</code> Alternative Representation (<code>ALTREP</code>) framework, giving lazy, zero-copy access. Shared objects serialize compactly as their shared memory name rather than their full contents. See <a href="https://cran.r-project.org/web/packages/mori/readme/README.html" rel="nofollow" target="_blank">README</a> to get started.</p>
<p><a href="https://cran.r-project.org/package=progressify" rel="nofollow" target="_blank">progressify</a> v0.1.0: The `<code>progressify()</code> function rewrites (transpiles) calls to sequential and parallel map-reduce functions such as <code>base::lapply()</code>, <code>purrr::map()</code>, <code>foreach::foreach()</code>, and <code>plyr::llply()</code> to signal progress updates. By combining this function with R’s native pipe operator, you have a straightforward way to report progress on iterative computations with minimal refactoring, e.g., <code>lapply(x, fcn) |&gt; progressify()</code> and <code>purrr::map(x, fcn) |&gt; progressify()</code>. It is compatible with the <code>futurize</code> package for parallelization, e.g. <code>lapply(x, fcn) |&gt; progressify() |&gt; futurize()</code> and <code>purrr::map(x, fcn) |&gt; futurize() |&gt; progressify()</code>. There are nine brief vignettes, including <a href="https://cran.r-project.org/web/packages/progressify/vignettes/progressify-11-base.html" rel="nofollow" target="_blank">Progress updates for base-R apply functions</a> and <a href="https://cran.r-project.org/web/packages/progressify/vignettes/progressify-21-purrr.html" rel="nofollow" target="_blank">Progress updates for ‘purrr’ functions</a>.</p>
<p><a href="https://cran.r-project.org/package=shard" rel="nofollow" target="_blank">shard</a> v0.1.1: Provides a parallel execution runtime for R that emphasizes deterministic memory behavior and efficient handling of large shared inputs, which enables zero-copy parallel reads via shared, memory-mapped segments, encourages explicit output buffers to avoid large result aggregation, and supervises worker processes to mitigate memory drift via controlled recycling. Diagnostics report peak memory usage, end-of-run memory return, and hidden copy/materialization events to support reproducible performance benchmarking. See the <a href="https://cran.r-project.org/web/packages/shard/vignettes/shard.html" rel="nofollow" target="_blank">vignette</a> to get started.</p>
</section>
<section id="statistics" class="level3">
<h3 class="anchored" data-anchor-id="statistics">Statistics</h3>
<p><a href="https://cran.r-project.org/package=balnet" rel="nofollow" target="_blank">balnet</a> v0.0.3: Provides pathwise estimation of regularized logistic propensity score models using covariate balancing loss functions rather than maximum likelihood. Regularization paths are fit via the <code>adelie</code> elastic-net solver with a <code>glmnet</code>-like interface, yielding balancing weights that target covariate balance for the ATE and ATT. Under lasso penalization, lambda bounds the maximum covariate imbalance, so the regularization path traces a sequence of decreasing imbalance tolerances. See <a href="https://arxiv.org/abs/2602.18577" rel="nofollow" target="_blank">Sverdrup &#038; Hastie (2026)</a> for details and the <a href="https://cran.r-project.org/web/packages/balnet/vignettes/balnet.html" rel="nofollow" target="_blank">vignette</a> for an introduction.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/balnet.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-23" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/balnet.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot showing model fit that aims to balance control covariate means toward those of the treated group" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=ngme2" rel="nofollow" target="_blank">ngme2</a> v0.9.8: Functions to fit and analyze linear latent non-Gaussian models for temporal, spatial, and space-time data, including autoregressive and Ornstein-Uhlenbeck processes, random walks, Matern fields based on stochastic partial differential equations, separable and non-separable space-time models, graph-based Matern models, bivariate type-G fields, and user-defined sparse operators. Latent fields and observation models can use Gaussian and non-Gaussian noise distributions. The modeling framework is described in <a href="https://arxiv.org/abs/2602.23987" rel="nofollow" target="_blank">Bolin et al. (2026)</a>. See the <a href="https://davidbolin.github.io/ngme2/articles/tensor-product.html" rel="nofollow" target="_blank">vignette</a> for exceptionally well-done documentation.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/ngme2.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-24" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/ngme2.png?w=578&#038;ssl=1" class="img-fluid" alt="Simulated Space-Time Field" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=nowcastr" rel="nofollow" target="_blank">nowcastr</a> v0.2.0: Implements tools for performing nowcasting using the <a href="https://en.wikipedia.org/wiki/Chain-ladder_method" rel="nofollow" target="_blank">Chain-Ladder method</a>, and supports both non-cumulative delay-based estimation and model-based completeness fitting (e.g., using logistic or Gompertz curves) to predict final counts from partially reported data. See the vignettes <a href="https://cran.r-project.org/web/packages/nowcastr/vignettes/nowcastr.html" rel="nofollow" target="_blank">Getting Started</a> and <a href="https://cran.r-project.org/web/packages/nowcastr/vignettes/eval.html" rel="nofollow" target="_blank">Evaluate Past Nowcasts Accuracy</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/nowcastr.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-25" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/nowcastr.png?w=578&#038;ssl=1" class="img-fluid" alt="Nowcast Evaluation Plots" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=TwoStepSDFM" rel="nofollow" target="_blank">TwoStepSDFM</a> v0.2.2: Provides functions to estimate a sparse Gaussian state-space model with mixed frequency data via sparse principal components analysis and the Kalman filter and smoother. For more details, see <a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4733872" rel="nofollow" target="_blank">Franjic and Schweikert (2024)</a>. The <a href="https://cran.r-project.org/web/packages/TwoStepSDFM/vignettes/IntroTwoStepSDFM.html" rel="nofollow" target="_blank">vignette</a> provides an introduction.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/TwoStepSDFM.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-26" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/TwoStepSDFM.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of Quarterly and Monthly Data" data-recalc-dims="1"></a></p>
</section>
<section id="time-series" class="level3">
<h3 class="anchored" data-anchor-id="time-series">Time Series</h3>
<p><a href="https://cran.r-project.org/package=MatchingPursuit" rel="nofollow" target="_blank">MatchingPursuit</a> v1.0.1: Provides tools for analyzing and decomposing time series data using the Matching Pursuit algorithm, a greedy signal decomposition technique that represents complex signals as a linear combination of simpler functions (called atoms) selected from a redundant dictionary. For more details, see <a href="https://ieeexplore.ieee.org/document/258082" rel="nofollow" target="_blank">Mallat and Zhang (1993)</a>, <a href="https://ieeexplore.ieee.org/document/342465" rel="nofollow" target="_blank">Pati et al. (1993)</a>, <a href="https://link.springer.com/book/10.1007/978-1-4419-7011-4" rel="nofollow" target="_blank">Elad (2010)</a>, and <a href="https://dl.acm.org/doi/10.1145/3674832" rel="nofollow" target="_blank">Różański (2024)</a>. See the <a href="https://cran.r-project.org/web/packages/MatchingPursuit/vignettes/MatchingPursuit.html" rel="nofollow" target="_blank">vignette</a>.</p>
<p><a href="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/MatchingPursuit.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-27" rel="nofollow" target="_blank"><img src="https://i0.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/MatchingPursuit.png?w=578&#038;ssl=1" class="img-fluid" alt="Frequency plot of a time series" data-recalc-dims="1"></a></p>
<p><a href="https://cran.r-project.org/package=xiacf" rel="nofollow" target="_blank">xiacf</a> v0.5.0: Computes Chatterjee’s non-parametric correlation coefficient for time series data. It extends the original metric to time series analysis by providing the Xi-Autocorrelation Function and Xi-Cross-Correlation Function. Allows users to test for non-linear dependence using Iterative Amplitude Adjusted Fourier Transform surrogate data with strict Family-Wise Error Rate control via Max-statistic approaches. Methodologies are based on <a href="https://www.tandfonline.com/doi/full/10.1080/01621459.2020.1758115" rel="nofollow" target="_blank">Chatterjee (2021)</a> and surrogate data testing methods by <a href="https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.635" rel="nofollow" target="_blank">Schreiber and Schmitz (1996)</a>. See <a href="https://cran.r-project.org/web/packages/xiacf/readme/README.html" rel="nofollow" target="_blank">README</a> for examples.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/xiacf.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-28" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/xiacf.png?w=578&#038;ssl=1" class="img-fluid" alt="Plot of mltivariate Xi correlation matrix" data-recalc-dims="1"></a></p>
</section>
<section id="utilities" class="level3">
<h3 class="anchored" data-anchor-id="utilities">Utilities</h3>
<p><a href="https://cran.r-project.org/package=abba" rel="nofollow" target="_blank">abba</a> v0.2.0: Enables users to submit and monitor batch execution of R programs across distributed computing backends, including <code>Kubernetes</code>, <code>SLURM</code>, and <code>Posit Workbench</code>. Provides end-user job submission functions, cluster interface functions using <code>kubectl</code> and <code>SLURM</code> commands, and a <code>plumber</code> API template for secure identity segregation. Supports parallel and sequential batch execution, file-based caching to skip unchanged programs, and <code>logrx</code> integration for execution logging. There are seven vignettes, including <a href="https://cran.r-project.org/web/packages/abba/vignettes/k8s_batch_job.html" rel="nofollow" target="_blank">Kubernetes Batch Jobs</a> and <a href="https://cran.r-project.org/web/packages/abba/vignettes/slurm_job_submission.html" rel="nofollow" target="_blank">SLURM Job Submission</a>.</p>
<p><a href="https://cran.r-project.org/package=vectra" rel="nofollow" target="_blank">vectra</a> v0.6.2: Implements a minimal columnar query engine with lazy execution on datasets larger than RAM. Provides <code>dplyr</code>-like verbs (<code>filter()</code>, <code>select()</code>, <code>mutate()</code>, <code>group_by()</code>, <code>summarise()</code>, joins, window functions) and common aggregations (<code>n()</code>, <code>sum()</code>, <code>mean()</code>, <code>min()</code>, <code>max()</code>, <code>sd(</code>), <code>first()</code>,<code>last()</code>) backed by a pure <code>C11</code> pull-based execution engine and a custom on-disk format (<code>.vtr</code>). Reads and writes <code>GeoTIFF</code> (including tiled and <code>BigTIFF</code> layouts) and a tiled raster format (<code>.vec</code>) with overview pyramids and time cubes for larger-than-RAM raster data. There are eight vignettes, including <a href="https://cran.r-project.org/web/packages/vectra/vignettes/quickstart.html" rel="nofollow" target="_blank">Getting Started</a> and <a href="https://cran.r-project.org/web/packages/vectra/vignettes/string-ops.html" rel="nofollow" target="_blank">String Operations and Fuzzy Matching</a>.</p>
</section>
<section id="visualization" class="level3">
<h3 class="anchored" data-anchor-id="visualization">Visualization</h3>
<p><a href="https://cran.r-project.org/package=janusplot" rel="nofollow" target="_blank">janusplot</a> v0.1.0: Provides functions to render a pairwise, asymmetric smoothed-association matrix of continuous variables. Each cell shows the fitted spline from an <code>mgcv</code> generalized additive model, with the upper triangle displaying <code>gam(x_j ~ s(x_i))</code> and the lower triangle <code>gam(x_i ~ s(x_j))</code>. Unlike Pearson’s correlation matrix, the visualization is intentionally asymmetric, revealing heteroscedasticity, leverage, and directional non-linearity that a single scalar correlation hides. An asymmetry index and a 24-category shape taxonomy quantify the directional difference and qualitative form of each fitted smooth. There are two vignettes, <a href="https://cran.r-project.org/web/packages/janusplot/vignettes/janusplot.html" rel="nofollow" target="_blank">Asymmetric Smoothed-Association Matrices</a> and <a href="https://cran.r-project.org/web/packages/janusplot/vignettes/shape-recognition-sensitivity.html" rel="nofollow" target="_blank">Shape-recognition sensitivity study</a>.</p>
<p><a href="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/janusplot.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-29" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/april-2026-top-40-new-cran-packages/janusplot.png?w=578&#038;ssl=1" class="img-fluid" alt="Example of a janusplot" data-recalc-dims="1"></a></p>
</section>
</div>
</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://rworks.dev/posts/april-2026-top-40-new-cran-packages/"> R Works</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/05/april-2026-top-40-new-cran-packages/">April 2026 Top 40 New CRAN Packages</a>]]></content:encoded>
					
		
		<enclosure url="https://rworks.dev/posts/april-2026-top-40-new-cran-packages/MatchingPursuit.png" length="0" type="image/png" />

		<post-id xmlns="com-wordpress:feed-additions:1">401552</post-id>	</item>
		<item>
		<title>[R] How to Position the Legend Inside a Plot in ggplot2</title>
		<link>https://www.r-bloggers.com/2026/05/r-how-to-position-the-legend-inside-a-plot-in-ggplot2/</link>
		
		<dc:creator><![CDATA[R on Zhenguo Zhang&#039;s Blog]]></dc:creator>
		<pubDate>Sat, 30 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Zhenguo Zhang's Blog https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/ -</p>
<p>By default, ggplot2 places the legend outside the plot area (usually on the right). However, sometimes you may want to move the legend inside the plot to sa...</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/05/r-how-to-position-the-legend-inside-a-plot-in-ggplot2/">[R] How to Position the Legend Inside a Plot in ggplot2</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://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/"> R on Zhenguo Zhang&#039;s 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>
Zhenguo Zhang&#8217;s Blog https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/ &#8211;


<p>By default, <code>ggplot2</code> places the legend outside the plot area (usually on the right). However, sometimes you may want to move the legend inside the plot to save space or improve the layout. This post explores how to achieve this using <code>theme()</code> parameters.</p>
<div id="example-1-basic-usage-of-legend.position" class="section level2">
<h2>Example 1: Basic Usage of <code>legend.position</code></h2>
<p>The simplest way to move a legend inside the plot is by providing a numeric vector of length two to the <code>legend.position</code> argument in <code>theme()</code>. These coordinates represent the relative position within the plot panel, ranging from 0 to 1. Check the
section on <a href="https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/#key-positioning-controls" rel="nofollow" target="_blank">“Key Positioning Controls”</a> at the end for a detailed explanation of the coordinate system.</p>
<pre>library(ggplot2)

ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  theme_bw() +
  theme(legend.position = c(0.8, 0.8))</pre>
<p><img src="https://i2.wp.com/fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example1-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="example-2-fine-tuning-with-justification-and-background" class="section level2">
<h2>Example 2: Fine-Tuning with Justification and Background</h2>
<p>When you place a legend inside, you often need to adjust which part of the legend box aligns with your coordinates and handle the background if it obscures data.</p>
<ul>
<li><strong><code>legend.justification</code></strong>: Controls the anchor point of the legend box. For example, <code>c(&quot;right&quot;, &quot;top&quot;)</code> means the top-right corner of the legend box will be placed at the specified coordinates.</li>
<li><strong><code>legend.background</code></strong>: Use <code>element_blank()</code> to make the background transparent.</li>
</ul>
<pre>ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  theme_bw() +
  theme(
    legend.position = c(1, 1),
    legend.justification = c(&quot;right&quot;, &quot;top&quot;),
    legend.background = element_blank()
  )</pre>
<p><img src="https://i1.wp.com/fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example2-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="example-3-positioning-in-faceted-plots" class="section level2">
<h2>Example 3: Positioning in Faceted Plots</h2>
<p>Moving the legend inside a faceted plot works similarly: the whole figure is considered as
one plot for the coordinate system. You can use the coordinates to place it within one of the empty spaces or over a specific panel.
In the following example, the legend is placed in the bottom center of the plot, and a background is added to improve readability:</p>
<pre>ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  facet_wrap(~am) +
  theme_bw() +
  theme(
    legend.position = c(0.5, 0.2),
    legend.background = element_rect(fill = &quot;white&quot;, color = &quot;grey80&quot;)
  )</pre>
<p><img src="https://i1.wp.com/fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example3-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="example-4-the-empty-space-trick-for-faceted-plots" class="section level2">
<h2>Example 4: The “Empty Space” Trick for Faceted Plots</h2>
<p>If you have a panel which doesn’t have any data, and then it is the perfect spot to
place legend. To show this, the facet variable <code>carb</code> has only 3 values, and we
will make a 2&#215;2 facet grid. We will also remove the background and border of the legend.
The following example shows this:</p>
<pre># Use 3 categories to leave the 4th spot in a 2x2 grid empty
mtcars_subset &lt;- mtcars[mtcars$carb %in% c(1, 2, 4), ]

ggplot(mtcars_subset, aes(wt, mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  facet_wrap(~carb, nrow = 2, ncol = 2) +
  theme_bw() +
  theme(
    # Coordinates c(0.75, 0.25) target the center of the empty bottom-right area
    legend.position = c(0.75, 0.25),
    legend.background = element_blank(),
    legend.justification = &quot;center&quot;
  )</pre>
<p><img src="https://i2.wp.com/fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/index_files/figure-html/example4-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="key-positioning-controls" class="section level2">
<h2>Key Positioning Controls</h2>
<p>To master legend positioning, keep these three controls in mind:</p>
<ol style="list-style-type: decimal">
<li><strong>Coordinate System</strong>: The coordinates <code>c(X, Y)</code> represent a relative scale from 0 to 1 across the plot panel.
<ul>
<li><code>c(0, 0)</code>: Bottom-left corner</li>
<li><code>c(1, 1)</code>: Top-right corner</li>
<li><code>c(0.5, 0.5)</code>: Center of the plot</li>
</ul></li>
<li><strong>Anchor Point (<code>legend.justification</code>)</strong>: Use this to control which part of the legend box aligns with your coordinates. For example, <code>c(&quot;right&quot;, &quot;top&quot;)</code> ensures the top-right corner of the legend box sits exactly at your target coordinate.</li>
<li><strong>Background Removal</strong>: If your legend blocks data points, clear the legend box background using <code>legend.background = element_blank()</code>. Alternatively, use <code>element_rect()</code> to give it a semi-transparent or styled background.</li>
</ol>
<p>Happy Programming!</p>
</div>
- https://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/ - 
<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://fortune9.netlify.app/2026/05/30/r-position-legend-inside-plot/"> R on Zhenguo Zhang&#039;s 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/05/r-how-to-position-the-legend-inside-a-plot-in-ggplot2/">[R] How to Position the Legend Inside a Plot in ggplot2</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401550</post-id>	</item>
		<item>
		<title>EuroBioC2026 Tidyomics Hackathon</title>
		<link>https://www.r-bloggers.com/2026/05/eurobioc2026-tidyomics-hackathon/</link>
		
		<dc:creator><![CDATA[Stefano Mangiola]]></dc:creator>
		<pubDate>Fri, 29 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://tidyomics.github.io/tidyomicsBlog/posts/2026-05-29-eurobioc2026-tidyomics-hackathon/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Join us in Turku<br />
The Tidyomics community is organising a hackathon during the pre-conference programme of EuroBioC2026 in Turku, Finland. The hackathon will take place on June 1-2, 2026, ahead of the main EuroBioC2026 conference on June 3-5, 20...</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/05/eurobioc2026-tidyomics-hackathon/">EuroBioC2026 Tidyomics Hackathon</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://tidyomics.github.io/tidyomicsBlog/posts/2026-05-29-eurobioc2026-tidyomics-hackathon/"> tidyomicsBlog</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="join-us-in-turku" class="level1">
<h1>Join us in Turku</h1>
<p>The Tidyomics community is organising a hackathon during the pre-conference programme of <a href="https://eurobioc2026.bioconductor.org/" rel="nofollow" target="_blank">EuroBioC2026</a> in Turku, Finland. The hackathon will take place on <strong>June 1-2, 2026</strong>, ahead of the main EuroBioC2026 conference on <strong>June 3-5, 2026</strong>.</p>
<p>This is a community event for people who build, use, teach, document, or are curious about tidy interfaces for omics data analysis in R. Whether you are a regular contributor or new to Tidyomics, the hackathon is a chance to meet collaborators, turn ideas into concrete issues, and make progress on packages, tutorials, and documentation together.</p>
<p>Grab your Tidyomics stickers during the conference! Thanks to Bea Campillo Minano for printing them.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/tidyomics.github.io/tidyomicsBlog/posts/2026-05-29-eurobioc2026-tidyomics-hackathon/tidyomics-sticker-giveaway.png?w=578&#038;ssl=1" class="cover-image img-fluid figure-img" style="width:70.0%" data-recalc-dims="1"></p>
<figcaption>Tidyomics stickers for hackathon participants</figcaption>
</figure>
</div>
</section>
<section id="what-we-will-work-on" class="level1">
<h1>What we will work on</h1>
<p>The hackathon will focus on practical, community-led work across the Tidyomics ecosystem. Possible projects include:</p>
<ul>
<li>fixing bugs and closing issues across Tidyomics packages</li>
<li>implementing new features and workflow improvements</li>
<li>improving documentation, tutorials, and vignettes</li>
<li>developing examples that make tidy omics workflows easier to learn and reuse</li>
</ul>
<p>The <a href="https://github.com/orgs/tidyomics/projects/1" rel="nofollow" target="_blank">Tidyomics open challenges board</a> will be the main source of ideas for the hackathon. Before the event, please browse the board and identify problems or projects you would like to work on.</p>
</section>
<section id="how-to-participate" class="level1">
<h1>How to participate</h1>
<p>Please start from the <a href="https://github.com/tidyomics/tidyomicsHackathonTurku2026" rel="nofollow" target="_blank">Tidyomics Hackathon Turku 2026 GitHub repository</a>. The repository includes event details, preparation steps, and space for participants to open or discuss hackathon issues.</p>
<p>Before the event:</p>
<ol type="1">
<li>Browse the <a href="https://github.com/orgs/tidyomics/projects/1" rel="nofollow" target="_blank">Tidyomics open challenges</a>.</li>
<li>Select a problem you are interested in.</li>
<li>Open an issue in the <a href="https://github.com/tidyomics/tidyomicsHackathonTurku2026" rel="nofollow" target="_blank">hackathon repository</a>, or comment/react if an issue already exists.</li>
<li>Join the community discussion in the <a href="https://community-bioc.zulipchat.com/#narrow/channel/595963-eurobioc2026-turku-hackathon" rel="nofollow" target="_blank">EuroBioC2026 Turku hackathon Zulip channel</a>.</li>
</ol>
<p>Groups and projects will be finalised during the first day, based on open issues, participant interest, and available expertise.</p>
</section>
<section id="communication" class="level1">
<h1>Communication</h1>
<p>The official discussion space for the hackathon is the <a href="https://community-bioc.zulipchat.com/#narrow/channel/595963-eurobioc2026-turku-hackathon" rel="nofollow" target="_blank">EuroBioC2026 Turku hackathon channel on the Bioconductor Zulip server</a>. The virtual meeting room link will also be shared through that channel and by email.</p>
<p>If you are attending EuroBioC2026 online, note that the conference provides <a href="https://eurobioc2026.bioconductor.org/" rel="nofollow" target="_blank">free live streaming</a> for remote participants.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i0.wp.com/tidyomics.github.io/tidyomicsBlog/posts/2026-05-29-eurobioc2026-tidyomics-hackathon/EuroBioC2026_sticker.png?w=578&#038;ssl=1" class="img-fluid figure-img" style="width:35.0%" data-recalc-dims="1"></p>
<figcaption>EuroBioC2026 Turku conference sticker</figcaption>
</figure>
</div>
</section>
<section id="people" class="level1">
<h1>People</h1>
<p>The hackathon was proposed by <strong>Nicholas Cooley</strong>.</p>
<p>The organising committee is:</p>
<ul>
<li>Juan Henao</li>
<li>Stevie Pederson</li>
<li>Michael Love</li>
<li>Nicholas Cooley</li>
</ul>
</section>
<section id="see-you-there" class="level1">
<h1>See you there</h1>
<p>EuroBioC2026 is an opportunity to bring the Bioconductor and Tidyomics communities together around shared tools, shared challenges, and shared documentation. We hope to see you in Turku, on Zulip, and in the hackathon repository.</p>


</section>

<p>
© 2025 tidyomics. 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://tidyomics.github.io/tidyomicsBlog/posts/2026-05-29-eurobioc2026-tidyomics-hackathon/"> tidyomicsBlog</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/05/eurobioc2026-tidyomics-hackathon/">EuroBioC2026 Tidyomics Hackathon</a>]]></content:encoded>
					
		
		<enclosure url="https://tidyomics.github.io/tidyomicsBlog/posts/2026-05-29-eurobioc2026-tidyomics-hackathon/tidyomics-sticker-giveaway.png" length="0" type="image/png" />

		<post-id xmlns="com-wordpress:feed-additions:1">401539</post-id>	</item>
		<item>
		<title>Behavior-Driven Development in R Shiny: Asserting Outcomes with Then Steps</title>
		<link>https://www.r-bloggers.com/2026/05/behavior-driven-development-in-r-shiny-asserting-outcomes-with-then-steps/</link>
		
		<dc:creator><![CDATA[jakub::sobolewski]]></dc:creator>
		<pubDate>Fri, 29 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://jakubsobolewski.com/blog/bdd-shiny-then</guid>

					<description><![CDATA[<p>Learn how to write Then steps that assert outcomes without coupling to implementation. Build custom testthat expectations and keep your BDD assertions at the right level.</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/05/behavior-driven-development-in-r-shiny-asserting-outcomes-with-then-steps/">Behavior-Driven Development in R Shiny: Asserting Outcomes with Then Steps</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://jakubsobolewski.com/blog/bdd-shiny-then"> jakub::sobolewski</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>You’ve set up all the preconditions.</p>
<p>You’ve triggered the action.</p>
<p>Now the specification needs to answer one question: did the system do the right thing?</p>
<p>That’s what Then steps are for. How precisely you answer it determines whether your specifications act as a reliable safety net or produce false confidence.</p>
<hr>
<p>This article is the 3rd part of a series on writing BDD specifications for Shiny applications. We’ve built a data submission form, managed preconditions with Given steps, and modeled user interactions with When steps.</p>
<p>Read the previous articles to get full context, or continue here to focus on writing Then steps.</p>
<ol>
<li><strong><a href="https://jakubsobolewski.com/blog/bdd-shiny-feature/" rel="nofollow" target="_blank">Behavior-Driven Development in R Shiny: A Step-By-Step Example</a></strong></li>
<li><strong><a href="https://jakubsobolewski.com/blog/bdd-shiny-given/" rel="nofollow" target="_blank">Behavior-Driven Development in R Shiny: Setting Up Test Preconditions with Given Steps</a></strong></li>
<li><strong><a href="https://jakubsobolewski.com/blog/bdd-shiny-when/" rel="nofollow" target="_blank">Behavior-Driven Development in R Shiny: Writing When Steps That Model User Behavior</a></strong></li>
</ol>
<hr>
<blockquote>
<p><a href="https://jakubsobolewski.com/get-roadmap/" rel="nofollow" target="_blank">Level-up your testing game! Grab your copy of the R testing roadmap.</a></p>
</blockquote>
<h2 id="the-purpose-of-then">The Purpose of Then</h2>
<p>Then steps answer one question: <strong>What changed as a result of the user’s action?</strong></p>
<p>Not how it changed internally. What the user can now observe — or what the system has now done — that wasn’t true before.</p>
<p>This distinction shapes every Then step you write. The assertion lives at the level of the outcome, not at the level of the mechanism that produced it.</p>
<pre># Outcome — what the user cares about
then_there_are_entries &lt;- function(context, n) {
  expect_equal(context$storage$size(), n)
  context
}

# Mechanism — how the app stored it internally
then_the_database_table_has_n_rows &lt;- function(context, n) {
  expect_equal(nrow(DBI::dbReadTable(context$conn, &quot;entries&quot;)), n)
  context
}</pre>
<p>Both assertions verify the same underlying fact — that the entry was saved — but the first one survives a switch from disk cache to database to API. The second breaks the moment you change the storage technology.</p>
<p><strong>Assert what the user or system guarantees. Leave how it’s achieved to implementation tests.</strong></p>
<h2 id="what-to-assert">What to Assert</h2>
<p>Then steps verify outcomes across three kinds of observable evidence. These categories describe <em>what</em> you can check — they don’t prescribe how many steps to use. How to group assertions into steps is a separate question, covered in the next section.</p>
<h3 id="user-visible-state">User-visible state</h3>
<p>Did the UI change the way a user would expect?</p>
<pre>then_i_am_prompted_to_provide_required_fields &lt;- function(context) {
  context$driver$expect_validation_feedback()
  context
}</pre>
<p>Assertions use driver methods — the same translation layer that When steps use — so they’re protected from UI implementation details.</p>
<h3 id="system-state">System state</h3>
<p>Sometimes the most meaningful evidence isn’t what the user sees but what the system now holds.</p>
<pre>then_the_entry_has_title &lt;- function(context, expected_title) {
  entry &lt;- context$storage$get_first()
  expect_equal(entry$title, expected_title)
  context
}</pre>
<p>These assertions reach directly into the storage object that Given steps configured. They’re faster than UI assertions and more precise: they check the exact state of the system without waiting for the browser to render anything.</p>
<h3 id="side-effects">Side effects</h3>
<p>Some behaviors produce no visible UI change and no stored data — they trigger side effects: emails, API calls, log entries. These still need verification.</p>
<pre>then_email_notification_is_sent &lt;- function(context) {
  context$email_service$expect_sent()
  context
}

then_i_am_informed_email_was_not_sent &lt;- function(context) {
  context$driver$expect_visible(&quot;email_failure_message&quot;)
  context
}</pre>
<p>The first assertion works because the email service test double (set up in Given) records whether it was called. The second checks that the app communicated the failure to the user — which is the behavior the specification is actually about.</p>
<h2 id="implementing-then-steps-in-the-driver">Implementing Then Steps in the Driver</h2>
<p>Then steps that check UI state belong in the driver, just like When steps. This keeps the assertion details hidden from the specification:</p>
<pre>#' tests/testthat/setup-driver.R
MyAppDriver &lt;- R6::R6Class(
  classname = &quot;MyAppDriver&quot;,
  inherit = shinytest2::AppDriver,
  public = list(
    expect_visible = function(output_id) {
      # Find if HTML element is visible
      invisible(self)
    },
    expect_entry_count = function(n) {
      # Find number of HTML elements
      invisible(self)
    },
    expect_validation_feedback = function() {
      # Check if HTML element is visible
      invisible(self)
    }
  )
)</pre>
<p>Each driver method is a named, reusable assertion. If the HTML structure changes, you fix it in one place.</p>
<h2 id="grouping-then-steps">Grouping Then Steps</h2>
<p>The right question is not “how many assertions per step?” but “which assertions belong together?”</p>
<p><strong>Group assertions that describe the same observable behavior. Split when outcomes can diverge independently.</strong></p>
<p>Consider what happens when a user successfully submits an entry: the storage grows by one, and a confirmation message appears. These two outcomes are two sides of the same coin — if one is true, the other should be too. Splitting them into separate steps implies they can diverge, which invites the reader to wonder what it would mean for storage to succeed but no confirmation to appear, or vice versa. Grouping them into a single step names the behavior directly:</p>
<pre>then_entry_is_submitted &lt;- function(context) {
  expect_equal(context$storage$size(), 1)
  context$driver$expect_visible(&quot;confirmation_message&quot;)
  context
}</pre>
<p>Email notification is different. Submission can succeed even when the email service fails — the two outcomes genuinely can diverge. That’s exactly when a separate step is the right call:</p>
<pre>it(&quot;should submit entry and send notification&quot;, {
  given_no_content() |&gt;
    given_an_authenticated_user(email = &quot;user@example.com&quot;) |&gt;
    given_email_service_is_available() |&gt;
    when_i_submit_entry_with_all_required_fields() |&gt;
    then_entry_is_submitted() |&gt;
    then_notification_was_sent_to_the_authenticated_user()
})</pre>
<p>Two steps, not three — because the spec now reflects the actual structure of the behavior. If <code>then_entry_is_submitted</code> fails, you know the core submission broke. If <code>then_notification_was_sent_to_the_authenticated_user</code> fails, you know the side effect broke. The split carries information because it maps to a real divergence point.</p>
<p>Then steps should also be <strong>read-only</strong>. They inspect state; they don’t change it. A Then step that modifies storage or triggers side effects is doing the wrong job. Keep the flow clean: Given sets up, When acts, Then observes.</p>
<h2 id="making-failure-messages-helpful">Making Failure Messages Helpful</h2>
<p>A failing test with a good message saves minutes. A failing test with a bad message wastes hours.</p>
<p>The most common mistake is letting low-level assertion failures surface directly. When <code>expect_equal(nrow(df), 2)</code> fails with <code>&quot;actual 0, expected 2&quot;</code>, that tells you nothing about which scenario failed or what the data looked like.</p>
<p>The <code>label</code> argument sets the name testthat uses for the <em>object</em> (first argument) in the failure message. Keep it short and descriptive — testthat appends the actual and expected values itself:</p>
<pre>then_there_are_entries &lt;- function(context, n) {
  testthat::expect_equal(
    context$storage$size(), n,
    label = &quot;number of entries in storage&quot;
  )
  context
}</pre>
<p>A failure now reads: <code>&quot;Expected number of entries in storage to equal n&quot;</code> — immediately clear without any manual formatting.</p>
<p>For UI assertions, label the element being checked:</p>
<pre>expect_visible = function(output_id) {
  val &lt;- self$get_value(output = output_id)
  testthat::expect_true(
    !is.null(val) &#038;&#038; nchar(val) &gt; 0,
    label = sprintf(&quot;output '%s'&quot;, output_id)
  )
  invisible(self)
}</pre>
<p>A failure reads: <code>&quot;Expected output 'confirmation_message' to be TRUE&quot;</code> — which immediately tells you which element to look at.</p>
<h2 id="what-doesnt-belong-in-then-steps">What Doesn’t Belong in Then Steps</h2>
<p><strong>Don’t push implementation tests up to Then steps.</strong></p>
<p>If you’re asserting the exact text of an error message, the CSS class applied to an invalid input, or the exact SQL that was executed — those are implementation details. They don’t belong here.</p>
<p>Acceptance-level Then steps answer: did the right thing happen from the user’s perspective?</p>
<h2 id="the-complete-picture">The Complete Picture</h2>
<p>Putting it all together, a well-formed specification has a clear structure at every level:</p>
<pre>describe(&quot;data submission&quot;, {
  it(&quot;should submit entry and send notification&quot;, {
    given_no_content() |&gt;
      given_an_authenticated_user(email = &quot;user@example.com&quot;) |&gt;
      given_email_service_is_available() |&gt;
      when_i_submit_entry_with_all_required_fields() |&gt;
      then_entry_is_submitted() |&gt;
      then_notification_was_sent_to_the_authenticated_user()
  })

  it(&quot;should handle email service failure gracefully&quot;, {
    given_no_content() |&gt;
      given_an_authenticated_user() |&gt;
      given_email_service_is_unavailable() |&gt;
      when_i_submit_entry_with_all_required_fields() |&gt;
      then_entry_is_submitted() |&gt;
      then_i_am_informed_email_was_not_sent()
  })

  it(&quot;should require all required fields&quot;, {
    given_no_content() |&gt;
      given_an_authenticated_user() |&gt;
      when_i_submit_entry_with_missing_required_fields() |&gt;
      then_i_am_prompted_to_provide_required_fields()
  })
})</pre>
<p>Each specification reads like a sentence. None of them mention <code>shinytest2</code>, input IDs, or database queries. The implementation lives behind the DSL, where it can change without touching the specifications.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Then steps are where a specification earns its credibility.</p>
<p>A specification that only tests easy outcomes produces false confidence. One that asserts user-visible state, system state, and side effects actually catches real problems. Assert outcomes, not implementations. Group assertions by observable behavior and split them only where they can genuinely diverge. Write failure messages that save time. Push implementation details down to unit and module tests.</p>
<p>With Given, When, and Then steps in place, the specifications read like requirements and run on every build. That’s worth more than any testing framework on its own.</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://jakubsobolewski.com/blog/bdd-shiny-then"> jakub::sobolewski</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/05/behavior-driven-development-in-r-shiny-asserting-outcomes-with-then-steps/">Behavior-Driven Development in R Shiny: Asserting Outcomes with Then Steps</a>]]></content:encoded>
					
		
		<enclosure url="https://jakubsobolewski.com/ogimg-home.png" length="0" type="image/png" />

		<post-id xmlns="com-wordpress:feed-additions:1">401533</post-id>	</item>
		<item>
		<title>Ghost in the Shell Script</title>
		<link>https://www.r-bloggers.com/2026/05/ghost-in-the-shell-script/</link>
		
		<dc:creator><![CDATA[The Jumping Rivers Blog]]></dc:creator>
		<pubDate>Thu, 28 May 2026 23:59:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.jumpingrivers.com/blog/ghost-in-shell-script/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>I’ve been using Unix and Linux systems for nearly 20 years. When I found myself with a Windows laptop for a while I felt lost and quickly installed Windows Subsystem for Linux. And yet the number of commands I know and use regularly is surprisingly short: cd, ls, rm, pwd, ...</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/05/ghost-in-the-shell-script/">Ghost in the Shell Script</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/ghost-in-shell-script/"> 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/ghost-in-shell-script/">
<img src="https://www.jumpingrivers.com/blog/ghost-in-shell-script/" width="400" style="width:400px" class="image-center" style="display: block; margin: auto;" />
</a>
</p>
<style>
ol li {
margin-bottom: unset;
}
.conversation-block {
background-color: var(--cream);
padding: 1rem;
}
[data-speaker="Claude"] .conversation-speaker::before,
[data-speaker="Tim"] .conversation-speaker::after {
content: '';
display: inline-block;
width: 28px;
height: 28px;
background-size: contain;
background-repeat: no-repeat;
background-color: var(--dark-blue);
border-radius: 25%;
}
[data-speaker="Claude"] .conversation-speaker::before {
background-image: url("clawd.svg");
transform: translateX(-4px) translateY(7px);
}
[data-speaker="Tim"] .conversation-speaker::after {
background-image: url("tim-brock-avatar.webp");
transform: translateX(4px) translateY(7px);
}
.highlight pre {
max-height: 400px;
resize: vertical;
width: fit-content !important;
}
/* https://stackoverflow.com/questions/79134605/resizable-div-with-a-maximum-initial-height */
/* Unset max-height when user drags resizer to allow expansion downwards */
.highlight pre[style*="height"] {
max-height: unset;
}
</style>
<p>I’ve been using Unix and Linux systems for nearly 20 years. When I found myself with a Windows laptop for a while I felt lost and quickly installed Windows Subsystem for Linux. And yet the number of commands I know and use regularly is surprisingly short: <code>cd</code>, <code>ls</code>, <code>rm</code>, <code>pwd</code>, <code>mkdir</code>, <code>source</code>, <code>node</code>, <code>npm</code> and, of course, <code>git</code>. Even then I don’t think I <em>really</em> “know” git.</p>
<p>In his excellent book “Don’t Make Me Think <em>Revisited</em>”, Steve Krug describes how</p>
<blockquote>
<p>Faced with any sort of technology, very few people take the time to read instructions. Instead, we forge ahead and muddle through, making up our own vaguely plausible stories about what we’re doing and why it works.</p>
</blockquote>
<p>Krug’s book is about web and mobile usability but the point he makes here applies across technological domains. And it is safe to say I’ve been “muddling through” with Unix. If you asked me whether something was a Unix command or part of the shell I’d probably just shrug and say something like <a href="https://www.youtube.com/watch?v=N68rAkmqA_A" rel="nofollow" target="_blank">“I… I don’t know that!?&#8221;</a>.</p>
<p>Until recently StackOverflow had always been my crutch to fall back on. From the frequency with which it appears in answers, I know that <code>find</code> is a very useful and versatile command. However, the odds of me remembering how to use it without looking it up are indistinguishable from 0.</p>
<p>But now we have alternatives in the form of various large language models or, to be more precise, multi-modal AI Assistants (like Claude and ChatGPT) and Agents (like Claude Code and Codex). I’d describe myself as AI-agnostic: I see benefits, I worry about issues. So it was with some (I’d say healthy) degree of scepticism I started using Claude to assist me in writing some shell functions to automate tasks I do or would like to do regularly.</p>
<p>I say “I” wrote. Reality is of course that I asked Claude to write and rewrite things for me and then I questioned why it did what it did. This probably makes it sound like I was assessing Claude for a job but it’s actually that I want to understand what has been written and, as we’ve already covered, I am not very good at this stuff.</p>
<h2 id="task-one-fixing-a-common-git-mistake">Task One: fixing a common <code>git</code> mistake</h2>
<p>One of my most common slip-ups with git is to commit changes to one branch (usually main) that were meant to go on a new feature branch. I know <em>a</em> way to recover from this: create the new branch I meant to create all along; do an interactive rebase to remove the commit from main; switch to the new feature branch I just created. There’s not that many steps to the process but it’s still a bit of a faff. Let’s ask Claude:</p>
<div class="conversation-block">
<style>
.conversation-turn {
padding: 0.5rem 1rem;
margin-bottom: 1rem;
border: 1px solid var(--burgundy);
}
.conversation-turn--right {
color: var(--off-white);
background-color: var(--burgundy);
margin-left: 2rem;
border-radius: 1.1rem 1.1rem 0.2rem 1.1rem;
}
.conversation-turn--left {
color: var(--dark-blue);
background-color: var(--off-white);
margin-right: 2rem;
border-radius: 1.1rem 1.1rem 1.1rem 0.2rem;
}
.conversation-turn p {
margin: 0.25rem 0;
}
.conversation-turn .conversation-speaker {
width: 100%;
font-weight: 500;
margin-bottom: 0.25rem;
}
.conversation-turn--right .conversation-speaker {
text-align: right;
}
.conversation-turn--right .conversation-body {
margin-right: 1rem;
}
.conversation-turn--left .conversation-body {
margin-left: 1rem;
}
</style>
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">If you git [commit] to a wrong branch, is there a quick way to move the change to a new branch and remove it from the last commit from the current branch?</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<div class="conversation-body"><p>Yes — assuming you haven’t pushed yet:</p>
<pre># Create and switch to the correct branch from current state
git switch -c correct-branch

# Go back to the wrong branch and undo the commit (keeps changes staged)
git switch wrong-branch
git reset --soft HEAD~1

# Discard the changes
git restore --staged .
git restore .
</pre><p>Or if you want to keep it even simpler — if the commit is only on the wrong branch and not pushed:</p>
<pre>git switch -c correct-branch
git switch wrong-branch
git reset --hard HEAD~1
</pre><p>The switch -c creates the new branch pointing at the same commit, so your work is preserved there. Then you just strip it off the wrong branch with reset –hard.</p>
</div>
</div>
<script>
</script>
</div>
<p>Here we hit a couple of issues.</p>
<ol>
<li>Neither of Claude’s suggestions did the final step of what I wanted: moving me back to the new, correct, branch. This is entirely on me. I forgot to ask (and didn’t notice for a bit). I do that kind of thing quite a lot.</li>
<li>Even for simple tasks, Claude’s responses can be quite verbose.</li>
</ol>
<p>The problem here is not a practical one regarding the use of Claude or an alternative agent, the real problem is it’s quite difficult to write a blog post of a sensible length that actually goes anywhere while accurately recounting the conversation you have with Claude. So what follows is a paraphrase rather than a transcript; shortened for the sake of your sanity and mine.</p>
<div class="conversation-block">
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">Can I use git switch &#8211; instead?</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<div class="conversation-body">Yes, and it’s cleaner. <code>git switch -</code> saves you typing the wrong branch name out explicitly.</div>
</div>
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">Seems like the sort of thing I should make into a Bash function</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<div class="conversation-body"><p>Agreed. Something like this (with an added safety check):</p>
<pre>git-move-last() {
 if [ -z &quot;$1&quot; ]; then
 echo &quot;Usage: git-move-last &lt;new-branch-name&gt;&quot;
 return 1
 fi
 git switch -c &quot;$1&quot;
 git switch -
 git reset --hard HEAD~1
}
</pre>
</div>
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">I’ve gone for git-behead. If I wanted to move to the new branch at the end I just add another git switch -?</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<div class="conversation-body"><p>Ha <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1fa93.png" alt="🪓" class="wp-smiley" style="height: 1em; max-height: 1em;" />.</p>
<p>Exactly:</p>
<pre>git-behead() {
 if [ -z &quot;$1&quot; ]; then
 echo &quot;Usage: git-behead &lt;new-branch-name&gt;&quot;
 return 1
 fi
 git switch -c &quot;$1&quot;
 git switch -
 git reset --hard HEAD~1
 git switch -
}
</pre>
</div>
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">Ok, one more idea. Suppose I haven’t been looking what I’m doing and I want to remove more than just the last commit to a new branch. Can we recreate the above but with an optional commit count that defaults to 1?</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<pre>git-behead() {
 if [ -z &quot;$1&quot; ]; then
 echo &quot;Usage: git-behead &lt;new-branch-name&gt; [commit-count]&quot;
 return 1
 fi
 local count=&quot;${2:-1}&quot;
 git switch -c &quot;$1&quot;
 git switch -
 git reset --hard HEAD~&quot;$count&quot;
 git switch -
}
</pre>
</div>
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">Could you comment anything you think isn’t self-explanatory?</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<pre>git-behead() {
 if [ -z &quot;$1&quot; ]; then
 echo &quot;Usage: git-behead &lt;new-branch-name&gt; [commit-count]&quot;
 return 1
 fi
 local count=&quot;${2:-1}&quot; # default to 1 commit if not specified
 git switch -c &quot;$1&quot; # create new branch at current HEAD
 git switch - # switch back to original branch
 git reset --hard HEAD~&quot;$count&quot; # remove commits from original branch
 git switch - # switch to new branch
}
</pre>
</div>
</div>
<p>Putting this in my .bashrc file gives me a simple command that I can invoke whenever I make the mistake I regularly make and an optional argument to use whenever I haven’t been paying attention to the branch I’m on for an extended period. It’s also short enough that I think I follow every line, despite my limited knowledge of Bash, and there are comments there for when future me has forgotten. This conversation took about fifteen minutes and included more questions than I’ve included here, plus time I spent running various ad-hoc tests with the different iterations of the function. That seems like a pretty efficient use of my time.</p>
<h2 id="task-two-listing-image-files">Task Two: listing image files</h2>
<p>I do a lot of data visualisation, I look after the artwork for this website and I take photographs for our conferences. In short, I deal with a lot of images in various formats. It would be nice to be able to list only images and their key properties in a specific location with a simple function. Let’s ask Claude:</p>
<div class="conversation-block">
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">Can you write me a Bash script for displaying a list of images in a directory that shows file name, width, height and size (in kB) and nothing else?</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<div class="conversation-body"><p>Here’s a clean script using identify from ImageMagick (widely available) with a du fallback for file size:</p>
<pre>#!/usr/bin/env bash

DIR=&quot;${1:-.}&quot;

if ! command -v identify &&gt;/dev/null; then
 echo &quot;Error: imagemagick is required (install with: brew install imagemagick / apt install imagemagick)&quot; &gt;&2
 exit 1
fi

shopt -s nullglob nocaseglob
files=(&quot;$DIR&quot;/*.{jpg,jpeg,png,gif,webp,bmp,tiff,tif,avif})
shopt -u nullglob nocaseglob

if [[ ${#files[@]} -eq 0 ]]; then
 echo &quot;No images found in: $DIR&quot; &gt;&2
 exit 0
fi

printf &quot;%-40s %8s %8s %10s\n&quot; &quot;FILENAME&quot; &quot;WIDTH&quot; &quot;HEIGHT&quot; &quot;SIZE (kB)&quot;
printf &quot;%-40s %8s %8s %10s\n&quot; &quot;--------&quot; &quot;-----&quot; &quot;------&quot; &quot;---------&quot;

for f in &quot;${files[@]}&quot;; do
 name=&quot;$(basename &quot;$f&quot;)&quot;
 read -r width height &lt; &lt;(identify -format &quot;%w %h&quot; &quot;$f&quot; 2&gt;/dev/null | head -1)
 size_kb=$(awk &quot;BEGIN { printf \&quot;%.1f\&quot;, $(wc -c &lt; &quot;$f&quot;) / 1024 }&quot;)
 printf &quot;%-40s %8s %8s %10s\n&quot; &quot;$name&quot; &quot;$width&quot; &quot;$height&quot; &quot;$size_kb&quot;
done
</pre><p>Supported formats: jpg, jpeg, png, gif, webp, bmp, tiff, avif.</p>
<p>Example output:</p>
<pre>FILENAME WIDTH HEIGHT SIZE (kB)
-------- ----- ------ ---------
banner.png 1200 630 84.3
avatar.jpg 400 400 32.1
hero.webp 1920 1080 210.7
</pre></div>
</div>
</div>
<p>I asked for a script, I got a script. I wanted a function. This one is, again, on me.</p>
<div class="conversation-block">
<div class="conversation-turn conversation-turn--tim conversation-turn--right" data-speaker="Tim">
<div class="conversation-speaker">Tim</div>
<div class="conversation-body">Sorry, can we make this a function rather than a script?</div>
</div>
<div class="conversation-turn conversation-turn--claude conversation-turn--left" data-speaker="Claude">
<div class="conversation-speaker">Claude</div>
<div class="conversation-body"><p>The key changes: shebang removed, exit replaced with return, and variables are now local. To use it, source it in your shell and call it as normal:</p>
<pre>imgls() {
 local dir=&quot;${1:-.}&quot;

 if ! command -v identify &&gt;/dev/null; then
 echo &quot;Error: imagemagick is required (install with: brew install imagemagick / apt install imagemagick)&quot; &gt;&2
 return 1
 fi

 shopt -s nullglob nocaseglob
 local files=(&quot;$dir&quot;/*.{jpg,jpeg,png,gif,webp,bmp,tiff,tif,avif})
 shopt -u nullglob nocaseglob

 if [[ ${#files[@]} -eq 0 ]]; then
 echo &quot;No images found in: $dir&quot; &gt;&2
 return 0
 fi

 printf &quot;%-40s %8s %8s %10s\n&quot; &quot;FILENAME&quot; &quot;WIDTH&quot; &quot;HEIGHT&quot; &quot;SIZE (kB)&quot;
 printf &quot;%-40s %8s %8s %10s\n&quot; &quot;--------&quot; &quot;-----&quot; &quot;------&quot; &quot;---------&quot;

 local f name width height size_kb
 for f in &quot;${files[@]}&quot;; do
 name=&quot;$(basename &quot;$f&quot;)&quot;
 read -r width height &lt; &lt;(identify -format &quot;%w %h&quot; &quot;$f&quot; 2&gt;/dev/null | head -1)
 size_kb=$(awk &quot;BEGIN { printf \&quot;%.1f\&quot;, $(wc -c &lt; &quot;$f&quot;) / 1024 }&quot;)
 printf &quot;%-40s %8s %8s %10s\n&quot; &quot;$name&quot; &quot;$width&quot; &quot;$height&quot; &quot;$size_kb&quot;
 done
}
</pre>
</div>
</div>
<p>The conversation goes on a bit from here and the function becomes more and more detailed. It would be tedious to show the whole conversation but to summarise, “we”:</p>
<ol>
<li>Add support for SVG, including extracting dimensions from the <code>viewBox</code> attribute.</li>
<li>Fix a bug with GIF dimensions.</li>
<li>Tweak the header text.</li>
<li>Add support for recursive searching of a directory.</li>
<li>Fix the layout when doing a recursive search.</li>
<li>Round the values in the SIZE column</li>
<li>Add comments.</li>
</ol>
<p>The final result:</p>
<pre>imgls() {
 local recursive=0

 # Parse flags — currently only -r (recursive) is supported
 while getopts &quot;:r&quot; opt; do
 case $opt in
 r) recursive=1 ;;
 *) echo &quot;Usage: imgls [-r] [directory]&quot; &gt;&2; return 1 ;;
 esac
 done
 # Shift past any parsed flags so $1 is the directory argument
 shift $((OPTIND - 1))
 OPTIND=1

 # Use first argument as directory, default to current directory if omitted
 local dir=&quot;${1:-.}&quot;

 # identify is provided by imagemagick and used to read raster image dimensions
 if ! command -v identify &&gt;/dev/null; then
 echo &quot;Error: imagemagick is required (install with: brew install imagemagick / apt install imagemagick)&quot; &gt;&2
 return 1
 fi

 local exts=(&quot;jpg&quot; &quot;jpeg&quot; &quot;png&quot; &quot;gif&quot; &quot;webp&quot; &quot;bmp&quot; &quot;tiff&quot; &quot;tif&quot; &quot;avif&quot; &quot;svg&quot;)
 local files=()

 if [[ $recursive -eq 1 ]]; then
 # Build a find command matching all extensions, case-insensitively
 local find_args=(&quot;$dir&quot; &quot;-type&quot; &quot;f&quot; &quot;(&quot;)
 for i in &quot;${!exts[@]}&quot;; do
 [[ $i -gt 0 ]] && find_args+=(&quot;-o&quot;)
 find_args+=(&quot;-iname&quot; &quot;*.${exts[$i]}&quot;)
 done
 find_args+=(&quot;)&quot;)
 while IFS= read -r f; do
 files+=(&quot;$f&quot;)
 done &lt; &lt;(find &quot;${find_args[@]}&quot; | sort)
 else
 # nullglob: unmatched globs expand to nothing instead of being treated as literals
 # nocaseglob: match extensions case-insensitively (e.g. .JPG as well as .jpg)
 shopt -s nullglob nocaseglob
 files=(&quot;$dir&quot;/*.{jpg,jpeg,png,gif,webp,bmp,tiff,tif,avif,svg})
 shopt -u nullglob nocaseglob
 fi

 if [[ ${#files[@]} -eq 0 ]]; then
 echo &quot;No images found in: $dir&quot; &gt;&2
 return 0
 fi

 # In recursive mode show the relative path, otherwise just the filename
 printf &quot;%-40s %11s %12s %10s\n&quot; &quot;FILENAME&quot; &quot;WIDTH (px)&quot; &quot;HEIGHT (px)&quot; &quot;SIZE (kB)&quot;
 printf &quot;%-40s %11s %12s %10s\n&quot; &quot;--------&quot; &quot;----------&quot; &quot;-----------&quot; &quot;---------&quot;

 local f name width height size_kb viewbox current_subdir=&quot;&quot; subdir
 for f in &quot;${files[@]}&quot;; do
 if [[ $recursive -eq 1 ]]; then
 # Print a header line when we enter a new subdirectory
 subdir=&quot;$(dirname &quot;${f#$dir/}&quot;)&quot;
 if [[ &quot;$subdir&quot; != &quot;$current_subdir&quot; ]]; then
 [[ -n &quot;$current_subdir&quot; ]] && echo &quot;&quot;
 echo &quot;${subdir}/&quot;
 current_subdir=&quot;$subdir&quot;
 fi
 name=&quot;$(basename &quot;$f&quot;)&quot;
 else
 name=&quot;$(basename &quot;$f&quot;)&quot;
 fi
 # wc -c counts raw bytes; awk converts to kB with one decimal place
 size_kb=$(awk &quot;BEGIN { printf \&quot;%d\&quot;, $(wc -c &lt; &quot;$f&quot;) / 1024 }&quot;)

 if [[ &quot;${f,,}&quot; == *.svg ]]; then
 # SVGs are XML so we can parse dimensions directly without imagemagick.
 # Prefer viewBox (e.g. &quot;0 0 100 200&quot;) as it&#39;s the most reliable source
 # of the intended coordinate space; fall back to width/height attributes.
 viewbox=$(grep -oi &#39;viewBox=&quot;[^&quot;]*&quot;&#39; &quot;$f&quot; 2&gt;/dev/null | head -1 | grep -oi &#39;&quot;[^&quot;]*&quot;&#39; | tr -d &#39;&quot;&#39;)
 if [[ -n &quot;$viewbox&quot; ]]; then
 # viewBox is &quot;min-x min-y width height&quot; — fields 3 and 4
 width=$(echo &quot;$viewbox&quot; | awk &#39;{printf &quot;%g&quot;, $3}&#39;)
 height=$(echo &quot;$viewbox&quot; | awk &#39;{printf &quot;%g&quot;, $4}&#39;)
 else
 width=$(grep -oi &#39;width=&quot;[^&quot;]*&quot;&#39; &quot;$f&quot; 2&gt;/dev/null | head -1 | grep -oi &#39;&quot;[^&quot;]*&quot;&#39; | tr -d &#39;&quot;px&quot;&#39;)
 height=$(grep -oi &#39;height=&quot;[^&quot;]*&quot;&#39; &quot;$f&quot; 2&gt;/dev/null | head -1 | grep -oi &#39;&quot;[^&quot;]*&quot;&#39; | tr -d &#39;&quot;px&quot;&#39;)
 fi
 # ${var:--} expands to &quot;-&quot; if var is empty or unset
 width=&quot;${width:--}&quot;
 height=&quot;${height:--}&quot;
 else
 # [0] selects only the first frame, preventing animated GIFs from
 # producing one line of output per frame
 read -r width height &lt; &lt;(identify -format &quot;%w %h&quot; &quot;${f}[0]&quot; 2&gt;/dev/null)
 fi

 printf &quot;%-40s %11s %12s %10s\n&quot; &quot;$name&quot; &quot;$width&quot; &quot;$height&quot; &quot;$size_kb&quot;
 done
}
</pre><p>The “development time” here was longer and I’d be fibbing if I said I understood every line of <em>this</em> function. But I do get the general principles and understand what it can and can’t do. And being able to list all images (of nearly all relevant types) in a directory with just their key properties in a nice tabular format is a very nice productivity win for me.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>Discussions about AI easily tend towards the extremes: ‘it’s amazing’, ‘it’s a disaster’, ‘it’s useless’. This is particularly unhelpful when focusing on how successfully or otherwise it executes a single task. All we’ve really done in this post is create a couple of Bash scripts. For me the AI in question, Claude, has been use<em>ful</em>. No more, no less. Despite this, the process did make me think a lot about how the way we interact with machines is changing. While it’s easy to over-personify AI, this line from the film that gave this blog post its title seems now, in 2026, to have been prescient:</p>
<figure>
<blockquote>
<p>The advent of computers, and the subsequent accumulation of incalculable data has given rise to a new system of memory and thought parallel to your own.</p>
</blockquote>
<figcaption>— Puppet Master, <i>Ghost in the Shell (1995)</i></figcaption>
</figure>
<p>
For updates and revisions to this article, see the <a href = "https://www.jumpingrivers.com/blog/ghost-in-shell-script/">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/ghost-in-shell-script/"> 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/05/ghost-in-the-shell-script/">Ghost in the Shell Script</a>]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">401507</post-id>	</item>
		<item>
		<title>rOpenSci News Digest, May 2026</title>
		<link>https://www.r-bloggers.com/2026/05/ropensci-news-digest-may-2026/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/05/28/news-may-2026/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<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>15 Years of rOpenSci, and we’re just getting started 🎉<br />
This y...</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/05/ropensci-news-digest-may-2026/">rOpenSci News Digest, May 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/05/28/news-may-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/05/28/news-may-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>
15 Years of rOpenSci, and we’re just getting started <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;" />
</h3><p>This year we celebrate 15 years of rOpenSci! First up we have a coworking session followed by a couple casual virtual community celebrations. Please join us! Read more in our <a href="https://ropensci.org/blog/2026/05/13/anniversary2026/" rel="nofollow" target="_blank">blog post</a> about these upcoming events.</p>
<p>Also exciting is that this blog post about our 15 year anniversary was featured in the R Weekly Highlights <a href="https://serve.podhome.fm/episodepage/r-weekly-highlights/227" rel="nofollow" target="_blank">podcast</a> hosted by Eric Nantz and Mike Thomas.</p>
<h3>
Champions Program update
</h3><p>The rOpenSci Champions Program is currently running two very active cohorts, with many activities happening across the program, and everything is on track!</p>
<p>The <strong>2025–2026 cohort</strong> is wrapping up their projects and outreach activities, with several Champions presenting their work at events such as R/Pharma, useR!, Posit, and LatinR 2026. We also recently held a cohort meeting bringing together Champions and mentors from the 2025–2026 cohort and one meeting for mentors only, to share mentoring tips and tricks.</p>
<p>Meanwhile, the <strong>2026–2027 cohort</strong> has started their training activities, including three weekly training workshops for Champions and a mentor training workshop. Mentors and Champions have also had their first meetings together to get to know each other and begin planning their collaboration.</p>
<p>Stay tuned to our <a href="https://ropensci.org/events" rel="nofollow" target="_blank">events page</a> for Champions outreach activities and to our blog to learn more about Champions’ projects.</p>
<h3>
May is Maintainer Month
</h3><p>Open source software doesn’t sustain itself. Behind every R package, there is at least one person who responds to issues, reviews pull requests, keeps up with dependency changes, and makes sure everything still works.</p>
<p>We joined the #MaintainerMonth celebration with a month-long series of rOpenSci’s maintainer spotlights: <strong>36 maintainers from 15 countries across 4 continents</strong>, maintaining more than 50 packages that together serve thousands of researchers and data practitioners around the world.</p>
<p>Follow our social media (<a href="https://hachyderm.io/@rOpenSci" rel="nofollow" target="_blank">Mastodon</a> and <a href="https://www.linkedin.com/company/ropensci/" rel="nofollow" target="_blank">LinkedIn</a>) and the official hashtag (#MaintainerMonth) to learn more about the maintainers who support open source software. <strong>Thank you</strong> to those who agreed to participate and to all the maintainers in our community.</p>
<h3>
Software-Review update
</h3><p>This month saw a slight policy shift in the way editors handle new software review submissions, assisted by an internal update to our bot system. Previously, our rotating Editor-in-Chief (EiC) was solely responsible for initial decisions on whether to proceed with submissions. Once a decision was reached, a handling editor took over, and generally guided each submission through to approval. However, recent increases in submissions have been too much for one EiC to handle. Submissions will now be passed on more quickly to handling editors who will then make initial scope and fit decisions.</p>
<p>As part of this change, the EiC can now ask the bot to “ping editors”. Editors will receive an email alerting them of this new submission and can then decide whether to take it on as editor. Each time an editor clicks on a link in the email, the EiC will receive an email notification, and once an editor is assigned, all links will be automatically deactivated.</p>
<p>For anybody interested in how to develop an email alert system like this, please contact us and we’ll have happy to share what we learnt along the way. Full code is in <a href="https://github.com/ropensci-review-tools/roreviewapi/blob/main/R/editor-search.R" rel="nofollow" target="_blank">this R file</a>.</p>
<h3>
Social media cards for R-universe
</h3><p>When posting about your package or universe on social media, you can get a pretty social media card featuring important information about (respectively):</p>
<ul>
<li>the package such as its maintainer, its <a href="https://docs.r-universe.dev/publish/metadata.html#keywords" rel="nofollow" target="_blank">keywords</a>, its URL, its version, its number of stars, etc.</li>
</ul>
<figure class="center"><img src="https://i1.wp.com/ropensci.r-universe.dev/targets/card.png?w=450&#038;ssl=1"  data-recalc-dims="1">
</figure>
<ul>
<li>the universe such as its number of packages and contributors, etc.</li>
</ul>
<figure class="center"><img src="https://i1.wp.com/ropensci.r-universe.dev/card.png?w=450&#038;ssl=1"  data-recalc-dims="1">
</figure>
<p><a href="https://docs.r-universe.dev/browse/packages.html#social-media-cards" rel="nofollow" target="_blank">More information in the R-universe documentation</a>.</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>Tuesday June 2nd 2026, 14:00 Europe Central (12:00 UTC) <a href="https://ropensci.org/events/coworking-2026-06/" rel="nofollow" target="_blank">“15 years with rOpenSci”</a>, with <a href="https://ropensci.org/author/steffi-lazerte/" rel="nofollow" target="_blank">Steffi LaZerte</a> and cohost <a href="https://ropensci.org/author/noam-ross" rel="nofollow" target="_blank">Noam Ross</a>.
<ul>
<li>Explore rOpenSci projects</li>
<li>Do a deep dive into the rOpenSci blog history</li>
<li>Meet rOpenSci’s Executive Director, Noam Ross, and discuss how rOpenSci has evolved over the last 15 years.</li>
</ul>
</li>
<li>Tuesday July 7nd 2026, 09:00 Americas Pacific (16:00 UTC) <a href="https://ropensci.org/events/coworking-2026-07/" rel="nofollow" target="_blank">“Debugging in R”</a>, with <a href="https://ropensci.org/author/yanina-bellini-saibene/" rel="nofollow" target="_blank">Yanina Bellini Saibene</a> and cohost <a href="https://ropensci.org/author/shannon-pileggi" rel="nofollow" target="_blank">Shannon Pileggi</a>.
<ul>
<li>Read up on debugging in R</li>
<li>Meet community host, Shannon Pileggi, and discuss tips and tricks for debugging in R.</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><h3>
New versions
</h3><p>The following ten packages have had an update since the last newsletter: <a href="https://docs.ropensci.org/cffr" title="Generate Citation File Format (CFF) Metadata for R Packages" rel="nofollow" target="_blank">cffr</a> (<a href="https://github.com/ropensci/cffr/releases/tag/v1.4.0" rel="nofollow" target="_blank"><code>v1.4.0</code></a>), <a href="https://docs.ropensci.org/gutenbergr" title="Download and Process Public Domain Works from Project Gutenberg" rel="nofollow" target="_blank">gutenbergr</a> (<a href="https://github.com/ropensci/gutenbergr/releases/tag/v0.5.1" rel="nofollow" target="_blank"><code>v0.5.1</code></a>), <a href="https://docs.ropensci.org/dataset" title="Create Data Frames for Exchange and Reuse" rel="nofollow" target="_blank">dataset</a> (<a href="https://github.com/ropensci/dataset/releases/tag/0.4.4" rel="nofollow" target="_blank"><code>0.4.4</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.1" rel="nofollow" target="_blank"><code>v0.4.1</code></a>), <a href="https://docs.ropensci.org/saperlipopette" title="Create Example Git Messes" rel="nofollow" target="_blank">saperlipopette</a> (<a href="https://github.com/ropensci-training/saperlipopette/releases/tag/v1.0.0" rel="nofollow" target="_blank"><code>v1.0.0</code></a>), <a href="https://docs.ropensci.org/medrxivr" title="Access and Search MedRxiv and BioRxiv Preprint Data" rel="nofollow" target="_blank">medrxivr</a> (<a href="https://github.com/ropensci/medrxivr/releases/tag/snapshot" rel="nofollow" target="_blank"><code>snapshot</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.1" rel="nofollow" target="_blank"><code>v1.0.1</code></a>), <a href="https://docs.ropensci.org/EDIutils" title="An API Client for the Environmental Data Initiative Repository" rel="nofollow" target="_blank">EDIutils</a> (<a href="https://github.com/ropensci/EDIutils/releases/tag/v2.1.0" rel="nofollow" target="_blank"><code>v2.1.0</code></a>), <a href="https://docs.ropensci.org/rsi" title="Efficiently Retrieve and Process Satellite Imagery" rel="nofollow" target="_blank">rsi</a> (<a href="https://github.com/Permian-Global-Research/rsi/releases/tag/v0.3.3" rel="nofollow" target="_blank"><code>v0.3.3</code></a>), and <a href="https://docs.ropensci.org/UCSCXenaTools" title="Download and Explore Datasets from UCSC Xena Data Hubs" rel="nofollow" target="_blank">UCSCXenaTools</a> (<a href="https://github.com/ropensci/UCSCXenaTools/releases/tag/v1.7.0" rel="nofollow" target="_blank"><code>v1.7.0</code></a>).</p>
<p>The writexl package has a <a href="https://github.com/ropensci/writexl/pull/98#issuecomment-4191858158" rel="nofollow" target="_blank">new maintainer</a>, Bill Denney. NLMR is now maintained by <a href="https://github.com/ropensci/NLMR/issues/116#issuecomment-4280937012" rel="nofollow" target="_blank">Jakub Nowosad</a>.</p>
<h2>
Software Peer Review
</h2><p>There are eighteen recently closed and active submissions and 4 submissions on hold. Issues are at different stages:</p>
<ul>
<li>
<p>One 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><a href="https://github.com/ropensci/software-review/issues/754" rel="nofollow" target="_blank">saperlipopette</a>, Create Example Git Messes. Submitted by <a href="https://masalmon.eu/" rel="nofollow" target="_blank">Maëlle Salmon</a>.</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%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/750" rel="nofollow" target="_blank">nycOpenData</a>, Convenient Access to NYC Open Data API Endpoints. Submitted by <a href="https://github.com/martinezc1" rel="nofollow" target="_blank">Christian Martinez</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/730" rel="nofollow" target="_blank">ernest</a>, A Toolkit for Nested Sampling. Submitted by <a href="https://github.com/kylesnap" rel="nofollow" target="_blank">Kyle Dewsnap</a>. (Stats).</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/671" rel="nofollow" target="_blank">pkgmatch</a>, Find R Packages Matching Either Descriptions or Other R Packages. Submitted by <a href="https://mpadge.github.io/" rel="nofollow" target="_blank">mark padgham</a>.</p>
</li>
</ul>
</li>
<li>
<p>Six 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/760" rel="nofollow" target="_blank">pvEBayes</a>, Empirical Bayes Methods for Pharmacovigilance. Submitted by <a href="https://github.com/YihaoTancn" rel="nofollow" target="_blank">Yihao Tan</a>. (Stats).</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/741" rel="nofollow" target="_blank">logolink</a>, An Interface for Running NetLogo Simulations. Submitted by <a href="http://danielvartan.com/" rel="nofollow" target="_blank">Daniel Vartanian</a>.</p>
</li>
<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/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.github.io/" rel="nofollow" target="_blank">Øystein Sørensen</a>. (Stats).</p>
</li>
</ul>
</li>
<li>
<p>One 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><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>.</li>
</ul>
</li>
<li>
<p>Five 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/763" rel="nofollow" target="_blank">EpiStrainDynamics</a>, Infer temporal trends of multiple pathogens. Submitted by <a href="http://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/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/740" rel="nofollow" target="_blank">fcmconfr</a>, Fuzzy Cognitive Map Analysis in R. Submitted by <a href="https://github.com/bhroston" rel="nofollow" target="_blank">benroston</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%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/752" rel="nofollow" target="_blank">metasurvey</a>, Reproducible Survey Data Processing with Step Pipelines. Submitted by <a href="https://github.com/mauroloprete" rel="nofollow" target="_blank">Mauro Loprete</a>.</p>
</li>
<li>
<p><a href="https://github.com/ropensci/software-review/issues/725" rel="nofollow" target="_blank">LBDiscoverAnalysis</a>, Co-occurrence Discovery Models and Visualization for Biomedical LBD. Submitted by <a href="https://github.com/chaoliu-cl" rel="nofollow" target="_blank">Chao Liu</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! -->
<ul>
<li>
<p><a href="https://ropensci.org/blog/2026/05/06/mentors-2026" rel="nofollow" target="_blank">New Mentoring Team with Experienced Mentors and New Voices</a> by Alber Hamersson Sánchez Ipia, Pablo Paccioretti, Erick Navarro Delgado, Guadalupe Pascal, Andrea Gomez Vargas, Monika Avila Marquez, Luis D. Verde Arregoitia, Francisco Cardozo, Elio Campitelli, Pao Corrales, and Yanina Bellini Saibene. Meet the new team of mentors who will accompany our Champions in their projects for a year, sharing experience, guidance and enthusiasm. Other languages: <a href='https://ropensci.org/es/blog/2026/05/26/mentoras_es-2026' lang='es' rel="nofollow" target="_blank">Nuevo equipo de mentor(a|e)s con experiencia y nuevas voces (es)</a>.</p>
</li>
<li>
<p><a href="https://ropensci.org/blog/2026/05/13/anniversary2026" rel="nofollow" target="_blank">15 Years of rOpenSci, and We’re Just Getting Started <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;" /></a> by The rOpenSci Team.</p>
</li>
</ul>
<figure class="center"><img src="https://i1.wp.com/ropensci.org/blog/2026/05/28/news-may-2026/PixelArt15yearrOpenSci.png?w=400&#038;ssl=1"
alt="Retro pixel-art graphic celebrating rOpenSci&#39;s 15th anniversary. The text &#39;rOpenSci&#39; appears at the top in pixel font, flanked by three pixel-art balloons. A browser window frames the central message: &#39;15 YEARS / TRANSFORMING OPEN SCIENCE&#39; in bold pixel letters, overlaid on the rOpenSci geometric network pattern. A pixel badge reads &#39;OMG&#39;. A pixel folder and sparkle icons complete the design."  data-recalc-dims="1">
</figure>
<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>
<p><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>.</p>
</li>
<li>
<p><a href="https://docs.ropensci.org/landscapetools" rel="nofollow" target="_blank">landscapetools</a>, R package for some of the less-glamorous tasks involved in landscape analysis. <a href="https://github.com/ropensci/landscapetools/issues/48" rel="nofollow" target="_blank">Issue for volunteering</a>.</p>
</li>
<li>
<p><a href="https://docs.ropensci.org/hddtools" rel="nofollow" target="_blank">hddtools</a>, Tools to discover hydrological data, accessing catalogues and databases from various data providers. <a href="https://github.com/ropensci/hddtools/issues/36" rel="nofollow" target="_blank">Issue for volunteering</a>.</p>
</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>
devtools’ install functions now officially deprecated
</h3><p>The <code>devtools::install_</code> functions like <a href="https://devtools.r-lib.org/reference/install-deprecated.html" rel="nofollow" target="_blank"><code>devtools::install_github()</code></a> have now been officially <a href="https://devtools.r-lib.org/reference/install-deprecated.html" rel="nofollow" target="_blank">deprecated</a> in favor of <a href="https://pak.r-lib.org/" rel="nofollow" target="_blank">pak</a>. The <a href="https://devtools.r-lib.org/reference/install-deprecated.html" rel="nofollow" target="_blank">manual page</a> contains a migration guide. Time to update older READMEs!</p>
<h3>
New Git commands!
</h3><p>New in Git: git history! Thanks to Hugo Gruson for bringing this to our attention.</p>
<p>Imagine…</p>
<p><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f630.png" alt="😰" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Oops, that old commit’s message had a typo! <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f60c.png" alt="😌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> git history reword <commit-id></p>
<p><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f630.png" alt="😰" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Oops, that old commit is too big! <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f60c.png" alt="😌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> git history split <commit-id> (to split it into two commits)</p>
<p><a href="https://github.blog/open-source/git/highlights-from-git-2-54/#h-rewrite-history-with-git-history" rel="nofollow" target="_blank">Git release notes</a>; <a href="https://git-scm.com/docs/git-history" rel="nofollow" target="_blank">Git docs</a>.</p>
<h3>
roxygen2 8.0.0
</h3><p>A new version of roxygen2 hit CRAN. Among the highlights are improvements for R6, S7, inheritance, and better documentation of roxygen2 itself, including the <a href="https://roxygen2.r-lib.org/articles/extending.html" rel="nofollow" target="_blank">vignette on extending roxygen2 with your own tags and roclets</a>. <a href="https://opensource.posit.co/blog/2026-05-01_roxygen2-8-0-0/" rel="nofollow" target="_blank">Release announcement</a>.</p>
<h3>
Comment <code>DESCRIPTION</code> files
</h3><p>Have you noticed this line in the release notes of R 4.6.0?</p>
<blockquote>
<p><a href="https://rdrr.io/r/base/dcf.html" rel="nofollow" target="_blank"><code>read.dcf()</code></a> now recognizes lines starting with <code>#</code> as comment lines. By Dirk Eddelbuettel, Laurent Gatto and Hugo Gruson.</p>
</blockquote>
<p>This means it’s becoming possible to add comments to <code>DESCRIPTION</code> files, for instance to note why you added a given dependency.</p>
<h3>
{cross} R package for running code with different package versions
</h3><p>Have you ever used <a href="https://masalmon.eu/2024/01/23/git-worktree/" rel="nofollow" target="_blank">Git worktree</a> to load several R package versions at once (in different sessions), for instance to create a benchmark? Davis Vaughan made such endeavors much smoother with his <a href="https://github.com/DavisVaughan/cross" rel="nofollow" target="_blank">cross R package</a>. cross uses Git worktree under the hood to install different package versions in temporary libraries and then runs R code with these different versions. Furthermore, it has a user-friendly interface!</p>
<p>For example, to compare the released version of vctrs to the developement version on GitHub, you could run the following.</p>
<pre>cross::bench_versions(pkgs = c(&quot;vctrs&quot;, &quot;r-lib/vctrs&quot;), {
 library(vctrs)
 x &lt;- c(1, NA, 2, 3, NA)
 bench::mark(missing = vec_detect_missing(x))
})
</pre><h3>
On attribution with a footnote
</h3><p>James Balamuta wrote two thoughtful posts on a <a href="https://blog.thecoatlessprofessor.com/posts/closing-the-footnote-loop/" rel="nofollow" target="_blank">tricky situation and its resolution</a>. TL;DR James’ work on portable R was used in another repository with only minimal acknowledgement through a footnote in a comment. However, those involved came together to work out a resolution through meaningful conversation.</p>
<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/05/28/news-may-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/05/ropensci-news-digest-may-2026/">rOpenSci News Digest, May 2026</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401509</post-id>	</item>
		<item>
		<title>survivoR now includes US50 and AU12</title>
		<link>https://www.r-bloggers.com/2026/05/survivor-now-includes-us50-and-au12/</link>
		
		<dc:creator><![CDATA[Daniel Oehm]]></dc:creator>
		<pubDate>Wed, 27 May 2026 09:54:21 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://gradientdescending.com/?p=3531</guid>

					<description><![CDATA[<p>After a couple of big seasons survivoR v2.3.12 has been updated with US50 and AU12. Get it on: As of […]<br />
The post survivoR now includes US50 and AU12 appeared first on Dan Oehm &#124; Gradient Descending.</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/05/survivor-now-includes-us50-and-au12/">survivoR now includes US50 and AU12</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="http://gradientdescending.com/survivor-now-includes-us50-and-au12/"> R Archives - Dan Oehm | Gradient Descending</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>
<a href="http://gradientdescending.com/survivor-now-includes-us50-and-au12/" title="survivoR now includes US50 and AU12" rel="nofollow" target="_blank"><img loading="lazy" src="https://i1.wp.com/gradientdescending.com/wp-content/uploads/2026/05/Screenshot-2026-05-27-195211-1024x767.png?w=450" class="webfeedsFeaturedVisual wp-post-image" alt="" style="display: block; margin: auto; margin-bottom: 5px;max-width: 100%;" link_thumbnail="1" decoding="async" srcset_temp="https://i1.wp.com/gradientdescending.com/wp-content/uploads/2026/05/Screenshot-2026-05-27-195211-1024x767.png?w=450 1024w, http://gradientdescending.com/wp-content/uploads/2026/05/Screenshot-2026-05-27-195211-300x225.png 300w, http://gradientdescending.com/wp-content/uploads/2026/05/Screenshot-2026-05-27-195211-768x575.png 768w, http://gradientdescending.com/wp-content/uploads/2026/05/Screenshot-2026-05-27-195211-1536x1151.png 1536w, http://gradientdescending.com/wp-content/uploads/2026/05/Screenshot-2026-05-27-195211.png 1567w" sizes="(max-width: 1024px) 100vw, 1024px" data-recalc-dims="1" /></a>
<p class="wp-block-paragraph">After a couple of big seasons survivoR v2.3.12 has been updated with US50 and AU12.</p>



<p class="wp-block-paragraph">Get it on:</p>



<ul class="wp-block-list">
<li><a href="https://github.com/doehm/survivoR" rel="nofollow" target="_blank">Github</a></li>



<li><a href="https://github.com/doehm/survivoR/raw/refs/heads/master/dev/xlsx/survivoR.xlsx" rel="nofollow" target="_blank">XLSX download</a></li>



<li><a href="https://github.com/doehm/survivoR/tree/master/dev/json" rel="nofollow" target="_blank">JSON endpoints</a></li>
</ul>



<p class="wp-block-paragraph">As of writing, the survivoR package is closing in on 30,000 downloads, which is wild to me. Not only that, but the CRAN installs are probably the lowest volume of all formats. Installs directly from Git and the XLSX downloads are probably much higher. I never thought this data would be so popular.</p>



<p class="wp-block-paragraph">I don’t have much of a social media presence, pretty much only Bluesky these days, and a very small following there. Regardless many people reach out to say thanks for pulling it together. It has enabled them to build their own websites (of which there are quite a few – <a href="https://survivor-reference.com/" rel="nofollow" target="_blank">Survivor-reference</a>, <a href="https://survivorelo.com/" rel="nofollow" target="_blank">survivorELO</a>), be used for research papers, Master theses, school projects, help potential castaways with their Survivor auditions, help future Survivor players prep for their season, even then <a href="https://survivordle.com/" rel="nofollow" target="_blank">Survivordle</a> web app.</p>



<p class="sg-ai-highlighted-block wp-block-paragraph">I enjoy hearing about how others are utilizing it. If you have experience with it as well, please reach out—I am always keen to discuss Survivor!</p>
<p>The post <a href="http://gradientdescending.com/survivor-now-includes-us50-and-au12/" rel="nofollow" target="_blank">survivoR now includes US50 and AU12</a> appeared first on <a href="http://gradientdescending.com/" rel="nofollow" target="_blank">Dan Oehm | Gradient Descending</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="http://gradientdescending.com/survivor-now-includes-us50-and-au12/"> R Archives - Dan Oehm | Gradient Descending</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/05/survivor-now-includes-us50-and-au12/">survivoR now includes US50 and AU12</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401490</post-id>	</item>
		<item>
		<title>Speeding up Stan model builds for R package developers</title>
		<link>https://www.r-bloggers.com/2026/05/speeding-up-stan-model-builds-for-r-package-developers/</link>
		
		<dc:creator><![CDATA[R &#124; Dr Tom Palmer]]></dc:creator>
		<pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://remlapmot.github.io/post/2026/stan-compile-speedup/</guid>

					<description><![CDATA[<p>Introduction<br />
In my previous job my work computer was a Windows desktop – yes, those were the days before laptops and hotdesking!<br />
My PhD student was interested in Bayesian methods and we put together an R package which included some<br />
Stan models. ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/05/speeding-up-stan-model-builds-for-r-package-developers/">Speeding up Stan model builds for R package developers</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/stan-compile-speedup/"> 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 my previous job my work computer was a Windows desktop – yes, those were the days before laptops and hotdesking!</p>
<p>My PhD student was interested in Bayesian methods and we put together an R package which included some 
<a href="https://mc-stan.org/" rel="nofollow" target="_blank">Stan</a> models. I was always frustrated by how slowly these compiled on our Windows machines. A few years later, when I got a MacBook Air I was shocked how much faster they compiled.</p>
<p>On my Windows machine our 
<a href="https://okezie94.github.io/mrbayes/" rel="nofollow" target="_blank">mrbayes</a> package takes 3 minutes 55 seconds to compile and install. On my M4 MacBook Air it takes 1 minute 16 seconds.</p>
<p>The following tips show how to improve those timings.</p>
<p>To generate the timings I used</p>
<pre>time R CMD INSTALL --preclean .
</pre>
<h2 id="big-win-1-enable-parallel-compilations-with-the-makeflags-environment-variable">Big win 1: Enable parallel compilations with the <code>MAKEFLAGS</code> environment variable</h2>
<p>Set the <code>MAKEFLAGS</code> environment variable in your <em>~/.Renviron</em> file. This controls how many <code>make</code> jobs run concurrently. Choose a number no larger than the number of processing cores your machine has. To find this run</p>
<pre># Windows - in a Git Bash shell
echo $NUMBER_OF_PROCESSORS

# macOS
sysctl -n hw.logicalcpu

# Ubuntu Linux
nproc
</pre>
<p>A reasonable starting point is your core count, or a few fewer to leave headroom for whatever else you’re doing during a compilation. For example,</p>
<pre># In ~/.Renviron
MAKEFLAGS=-j6
</pre>
<p>Close and restart R/RStudio after making this change.</p>
<p>On my Windows machine this reduced the build from 3:55 to 1:15. To find your own sweet spot empirically, see the example at the end of Big win 2.</p>
<h2 id="big-win-2-enable-cc-compiler-cache-using-ccache">Big win 2: Enable C/C++ compiler cache using <code>ccache</code></h2>
<p>Install 
<a href="https://ccache.dev/" rel="nofollow" target="_blank"><code>ccache</code></a>, I find it easiest to use a package manager, e.g.,</p>
<pre># macOS
brew install ccache

# Ubuntu/Debian Linux
apt install ccache

# Windows
winget install ccache
</pre>
<p>Whichever installation method you use make sure <code>ccache</code> is on your <code>PATH</code> after installation. You can test with, say,</p>
<pre>ccache --version
</pre>
<p>To enable <code>ccache</code>, on macOS and Linux this goes in <em>~/.R/Makevars</em>; on Windows it’s <em>~/.R/Makevars.win</em> (create the directory and file if they don’t exist), set</p>
<pre># macOS
CC = ccache clang
CXX = ccache clang++
CXX17 = ccache clang++

# Windows and Linux
# Most Linux users will be on gcc by default
# Change to clang if you're using that
CC = ccache gcc
CXX = ccache g++
CXX17 = ccache g++
</pre>
<p>After a first compilation run for the cache to be generated, subsequent compilations are much faster.</p>
<ul>
<li>Windows, second compilation: 18 seconds</li>
<li>M4 MacBook Air, second compilation: 5 seconds</li>
</ul>
<p>Perhaps more importantly, if, say, your package has 5 models and you only amend the code for one of them, <code>ccache</code> knows to use the cache for the 4 unchanged models.</p>
<ul>
<li>Windows, second compilation, only 1 model edited: 1 minute 10 seconds</li>
<li>M4 MacBook Air, second compilation, only 1 model edited: 19 seconds</li>
</ul>
<p>You can verify <code>ccache</code> is working, by observing the timing decrease and by checking the output of</p>
<pre>ccache -s
</pre>
<p>It is also useful to zero the ccache statistics before a timing run with</p>
<pre>ccache -z
</pre>
<h3 id="testing-which-of-your-models-takes-the-longest-to-compile">Testing which of your models takes the longest to compile</h3>
<p>Here’s a quick script to test which model takes the longest to compile. Save it as say <em>test.sh</em> at the top level of your repo and add <code>^test\.sh$</code> to your <em>.Rbuildignore</em> file (to avoid an <code>R CMD check</code> NOTE about unknown files at the top level).</p>
<pre>for model in inst/stan/*.stan; do
  cp &quot;$model&quot; &quot;$model.bak&quot;
  # Insert at the top of the file
  sed -i &quot;1i // benchmark $(date +%s%N)&quot; &quot;$model&quot;
  ccache -z
  SECONDS=0
  R CMD INSTALL --preclean . &gt;/dev/null 2&gt;&1
  echo &quot;$(basename $model): ${SECONDS}s&quot;
  ccache -s | grep -E &quot;Hits|Misses&quot; | head -2
  mv &quot;$model.bak&quot; &quot;$model&quot;
done
</pre>
<h3 id="finding-your-makeflags-sweet-spot">Finding your <code>MAKEFLAGS</code> sweet spot</h3>
<p>With <code>ccache</code> installed you can now benchmark different <code>-jN</code> values cleanly (the <code>ccache -C</code> calls ensure each run is a cold compile, so you measure raw compilation cost rather than cache hits). You can increase the number sequence up to the number of processing cores your machine has.</p>
<pre>for j in 1 2 3 4 6 8 10; do
  ccache -C &gt;/dev/null
  echo &quot;=== -j$j ===&quot;
  SECONDS=0
  MAKEFLAGS=-j$j R CMD INSTALL --preclean . &gt;/dev/null 2&gt;&1
  echo &quot;elapsed: ${SECONDS}s&quot;
done
</pre>
<p>The timings on my MacBook Air were</p>
<pre>=== -j1 ===
elapsed: 76s
=== -j2 ===
elapsed: 48s
=== -j3 ===
elapsed: 35s
=== -j4 ===
elapsed: 36s
=== -j6 ===
elapsed: 27s
=== -j8 ===
elapsed: 27s
=== -j10 ===
elapsed: 28s
</pre>
<p>My MacBook Air has 10 cores, but only 4 of those are performance cores, so I settled on <code>-j6</code> as that is where my timings plateaued — and it leaves headroom for me inevitably checking my email during a compilation.</p>
<h2 id="big-win-3-combining-these-in-github-actions-workflows">Big win 3: Combining these in GitHub Actions workflows</h2>
<p>In my <em>.github/workflows/R-CMD-check.yaml</em> I have steps for these speedups. Firstly, to set <code>MAKEFLAGS</code>.</p>
<pre>      - name: Set parallel compilation flags (Linux and macOS)
        if: runner.os != 'Windows'
        shell: bash
        run: |
          NCPUS=$(nproc 2&gt;/dev/null || sysctl -n hw.logicalcpu)
          echo &quot;Detected ${NCPUS} processors&quot;
          echo &quot;MAKEFLAGS=-j${NCPUS}&quot; &gt;&gt; ~/.Renviron

      - name: Set parallel compilation flags (Windows)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          Write-Output &quot;Detected $env:NUMBER_OF_PROCESSORS processors&quot;
          Add-Content -Path &quot;$HOME\.Renviron&quot; -Value &quot;MAKEFLAGS=-j$env:NUMBER_OF_PROCESSORS&quot;
</pre>
<p>You can also use ccache in GitHub Actions, as follows:</p>
<pre>      # ccache speeds up Stan model compilation dramatically on warm cache.
      # Note: Windows support via ccache-action is documented as &quot;probably works&quot;
      # rather than fully stable; if it causes issues, scope this step to non-Windows.
      - name: Setup ccache
        uses: hendrikmuhs/ccache-action@v1.2.23
        with:
          # Key invalidates when Stan models or DESCRIPTION change.
          # Older caches partially seed new ones via restore-keys.
          key: ccache-${{ matrix.config.os }}-R-${{ matrix.config.r }}-${{ hashFiles('inst/stan/**/*.stan', 'DESCRIPTION') }}
          restore-keys: |
            ccache-${{ matrix.config.os }}-R-${{ matrix.config.r }}-
            ccache-${{ matrix.config.os }}-R-
          max-size: &quot;2G&quot;

      - name: Configure R to use ccache (Linux and macOS)
        if: runner.os != 'Windows'
        shell: bash
        run: |
          mkdir -p ~/.R
          if [ &quot;$RUNNER_OS&quot; = &quot;macOS&quot; ]; then
            cat &gt;&gt; ~/.R/Makevars &lt;&lt;'EOF'
          CC = ccache clang
          CXX = ccache clang++
          CXX14 = ccache clang++
          CXX17 = ccache clang++
          CXX20 = ccache clang++
          EOF
          else
            cat &gt;&gt; ~/.R/Makevars &lt;&lt;'EOF'
          CC = ccache gcc
          CXX = ccache g++
          CXX14 = ccache g++
          CXX17 = ccache g++
          CXX20 = ccache g++
          EOF
          fi
          echo &quot;--- ~/.R/Makevars ---&quot;
          cat ~/.R/Makevars

      - name: Configure R to use ccache (Windows)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          New-Item -ItemType Directory -Force -Path &quot;$HOME\.R&quot; | Out-Null
          $makevars = @&quot;
          CC = ccache gcc
          CXX = ccache g++
          CXX14 = ccache g++
          CXX17 = ccache g++
          CXX20 = ccache g++
          &quot;@
          Add-Content -Path &quot;$HOME\.R\Makevars.win&quot; -Value $makevars
          Write-Output &quot;--- ~/.R/Makevars.win ---&quot;
          Get-Content &quot;$HOME\.R\Makevars.win&quot;
</pre>
<p>You can see the 
<a href="https://github.com/okezie94/mrbayes/blob/master/.github/workflows/R-CMD-check.yaml" rel="nofollow" target="_blank">full file in my repo</a>.</p>
<p>This reduced my ubuntu-latest run for r-release from 7 minutes 30 seconds to 4 minutes 49 seconds.</p>
<h2 id="big-win-4-switch-to-clang">Big win 4: Switch to <code>clang</code></h2>
<p>I found that switching from <code>gcc</code> to <code>clang</code> gives a noticeable speedup; the single core compile time dropped from 3 minutes 55 seconds to 3 minutes flat on my Windows machine.</p>
<p>To do this you need to install <code>clang</code>. On Windows you install <code>clang</code> within RTools45 — more involved than on Linux, but doable.</p>
<pre># Windows within RTools45 Bash shell
# Launch C:\rtools45\ucrt64.exe
# You may need to close and reopen the shell after the first command
pacman -Syu
pacman -S mingw-w64-ucrt-x86_64-clang

# Ubuntu/Debian Linux
sudo apt install clang
</pre>
<p>At this point on Windows running</p>
<pre>which clang
</pre>
<p>should return <code>/ucrt/bin/clang</code>.</p>
<p>Switch to <code>clang</code> in <em>~/.R/Makevars</em> (if you’re not using <code>ccache</code> delete that prefix)</p>
<pre># On Linux
CC = ccache clang
CXX = ccache clang++
CXX14 = ccache clang++
CXX17 = ccache clang++
CXX20 = ccache clang++
</pre>
<p>and in <em>~/.R/Makevars.win</em> on Windows</p>
<pre># On Windows
CC = ccache C:/rtools45/ucrt64/bin/clang.exe
CXX = ccache C:/rtools45/ucrt64/bin/clang++.exe
CXX17 = ccache C:/rtools45/ucrt64/bin/clang++.exe
</pre>
<p>Windows users will need to add the following to <code>PATH</code></p>
<pre>C:\rtools45\ucrt64\bin
C:\rtools45\usr\bin
</pre>
<p>You can verify things are working by running</p>
<pre>R CMD config CXX17
</pre>
<p>I believe you need <code>clang</code> version 18 or later to see the speedups.</p>
<h2 id="small-win-1-wsl-users-should-use-the-native-file-system">Small win 1: WSL users should use the native file system</h2>
<p>Within WSL it is possible to access files from within its native Linux filesystem, i.e., within <code>/home/user/...</code>, and also on the Windows filesystem, e.g., in <code>/mnt/c/...</code>. I believe file operations are noticeably faster within <code>/home/user/...</code>.</p>
<h2 id="naive-guesses-that-made-no-difference">Naive guesses that made no difference</h2>
<p>I had wondered whether running a non-debug compilation with say</p>
<pre>pkgbuild::compile_dll(debug = FALSE)
</pre>
<p>would speed things up. It turns out it does not. For Stan models, most of the time is spent in C++ template instantiation by the compiler, not in optimisation passes — so disabling debug flags or lowering the optimisation level barely helps.</p>
<p>I also wondered whether using R on Windows Subsystem for Linux would speed things up just by virtue of being on Linux. It did not, timings using <code>gcc</code> on Windows and WSL Ubuntu were essentially identical. The advantage of using WSL is that it is easier to switch to using <code>clang</code> on Linux.</p>
<h2 id="money-no-object-big-win-5-switch-to-an-apple-silicon-mac">(Money no object) Big win 5: Switch to an Apple Silicon Mac</h2>
<p>Apple silicon Macs have excellent single threaded performance, their unified memory architecture has very high bandwidth, they have large L1 and L2 caches, and fast NVMe SSDs. Together these produce very fast Stan model compilation times, even on the lowest end Apple Silicon Macs.</p>
<h2 id="summary">Summary</h2>
<p>In summary, five big wins and one small win for speeding up Stan model compilation in R packages.</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/stan-compile-speedup/"> 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/05/speeding-up-stan-model-builds-for-r-package-developers/">Speeding up Stan model builds for R package developers</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401488</post-id>	</item>
		<item>
		<title>Behavior-Driven Development in R Shiny: Modeling User Behavior with When Steps</title>
		<link>https://www.r-bloggers.com/2026/05/behavior-driven-development-in-r-shiny-modeling-user-behavior-with-when-steps/</link>
		
		<dc:creator><![CDATA[jakub::sobolewski]]></dc:creator>
		<pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://jakubsobolewski.com/blog/bdd-shiny-when</guid>

					<description><![CDATA[<p>Learn how to write When steps that describe user actions without leaking implementation details. Build a clean DSL that survives UI refactors and keeps specifications readable.</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/05/behavior-driven-development-in-r-shiny-modeling-user-behavior-with-when-steps/">Behavior-Driven Development in R Shiny: Modeling User Behavior with When Steps</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://jakubsobolewski.com/blog/bdd-shiny-when"> jakub::sobolewski</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 specification tells a story.</p>
<p>Given describes the world before the action. When describes the action itself. Then describes what changed as a result.</p>
<p>If Given is the setup and Then is the payoff, When is where the plot happens, and how you write it determines whether your specifications stay readable for months or decay after the very first change you make to your project.</p>
<hr>
<p>This article is part of a series on Behavior-Driven Development for Shiny applications. We’ve been building a data submission form from scratch, adding an email notification feature, and managing preconditions with Given steps.</p>
<p>Read the previous articles to get up to speed, or continue here to focus on how to write When steps.</p>
<ol>
<li><strong><a href="https://jakubsobolewski.com/blog/bdd-shiny-feature/" rel="nofollow" target="_blank">Behavior-Driven Development in R Shiny: A Step-By-Step Example</a></strong></li>
<li><strong><a href="https://jakubsobolewski.com/blog/bdd-shiny-given/" rel="nofollow" target="_blank">Behavior-Driven Development in R Shiny: Setting Up Test Preconditions with Given Steps</a></strong></li>
</ol>
<hr>
<blockquote>
<p><a href="https://jakubsobolewski.com/get-roadmap/" rel="nofollow" target="_blank">Level-up your testing game! Grab your copy of the R testing roadmap.</a></p>
</blockquote>
<h2 id="the-purpose-of-when">The Purpose of When</h2>
<p>When steps answer one question: <strong>What is the user doing?</strong></p>
<p>Not what button they clicked. Not what input ID was set. What <strong>they</strong> did, from <strong>their perspective</strong>.</p>
<p>The difference matters because UI implementations change constantly, user behaviors change only when business requirements change. Input IDs get renamed, buttons get moved, forms get refactored into modules. But users still interact with the same form, they submit the same data. If your When steps name those interface details, they break every time something shifts. If they describe user intent, they survive almost any UI change.</p>
<p>A When step should read like a sentence from a user story.</p>
<p>Compare these two:</p>
<pre># Implementation-coupled! Breaks when the submit button is removed or chaned
when_i_click_submit_button &lt;- function(context) {
  ...
}

# Behavior-focused! Survives any UI refactor
when_i_submit_entry_with_all_required_fields &lt;- function(context) {
  ...
}</pre>
<p>The second version tells you what the user accomplished, not how the app was wired.</p>
<p>That’s the goal.</p>
<h2 id="starting-the-app">Starting the App</h2>
<p>When steps are where the Shiny app launches — but not directly.</p>
<p>As we established in the Given article, the app starts lazily: the first When step calls <code>.start_driver()</code>, which creates the driver with all configuration accumulated from Given steps. Every subsequent When step interacts with the already-running app.</p>
<pre>#' tests/testthat/setup-dsl.R
when_i_submit_entry_with_all_required_fields &lt;- function(context) {
  .start_driver(context)
  context$driver$fill_required_fields(
    url = &quot;https://example.com/&quot;,
    title = &quot;Example&quot;
  )
  context
}</pre>
<p>Calling <code>.start_driver()</code> in every When step is safe — it’s a no-op if the driver already exists:</p>
<pre>.start_driver &lt;- function(context) {
  if (is.null(context$driver)) {
    context$driver &lt;- MyAppDriver$new(
      storage = context$storage,
      email_service = context$email_service,
      user = context$user
    )
  }
  context
}</pre>
<p>This means Given steps are purely configuration — they never touch the running app. The app only starts when the user’s first action is about to happen. This makes Given steps fast, composable, and easy to reason about.</p>
<h2 id="implementing-when-steps">Implementing When Steps</h2>
<p>The When step function itself should be thin. Its only job is to delegate to the driver and pass the context forward.</p>
<p>All implementation details live in the driver:</p>
<pre>#' tests/testthat/setup-driver.R
MyAppDriver &lt;- R6::R6Class(
  classname = &quot;MyAppDriver&quot;,
  inherit = shinytest2::AppDriver,
  public = list(
    fill_required_fields = function(url, title) {
      self$set_inputs(url = url, title = title)
      self$click(input = &quot;submit&quot;)
      self$wait_for_idle()
    },
    fill_all_fields = function(url, title, description) {
      self$set_inputs(url = url, title = title, description = description)
      self$click(input = &quot;submit&quot;)
      self$wait_for_idle()
    }
  )
)</pre>
<p>The driver encapsulates everything: which inputs to set, what to click, what to wait for. The When step just calls the right driver method by name.</p>
<p>This separation pays off immediately. If the submit button ID changes from <code>&quot;submit&quot;</code> to <code>&quot;btn_submit&quot;</code>, you fix it in one place — the driver method — and every specification that calls <code>fill_required_fields</code> continues to work unchanged.</p>
<p>Note that the same driver method can be called from multiple When steps.</p>
<p><strong>The driver is a translation layer.</strong> It translates user intent (fill required fields) into UI mechanics (set these inputs, click this button).</p>
<h2 id="naming-when-steps">Naming When Steps</h2>
<p>Good names follow a simple pattern: <code>when_&lt;who&gt;_&lt;verb&gt;_&lt;object&gt;</code>.</p>
<p>The verb describes the action from the user’s point of view. The object is what they acted on — expressed in domain language, not UI language.</p>
<pre># Good! We use domain language
when_i_submit_entry_with_all_required_fields()
when_i_submit_entry_with_missing_title()
when_i_delete_the_first_entry()
when_i_inspect_the_submissions_list()

# Bad! UI details are leaking in
when_i_click_the_submit_button()
when_i_clear_the_title_input()
when_i_navigate_to_slash_list()</pre>
<p>Domain names survive redesigns. UI names couple your specifications to the current implementation.</p>
<p>One more rule: <strong>one action per When step</strong>. A step named <code>when_i_fill_and_submit_the_form()</code> is doing two things. That makes it harder to compose and harder to understand which action caused which outcome.</p>
<pre># One action per step — composable
when_i_fill_in_the_form(url = &quot;https://example.com&quot;, title = &quot;Example&quot;) |&gt;
  when_i_submit_the_form()

# Doing too much — hard to reuse
when_i_fill_and_submit_the_form()</pre>
<p>Smaller, focused steps let you test multi-step flows precisely.</p>
<h3 id="my-thoughts-on-navigation-steps">My Thoughts on Navigation Steps</h3>
<p>You might think, this is an easy example.</p>
<p>Real apps aren’t single-page forms. They have navigation, multiple pages, modals, they might feel like navigating through a maze. It’s not that simple to interact with all the features! It’s not, but we can apply the same principles to more complex interactions.</p>
<p><strong>Here’s what I do.</strong></p>
<p>In apps we have pages. Users need to navigate to those pages before they can interact with them. How do we name When steps that represent navigation?</p>
<p>I prefer to name them based on their intent, what they’re trying to achieve, rather than the mechanics of how they get there. For example:</p>
<pre># Good! Focuses on the user's goal, not the UI structure
when_i_inspect_submissions()
when_i_open_the_first_entry()
when_i_change_settings(...) # composite step, implicitly navigates to the settings page and changes settings

# Bad! Reveals that there is a &quot;submissions&quot; page, what if we move submissions listing to another page?
when_i_navigate_to_submissions_page()</pre>
<p>This way, the step names reflect the user’s goals, not the UI structure. If we later change how navigation works — say we add another page and move our targeted component there — we can update the driver methods without changing the When step names.</p>
<p>That’s how specifications stay stable and focused on user behavior, not implementation details.</p>
<p>That’s how specifications stay stable and don’t need updating every time the UI changes.</p>
<h2 id="multi-step-flows">Multi-Step Flows</h2>
<p>When a scenario involves several user actions in sequence, chain them:</p>
<pre>it(&quot;should allow editing a submitted entry&quot;, {
  given_existing_entries() |&gt;
    given_an_authenticated_user() |&gt;
    when_i_open_the_first_entry() |&gt;
    when_i_update_the_title(title = &quot;Updated Title&quot;) |&gt;
    when_i_submit_the_form() |&gt;
    then_the_entry_has_title(&quot;Updated Title&quot;)
})</pre>
<p>Each step does one thing. The sequence reads like a user story. And if the assertion fails, you can see exactly which action preceded the broken state.</p>
<p>This is also where the context pipeline pattern earns its keep. Every step receives context and returns context, so you can add or remove steps in the middle without restructuring the whole test:</p>
<pre>it(&quot;should show confirmation after submitting&quot;, {
  given_no_content() |&gt;
    given_an_authenticated_user() |&gt;
    when_i_submit_entry_with_all_required_fields() |&gt;
    then_entry_is_submitted()
})</pre>
<p>The pipeline works because every step function — Given, When, or Then — has the same contract: receive <code>context</code>, return <code>context</code>. That uniformity is what lets pipe operator connect them.</p>
<p>Here is the pattern on its own, with no Shiny involved. The When steps are thin — each one just calls a driver method and returns context:</p>
<pre># A minimal driver that records what it was asked to do
MockDriver &lt;- function() {
  form &lt;- NULL
  submitted &lt;- FALSE
  list(
    fill_required_fields = function(url, title) {
      form &lt;&lt;- list(url = url, title = title)
    },
    submit = function() {
      submitted &lt;&lt;- TRUE
    },
    get_form = function() form,
    was_submitted = function() submitted
  )
}

# When steps are thin: call a driver method, return context
when_i_fill_in_the_form &lt;- function(context, url, title) {
  context$driver$fill_required_fields(url = url, title = title)
  context
}

when_i_submit_the_form &lt;- function(context) {
  context$driver$submit()
  context
}

ctx &lt;- new.env(parent = emptyenv())
ctx$driver &lt;- MockDriver()

ctx |&gt;
  when_i_fill_in_the_form(url = &quot;https://example.com&quot;, title = &quot;Example&quot;) |&gt;
  when_i_submit_the_form()</pre>
<p>The step functions contain no UI details — those live entirely inside <code>MockDriver</code>. Swapping the driver is all it takes to point the same steps at a different implementation.</p>
<h2 id="testing-error-paths">Testing Error Paths</h2>
<p>User interactions that trigger failures are still described with When steps. The step describes what the user attempted, not whether it succeeded.</p>
<pre>it(&quot;should require all required fields&quot;, {
  given_no_content() |&gt;
    given_an_authenticated_user() |&gt;
    when_i_submit_entry_with_missing_required_fields() |&gt;
    then_i_am_prompted_to_provide_required_fields()
})</pre>
<p>The When step looks identical to the happy-path version — which is the point. The user tried to submit with missing fields. The difference lives in the Then step, which checks what the app communicated back to the user.</p>
<pre>when_i_submit_entry_with_missing_required_fields &lt;- function(context) {
  .start_driver(context)
  context$driver$fill_required_fields(
    url = &quot;https://example.com/&quot;,
    title = &quot;&quot;  # Missing required field
  )
  context
}</pre>
<p>You don’t need to create special “error path” When steps with names like <code>when_i_incorrectly_submit()</code>. Name them after what the user did, not after whether it was correct. The Then step will check whether the app responded correctly to that action.</p>
<h2 id="parameterising-when-steps">Parameterising When Steps</h2>
<p>Sometimes you want to test multiple variations of the same action. Rather than creating a separate step for each variation, make the step accept parameters:</p>
<pre>when_i_submit_entry_with_title &lt;- function(context, title) {
  .start_driver(context)
  context$driver$fill_required_fields(
    url = &quot;https://example.com/&quot;,
    title = title
  )
  context
}</pre>
<p>Now you can express different scenarios clearly:</p>
<pre>it(&quot;should reject titles that are too short&quot;, {
  given_no_content() |&gt;
    given_an_authenticated_user() |&gt;
    when_i_submit_entry_with_title(title = &quot;ab&quot;) |&gt;
    then_i_am_prompted_to_provide_required_fields()
})

it(&quot;should accept titles of the minimum length&quot;, {
  given_no_content() |&gt;
    given_an_authenticated_user() |&gt;
    when_i_submit_entry_with_title(title = &quot;abc&quot;) |&gt;
    then_there_are_entries(n = 1)
})</pre>
<p>Use parameters when the difference between scenarios is data, not behavior. Keep the step name stable; let the arguments express the variation.</p>
<hr>
<h3 id="the-cucumber-way">The Cucumber Way</h3>
<p><a href="https://github.com/jakubsob/cucumber" rel="nofollow" target="_blank">cucumber</a> allows you to write multiple variants of scenarios with <code>Scenario Outline</code> and <code>Examples</code> keywords.</p>
<p>In a cucumber feature file this scenario would look like this:</p>
<pre>Feature: Data submission form validation
  Scenario Outline: Submitting entries with different title lengths
    Given no content
    And an authenticated user
    When I submit an entry with title &lt;title&gt;
    Then I am prompted to provide required fields
    Examples:
      | title |
      | &quot;ab&quot;  |
      | &quot;abc&quot; |</pre>
<p>In our implementation we don’t have a built-in way to express scenario outlines, but we can achieve the same effect by writing multiple scenarios that call the same parameterised When step.</p>
<hr>
<h2 id="signs-a-when-step-is-too-complex">Signs a When Step Is Too Complex</h2>
<p>A When step that runs more than a few lines of logic is usually trying to do too much.</p>
<ol>
<li><strong>If a step is setting state</strong> — checking whether something exists before interacting with it, or conditionally taking different actions — that logic belongs in Given steps or in the driver implementation, not the When step itself.</li>
<li><strong>If a step has branching logic</strong> — doing one thing when a flag is set and another when it isn’t — split it into two steps with clear names.</li>
<li><strong>If a step is making assertions</strong> — checking that something happened mid-flow — move those checks to Then steps. A When step should interact and nothing more. Mixing assertions into When steps makes failures ambiguous: you can’t tell whether the setup was wrong or the outcome was wrong. There might be exceptions to assert whether we’re on the right path in a multi-step flow – to fail eagerly – but generally, try to keep assertions in Then steps.</li>
</ol>
<h2 id="the-pattern-in-practice">The Pattern in Practice</h2>
<p>Here’s the full shape of a specification using the When steps we’ve built:</p>
<pre>describe(&quot;data submission&quot;, {
  it(&quot;should submit entry and send notification&quot;, {
    given_no_content() |&gt;
      given_an_authenticated_user() |&gt;
      given_email_service_is_available() |&gt;
      when_i_submit_entry_with_all_required_fields() |&gt;
      then_entry_is_submitted() |&gt;
      then_email_notification_is_sent()
  })

  it(&quot;should require all required fields&quot;, {
    given_no_content() |&gt;
      given_an_authenticated_user() |&gt;
      when_i_submit_entry_with_missing_required_fields() |&gt;
      then_i_am_prompted_to_provide_required_fields()
  })

  it(&quot;should allow editing a submitted entry&quot;, {
    given_existing_entries() |&gt;
      given_an_authenticated_user() |&gt;
      when_i_open_the_first_entry() |&gt;
      when_i_update_the_title(title = &quot;Updated Title&quot;) |&gt;
      when_i_submit_the_form() |&gt;
      then_the_entry_has_title(&quot;Updated Title&quot;)
  })
})</pre>
<p>The specifications read naturally. None of them mention input IDs, button selectors, or shinytest2. Those details live in the driver, hidden away from the scenarios they serve.</p>
<hr>
<p>Note how specifications don’t even reveal it’s a Shiny app. They don’t reveal how we interact with it — <code>shinytest2</code>, <code>selenider</code>, anything goes. It’s all hidden and irrelevant to the story we’re telling. You can swap the internals and keep the same specifications true. You can even swap the system implementation entirely, and as long as it still satisfies the same business requirements, the specifications don’t need touching. It’s live and executable — not a list of tickets in an external system no one reads.</p>
<p>That’s exactly the opposite of what happens when we write tests that are tightly coupled to the UI structure.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>When steps model user behavior, not UI implementation.</p>
<p>That distinction is what keeps specifications readable days, weeks, months later, when the form has been refactored, button IDs have changed, and nobody remembers what the original implementation looked like. Keep steps focused on one action. Put the implementation details in the driver. Use domain language, not UI language.</p>
<p>Specifications written this way tend to survive refactors you haven’t thought of yet.</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://jakubsobolewski.com/blog/bdd-shiny-when"> jakub::sobolewski</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/05/behavior-driven-development-in-r-shiny-modeling-user-behavior-with-when-steps/">Behavior-Driven Development in R Shiny: Modeling User Behavior with When Steps</a>]]></content:encoded>
					
		
		<enclosure url="https://jakubsobolewski.com/rss-image.png" length="0" type="image/png" />

		<post-id xmlns="com-wordpress:feed-additions:1">401486</post-id>	</item>
		<item>
		<title>Repost: ctrlvee: Extract external R code and insert inline</title>
		<link>https://www.r-bloggers.com/2026/05/repost-ctrlvee-extract-external-r-code-and-insert-inline/</link>
		
		<dc:creator><![CDATA[Stephen Turner]]></dc:creator>
		<pubDate>Fri, 22 May 2026 13:34:48 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://www.r-bloggers.com/?guid=51c6de9f15cc0593fa890ee28b39bc84</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Reposted from the original at https://blog.stephenturner.us/p/ctrlvee-extract-external-r-code-insert-inline-positron-rstudio-addin. Ever find yourself looking through a pkgdown page or a Quarto<br />
book, copying and pasting code chunks from your brow...</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/05/repost-ctrlvee-extract-external-r-code-and-insert-inline/">Repost: ctrlvee: Extract external R code and insert inline</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="http://gettinggeneticsdone.blogspot.com/2026/05/ctrlvee-extract-external-r-code-insert-inline-positron-rstudio-addin.html"> Getting Genetics Done</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><mark><b>Reposted from the original at <a href="https://blog.stephenturner.us/p/ctrlvee-extract-external-r-code-insert-inline-positron-rstudio-addin" rel="nofollow" target="_blank">https://blog.stephenturner.us/p/ctrlvee-extract-external-r-code-insert-inline-positron-rstudio-addin</a>.</b></mark> </p><p><span>Ever find yourself looking through a pkgdown page or a Quarto 
book, copying and pasting code chunks from your browser into your IDE? I
 do, and it’s a minor annoyance.</span><span data-state="closed" style="min-width: 0px;"><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" href="https://blog.stephenturner.us/p/ctrlvee-extract-external-r-code-insert-inline-positron-rstudio-addin#footnote-1" id="footnote-anchor-1" rel="nofollow" target="_blank">1</a></span></p><p><span>My friend and colleague VP Nagraj published a new R package called </span><strong>ctrlvee</strong><span> that makes this a lot easier.</span></p><ul><li><p><strong><span>CRAN: </span><a href="https://cran.r-project.org/package=ctrlvee" rel="nofollow" target="_blank">https://cran.r-project.org/package=ctrlvee</a></strong></p></li><li><p><strong><span>GitHub: </span><a href="https://github.com/vpnagraj/ctrlvee" rel="nofollow" target="_blank">https://github.com/vpnagraj/ctrlvee</a></strong></p></li></ul><p><span>It
 does one thing. Put your cursor anywhere in an R script in Positron or 
RStudio, call the add-in, provide a URL, and a few milliseconds later 
you’ll have all the code from that page in your editor, separated by 
chunk boundaries (along with some metadata and a note to </span><a href="https://blog.stephenturner.us/p/pick-a-license-not-any-license" rel="nofollow" target="_blank">check the license!</a><span>).</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img can-restack" data-component-name="Image2ToDOM" href="https://substackcdn.com/image/fetch/$s_!7WR6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbfbd671-9318-421e-93b9-8ea4f5ef9e9a_1410x782.png" rel="nofollow" target="_blank"><div class="image2-inset"><picture><source type="image/webp"></source><img loading="lazy" alt="" class="sizing-large" data-attrs="{"src":"https://substack-post-media.s3.amazonaws.com/public/images/dbfbd671-9318-421e-93b9-8ea4f5ef9e9a_1410x782.png","srcNoWatermark":null,"fullscreen":false,"imageSize":"large","height":782,"width":1410,"resizeWidth":1200,"bytes":182436,"alt":null,"title":null,"type":"image/png","href":null,"belowTheFold":false,"topImage":true,"internalRedirect":"https://blog.stephenturner.us/i/197973095?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbfbd671-9318-421e-93b9-8ea4f5ef9e9a_1410x782.png","isProcessing":false,"align":"center","offset":false}" height="665 .531914893617" src="https://i1.wp.com/substackcdn.com/image/fetch/$s_!7WR6!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbfbd671-9318-421e-93b9-8ea4f5ef9e9a_1410x782.png?resize=450%2C665&#038;ssl=1" width="450" data-recalc-dims="1" /></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"></div></div></div></a></figure></div><p><span>The package README provides a demonstration using the “Data Validation and QA” chapter of my </span><em>Data Science Team Training</em><span> book (</span><strong><a href="https://dstt.stephenturner.us/" rel="nofollow" target="_blank">dstt.stephenturner.us</a></strong><span>).</span></p><ol><li><p><span>Install the package: </span><code>install.packages(&quot;ctrlvee&quot;)</code></p></li><li><p><span>Run the add-in. In Positron you’ll open the command palette, search for Run RStudio Addin, then </span><em>extract external R code and insert inline</em><span>. You’ll get a modal asking you for a URL. </span></p></li><li><p><span>Paste one in. E.g., </span><strong>https://dstt.stephenturner.us/validation.html</strong></p></li><li><p>The R code from the website appears in your editor <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></li></ol><p>Here’s a demo.</p><div class="videoScrollTarget-SzB20Y" data-component-name="VideoEmbedPlayer" id="media-32b2a9b2-d551-43a0-bffc-647ab75b032e"><div class="videoEmbed-_FycLU"><div aria-label="Video player" class="with-preview video-player-with-background video-player-wrapper" role="region"><div class="video-player video-player video-player-with-background videoPlayer-vlcedM" style="padding-bottom: 60%;"><video class="video-P2qgwZ" controls="" crossorigin="anonymous" data-video-id="32b2a9b2-d551-43a0-bffc-647ab75b032e" poster="https://substack-video.s3.amazonaws.com/video_upload/post/197973095/32b2a9b2-d551-43a0-bffc-647ab75b032e/transcoded-00001.png?refresh=Fri May 22 2026 09:32:43 GMT-0400 (Eastern Daylight Time)" preload="metadata"></video><div class="pencraft pc-position-absolute pc-reset buttonContainer-tH3LP9 video-player-button"></div></div></div></div></div><div class="subscribe-widget is-signed-up is-fully-subscribed" data-component-name="SubscribeWidget"><div class="pencraft pc-reset button-wrapper"><div class="pencraft pc-display-flex pc-justifyContent-center pc-reset"></div></div></div><p><span>Here’s what the extracted/inserted code looks like, from </span><a href="https://dstt.stephenturner.us/validation.html" rel="nofollow" target="_blank">this source</a><span>.</span></p><pre># -----------------------------------------------------------------
# Chunks fetched by ctrlvee from: https://dstt.stephenturner.us/validation.html
# Strategy: Rendered HTML page
# Date: 2026-05-16 05:14:44
# Chunks: 8
# NOTE: Check the source license before reusing this code.
# -----------------------------------------------------------------

flu &lt;- data.frame(
    week = c(1, 2, 3, 4, 4),
    county = c(&quot;Fairfax&quot;, &quot;Arlington&quot;, NA, &quot;Loudoun&quot;, &quot;Loudoun&quot;),
    disease = c(&quot;Flu&quot;, &quot;Flu&quot;, &quot;Flu&quot;, &quot;Flu&quot;, &quot;Flu&quot;),
    cases = c(23, 41, 18, -5, 12),
    rate = c(2.1, 3.8, 1.6, NA, 1.1)
)

flu

# ---- chunk boundary ----

if (any(flu$cases &lt; 0, na.rm = TRUE)) {
    stop(&quot;Negative case counts detected. Inspect raw data before proceeding.&quot;)
}

# ---- chunk boundary ----

stopifnot(
    &quot;Negative case counts&quot; = all(flu$cases &gt;= 0, na.rm = TRUE),
    &quot;Missing county values&quot; = !anyNA(flu$county),
    &quot;Duplicate records&quot; = !anyDuplicated(flu[, c(&quot;week&quot;, &quot;county&quot;)])
)

# ---- chunk boundary ----

install.packages(&quot;pointblank&quot;)

# ---- chunk boundary ----

library(pointblank)

agent &lt;- create_agent(tbl = flu, label = &quot;Weekly flu surveillance&quot;) |&gt;
    col_vals_gte(
        columns = cases,
        value = 0,
        label = &quot;Case counts must be non-negative&quot;
    ) |&gt;
    col_vals_not_null(
        columns = c(week, county),
        label = &quot;Week and county cannot be missing&quot;
    ) |&gt;
    rows_distinct(
        columns = c(week, county),
        label = &quot;No duplicate week/county records&quot;
    ) |&gt;
    interrogate()

agent

# ---- chunk boundary ----

create_agent(tbl = flu, label = &quot;Weekly flu surveillance — extended&quot;) |&gt;
    col_is_numeric(
        columns = c(cases, rate),
        label = &quot;Case count and rate must be numeric&quot;
    ) |&gt;
    col_vals_in_set(
        columns = disease,
        set = c(&quot;Flu&quot;, &quot;COVID-19&quot;, &quot;RSV&quot;),
        label = &quot;Disease must be from the approved list&quot;
    ) |&gt;
    col_vals_between(
        columns = week,
        left = 1,
        right = 52,
        label = &quot;Week must be between 1 and 52&quot;
    ) |&gt;
    col_vals_gte(
        columns = rate,
        value = 0,
        na_pass = TRUE,
        label = &quot;Rate must be non-negative (NAs allowed)&quot;
    ) |&gt;
    interrogate()

# ---- chunk boundary ----

if (!all_passed(agent)) {
    stop(&quot;Data validation failed. Review the agent report before proceeding.&quot;)
}

# ---- chunk boundary ----

library(readr)
library(pointblank)

flu &lt;- read_csv(&quot;data/flu-2024.csv&quot;)

# Validate immediately after reading
agent &lt;- create_agent(tbl = flu, label = &quot;flu-2024 validation&quot;) |&gt;
    col_vals_gte(columns = cases, value = 0, label = &quot;No negative counts&quot;) |&gt;
    col_vals_not_null(columns = c(week, county), label = &quot;No missing keys&quot;) |&gt;
    rows_distinct(columns = c(week, county), label = &quot;No duplicate records&quot;) |&gt;
    interrogate()

if (!all_passed(agent)) {
    stop(&quot;Validation failed — see agent report above.&quot;)
}</pre><div class="subscribe-widget is-signed-up is-fully-subscribed" data-component-name="SubscribeWidget"><div class="pencraft pc-reset button-wrapper"><div class="pencraft pc-display-flex pc-justifyContent-center pc-reset"></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><br /></div><p> </p><div class="blogger-post-footer">Getting Genetics Done by Stephen Turner is licensed under a Creative Commons Attribution (CC BY) License.</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="http://gettinggeneticsdone.blogspot.com/2026/05/ctrlvee-extract-external-r-code-insert-inline-positron-rstudio-addin.html"> Getting Genetics Done</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/05/repost-ctrlvee-extract-external-r-code-and-insert-inline/">Repost: ctrlvee: Extract external R code and insert inline</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401398</post-id>	</item>
		<item>
		<title>[R] How to use ggpattern to add patterns to ggplot2 plots</title>
		<link>https://www.r-bloggers.com/2026/05/r-how-to-use-ggpattern-to-add-patterns-to-ggplot2-plots/</link>
		
		<dc:creator><![CDATA[R on Zhenguo Zhang&#039;s Blog]]></dc:creator>
		<pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Zhenguo Zhang's Blog https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/ -</p>
<p>Adding patterns to plots is a great way to improve accessibility (making plots colorblind-friendly) and to add an extra dimension of information. The ggpattern pac...</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/05/r-how-to-use-ggpattern-to-add-patterns-to-ggplot2-plots/">[R] How to use ggpattern to add patterns to ggplot2 plots</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://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/"> R on Zhenguo Zhang&#039;s 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>
Zhenguo Zhang&#8217;s Blog https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/ &#8211;


<p>Adding patterns to plots is a great way to improve accessibility (making plots colorblind-friendly) and to add an extra dimension of information. The <code>ggpattern</code> package provides a rich set of tools to achieve this in <code>ggplot2</code>.</p>
<div id="basic-example-with-mtcars" class="section level2">
<h2>Basic Example with mtcars</h2>
<p>Let’s start with a barplot using the <code>mtcars</code> dataset. We will map <code>cyl</code> to the x-axis, <code>mpg</code> to the y-axis, use <code>gear</code> for the fill color, and <code>am</code> for the pattern.</p>
<pre># Prepare data
df &lt;- mtcars %&gt;%
  mutate(
    cyl = factor(cyl),
    gear = factor(gear),
    am = factor(am, labels = c(&quot;Automatic&quot;, &quot;Manual&quot;))
  )

# Create barplot
ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &quot;identity&quot;,
    position = &quot;dodge&quot;,
    color = &quot;black&quot;,
    pattern_fill = &quot;black&quot;,
    pattern_spacing = 0.02,
    pattern_key_scale_factor = 1.5
  ) +
  scale_pattern_manual(values = c(&quot;stripe&quot;, &quot;crosshatch&quot;)) +
  labs(
    title = &quot;MPG by Cylinders, Gear, and Transmission&quot;,
    subtitle = &quot;Patterns representing transmission type&quot;,
    x = &quot;Cylinders&quot;,
    y = &quot;Miles Per Gallon (MPG)&quot;,
    fill = &quot;Gears&quot;,
    pattern = &quot;Transmission&quot;
  ) +
  theme_minimal()</pre>
<p><img src="https://i0.wp.com/fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/basic-example-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="exploring-key-parameters" class="section level2">
<h2>Exploring Key Parameters</h2>
<p>The <code>ggpattern</code> package allows for fine-grained control over how patterns are rendered. Here are some of the most important parameters:</p>
<div id="pattern_key_scale_factor" class="section level3">
<h3>1. pattern_key_scale_factor</h3>
<p>This parameter scales the pattern density specifically in the legend. Without it, patterns in the legend can often be hard to see.
Reduce the value will make it easier fit into the legend keys.</p>
<pre>p_base &lt;- ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &quot;identity&quot;,
    position = &quot;dodge&quot;,
    color = &quot;black&quot;,
    pattern_spacing = 0.02
  ) +
  theme_minimal() +
  labs(title = &quot;Default vs Scaled Legend&quot;)

# Default legend
p1 &lt;- p_base + labs(subtitle = &quot;Default (key_scale_factor = 1)&quot;)

# Scaled legend
p2 &lt;- p_base + 
  geom_bar_pattern(
    aes(pattern = am),
    stat = &quot;identity&quot;,
    position = &quot;dodge&quot;,
    color = &quot;black&quot;,
    pattern_spacing = 0.02,
    pattern_key_scale_factor = 0.5
  ) +
  labs(subtitle = &quot;Scaled (key_scale_factor = 0.5)&quot;)

p1</pre>
<p><img src="https://i0.wp.com/fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/scale-factor-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
<pre>p2</pre>
<p><img src="https://i0.wp.com/fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/scale-factor-2.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="pattern_spacing-and-pattern_density" class="section level3">
<h3>2. pattern_spacing and pattern_density</h3>
<p><code>pattern_spacing</code> controls the distance between pattern elements (lower = denser), while <code>pattern_density</code> controls the coverage (higher = denser).</p>
<pre># Spacing effect
ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &quot;identity&quot;,
    position = &quot;dodge&quot;,
    pattern_spacing = 0.2, # Wider spacing
    pattern_key_scale_factor = 1.5
  ) +
  labs(title = &quot;Effect of pattern_spacing = 0.5 (Sparser)&quot;) +
  theme_minimal()</pre>
<p><img src="https://i0.wp.com/fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/spacing-density-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
<pre># Density effect
ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &quot;identity&quot;,
    position = &quot;dodge&quot;,
    pattern_density = 0.8, # Higher density
    pattern_key_scale_factor = 1.5
  ) +
  labs(title = &quot;Effect of pattern_density = 0.8 (Denser)&quot;) +
  theme_minimal()</pre>
<p><img src="https://i2.wp.com/fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/spacing-density-2.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="pattern_fill" class="section level3">
<h3>3. pattern_fill</h3>
<p><code>pattern_fill</code> sets the color of the pattern elements themselves.</p>
<pre>ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern = am),
    stat = &quot;identity&quot;,
    position = &quot;dodge&quot;,
    pattern_fill = &quot;white&quot;, # White pattern lines
    pattern_density = 0.5, # make it bigger so that the color is easier to see
    pattern_spacing = 0.02,
    pattern_key_scale_factor = 1.5
  ) +
  labs(title = &quot;Effect of pattern_fill = &#39;white&#39;&quot;) +
  theme_minimal()</pre>
<p><img src="https://i0.wp.com/fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/pattern-fill-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
<div id="pattern_shape" class="section level3">
<h3>4. pattern_shape</h3>
<p>For geometric patterns like <code>regular_polygon</code>, <code>pattern_shape</code> controls the number of sides.</p>
<pre>ggplot(df, aes(x = cyl, y = mpg, fill = gear)) +
  geom_bar_pattern(
    aes(pattern_shape=gear),
    pattern = &quot;regular_polygon&quot;,
    stat = &quot;identity&quot;,
    position = &quot;dodge&quot;,
    pattern_size = 0.5, # stroke line width
    pattern_density = 0.5, # increase the shape size
    pattern_spacing = 0.03,
    pattern_key_scale_factor = 1.5
  ) +
  scale_pattern_shape_manual(values = c(&quot;3&quot;=&quot;circle&quot;, &quot;4&quot;=&quot;convex6&quot;, &quot;5&quot;=&quot;square&quot;)) + # manually set shapes
  labs(title = &quot;Effect of mapping pattern_shape to gear&quot;) +
  theme_minimal()</pre>
<p><img src="https://i2.wp.com/fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/index_files/figure-html/pattern-shape-1.png?w=450&#038;ssl=1" alt="" data-recalc-dims="1" /></p>
</div>
</div>
<div id="troubleshooting" class="section level2">
<h2>Troubleshooting</h2>
<p>If you encounter issues installing or loading <code>ggpattern</code> on Linux, you may need to install the following system dependencies:</p>
<pre>sudo apt-get install libudunits2-dev
sudo apt install libproj22
sudo apt-get install libgdal-dev</pre>
</div>
- https://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/ - 
<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://fortune9.netlify.app/2026/05/22/r-how-to-use-ggpattern/"> R on Zhenguo Zhang&#039;s 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/05/r-how-to-use-ggpattern-to-add-patterns-to-ggplot2-plots/">[R] How to use ggpattern to add patterns to ggplot2 plots</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401411</post-id>	</item>
		<item>
		<title>Functions over Idioms &#8211; Writing R in Python with rfuns</title>
		<link>https://www.r-bloggers.com/2026/05/functions-over-idioms-writing-r-in-python-with-rfuns/</link>
		
		<dc:creator><![CDATA[Jonathan Carroll]]></dc:creator>
		<pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://jcarroll.com.au/2026/05/22/functions-over-idioms-rfuns/</guid>

					<description><![CDATA[<p>If you’ve read any of my past posts you know I like to program in several<br />
different languages, some of which I like more than others. Sometimes a problem<br />
calls for a particular language to be used, and with that comes adjusting one’s<br />
brain to thinking in that ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/05/functions-over-idioms-writing-r-in-python-with-rfuns/">Functions over Idioms – Writing R in Python with rfuns</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://jcarroll.com.au/2026/05/22/functions-over-idioms-rfuns/"> rstats on Irregularly Scheduled 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>If you’ve read any of my past posts you know I like to program in several
different languages, some of which I like more than others. Sometimes a problem
calls for a particular language to be used, and with that comes adjusting one’s
brain to thinking in that language and using the appropriate idioms to leverage
that language’s features. But what if I don’t want to?</p>
<div class="float">
<img src="https://i2.wp.com/jcarroll.com.au/2026/05/22/functions-over-idioms-rfuns/images/IDontWantTo.gif?w=450&#038;ssl=1" alt="I don’t want to" data-recalc-dims="1" />
<div class="figcaption">I don’t want to</div>
</div>
<p>The line between R and Python has been heavily blurred the last few years,
particularly with <a href="https://rstudio.github.io/reticulate/" rel="nofollow" target="_blank">{reticulate}</a> enabling
us to use Python within R code, RStudio rebranding as <a href="https://posit.co/" rel="nofollow" target="_blank">Posit</a>
and taking on a strong Python development effort, releasing
<a href="https://posit.co/products/ide/positron" rel="nofollow" target="_blank">Positron</a> as a multi-language IDE, and
<a href="https://quarto.org/" rel="nofollow" target="_blank">Quarto</a> being a multi-language rethink of Rmarkdown.</p>
<p>I occasionally <em>need</em> to use Python directly &#8211; an SDK wrapping an API exists and
I don’t particularly want to spend a lot of time writing my own R version,
especially before I know what I want to get out of the endpoints. At this point
I tend to bump up against my muscle-memory from R and try to use functions I’m
familiar with from R, but which don’t actually exist in Python. Now, that might
sometimes be because the pattern I’m trying to encode simply has a different name
in Python; instead of an <code>sapply(x, f)</code></p>
<pre>sapply(c(2, 3, 4, 5), \(x) x ^ 2)
## [1]  4  9 16 25</pre>
<p>I should reach for <code>map</code>, in which case I am reminded that this produces a lazy
iterator that doesn’t show me the results</p>
<pre>map(lambda x: x ** 2, [2, 3, 4, 5])
## &lt;map object at 0x10d7fbee0&gt;</pre>
<p>and so I need to wrap it into a list to get the values out</p>
<pre>list(map(lambda x: x ** 2, [2, 3, 4, 5]))
## [4, 9, 16, 25]</pre>
<p>Or, I could use a list comprehension which <em>isn’t</em> lazy</p>
<pre>[v ** 2 for v in [2, 3, 4, 5]]
## [4, 9, 16, 25]</pre>
<p>That’s the <em>idiom</em> that I should be reaching for. Sure.</p>
<p>Other times there’s a package I need to use and a slightly different way of
approaching the problem. In R I love the <code>table()</code> function for getting
histogram-like counts of the unique values of a vector</p>
<pre>table(c(&quot;b&quot;, &quot;a&quot;, &quot;c&quot;, &quot;a&quot;, &quot;b&quot;, &quot;a&quot;))
## 
## a b c 
## 3 2 1</pre>
<p>which in Python looks like</p>
<pre>from collections import Counter

sorted(Counter([&quot;b&quot;, &quot;a&quot;, &quot;c&quot;, &quot;a&quot;, &quot;b&quot;, &quot;a&quot;]).items())
## [(&#39;a&#39;, 3), (&#39;b&#39;, 2), (&#39;c&#39;, 1)]</pre>
<p>Probably Pythonistas remember that idiom and the package to import and the
<code>.items()</code> extractor and the fact that they maybe want to sort the result. But I
kept coming back to a question I ask myself: <em>what if I don’t want to</em>? Why is
there not a function that wraps this idiom? If there was, why not just call it
“table”? Admittedly, it’s far from the catchiest, most memorable, or most useful
name, but it’s immediately recognisable to an R user (ditto for “sapply”).</p>
<p>One approach I considered here was to just call R from Python. That <em>can</em> be done,
but I doubt I or anyone else wants to deal with that every time we want to iterate
over a list. There’s a package on the Python package index which seems to support
this nicely: <a href="https://pypi.org/project/r-functions/" class="uri" rel="nofollow" target="_blank">https://pypi.org/project/r-functions/</a> but it’s wrappers around
individual R files, via RScript. I’m thinking more along the lines of ‘native
Python with an R interface’.</p>
<p>Python is an object-oriented language, but it <em>has</em> functions, so why not make one</p>
<pre>from collections import Counter

def table(x):
    return dict(sorted(Counter(x).items()))

table([&quot;b&quot;, &quot;a&quot;, &quot;c&quot;, &quot;a&quot;, &quot;b&quot;, &quot;a&quot;])
## {&#39;a&#39;: 3, &#39;b&#39;: 2, &#39;c&#39;: 1}
def sapply(x, func):
    return [func(v) for v in x]
  
sapply([2, 3, 4, 5], lambda x: x ** 2)
## [4, 9, 16, 25]</pre>
<p>and have a nicer function interface to apply these idioms? I thought about this
a bit longer, and realised there’s <strong>lots</strong> of functions I use in R that I wish
I could use in Python. An idiom for finding the index of elements of a ‘vector’
(list in Python) which are true (<code>TRUE</code> in R, <code>True</code> in Python) is</p>
<pre>[i for i, v in enumerate(x) if v]</pre>
<p>but I just want to call <code>which(x)</code></p>
<pre>which(c(FALSE, FALSE, TRUE, FALSE , TRUE))
## [1] 3 5</pre>
<p>so why not define this</p>
<pre>def which(x):
    return [i for i, v in enumerate(x) if v]
  
which([False, False, True, False, True])
## [2, 4]</pre>
<p>(remembering that Python is 0-indexed).</p>
<p>How far could one take this? Quite a long way!</p>
<p>I thought more about what differences would need to be accounted for, and one that
immediately came to mind was that R is vectorised. If I was to recreate R’s
character counting function <code>nchar(s)</code> as essentially <code>len(s)</code>, I’d need to consider
whether I wanted it to work on a single string or a ‘vector’ of strings</p>
<p>In R:</p>
<pre>nchar(c(&quot;these&quot;, &quot;all&quot;, &quot;have&quot;, &quot;different&quot;, &quot;lengths&quot;))
## [1] 5 3 4 9 7</pre>
<p>But in Python, <code>len()</code> expects a single value, so it calculates the length of
the list</p>
<pre>len([&quot;these&quot;, &quot;all&quot;, &quot;have&quot;, &quot;different&quot;, &quot;lengths&quot;])
## 5</pre>
<p>The ‘proper’ way to do it is to map over the list</p>
<pre>[len(s) for s in [&quot;these&quot;, &quot;all&quot;, &quot;have&quot;, &quot;different&quot;, &quot;lengths&quot;]]
## [5, 3, 4, 9, 7]</pre>
<p>but again, why do I need to use an idiom for this? What if I just made a decorator
to change a regular function to a vectorised one by applying this list
comprehension internally when it’s passed a list (or a tuple), and which otherwise
just evaluates the function with the argument?</p>
<pre>import functools

def make_vec(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if isinstance(args[0], (list, tuple)):
            return [func(xi, *args[1:], **kwargs) for xi in args[0]]
        return func(*args, **kwargs)
    return wrapper

@make_vec
def my_len(s):
    return len(s)

my_len([&quot;these&quot;, &quot;all&quot;, &quot;have&quot;, &quot;different&quot;, &quot;lengths&quot;])
## [5, 3, 4, 9, 7]</pre>
<p>and I could name it… “nchar”!</p>
<p>The other use-case that came to mind was <a href="https://fosstodon.org/deck/@eliocamp@mastodon.social/116531644286276585" rel="nofollow" target="_blank">Elio venting</a>
(and referencing a post to which <a href="https://jcarroll.com.au/2025/12/05/haskell-is-a-great-language-for-data-science/" rel="nofollow" target="_blank">I also wrote a sort of response</a>)
that they needed to list the files in the current directory</p>
<blockquote class="mastodon-embed" data-embed-url="https://mastodon.social/@eliocamp/116531644254157709/embed" style="background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;">
<a href="https://mastodon.social/@eliocamp/116531644254157709" style="align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;" rel="nofollow" target="_blank"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 79 75"><path d="M63 45.3v-20c0-4.1-1-7.3-3.2-9.7-2.1-2.4-5-3.7-8.5-3.7-4.1 0-7.2 1.6-9.3 4.7l-2 3.3-2-3.3c-2-3.1-5.1-4.7-9.2-4.7-3.5 0-6.4 1.3-8.6 3.7-2.1 2.4-3.1 5.6-3.1 9.7v20h8V25.9c0-4.1 1.7-6.2 5.2-6.2 3.8 0 5.8 2.5 5.8 7.4V37.7H44V27.1c0-4.9 1.9-7.4 5.8-7.4 3.5 0 5.2 2.1 5.2 6.2V45.3h8ZM74.7 16.6c.6 6 .1 15.7.1 17.3 0 .5-.1 4.8-.1 5.3-.7 11.5-8 16-15.6 17.5-.1 0-.2 0-.3 0-4.9 1-10 1.2-14.9 1.4-1.2 0-2.4 0-3.6 0-4.8 0-9.7-.6-14.4-1.7-.1 0-.1 0-.1 0s-.1 0-.1 0 0 .1 0 .1 0 0 0 0c.1 1.6.4 3.1 1 4.5.6 1.7 2.9 5.7 11.4 5.7 5 0 9.9-.6 14.8-1.7 0 0 0 0 0 0 .1 0 .1 0 .1 0 0 .1 0 .1 0 .1.1 0 .1 0 .1.1v5.6s0 .1-.1.1c0 0 0 0 0 .1-1.6 1.1-3.7 1.7-5.6 2.3-.8.3-1.6.5-2.4.7-7.5 1.7-15.4 1.3-22.7-1.2-6.8-2.4-13.8-8.2-15.5-15.2-.9-3.8-1.6-7.6-1.9-11.5-.6-5.8-.6-11.7-.8-17.5C3.9 24.5 4 20 4.9 16 6.7 7.9 14.1 2.2 22.3 1c1.4-.2 4.1-1 16.5-1h.1C51.4 0 56.7.8 58.1 1c8.4 1.2 15.5 7.5 16.6 15.6Z" fill="currentColor"/></svg>
<div style="color: #787588; margin-top: 16px;">
Post by <span class="citation">@eliocamp</span><span class="citation">@mastodon.social</span>
</div>
<div style="font-weight: 500;">
View on Mastodon
</div>
</a>
</blockquote>
<script data-allowed-prefixes="https://mastodon.social/" async src="https://mastodon.social/embed.js"></script>
<p>with the idiom</p>
<pre>import os

[os.path.join(path, f) for f in os.listdir(path)]</pre>
<p>The supplied suggestions included</p>
<pre>from pathlib import Path

list(Path(path).iterdir())</pre>
<p>(just rolls off the tongue, doesn’t it?) which returns a list of <code>PosixPath()</code>
objects and is hardly easy to parse visually.</p>
<p>So, why not have a function?!?</p>
<pre>import os

def list_files(path):
    return [os.path.join(path, f) for f in os.listdir(path)]

path = &quot;path/to/files&quot;

list_files(path)
## [&#39;path/to/files/file1.txt&#39;, &#39;path/to/files/file2.txt&#39;, &#39;path/to/files/file3.csv&#39;]</pre>
<p>I would have liked to call this <code>list.files()</code> but, since Python strictly uses
the dot for method calling, it can’t be that.</p>
<p>This then raises the question of “should I support the arguments already in the R
functions?” In this case, should it support a <code>recursive</code> argument? Yes, that
adds complexity, but it’s surely do-able. At this point I reached for some AI
assistance and had Claude help me to implement as many functions as we could think
of, supporting as many common arguments as possible. This involved extending the
decorator to support vectorising other arguments (which also need to be careful
about dots).</p>
<p>On testing it out, it looked like we had something viable.</p>
<p>One last piece I wanted to support, though: the <code>which()</code> example above extracts
the elements of a <em>logical</em> vector which are <code>True</code>, but in order to build that vector
in the first place, I would naturally leverage R’s vectorisation as an array
language. The two steps involved here are to first compute the comparison resulting
in a logical vector, then to use <code>which()</code> to identify the indices of those which are
true</p>
<pre>which(c(&quot;c&quot;, &quot;b&quot;, &quot;a&quot;, &quot;c&quot;, &quot;a&quot;, &quot;b&quot;) == &quot;a&quot;)
## [1] 3 5</pre>
<p>The vectorisation decorator above doesn’t help here, because it’s at the point of
<code>==</code> that we want to vectorise</p>
<pre>[&#39;c&#39;, &#39;b&#39;, &#39;a&#39;, &#39;c&#39;, &#39;a&#39;, &#39;b&#39;] == &#39;a&#39;
## False</pre>
<p>This is <code>False</code> because the character <code>'a'</code> is not equal to the given list.</p>
<p>The appropriate idiom is once again to use a list comprehension</p>
<pre>which(x == &#39;a&#39; for x in [&#39;c&#39;, &#39;b&#39;, &#39;a&#39;, &#39;c&#39;, &#39;a&#39;, &#39;b&#39;])
## [2, 4]</pre>
<p>The solution I’m fond of is to create a new ‘Vec’ class which wraps binary operators
with a list comprehension, again abstracting away this detail. This means
implementing <code>__eq__</code>, <code>__add__</code>, <code>__and__</code> and lots of other binary operations,
but with that, and a wrapper to create such an object, the comparison operators
can be vectorised</p>
<pre>vals = vec([&#39;c&#39;, &#39;b&#39;, &#39;a&#39;, &#39;c&#39;, &#39;a&#39;, &#39;b&#39;])
which(vals == &#39;a&#39;)
## [2, 4]</pre>
<p>Not pristine, but quite clean, if you ask me.</p>
<p>With all these pieces in place, adding implementations for common base R functions
including most arguments and a way to vectorise lists, I wrapped everything up
into a Python package (my first) to learn how to do it.</p>
<p>The workflow isn’t particularly painful, with my biggest complication being
different versions of Python supporting different requirements in <code>pyproject.toml</code>,
and so some GitHub Actions are failing because of that.</p>
<p>As part of building out the implementations I had Claude add tests for each of the
functions with some expected values &#8211; if I <em>do</em> want to improve some of the idioms
internally, I want to ensure I don’t change the values produced. That works for
having any testing at all, but how can I be sure that I’m reproducing what I
would get if I was working in R? One option was to just run all of the test
functions by hand and confirm that the values look similar enough, accounting for
list vs vector and 0 vs 1 indexing. Instead, Claude managed to write an adaptor
for <code>pytest</code> which does the realignment of e.g. <code>list_files</code> to <code>list.files</code>
(and similarly for arguments), realigns the indexing where needed, and runs all
existing tests directly in R via <code>rpy2</code> (skipping over some for which I don’t
have tests yet). I’m disabling automated testing of this because I suspect it
could get flaky dealing with both R <em>and</em> Python on GitHub Actions, but I can
confirm that all the current tests pass.</p>
<p>I wanted to have a documentation website similar to what we have via {pkgdown} and
came across <a href="https://github.com/machow/quartodoc" rel="nofollow" target="_blank">quartodoc</a> which is what the
<a href="https://rstudio.github.io/pins-python/" rel="nofollow" target="_blank">Python version of {pins} uses</a>. Getting
that to work required downgrading a specific Python dependency, but was otherwise
painless.</p>
<p>I have a working package locally &#8211; how do I share it? This seemed like the perfect
opportunity to learn what the release process looks like for Python. I have a
handful of packages on CRAN and one on Bioconductor, and the process there is
far from frictionless, with the side-effect that there’s some trust you can place
on the interoperability of packages and minimal (automated) code checking. While
Python is more ‘wild west’ in terms of what can be uploaded, it’s really nice to see
that they do have an <a href="https://test.pypi.org/" rel="nofollow" target="_blank">entirely separate test server</a>
where you can upload your package and see how it looks. I’m reminded of the quote</p>
<blockquote>
<p>Everybody has a testing environment. Some people are lucky enough to have a totally separate environment to run production in.</p>
</blockquote>
<p>Given that it’s not currently possible to run 100% of the CRAN checks locally
(and even some that you <em>can</em> give a different result to what’s on their systems)
this does make me a little jealous. I wonder whether the decrease in load from
rejecting failing submissions would offset supporting a test submission server.</p>
<p>All went well pushing to the test server (via an authentication key) and I managed
to build up the courage to push to the production instance…
<a href="https://pypi.org/project/rfuns/" rel="nofollow" target="_blank">it’s live!</a></p>
<div class="float">
<img src="https://i2.wp.com/jcarroll.com.au/2026/05/22/functions-over-idioms-rfuns/images/rfuns_logo_small.png?w=300&#038;ssl=1" alt="rfuns logo - R functions in Python… are fun" data-recalc-dims="1" />
<div class="figcaption">rfuns logo &#8211; R functions in Python… are fun</div>
</div>
<p>and the <a href="https://jonocarroll.github.io/rfuns/" rel="nofollow" target="_blank">documentation site</a> isn’t too bad,
either (in my opinion).</p>
<p>This means that you can now run</p>
<pre>uv add rfuns</pre>
<p>(or the equivalent in whatever virtual environment management configuration you’re
using, e.g. <code>pip install rfuns</code>) and start using some R functions directly in
Python!</p>
<p>Depending on how you like to manage your imports, you can import everything</p>
<pre>from rfuns import *

which([False, False, True, False, True])
## [2, 4]</pre>
<p>or, if you prefer to namespace</p>
<pre>import rfuns as r

r.which([False, False, True, False, True])
## [2, 4]</pre>
<p>The list of functions currently imported, grouped into sections is:</p>
<div id="strings" class="section level3">
<h3>Strings</h3>
<ul>
<li><code>nchar(x)</code></li>
<li><code>nzchar(x)</code></li>
<li><code>paste(*args, sep=&quot; &quot;, collapse=None)</code></li>
<li><code>paste0(*args, collapse=None)</code></li>
<li><code>grepl(pattern, x, ignore_case=False, fixed=False)</code></li>
<li><code>grep(pattern, x, ignore_case=False, fixed=False, value=False, invert=False)</code></li>
<li><code>gsub(pattern, replacement, x, ignore_case=False, fixed=False)</code></li>
<li><code>sub(pattern, replacement, x, ignore_case=False, fixed=False)</code></li>
<li><code>trimws(x, which=&quot;both&quot;, whitespace=r&quot;[ \t\r\n]&quot;)</code></li>
<li><code>toupper(x)</code></li>
<li><code>tolower(x)</code></li>
<li><code>startsWith(x, prefix)</code></li>
<li><code>endsWith(x, suffix)</code></li>
<li><code>strsplit(x, split, fixed=False)</code></li>
<li><code>substr(x, start, stop)</code></li>
<li><code>chartr(old, new, x)</code></li>
<li><code>formatC(x, digits=6, format=&quot;g&quot;, width=None)</code></li>
</ul>
</div>
<div id="vectors" class="section level3">
<h3>Vectors</h3>
<ul>
<li><code>which(x)</code></li>
<li><code>which_min(x)</code></li>
<li><code>which_max(x)</code></li>
<li><code>diff(x, lag=1)</code></li>
<li><code>cumsum(x)</code></li>
<li><code>cumprod(x)</code></li>
<li><code>cummax(x)</code></li>
<li><code>cummin(x)</code></li>
<li><code>rev(x)</code></li>
<li><code>duplicated(x)</code></li>
<li><code>setdiff(x, y)</code></li>
<li><code>intersect(x, y)</code></li>
<li><code>union(x, y)</code></li>
<li><code>unique(x)</code></li>
<li><code>seq_along(x)</code></li>
<li><code>seq_len(n)</code></li>
<li><code>seq(from_=0, to=None, by=None, length_out=None)</code> (<code>from</code> is a reserved keyword)</li>
<li><code>sign(x)</code></li>
<li><code>r_range(x)</code> (renamed to not conflict with <code>range()</code>)</li>
</ul>
</div>
<div id="math" class="section level3">
<h3>Math</h3>
<ul>
<li><code>sign(x)</code></li>
<li><code>trunc(x)</code></li>
<li><code>ceiling(x)</code></li>
<li><code>floor(x)</code></li>
<li><code>sqrt(x)</code></li>
<li><code>log(x, base=None)</code></li>
<li><code>log2(x)</code></li>
<li><code>log10(x)</code></li>
<li><code>exp(x)</code></li>
<li><code>abs(x)</code></li>
<li><code>var(x, na_rm=False)</code></li>
<li><code>sd(x, na_rm=False)</code></li>
<li><code>mean(x, na_rm=False)</code></li>
<li><code>median(x, na_rm=False)</code></li>
<li><code>quantile(x, probs=None, na_rm=False)</code></li>
<li><code>scale(x, center=True, scale_=True)</code></li>
<li><code>round(x, digits=0)</code></li>
</ul>
</div>
<div id="files" class="section level3">
<h3>Files</h3>
<ul>
<li><code>list_files(path=&quot;.&quot;, pattern=None, all_files=False, full_names=False, recursive=False, ignore_case=False, include_dirs=False, no_dot=False)</code></li>
<li><code>file_exists(path)</code></li>
<li><code>dir_exists(path)</code></li>
<li><code>basename(path)</code></li>
<li><code>dirname(path)</code></li>
<li><code>file_path(*args)</code></li>
</ul>
</div>
<div id="table" class="section level3">
<h3>Table</h3>
<ul>
<li><code>table(x)</code></li>
<li><code>prop_table(x)</code></li>
<li><code>margin_table(x)</code></li>
</ul>
</div>
<div id="functional" class="section level3">
<h3>Functional</h3>
<ul>
<li><code>lapply(x, func)</code></li>
<li><code>sapply(x, func)</code></li>
<li><code>vapply(x, func, expected_type)</code></li>
<li><code>tapply(x, index, func)</code></li>
<li><code>rapply(x, func)</code></li>
<li><code>Filter(func, x)</code></li>
<li><code>Map(func, *args)</code></li>
<li><code>Reduce(func, x, init=None, accumulate=False)</code></li>
</ul>
</div>
<div id="inspect" class="section level3">
<h3>Inspect</h3>
<ul>
<li><code>head(x, n=6)</code></li>
<li><code>tail(x, n=6)</code></li>
<li><code>length(x)</code></li>
<li><code>nrow(x)</code></li>
<li><code>ncol(x)</code></li>
<li><code>dim(x)</code></li>
<li><code>summary(x)</code></li>
<li><code>rstr(x)</code> (renamed to not conflict with <code>str()</code>)</li>
</ul>
</div>
<div id="utils" class="section level3">
<h3>Utils</h3>
<ul>
<li><code>vec(x)</code></li>
</ul>
<p>Some of these are vectorised</p>
<pre>nchar([&quot;these&quot;, &quot;all&quot;, &quot;have&quot;, &quot;different&quot;, &quot;lengths&quot;])
## [5, 3, 4, 9, 7]
grepl(&quot;ar&quot;, [&quot;frog&quot;, &quot;carpet&quot;, &quot;basket&quot;, &quot;dart&quot;])
## [False, True, False, True]
sqrt([36, 81, 9])
## [6.0, 9.0, 3.0]</pre>
<p>while others (approximately, up to 0-indexing) preserve the R behaviour, such as
how <code>seq()</code> works</p>
<pre>seq(5)
## [0, 1, 2, 3, 4]
seq(from_=0, to=10, by=2)
## [0, 2, 4, 6, 8, 10]</pre>
<p>(note that <code>from</code> is a keyword in Python, so the argument here is now <code>from_</code>)
and set operations</p>
<pre>setdiff([5, 2, 4, 1], [2, 1])
## [5, 4]</pre>
<p>whereas this does not preserve order</p>
<pre>set([5, 2, 4, 1]) -  set([2, 1])
## {4, 5}</pre>
<p>Doing <em>all</em> of this myself would have taken quite some time, so I’m grateful to
be able to direct an agent towards accomplishing some of the tedious parts of this
project. I still drove the decision making and made sure to verify outputs, so I
don’t consider this a ‘vibe-coded’ project.</p>
<p>I’m not recommending you use this in production at all &#8211; I’ve taken whatever
idiom I could find (or generate) for the internals of all of these, and haven’t
paid any attention to their performance. The goal was to make it easier for me
to work interactively in a REPL when I’m reaching for particular functions. That
being said, I’ll gladly do my best to understand the Pythonic versions as best
as I can so that I can better appreciate native Python and use the idioms when
my helper package isn’t available (or unsuitable). I’d say it’s fair to argue
that R users using Python <em>should</em> learn how to do things in a Pythonic way, but
I also just want to get some small things done occasionally, so I’m happy this
now exists.</p>
<p>If you’re working with non-R colleagues then introducing these abstractions —
while they may make your life simpler in the moment — will probably result in
confusion as you’re hiding away the implementation and giving it a name they
won’t recognise. That’s precisely what functions are for (with helpful names),
of course, but unless this package becomes popular, I’ll bet that the inline
idioms are more welcomed in a codebase.</p>
<p>I’d love to hear what people think about this, although I’m entirely fine with me
being the sole user of it. Should I just force my muscle-memory to take on the
Python idioms? Am I going to be punished for <a href="https://ghostbusters.fandom.com/wiki/Cross_the_Streams" rel="nofollow" target="_blank">‘crossing the
streams’</a> of two
incompatible languages? Would this be helpful to you? Are there other
considerations I’ve missed? As always, I can be found on
<a href="https://fosstodon.org/@jonocarroll" rel="nofollow" target="_blank">Mastodon</a> and the comment section below.</p>
<p>Shoutouts to Elio Campitelli and Michael Sumner for feedback on a draft of this
post.</p>
<br />
<details>
<summary>
<tt>devtools::session_info()</tt>
</summary>
<pre>## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value
##  version  R version 4.5.3 (2026-03-11)
##  os       macOS Tahoe 26.3.1
##  system   aarch64, darwin20
##  ui       X11
##  language (EN)
##  collate  en_US.UTF-8
##  ctype    en_US.UTF-8
##  tz       Australia/Adelaide
##  date     2026-05-22
##  pandoc   3.6.3 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown)
##  quarto   1.7.31 @ /usr/local/bin/quarto
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package     * version date (UTC) lib source
##  blogdown      1.23    2026-01-18 [1] CRAN (R 4.5.2)
##  bookdown      0.46    2025-12-05 [1] CRAN (R 4.5.2)
##  bslib         0.10.0  2026-01-26 [1] CRAN (R 4.5.2)
##  cachem        1.1.0   2024-05-16 [1] CRAN (R 4.5.0)
##  cli           3.6.5   2025-04-23 [1] CRAN (R 4.5.0)
##  devtools      2.4.6   2025-10-03 [1] CRAN (R 4.5.0)
##  digest        0.6.39  2025-11-19 [1] CRAN (R 4.5.2)
##  ellipsis      0.3.2   2021-04-29 [1] CRAN (R 4.5.0)
##  evaluate      1.0.5   2025-08-27 [1] CRAN (R 4.5.0)
##  fastmap       1.2.0   2024-05-15 [1] CRAN (R 4.5.0)
##  fs            1.6.7   2026-03-06 [1] CRAN (R 4.5.2)
##  glue          1.8.1   2026-04-17 [1] CRAN (R 4.5.2)
##  htmltools     0.5.9   2025-12-04 [1] CRAN (R 4.5.2)
##  jquerylib     0.1.4   2021-04-26 [1] CRAN (R 4.5.0)
##  jsonlite      2.0.0   2025-03-27 [1] CRAN (R 4.5.0)
##  knitr         1.51    2025-12-20 [1] CRAN (R 4.5.2)
##  lattice       0.22-9  2026-02-09 [1] CRAN (R 4.5.3)
##  lifecycle     1.0.5   2026-01-08 [1] CRAN (R 4.5.2)
##  magrittr      2.0.4   2025-09-12 [1] CRAN (R 4.5.0)
##  Matrix        1.7-4   2025-08-28 [1] CRAN (R 4.5.3)
##  memoise       2.0.1   2021-11-26 [1] CRAN (R 4.5.0)
##  otel          0.2.0   2025-08-29 [1] CRAN (R 4.5.0)
##  pkgbuild      1.4.8   2025-05-26 [1] CRAN (R 4.5.0)
##  pkgload       1.5.0   2026-02-03 [1] CRAN (R 4.5.2)
##  png           0.1-9   2026-03-15 [1] CRAN (R 4.5.2)
##  purrr         1.2.2   2026-04-10 [1] CRAN (R 4.5.2)
##  R6            2.6.1   2025-02-15 [1] CRAN (R 4.5.0)
##  Rcpp          1.1.1   2026-01-10 [1] CRAN (R 4.5.2)
##  remotes       2.5.0   2024-03-17 [1] CRAN (R 4.5.0)
##  reticulate    1.45.0  2026-02-13 [1] CRAN (R 4.5.2)
##  rlang         1.1.7   2026-01-09 [1] CRAN (R 4.5.2)
##  rmarkdown     2.30    2025-09-28 [1] CRAN (R 4.5.0)
##  rstudioapi    0.18.0  2026-01-16 [1] CRAN (R 4.5.2)
##  sass          0.4.10  2025-04-11 [1] CRAN (R 4.5.0)
##  sessioninfo   1.2.3   2025-02-05 [1] CRAN (R 4.5.0)
##  usethis       3.2.1   2025-09-06 [1] CRAN (R 4.5.0)
##  vctrs         0.7.1   2026-01-23 [1] CRAN (R 4.5.2)
##  withr         3.0.2   2024-10-28 [1] CRAN (R 4.5.0)
##  xfun          0.56    2026-01-18 [1] CRAN (R 4.5.2)
##  yaml          2.3.12  2025-12-10 [1] CRAN (R 4.5.2)
## 
##  [1] /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library
## 
## ─ Python configuration ───────────────────────────────────────────────────────
##  python:         /Users/jono/.cache/uv/archive-v0/Q1veGTfRq3GBaNYBXjagV/bin/python
##  libpython:      /Users/jono/.local/share/uv/python/cpython-3.12.12-macos-aarch64-none/lib/libpython3.12.dylib
##  pythonhome:     /Users/jono/.cache/uv/archive-v0/Q1veGTfRq3GBaNYBXjagV:/Users/jono/.cache/uv/archive-v0/Q1veGTfRq3GBaNYBXjagV
##  virtualenv:     /Users/jono/.cache/uv/archive-v0/Q1veGTfRq3GBaNYBXjagV/bin/activate_this.py
##  version:        3.12.12 (main, Oct 28 2025, 11:52:25) [Clang 20.1.4 ]
##  numpy:          /Users/jono/.cache/uv/archive-v0/Q1veGTfRq3GBaNYBXjagV/lib/python3.12/site-packages/numpy
##  numpy_version:  2.4.6
##  
##  NOTE: Python version was forced by VIRTUAL_ENV
## 
## ──────────────────────────────────────────────────────────────────────────────</pre>
</details>
<p><br /></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://jcarroll.com.au/2026/05/22/functions-over-idioms-rfuns/"> rstats on Irregularly Scheduled 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/05/functions-over-idioms-writing-r-in-python-with-rfuns/">Functions over Idioms – Writing R in Python with rfuns</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

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