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

<channel>
	<title>R-bloggers</title>
	<atom:link href="https://www.r-bloggers.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.r-bloggers.com</link>
	<description>R news and tutorials contributed by hundreds of R bloggers</description>
	<lastBuildDate>Fri, 19 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>EuroBioC2026 conference recap</title>
		<link>https://www.r-bloggers.com/2026/06/eurobioc2026-conference-recap/</link>
		
		<dc:creator><![CDATA[Laurah Ondari]]></dc:creator>
		<pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>The European Bioconductor Conference 2026 (EuroBioC2026) took place from June 3-5, 2026, in Turku, Finland. Hosted by the University of Turku and the Finnish Society for Bioinformatics at BioCity, the conference brought together the Bioconducto...</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/eurobioc2026-conference-recap/">EuroBioC2026 conference recap</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/"> Bioconductor community blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 





<p><a href="https://i2.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/eurobioc-main-image.jpg?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-1" rel="nofollow" target="_blank"><img src="https://i2.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/eurobioc-main-image.jpg?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1"></a></p>
<p>The European Bioconductor Conference 2026 (<a href="https://eurobioc2026.bioconductor.org/" rel="nofollow" target="_blank">EuroBioC2026</a>) took place from June 3-5, 2026, in Turku, Finland. Hosted by the <a href="https://www.utu.fi/en" rel="nofollow" target="_blank">University of Turku</a> and the <a href="https://www.bioinf.fi/" rel="nofollow" target="_blank">Finnish Society for Bioinformatics</a> at BioCity, the conference brought together the Bioconductor community to showcase the latest developments in Bioconductor software packages and discuss emerging technologies shaping computational biology. This year’s conference welcomed 147 in-person participants from 23 countries. Across three days, attendees participated in keynote lectures, short and flash talks, workshops, poster sessions, Birds-of-a-Feather discussions, and community events. The conference also marked an important milestone for the project as Bioconductor celebrated its 25th anniversary. The figures below summarise EuroBioC2026 at a glance: 147 attendees from 23 countries, 4 keynote speakers, 25 speakers, 68 posters, 6 workshops, 9 flash talks, and 3 Birds-of-a-Feather sessions.</p>
<p><a href="https://i2.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/eurobioc-by-numbers.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-2" rel="nofollow" target="_blank"><img src="https://i2.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/eurobioc-by-numbers.png?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1"></a></p>
<section id="participants-by-country" class="level2">
<h2 class="anchored" data-anchor-id="participants-by-country">Participants by country</h2>
<p>Participants travelled to Turku from across Europe and beyond, reflecting the increasingly global nature of the Bioconductor community. While Finland represented the largest delegation, attendees also joined from Italy, Belgium, Germany, Switzerland, the United States, Sweden, the United Kingdom, Ireland, Spain, Kenya, South Korea, Australia, and several other countries.</p>
<iframe src="https://blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/eurobioc2026-participants-map.html" width="450" height="600" frameborder="0">
</iframe>
</section>
<section id="preconference" class="level2">
<h2 class="anchored" data-anchor-id="preconference">Preconference</h2>
<p>Ahead of the main conference, EuroBioC2026 hosted two preconference events on June 1-2. These were delivered in collaboration with the University of Turku, CompLifeSci, the Finnish Society for Bioinformatics, and members of the Bioconductor community. Running in parallel over two days, the events allowed participants to either strengthen their analytical skills through hands-on training or contribute directly to the development of Bioconductor software through collaborative coding projects.</p>
<section id="workshop-orchestrating-microbiome-analysis-with-bioconductor" class="level3">
<h3 class="anchored" data-anchor-id="workshop-orchestrating-microbiome-analysis-with-bioconductor">Workshop: Orchestrating Microbiome Analysis with Bioconductor</h3>
<p>The preconference workshop focused on microbiome data analysis using Bioconductor and followed the Bioconductor Carpentry model, combining interactive instruction with practical exercises. Over two days, participants learned how to import, process, and analyse microbiome datasets using the established Bioconductor workflow; <a href="https://microbiome.github.io/OMA/docs/devel/" rel="nofollow" target="_blank">Orchestrating Microbiome Analysis (OMA)</a>. The workshop covered diversity analyses, differential abundance testing, and approaches for integrating microbiome data with other omics data types. A key component of the workshop was the use of cloud computing resources from <a href="https://csc.fi/en/" rel="nofollow" target="_blank">CSC</a> (Finnish IT Centre for Science) using <a href="https://noppe.2.rahtiapp.fi/welcome" rel="nofollow" target="_blank">Noppe</a>, which provided participants with immediate access to all required datasets, software, and computing resources. By removing installation and configuration barriers, instructors were able to begin teaching immediately and spend more time focusing on the workshop content rather than troubleshooting technical issues. The platform also ensured that all participants worked within the same environment, creating a smoother learning experience for everyone involved. The workshop was hands-on throughout: participants asked questions, worked through exercises, and discussed how the workflows related to their own projects. This practical, open, instructor-led format aligns well with the approach shared by both Bioconductor and The Carpentries. The workshop was led by Leo Lahti, Himel Mallick, Thomaz Bastiaanssen, Tuomas Borman, and Giulio Benedetti.</p>
<img src="https://i0.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/benedetti-workshop.jpg?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1">
<center>
<p><em>Participants during the pre-conference microbiome workshop.</em></p>
</center>
</section>
<section id="hackathon" class="level3">
<h3 class="anchored" data-anchor-id="hackathon">Hackathon</h3>
<p>We held the first of our series of hackathons attached to Bioconductor conferences this June at EuroBioC2026 in Turku, Finland. Eighteen in-person attendees worked on four projects focused on interoperability, and at least three of those are now being prepared for submission to <a href="https://index.biohackrxiv.org/tag/EuroBioc2026" rel="nofollow" target="_blank">BioHackrXiv</a>.</p>
<p>A big congratulations to all the participants for their effort. You can read more about the projects from the <a href="https://github.com/BiocCodingCollaborations/EuroBioc2026_Hackathon" rel="nofollow" target="_blank">EuroBioC2026 Hackathon</a>. We’re looking forward to building on this work at the North American Bioconductor conference, BioC2026, in Seattle this August. See the <a href="https://github.com/BiocCodingCollaborations/BiocNA2026_Hackathon" rel="nofollow" target="_blank">BioC2026 Hackathon</a> for more details.</p>
<img src="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/hackathon.jpg?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1">
<center>
<p><em>Participants during the pre-conference hackathon.</em></p>
</center>
</section>
</section>
<section id="programme-overview" class="level2">
<h2 class="anchored" data-anchor-id="programme-overview">Programme overview</h2>
<p>EuroBioC2026 covered both established and emerging areas of computational biology, with a consistent focus on reproducible and open-source research.</p>
<section id="keynotes" class="level3">
<h3 class="anchored" data-anchor-id="keynotes">Keynotes</h3>
<p>Keynotes at EuroBioC2026 covered functional genomics, machine learning, microbiome research, and the direction of computational biology. Across four talks, speakers addressed how data science is changing biological research, and what that means for reproducibility, interpretation, and open-source software.</p>
<p><strong>Helena Kilpinen</strong> opened the conference with <em>Morphological profiling of in vitro neurons: Visualizing complexity in cellular disease models</em>. Her talk explored how high-content imaging and morphological profiling can be used to better understand cellular phenotypes in disease models. By combining large-scale imaging data with computational approaches, she demonstrated how researchers can uncover subtle cellular differences that may provide insights into disease mechanisms.</p>
<p><strong>Anders Krogh</strong> presented <em>A Deep Generative Model for Gene Expression and Multimodal Data</em>, showcasing how modern machine learning approaches can be used to model increasingly complex biological datasets. His keynote highlighted the potential of generative models to integrate multiple data modalities and improve our understanding of gene regulation and cellular states.</p>
<p><strong>Aura Raulo</strong> delivered a keynote titled <em>Modeling the spread of microbial communities in contact networks</em>. Drawing on concepts from ecology, microbiology, and network science, they explored how microbial communities are transmitted between individuals and populations. The talk examined how host-associated microbiomes are shaped and shared, and what drives their spread.</p>
<p>The final keynote was delivered by <strong>Levi Waldron</strong>, who addressed a topic now central to many scientific discussions: <em>Bioconductor in the age of AI. What do we do now?</em> His talk examined the opportunities and challenges that AI presents for open-source scientific software. He encouraged the community to think about how AI tools can complement existing work, without compromising the transparency, reproducibility, and scientific rigour the project has built over 25 years.</p>
</section>
<section id="short-talks-and-flash-talks" class="level3">
<h3 class="anchored" data-anchor-id="short-talks-and-flash-talks">Short talks and flash talks</h3>
<p>The short talks at EuroBioC2026 reflected the diversity of the Bioconductor community, spanning topics from single-cell and spatial biology to microbiome research, proteomics, metabolomics, and multi-omics data integration. Several presentations introduced new software packages and statistical methods aimed at improving reproducibility, scalability, and interoperability in biological data analysis. Alongside methodological advances, speakers also covered broader community topics, including sustainable open-source software, environmentally conscious computing, training initiatives, and the growing role of artificial intelligence in computational biology. Together, the talks provided a good picture of the scientific questions being addressed with Bioconductor and the people driving its development.</p>
</section>
<section id="poster-sessions" class="level3">
<h3 class="anchored" data-anchor-id="poster-sessions">Poster sessions</h3>
<p>The 68 posters presented at EuroBioC2026 covered a broad mix of biological applications and software development. Topics included spatial omics, microbiome research, proteomics, metabolomics, disease modelling, and machine learning, as well as new packages and infrastructure projects from across the Bioconductor ecosystem. The poster sessions encouraged interactions between package developers, researchers, students, and first-time conference attendees, helping strengthen collaborations across the community.</p>
<img src="https://i0.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/poster-session.jpg?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1">
<center>
<p><em>EuroBioC2026 participants during a poster session.</em></p>
</center>
</section>
<section id="birds-of-a-feather-sessions" class="level3">
<h3 class="anchored" data-anchor-id="birds-of-a-feather-sessions">Birds-of-a-Feather sessions</h3>
<p>The three 90-minute Birds-of-a-Feather (BoF) sessions offered attendees an opportunity to connect around shared interests and exchange experiences, discuss challenges, and share ideas. The sessions were proposed by participants during the conference including sessions focused on strengthening the Finnish Bioconductor community, supporting early-career researchers, and embedding environmental sustainability into Bioconductor packages and research workflows. One outcome from the early-career researcher discussion was the creation of a dedicated student–ECR Zulip channel to support continued connection within the community. The BoF sessions continued a tradition of community-led discussion that has been part of Bioconductor events for years.</p>
</section>
<section id="workshops" class="level3">
<h3 class="anchored" data-anchor-id="workshops">Workshops</h3>
<p>The workshop sessions offered attendees an opportunity to explore a range of Bioconductor tools and workflows through hands-on demonstrations led by community members. Topics included proteomics data analysis, integrative analysis of histopathological images and multi-omics data, ChIP-seq analysis, differential expression analysis, post-translational modification analysis, and interoperable mass spectrometry workflows combining R and Python. Participants had the opportunity to engage directly with instructors, ask questions, and learn how the presented tools could be applied to their own research projects. Together, the workshops showcased the breadth of analytical domains supported by the Bioconductor ecosystem.</p>
</section>
<section id="celebrating-25-years-of-bioconductor" class="level3">
<h3 class="anchored" data-anchor-id="celebrating-25-years-of-bioconductor">Celebrating 25 years of Bioconductor</h3>
<p>A major highlight of EuroBioC2026 was the celebration of Bioconductor’s 25th anniversary. Since its founding in 2001, Bioconductor has grown from a small collection of software packages into a global open-source community used by thousands of researchers worldwide. Over the past quarter-century, it has become a central resource for reproducible computational biology, providing infrastructure, software, training, and community support across numerous biological disciplines.</p>
<p><a href="https://blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/bioc25years.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" rel="nofollow" target="_blank"><img src="https://blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/bioc25years.svg" class="zoomable img-fluid" style="width:100.0%"></a></p>
<p>One of the highlights of the celebration was a retrospective presented by Maria Doyle, Bioconductor Community Manager, who took attendees through the history of Bioconductor, from the earliest contribution on the Bioconductor support site to the project’s growth into the global community it is today. The presentation highlighted how the project has evolved over the past 25 years and its impact on computational biology. The celebrations continued at the conference dinner, where attendees marked the occasion with a special anniversary cake. During the evening, Levi Waldron, one of Bioconductor’s Principal Investigators, shared a personal reflection on his journey with Bioconductor, from first encountering the project through his collaborations with Martin Morgan to becoming part of its leadership.</p>
<img src="https://i2.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/levispeech.jpg?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1">
<center>
<p><em>Levi Waldron shares a personal reflection on his journey with Bioconductor during the 25th anniversary celebrations.</em></p>
</center>
</section>
</section>
<section id="infrastructure-and-tools" class="level2">
<h2 class="anchored" data-anchor-id="infrastructure-and-tools">Infrastructure and tools</h2>
<section id="zulip" class="level3">
<h3 class="anchored" data-anchor-id="zulip">Zulip</h3>
<p>EuroBioC2026 continued to use Zulip as its primary communication platform. A dedicated conference channel, along with a separate hackathon channel, organised into topic-based threads, served as a central location for announcements, technical support, social interactions, and discussions before, during, and after the event. The threaded conversation model made it easier to follow discussions and kept participants connected throughout the conference.</p>
</section>
<section id="sticker-hexwall" class="level3">
<h3 class="anchored" data-anchor-id="sticker-hexwall">Sticker Hexwall</h3>
<p>The sticker hexwall returned for EuroBioC2026 following its successful introduction in 2025. The display showcased Bioconductor package stickers contributed by Bioconductor community members and served as a visual representation of the diversity of software projects within the ecosystem.</p>
<p>The hexwall quickly became a popular gathering point and photo location throughout the conference.</p>
<img src="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/hexwall.jpg?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1">
<center>
<p><em>The hexwall at EuroBioC2026.</em></p>
</center>
</section>
</section>
<section id="social-interactions-and-networking" class="level2">
<h2 class="anchored" data-anchor-id="social-interactions-and-networking">Social interactions and networking</h2>
<section id="conference-dinner" class="level3">
<h3 class="anchored" data-anchor-id="conference-dinner">Conference Dinner</h3>
<p>The conference dinner took place on the island of Ruissalo, one of Turku’s most popular recreational areas and the gateway to the Turku Archipelago. It was hosted at the historic Villa Marjaniemi, a 150-year-old villa overlooking the sea, and the evening was inspired by Juhannus, Finland’s traditional midsummer celebration.</p>
<p>Attendees were welcomed by a live band as they arrived, then enjoyed dinner and celebrations marking 25 years of Bioconductor. The evening continued with outdoor games and activities, and was a good chance to catch up with familiar faces and meet people for the first time.</p>
</section>
<section id="walking-tour" class="level3">
<h3 class="anchored" data-anchor-id="walking-tour">Walking tour</h3>
<p>On Thursday evening, participants joined an optional walking tour. During the tour, participants learned about Finnish history while exploring the historic city centre, stopping at the Old Great Square and Brinkkala Hall, whose balcony has served as the site of the annual (<a href="https://en.wikipedia.org/wiki/Christmas_Peace" rel="nofollow" target="_blank">Christmas Peace</a>) declaration since the Middle Ages. The tour also highlighted notable Finnish figures, including the legendary runner (<a href="https://en.wikipedia.org/wiki/Paavo_Nurmi" rel="nofollow" target="_blank">Paavo Nurmi</a>), famously known as the “Flying Finn.”</p>
<p>The tour naturally flowed into the evening’s social activities. Some participants stopped at a traditional Finnish grill kiosk to try makkaraperunat, a popular local fast-food dish, while others continued their conversations at Office (Toimisto in Finnish), a local bar where they sang karaoke until 3 AM.</p>
<div class="columns">
<div class="column" style="width:48%;">
<p><a href="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/walking-tour.jpg?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-4" rel="nofollow" target="_blank"><img src="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/walking-tour.jpg?w=578&#038;ssl=1" class="img-fluid" style="width:100.0%" data-recalc-dims="1"></a></p>
</div><div class="column" style="width:48%;">
<p><a href="https://i0.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/dinner.jpg?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-5" rel="nofollow" target="_blank"><img src="https://i0.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/dinner.jpg?w=578&#038;ssl=1" class="img-fluid" style="width:100.0%" data-recalc-dims="1"></a></p>
</div>
</div>
<center>
<p><em>EuroBioC2026 Participants during the walking tour (left) and enjoying the conference dinner (right).</em></p>
</center>
</section>
</section>
<section id="conference-materials" class="level2">
<h2 class="anchored" data-anchor-id="conference-materials">Conference materials</h2>
<p>Conference recordings will be available on the <a href="https://www.youtube.com/@bioconductor" rel="nofollow" target="_blank">Bioconductor YouTube channel</a> in the coming weeks. Auditorium sessions were also live streamed, and Slido was used to facilitate audience questions from both in-person and remote participants, alongside traditional in-room discussion. Presenters were encouraged to upload their slides, posters, and supplementary materials to the <a href="https://zenodo.org/communities/bioconductor" rel="nofollow" target="_blank">Bioconductor Zenodo Community</a>, making conference outputs openly available and citable through persistent digital object identifiers (DOIs).</p>
<p>These resources make conference outputs available to those who could not attend and support continued learning across the community. Additional photos from EuroBioC2026, including talks, workshops, posters, social events, and the conference dinner, are available in the <a href="https://eurobioc2026.bioconductor.org/pages/photo-gallery.html" rel="nofollow" target="_blank">conference photo gallery</a>. A short recap video capturing moments from the conference is also available <a href="https://youtube.com/shorts/PdMdkpTPMeM?si=Q7hu4AwztXEsZuZM" rel="nofollow" target="_blank">on YouTube</a>.</p>
</section>
<section id="coming-up" class="level2">
<h2 class="anchored" data-anchor-id="coming-up">Coming up…</h2>
<p>The 25th anniversary year will continue when the Bioconductor community gather next at (<a href="https://bioc2026.bioconductor.org/" rel="nofollow" target="_blank">BioC2026</a>), which will take place from August 10-12, 2026 at the Fred Hutch Cancer Center in Seattle, Washington. The conference will continue the tradition of bringing together developers, researchers, and educators to share new software, methods, and applications in computational biology.</p>
<p>Later in the year, the community will head to Melbourne, Australia, for (<a href="https://biocasia2026.bioconductor.org/" rel="nofollow" target="_blank">BioCAsia2026</a>), taking place on November 19-20, 2026, immediately following the ABACBS conference. BioCAsia brings together researchers across the Asia-Pacific region for scientific exchange, training, and community building. The (<a href="https://brisbanebioinformatics.org/event/qld-week-biocasia/" rel="nofollow" target="_blank">BioCAsia Seminar Series</a>) has also expanded to a bi-monthly schedule. alongside growing regional initiatives such as the (<a href="https://training.bioconductor.org/workshops/bioc-africa-seminars/" rel="nofollow" target="_blank">Bioconductor Africa Seminar Series</a>) and the Bioconductor Latin America seminar series. Stay connected with the community through dedicated Zulip channels.</p>
<p>EuroBioC2026 concluded with an invitation to Basel, Switzerland, where EuroBioC2027 will take place from September 8–10, 2027. See you there.</p>
</section>
<section id="acknowledgements" class="level2">
<h2 class="anchored" data-anchor-id="acknowledgements">Acknowledgements</h2>
<section id="sponsors" class="level3">
<h3 class="anchored" data-anchor-id="sponsors">Sponsors</h3>
<p>EuroBioC2026 gratefully acknowledges the support of all sponsors and partners whose contributions and support made the conference possible.</p>
<p><a href="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/sponsors-partners.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-6" rel="nofollow" target="_blank"><img src="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-19-EuroBioc2026-recap/media/sponsors-partners.png?w=578&#038;ssl=1" class="zoomable img-fluid" style="width:100.0%" data-recalc-dims="1"></a></p>
</section>
<section id="diamond-sponsors" class="level3">
<h3 class="anchored" data-anchor-id="diamond-sponsors">Diamond sponsors</h3>
<ul>
<li><a href="https://www.tsv.fi/en" rel="nofollow" target="_blank">Federation of Finnish Learned Societies</a></li>
<li><a href="https://skr.fi/en/" rel="nofollow" target="_blank">Finnish Cultural Foundation</a></li>
</ul>
</section>
<section id="gold-sponsors" class="level3">
<h3 class="anchored" data-anchor-id="gold-sponsors">Gold sponsors</h3>
<ul>
<li><a href="https://biocityturku.fi/" rel="nofollow" target="_blank">BioCity, Turku</a></li>
<li><a href="https://stiftelsenabo.fi/en/" rel="nofollow" target="_blank">Åbo Akademi University Foundation</a></li>
</ul>
</section>
<section id="bronze-sponsors" class="level3">
<h3 class="anchored" data-anchor-id="bronze-sponsors">Bronze sponsors</h3>
<ul>
<li><a href="https://bigomics.ch/" rel="nofollow" target="_blank">BigOmics Analytics</a></li>
<li><a href="https://www.physalia-courses.org/" rel="nofollow" target="_blank">Physalia Courses</a></li>
<li><a href="https://r-consortium.org/" rel="nofollow" target="_blank">R Consortium</a></li>
<li><a href="https://www.loimu.fi/en/" rel="nofollow" target="_blank">LOIMU</a></li>
<li><a href="https://liedonsaastopankkisaatio.fi/" rel="nofollow" target="_blank">Liedon Säästöpankkisäätiö</a></li>
</ul>
</section>
<section id="supporting-organisations" class="level3">
<h3 class="anchored" data-anchor-id="supporting-organisations">Supporting organisations</h3>
<ul>
<li><a href="https://csc.fi/en/" rel="nofollow" target="_blank">CSC &#8211; IT Center for Science, Finland</a> for providing computational resources for the workshops</li>
<li><a href="https://www.nordic-compbio.org/" rel="nofollow" target="_blank">Nordic Computational Biology</a></li>
</ul>
</section>
<section id="hosts" class="level3">
<h3 class="anchored" data-anchor-id="hosts">Hosts</h3>
<ul>
<li><a href="https://www.utu.fi/en" rel="nofollow" target="_blank">University of Turku</a></li>
<li><a href="https://biocityturku.fi/research-programs/complifesci/" rel="nofollow" target="_blank">CompLifeSci, BioCity Turku</a></li>
<li><a href="https://www.bioinf.fi/" rel="nofollow" target="_blank">Finnish Society for Bioinformatics</a></li>
</ul>
</section>
<section id="organising-committee" class="level3">
<h3 class="anchored" data-anchor-id="organising-committee">Organising committee</h3>
<p>We thank the local organisers, programme committee, workshop instructors, keynote speakers, volunteers, sponsors, and all participants whose contributions made EuroBioC2026 a success.</p>
<p><strong>Organising Committee</strong></p>
<ul>
<li>Leo Lahti (Chair)</li>
<li>Tuomas Borman (Local Chair)</li>
<li>Akewak Jeba (Website)</li>
<li>Anna Kaisanlahti (Local Organiser)</li>
<li>Annekathrin Nedwed</li>
<li>Charlotte Soneson (Scientific Programme)</li>
<li>Dania Machlab</li>
<li>Dario Righelli</li>
<li>Eliana Ibrahimi</li>
<li>Federico Marini</li>
<li>James Dalgleish</li>
<li>Julia Mathlin (Local Organiser)</li>
<li>Kevin Rue-Albrecht</li>
<li>Laurent Gatto</li>
<li>Lieven Clement</li>
<li>Maria Doyle (Communications)</li>
<li>Mark Robinson</li>
<li>Michael Love</li>
<li>Michael Stadler</li>
<li>Miina Vulli (Local Organiser)</li>
<li>Najla Abassi</li>
<li>Nicholas Cooley (Hackathon)</li>
<li>Nyasita Laurah Ondari (Communications)</li>
<li>Robert Castelo</li>
<li>Robert Ivánek</li>
<li>Teemu Daniel Laajala (Local Organiser)</li>
</ul>


</section>
</section>

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

		<post-id xmlns="com-wordpress:feed-additions:1">402139</post-id>	</item>
		<item>
		<title>Likert Scale Questions: Your In-Depth Guide</title>
		<link>https://www.r-bloggers.com/2026/06/likert-scale-questions-your-in-depth-guide/</link>
		
		<dc:creator><![CDATA[Unknown]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 09:46:51 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://www.r-bloggers.com/?guid=a33731dcb43d9afa478c996b68adee3b</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
A Likert scale (pronounced LICK-ert, not "LIKE-ert") is a psychometric rating scale used in surveys and questionnaires to measure attitudes, opinions, and perceptions. Named after American social psychologist Rensis Likert, who developed it in 1932, it remains the most widely used approach to scaling responses in survey research today.</p>
<p>Key Takeaways</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/likert-scale-questions-your-in-depth-guide/">Likert Scale Questions: Your In-Depth Guide</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.rstudiodatalab.com/2023/07/Likert-Scale.html"> RStudioDataLab</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 style="text-align: justify;">
A <strong>Likert scale</strong> (pronounced <em>LICK-ert</em>, not &#8220;LIKE-ert&#8221;) is a psychometric rating scale used in surveys and questionnaires to measure attitudes, opinions, and perceptions. Named after American social psychologist <strong><a href="https://en.wikipedia.org/wiki/Rensis_Likert" rel="nofollow" target="_blank">Rensis Likert</a></strong>, who developed it in 1932, it remains the most widely used approach to scaling responses in survey research today.
</p>

<div style="background: rgb(240, 247, 255); border-left: 4px solid rgb(37, 99, 235); border-radius: 4px; margin: 20px 0px; padding: 16px 20px;">
<strong>Key Takeaways</strong>
<ul style="margin-top: 8px;">
<li><strong>Definition:</strong> A Likert scale measures how strongly people agree or disagree with a statement, typically using 5 or 7 ordered response options.</li>
<li><strong>Structure:</strong> Each item presents a statement followed by response options from &#8220;Strongly Disagree&#8221; to &#8220;Strongly Agree.&#8221;</li>
<li><strong>Purpose:</strong> Turns subjective opinions into quantitative data for statistical analysis.</li>
<li><strong>Formats:</strong> 4-point, 5-point, 6-point, and 7-point scales each serve different research needs.</li>
<li><strong>Analysis:</strong> Use median and frequency tables for single items; use mean and Cronbach&#8217;s alpha for multi-item scales.</li>
</ul>
</div>

<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEje02Hn87SzxgBr_lLBu7UeDc1qob7R_O8JN6cpwFqMacbvGomUp0Dcg_4EvC99ooP_mcQ6DSyYBGeIue47Jf71vm1GVRCh5eSE_lcE_os8oEZ88zrTxK1f2B22Ezs25OkKcPmtNrf0XImv8eBYZWShjM_cM9COW0-CVuVpVoj-wtzO_EY0hZkbBWymiJE/s1200/Feedback%20scale,%20satisfaction%20rating%20design%20(Instagram%20Post).webp" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank">
<img loading="lazy" alt="Likert scale rating options ranging from strongly disagree to strongly agree — complete guide with examples" border="0" data-original-height="640" data-original-width="450" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEje02Hn87SzxgBr_lLBu7UeDc1qob7R_O8JN6cpwFqMacbvGomUp0Dcg_4EvC99ooP_mcQ6DSyYBGeIue47Jf71vm1GVRCh5eSE_lcE_os8oEZ88zrTxK1f2B22Ezs25OkKcPmtNrf0XImv8eBYZWShjM_cM9COW0-CVuVpVoj-wtzO_EY0hZkbBWymiJE/w640-h342/Feedback%20scale,%20satisfaction%20rating%20design%20(Instagram%20Post).webp" title="Likert scale complete guide with examples and analysis" width="450" />
</a>
</div>

<h2>Likert Scale vs. Likert Item: An Important Distinction</h2>
<p style="text-align: justify;">
These two terms are often used interchangeably — that is technically incorrect and matters for your analysis.
</p>
<ul>
<li>A <strong>Likert item</strong> is a single statement with a rated response (e.g., &#8220;I am satisfied with the service: Strongly Disagree → Strongly Agree&#8221;).</li>
<li>A <strong>Likert scale</strong> is the sum or average of several related Likert items designed to measure a single construct.</li>
</ul>
<p style="text-align: justify;">
Treating a single item as a complete &#8220;scale&#8221; is one of the most common errors in survey design. If you have only one question, you have a Likert item, not a Likert scale — and the appropriate statistical treatment differs.
</p>

<h2>Types of Likert Scale Response Options</h2>
<p style="text-align: justify;">
Likert scales are not limited to measuring agreement. Depending on your research objective, you can measure frequency, importance, quality, or likelihood using the same format. The table below shows the most common response option sets:
</p>

<div style="overflow-x: auto;">
<table style="border-collapse: collapse; font-size: 0.95em; width: 100%;">
<thead>
<tr style="background: rgb(37, 99, 235); color: white;">
<th style="padding: 10px 12px; text-align: left;">Dimension</th>
<th style="padding: 10px 12px;">Option 1</th>
<th style="padding: 10px 12px;">Option 2</th>
<th style="padding: 10px 12px;">Option 3</th>
<th style="padding: 10px 12px;">Option 4</th>
<th style="padding: 10px 12px;">Option 5</th>
</tr>
</thead>
<tbody>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">Agreement</td>
<td style="padding: 9px 12px; text-align: center;">Strongly Disagree</td>
<td style="padding: 9px 12px; text-align: center;">Disagree</td>
<td style="padding: 9px 12px; text-align: center;">Neither</td>
<td style="padding: 9px 12px; text-align: center;">Agree</td>
<td style="padding: 9px 12px; text-align: center;">Strongly Agree</td>
</tr>
<tr>
<td style="font-weight: 600; padding: 9px 12px;">Frequency</td>
<td style="padding: 9px 12px; text-align: center;">Never</td>
<td style="padding: 9px 12px; text-align: center;">Rarely</td>
<td style="padding: 9px 12px; text-align: center;">Sometimes</td>
<td style="padding: 9px 12px; text-align: center;">Often</td>
<td style="padding: 9px 12px; text-align: center;">Always</td>
</tr>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">Importance</td>
<td style="padding: 9px 12px; text-align: center;">Not Important</td>
<td style="padding: 9px 12px; text-align: center;">Slightly Important</td>
<td style="padding: 9px 12px; text-align: center;">Moderately Important</td>
<td style="padding: 9px 12px; text-align: center;">Important</td>
<td style="padding: 9px 12px; text-align: center;">Very Important</td>
</tr>
<tr>
<td style="font-weight: 600; padding: 9px 12px;">Quality</td>
<td style="padding: 9px 12px; text-align: center;">Very Poor</td>
<td style="padding: 9px 12px; text-align: center;">Poor</td>
<td style="padding: 9px 12px; text-align: center;">Fair</td>
<td style="padding: 9px 12px; text-align: center;">Good</td>
<td style="padding: 9px 12px; text-align: center;">Excellent</td>
</tr>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">Likelihood</td>
<td style="padding: 9px 12px; text-align: center;">Definitely Not</td>
<td style="padding: 9px 12px; text-align: center;">Probably Not</td>
<td style="padding: 9px 12px; text-align: center;">Possibly</td>
<td style="padding: 9px 12px; text-align: center;">Probably</td>
<td style="padding: 9px 12px; text-align: center;">Definitely</td>
</tr>
<tr>
<td style="font-weight: 600; padding: 9px 12px;">Satisfaction</td>
<td style="padding: 9px 12px; text-align: center;">Very Dissatisfied</td>
<td style="padding: 9px 12px; text-align: center;">Dissatisfied</td>
<td style="padding: 9px 12px; text-align: center;">Neutral</td>
<td style="padding: 9px 12px; text-align: center;">Satisfied</td>
<td style="padding: 9px 12px; text-align: center;">Very Satisfied</td>
</tr>
</tbody>
</table>
</div>

<h2>Likert Scale Formats: 4, 5, 6, and 7 Points Compared</h2>
<p style="text-align: justify;">
Choosing the right number of response points affects the precision of your data and the cognitive load on respondents. Here is how each format differs in practice:
</p>

<div style="overflow-x: auto;">
<table style="border-collapse: collapse; font-size: 0.95em; margin-top: 12px; width: 100%;">
<thead>
<tr style="background: rgb(37, 99, 235); color: white;">
<th style="padding: 10px 12px; text-align: left;">Format</th>
<th style="padding: 10px 12px;">Neutral Point?</th>
<th style="padding: 10px 12px;">Best For</th>
<th style="padding: 10px 12px;">Main Trade-off</th>
</tr>
</thead>
<tbody>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">4-Point</td>
<td style="padding: 9px 12px; text-align: center;">No (forced choice)</td>
<td style="padding: 9px 12px;">When you need a clear directional opinion</td>
<td style="padding: 9px 12px;">Can frustrate genuinely neutral respondents</td>
</tr>
<tr>
<td style="font-weight: 600; padding: 9px 12px;">5-Point</td>
<td style="padding: 9px 12px; text-align: center;">Yes</td>
<td style="padding: 9px 12px;">Most general research; most familiar to respondents</td>
<td style="padding: 9px 12px;">Central tendency bias is common</td>
</tr>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">6-Point</td>
<td style="padding: 9px 12px; text-align: center;">No (forced choice)</td>
<td style="padding: 9px 12px;">When you want fine-grained data without a fence-sitter option</td>
<td style="padding: 9px 12px;">Less intuitive labeling</td>
</tr>
<tr>
<td style="font-weight: 600; padding: 9px 12px;">7-Point</td>
<td style="padding: 9px 12px; text-align: center;">Yes</td>
<td style="padding: 9px 12px;">Academic research requiring maximum discrimination</td>
<td style="padding: 9px 12px;">Harder to label all points meaningfully</td>
</tr>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">10-Point</td>
<td style="padding: 9px 12px; text-align: center;">Yes (implied midpoint)</td>
<td style="padding: 9px 12px;">NPS-style scoring; familiarity from school grades</td>
<td style="padding: 9px 12px;">Data often clusters; not true Likert by strict definition</td>
</tr>
</tbody>
</table>
</div>

<h3>5-Point Likert Scale (Most Common)</h3>
<p style="text-align: justify;">
The 5-point scale is the default choice in most survey research because it balances nuance with simplicity. Example:
</p>
<p style="text-align: justify;"><em>&#8220;The quality of food at XYZ Restaurant is excellent.&#8221;</em></p>
<ol>
<li>Strongly Disagree</li>
<li>Disagree</li>
<li>Neither Agree nor Disagree</li>
<li>Agree</li>
<li>Strongly Agree</li>
</ol>

<h3>4-Point Likert Scale (Forced Choice)</h3>
<p style="text-align: justify;">
Removing the neutral midpoint forces respondents to take a position. Use this when fence-sitting would undermine your research objective — for example, when measuring purchase intent or policy support where &#8220;no opinion&#8221; is not useful data.
</p>
<ol>
<li>Strongly Disagree</li>
<li>Disagree</li>
<li>Agree</li>
<li>Strongly Agree</li>
</ol>

<h3>6-Point Likert Scale</h3>
<p style="text-align: justify;">
Like the 4-point, this eliminates the neutral option while providing more granularity. Useful in employee satisfaction or consumer preference research where a clearer lean is needed.
</p>
<ol>
<li>Strongly Disagree</li>
<li>Disagree</li>
<li>Slightly Disagree</li>
<li>Slightly Agree</li>
<li>Agree</li>
<li>Strongly Agree</li>
</ol>

<h3>7-Point Likert Scale</h3>
<p style="text-align: justify;">
The 7-point scale is preferred in academic and psychological research where capturing subtle differences in attitude matters. It improves statistical reliability but requires more careful labeling.
</p>
<ol>
<li>Strongly Disagree</li>
<li>Moderately Disagree</li>
<li>Slightly Disagree</li>
<li>Neither Agree nor Disagree</li>
<li>Slightly Agree</li>
<li>Moderately Agree</li>
<li>Strongly Agree</li>
</ol>

<h2>When to Use a Likert Scale</h2>
<p style="text-align: justify;">
A Likert scale is the right tool when you need to measure characteristics that have no objective measurement — attitudes, opinions, satisfaction levels, or perceived likelihood. It is not appropriate when:
</p>
<ul>
<li>A simple yes/no question would fully answer your research question</li>
<li>You are measuring factual behaviors (e.g., &#8220;How many times per week do you exercise?&#8221; — use a numerical input instead)</li>
<li>Respondents lack sufficient knowledge of the topic to have a genuine opinion</li>
</ul>
<p style="text-align: justify;">
Use a Likert scale when you need to distinguish between degrees of agreement, not just direction. The difference between &#8220;Agree&#8221; and &#8220;Strongly Agree&#8221; often carries meaningful information in customer satisfaction and employee engagement research.
</p>

<h2>How to Design an Effective Likert Scale</h2>

<h3>Write Clear, Single-Focus Statements</h3>
<p style="text-align: justify;">
Each item must address exactly one idea. A statement like &#8220;The service was fast and the staff were friendly&#8221; is a double-barreled item — the respondent may agree with one half and disagree with the other, making their response uninterpretable.
</p>

<h3>Balance Your Scale</h3>
<p style="text-align: justify;">
A well-designed Likert scale includes an equal number of positively and negatively worded items. This counteracts <strong>acquiescence bias</strong> — the tendency of some respondents to agree with statements regardless of content. If all your items are positive, respondents who habitually agree will appear more satisfied than they actually are.
</p>

<h3>Avoid Leading Language</h3>
<p style="text-align: justify;">
Avoid adverbs like &#8220;very,&#8221; &#8220;extremely,&#8221; or &#8220;always&#8221; inside the item statement itself. &#8220;This website is extremely fast&#8221; will yield fewer &#8220;Strongly Agree&#8221; responses than &#8220;This website is fast,&#8221; not because respondents think differently but because the bar is higher.
</p>

<h3>Keep the Scale Consistent Throughout the Survey</h3>
<p style="text-align: justify;">
Switching between a 5-point and 7-point scale in the same questionnaire forces respondents to mentally reset and increases error rates. Choose one format and use it throughout.
</p>

<h2>Likert Scale Response Bias: What Can Distort Your Data</h2>
<p style="text-align: justify;">
Understanding bias is not optional for anyone analyzing Likert data — it directly affects whether your conclusions are valid.
</p>

<div style="overflow-x: auto;">
<table style="border-collapse: collapse; font-size: 0.95em; margin-top: 12px; width: 100%;">
<thead>
<tr style="background: rgb(37, 99, 235); color: white;">
<th style="padding: 10px 12px; text-align: left;">Bias Type</th>
<th style="padding: 10px 12px;">What Happens</th>
<th style="padding: 10px 12px;">How to Reduce It</th>
</tr>
</thead>
<tbody>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">Acquiescence bias</td>
<td style="padding: 9px 12px;">Respondents agree with statements regardless of content</td>
<td style="padding: 9px 12px;">Include negatively worded items; balance scale direction</td>
</tr>
<tr>
<td style="font-weight: 600; padding: 9px 12px;">Central tendency bias</td>
<td style="padding: 9px 12px;">Respondents cluster around the midpoint, avoiding extremes</td>
<td style="padding: 9px 12px;">Use an even-point scale to remove the neutral option when appropriate</td>
</tr>
<tr style="background: rgb(249, 250, 251);">
<td style="font-weight: 600; padding: 9px 12px;">Social desirability bias</td>
<td style="padding: 9px 12px;">Respondents choose the answer they think is most socially acceptable</td>
<td style="padding: 9px 12px;">Ensure anonymity; frame items neutrally</td>
</tr>
<tr>
<td style="font-weight: 600; padding: 9px 12px;">Extreme response bias</td>
<td style="padding: 9px 12px;">Some respondents always select the most extreme option</td>
<td style="padding: 9px 12px;">Use more scale points (7-point) to better distinguish genuine extremes</td>
</tr>
</tbody>
</table>
</div>

<h2>How to Analyze Likert Scale Data</h2>

<h3>Single Item vs. Multi-Item Scale: Different Rules Apply</h3>
<p style="text-align: justify;">
This is the most commonly misunderstood part of Likert analysis. A single Likert item produces <strong>ordinal data</strong> — the intervals between response options are not guaranteed to be equal. Calculating a mean on ordinal data is statistically questionable. For a single item, use:
</p>
<ul>
<li><strong>Median</strong> as your measure of central tendency</li>
<li><strong>Frequency tables and percentages</strong> for distribution</li>
<li><strong>Chi-square tests</strong> or <strong>Mann-Whitney U</strong> for group comparisons</li>
</ul>
<p style="text-align: justify;">
A full Likert scale (summed or averaged across multiple items) behaves more like interval data, especially with 5+ items and a reasonable sample size. In this case, parametric statistics become more defensible:
</p>
<ul>
<li><strong>Mean and standard deviation</strong> for descriptive summaries</li>
<li><strong>Cronbach&#8217;s alpha (α)</strong> to test internal consistency — aim for α > 0.7</li>
<li><strong>t-tests or ANOVA</strong> for group comparisons</li>
<li><strong>Spearman correlation</strong> for relationships between Likert scores and other variables</li>
</ul>

<h3>Cronbach&#8217;s Alpha: Checking if Your Scale Holds Together</h3>
<p style="text-align: justify;">
If you are using multiple Likert items to measure the same construct, run Cronbach&#8217;s alpha before reporting results. An alpha above 0.8 indicates strong internal consistency. Values between 0.7 and 0.8 are acceptable. Below 0.7 suggests your items are not measuring the same thing — revise or remove items with low item-total correlations.
</p>

<h3>Likert Scale Examples Across Research Domains</h3>

<p style="text-align: justify;"><strong>Customer satisfaction survey:</strong></p>
<p style="text-align: justify;"><em>&#8220;How satisfied are you with the cleanliness of our facilities?&#8221;</em></p>
<ul>
<li>Very Dissatisfied</li>
<li>Dissatisfied</li>
<li>Neither Satisfied nor Dissatisfied</li>
<li>Satisfied</li>
<li>Very Satisfied</li>
</ul>

<p style="text-align: justify;"><strong>Employee engagement survey:</strong></p>
<p style="text-align: justify;"><em>&#8220;To what extent do you agree: &#8216;The new company policy enhances employee productivity&#8217;?&#8221;</em></p>
<ul>
<li>Strongly Disagree</li>
<li>Disagree</li>
<li>Neither Agree nor Disagree</li>
<li>Agree</li>
<li>Strongly Agree</li>
</ul>

<p style="text-align: justify;"><strong>Online UX research:</strong></p>
<p style="text-align: justify;"><em>&#8220;Rate your agreement: &#8216;The online shopping experience was user-friendly and intuitive.'&#8221;</em></p>
<ul>
<li>Strongly Disagree</li>
<li>Disagree</li>
<li>Neither Agree nor Disagree</li>
<li>Agree</li>
<li>Strongly Agree</li>
</ul>

<h2>Advantages and Disadvantages of Likert Scales</h2>

<div style="overflow-x: auto;">
<table style="border-collapse: collapse; font-size: 0.95em; margin-top: 12px; width: 100%;">
<thead>
<tr style="background: rgb(37, 99, 235); color: white;">
<th style="padding: 10px 12px; text-align: left;">Advantages</th>
<th style="padding: 10px 12px; text-align: left;">Disadvantages</th>
</tr>
</thead>
<tbody>
<tr style="background: rgb(249, 250, 251);">
<td style="padding: 9px 12px;">Easy for respondents to understand and complete</td>
<td style="padding: 9px 12px;">Prone to acquiescence and social desirability bias</td>
</tr>
<tr>
<td style="padding: 9px 12px;">Produces quantitative data from subjective opinions</td>
<td style="padding: 9px 12px;">Ordinal data is not strictly interval — mean can be misleading</td>
</tr>
<tr style="background: rgb(249, 250, 251);">
<td style="padding: 9px 12px;">Flexible: measures agreement, frequency, satisfaction, likelihood</td>
<td style="padding: 9px 12px;">Central tendency bias reduces discrimination</td>
</tr>
<tr>
<td style="padding: 9px 12px;">Widely understood — high response rates</td>
<td style="padding: 9px 12px;">A single item cannot represent a full scale</td>
</tr>
<tr style="background: rgb(249, 250, 251);">
<td style="padding: 9px 12px;">Supports statistical analysis across groups</td>
<td style="padding: 9px 12px;">Does not capture why a respondent chose a particular point</td>
</tr>
</tbody>
</table>
</div>

<h2>Conclusion</h2>
<p style="text-align: justify;">
A Likert scale is one of the most versatile and reliable tools in survey research — when used correctly. The key decisions are choosing the right number of response points for your research goal, writing items that are balanced and unambiguous, and applying the correct statistical method depending on whether you are working with a single item or a multi-item scale. Whether you are measuring customer satisfaction, employee engagement, student attitudes, or any other opinion-based construct, the principles remain the same: clarity in item wording, consistency in format, and honesty about what ordinal data can and cannot tell you.
</p>

<div class="dldCo" id="download1">
  <div class="dldBx">
    <div class="dldTp">
      <div class="dldIm" data-text=".png" style="background-image: url(image_url_here);">
        <svg class="dldSv" viewbox="0 0 34 34">
          <circle class="b" cx="17" cy="17" r="15.92">
          <circle class="c dldPg" cx="17" cy="17" r="15.92">
        </circle></circle></svg>
      </div>
      <div class="dldIn">
        <span data-text="Name">Likert Scale.docs</span>
        <span data-text="Category">Word Document</span>
      </div>
    </div>
    <button class="dldBt dldDl" onclick="download("https://www.rstudiodatalab.com/p/download-2.html?~=JTdCJTIydXJsJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZkcml2ZS5nb29nbGUuY29tJTJGdWMlM0ZleHBvcnQlM0Rkb3dubG9hZCUyNmlkJTNEMTA1Njk1MTQwNDI3MzIyOTEzNjEwJTI2cnRwb2YlM0R0cnVlJTI2c2QlM0R0cnVlJTIyJTdE", "30", "false", "#download1")"><svg class="line" viewbox="0 0 24 24"><polyline points="8 17 12 21 16 17"><line x1="12" x2="12" y1="12" y2="21"><path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29"></path></line></polyline></svg></button>
    <button class="dldBt dldRt"><svg class="line" viewbox="0 0 24 24"><polyline points="23 4 23 10 17 10"><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></polyline></svg></button>
  </div>
  <div class="dldSl">
    <div class="dldMe"></div>
  </div>
</div>

<h2>Frequently Asked Questions</h2>

<p style="text-align: justify;"><strong>Q: What is the difference between a Likert scale and a Likert item?</strong></p>
<p style="text-align: justify;">A: A Likert item is a single rated statement. A Likert scale is the aggregate of multiple related items. The distinction matters for analysis: single items should use median and nonparametric tests; full scales can use mean and parametric tests.</p>

<p style="text-align: justify;"><strong>Q: How do you pronounce Likert?</strong></p>
<p style="text-align: justify;">A: The correct pronunciation is &#8220;LICK-ert,&#8221; not &#8220;LIKE-ert.&#8221; It is named after Rensis Likert, who created the scale in 1932.</p>

<p style="text-align: justify;"><strong>Q: Should I use a 5-point or 7-point Likert scale?</strong></p>
<p style="text-align: justify;">A: For general surveys and applied research, a 5-point scale is easier for respondents and produces reliable results. For academic or psychological research where detecting subtle attitude differences matters, a 7-point scale offers better statistical discrimination. Research comparing 5-point and 7-point scales finds that both produce similar mean scores once rescaled — so the choice depends more on respondent context than statistical superiority.</p>

<p style="text-align: justify;"><strong>Q: Can you calculate the mean from Likert scale data?</strong></p>
<p style="text-align: justify;">A: For a single Likert item, technically no — the data is ordinal, so the median is more appropriate. For a complete Likert scale (multiple items summed), calculating the mean is widely practiced and generally acceptable, especially with a sample size above 30 and if the data distribution is approximately normal.</p>

<p style="text-align: justify;"><strong>Q: What is acquiescence bias in Likert scales?</strong></p>
<p style="text-align: justify;">A: Acquiescence bias is the tendency of some respondents to agree with statements regardless of content. It is reduced by including both positively and negatively worded items in your scale, so that habitual agreement on one item is balanced by habitual agreement on an item that pulls in the opposite direction.</p>

<p style="text-align: justify;"><strong>Q: Are Likert scale questions suitable for all types of research?</strong></p>
<p style="text-align: justify;">A: Likert scales work well in social sciences, market research, psychology, education research, healthcare, and UX research. They are not appropriate when you need objective behavioral counts or factual data — use open-ended questions or numerical inputs for those cases.</p>

<p style="text-align: justify;"><strong>Q: Is it necessary to include a neutral response option in a Likert scale?</strong></p>
<p style="text-align: justify;">A: No. Including a neutral option (odd-point scale) allows genuinely ambivalent respondents to express that accurately. Removing it (even-point scale) forces a directional choice, which can reduce central tendency bias but may frustrate respondents who truly have no strong view. Choose based on whether neutrality is meaningful in your research context.</p>

<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "BlogPosting",
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://www.rstudiodatalab.com/2023/07/Likert-Scale.html"
  },
  "headline": "Likert Scale: Definition, Examples & Complete Guide",
  "description": "Learn what a Likert scale is, see examples of 4, 5, 6, and 7-point formats, and find out how to design, use, and analyze one. Free template included.",
  "image": {
    "@type": "ImageObject",
    "url": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEje02Hn87SzxgBr_lLBu7UeDc1qob7R_O8JN6cpwFqMacbvGomUp0Dcg_4EvC99ooP_mcQ6DSyYBGeIue47Jf71vm1GVRCh5eSE_lcE_os8oEZ88zrTxK1f2B22Ezs25OkKcPmtNrf0XImv8eBYZWShjM_cM9COW0-CVuVpVoj-wtzO_EY0hZkbBWymiJE/s1200/Feedback%20scale,%20satisfaction%20rating%20design%20(Instagram%20Post).webp",
    "width": "1200",
    "height": "640"
  },
  "author": {
    "@type": "Person",
    "name": "Zubair Goraya"
  },
  "publisher": {
    "@type": "Organization",
    "name": "RStudio Data Lab",
    "logo": {
      "@type": "ImageObject",
      "url": "https://blogger.googleusercontent.com/img/a/AVvXsEiCXGi5dTbTiGMtIoMBoUIdBLlK8YKecPFRRFjiZxHQUt5TTE2CZV4mYhDxAglSpQiGmiugEqp2kkDIzYAjCdGvRe3S3ms4gYzXgcb1EKzOIqj6h2JNExebJ4_NE5Ch4BGTECKIxrM63T6lNElmxUbzwKSNEtQ8VFt-oy4iuH8feMki83oJG6GgBQIb4t0=w200",
      "width": "200",
      "height": "50"
    }
  },
  "datePublished": "2023-07-16",
  "dateModified": "2026-06-18"
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is the difference between a Likert scale and a Likert item?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A Likert item is a single rated statement. A Likert scale is the aggregate of multiple related items. Single items should use median and nonparametric tests; full scales can use mean and parametric tests."
      }
    },
    {
      "@type": "Question",
      "name": "How do you pronounce Likert?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The correct pronunciation is LICK-ert, not LIKE-ert. It is named after Rensis Likert, who created the scale in 1932."
      }
    },
    {
      "@type": "Question",
      "name": "Should I use a 5-point or 7-point Likert scale?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "For general surveys, a 5-point scale is easier for respondents. For academic research requiring fine discrimination between attitudes, a 7-point scale is preferred. Both produce similar mean scores once rescaled."
      }
    },
    {
      "@type": "Question",
      "name": "Can you calculate the mean from Likert scale data?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "For a single Likert item, the median is more appropriate because the data is ordinal. For a multi-item Likert scale with a reasonable sample size, calculating the mean is widely accepted in practice."
      }
    },
    {
      "@type": "Question",
      "name": "What is acquiescence bias in Likert scales?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Acquiescence bias is the tendency of respondents to agree with statements regardless of content. It is reduced by including both positively and negatively worded items in the same scale."
      }
    },
    {
      "@type": "Question",
      "name": "Is it necessary to include a neutral response option in a Likert scale?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. An odd-point scale includes a neutral midpoint; an even-point scale forces a directional choice. The decision depends on whether genuine neutrality is meaningful in your research context."
      }
    }
  ]
}
</script>
<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.rstudiodatalab.com/2023/07/Likert-Scale.html"> RStudioDataLab</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/likert-scale-questions-your-in-depth-guide/">Likert Scale Questions: Your In-Depth Guide</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402108</post-id>	</item>
		<item>
		<title>2026 Rousseeuw Prize for Statistics Awarded to R Core Team for Transforming Statistics Computing Worldwide</title>
		<link>https://www.r-bloggers.com/2026/06/2026-rousseeuw-prize-for-statistics-awarded-to-r-core-team-for-transforming-statistics-computing-worldwide/</link>
		
		<dc:creator><![CDATA[Lauren Livingston]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:09:44 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://r-posts.com/?p=19312</guid>

					<description><![CDATA[<p>The Rousseeuw Prize honors five pioneering developers for nearly three decades of unpaid work building R, the foundational open-source computing language behind artificial intelligence, healthcare, and economic decision-making. The $1 million Rousseeuw Prize for Statistics recognizes three decades of foundational work that transformed how statistical methods are developed, validated, and shared ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/2026-rousseeuw-prize-for-statistics-awarded-to-r-core-team-for-transforming-statistics-computing-worldwide/">2026 Rousseeuw Prize for Statistics Awarded to R Core Team for Transforming Statistics Computing Worldwide</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://r-posts.com/2026-rousseeuw-prize-for-statistics-awarded-to-r-core-team-for-transforming-statistics-computing-worldwide/"> R-posts.com</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><i><span style="font-weight: 400">The Rousseeuw Prize honors five pioneering developers for nearly three decades of unpaid work building R, the foundational open-source computing language behind artificial intelligence, healthcare, and economic decision-making.</span></i></p>
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">The $1 million Rousseeuw Prize for Statistics recognizes three decades of foundational work that transformed how statistical methods are developed, validated, and shared globally.</span></li>
	<li style="font-weight: 400"><span style="font-weight: 400">R, the open-source statistical computing language, underpins modern AI development, pharmaceutical research, financial modeling, and global scientific analysis.</span></li>
	<li style="font-weight: 400"><span style="font-weight: 400">Used by organizations including the U.S. Food and Drug Administration, major pharmaceutical companies, and global central banks, R has become the trusted infrastructure for high-stakes analysis because it is stable, auditable, and reproducible.</span></li>
</ul>
<p><b>NEW YORK – June 17, 2026 </b><span style="font-weight: 400">— Five members of the R Core Team have been awarded the prestigious </span><a href="https://www.rousseeuwprize.org/" rel="nofollow" target="_blank"><span style="font-weight: 400">Rousseeuw Prize for Statistics</span></a><span style="font-weight: 400"> for their decades of work building and maintaining the </span><a href="https://www.r-project.org/" rel="nofollow" target="_blank"><span style="font-weight: 400">R Project</span></a><span style="font-weight: 400">, “R,” a free and open-source statistical computing language used across global research institutions, healthcare systems, financial organizations, and technology companies. The Rousseeuw Prize is an international award recognizing major contributions to statistical research. </span></p>
<p><span style="font-weight: 400">The 2026 </span><span style="font-weight: 400">Rousseeuw Prize </span><span style="font-weight: 400">honorees are:</span></p>
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">Brian D. Ripley, emeritus professor at the University of Oxford</span></li>
	<li style="font-weight: 400"><span style="font-weight: 400">Martin Maechler, emeritus professor at ETH Zurich</span></li>
	<li style="font-weight: 400"><span style="font-weight: 400">Kurt Hornik, department chair at WU Vienna University of Economics and Business</span></li>
	<li style="font-weight: 400"><span style="font-weight: 400">Peter Dalgaard, professor at Copenhagen Business School</span></li>
	<li style="font-weight: 400"><span style="font-weight: 400">Luke Tierney, professor at the University of Iowa</span></li>
</ul>
<p><span style="font-weight: 400">The five laureates receive half of the prize money because they are deemed to have made the longest sustained contributions to the R project; the other half of the prize is shared among the many others who have been active on the R Core Team.</span></p>
<p><span style="font-weight: 400">Together, the R Project volunteers have spent the last 27 years and a collective 28,000 coding hours on R, developing an open-source programming language and software environment that transformed statistics from a proprietary corporate tool into a global public good. The software is relied upon by organizations including the U.S. Food and Drug Administration, pharmaceutical companies, and central banks such as the European Central Bank and the Bank of England.</span></p>
<p><span style="font-weight: 400">The award recognizes the team’s role in making advanced statistical tools widely accessible. By keeping R free and open-source under the GNU General Public License, the R Core Team removed many of the financial barriers that have historically limited access to advanced analytics software. Due to this increased accessibility, hundreds of thousands of users including researchers, students, hospitals, public health organizations, and governments around the world are able to utilize the same statistical tools regardless of institutional resources. In addition, they use R to share transcripts of their data analyses, allowing one user’s workflows to power other users data analyses everywhere around the world. The frictionless spread of these transcripts has powered countless educational data science projects globally and hundreds of course textbooks at the PhD and Master’s level. In a recent twist, it’s not only humans who use R: AI data analyst `agents’ have been learning from the massive volume of published R transcripts and are now able to assist with many everyday data analysis tasks. </span></p>
<p><span style="font-weight: 400">“Long before AI became a global conversation, the R Core Team was building the statistical infrastructure that made today’s data-driven world possible,” said Stanford University statistics professor and leading statistician David Donoho, PhD. “This team’s stewardship of R created an open and trusted foundation for research across disciplines and continents. Few innovations have had such a profound effect on how knowledge is produced, shared, and validated in the modern era.”</span></p>
<p><span style="font-weight: 400">Named after Professor Peter Rousseeuw, a pioneering Belgian statistician known for his foundational work in robust statistics and data analysis, the Rousseeuw Prize for Statistics recognizes innovations that have transformed the understanding and application of data for the benefit of society. Past laureates include internationally renowned statisticians and researchers whose work has advanced fields ranging from epidemiology and artificial intelligence to public policy and scientific discovery.</span></p>
<p><span style="font-weight: 400">For more information, visit </span><a href="https://www.rousseeuwprize.org/" rel="nofollow" target="_blank"><span style="font-weight: 400">https://www.rousseeuwprize.org/</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">###</span></p>
<p><span style="font-weight: 400">Media Contact:</span></p>
<p><a href="mailto:rousseeuwprize@ampublicrelations.com" rel="nofollow" target="_blank"><span style="font-weight: 400">rousseeuwprize@ampublicrelations.com</span></a></p><hr style="border-top: black solid 1px" /><a href="http://r-posts.com/2026-rousseeuw-prize-for-statistics-awarded-to-r-core-team-for-transforming-statistics-computing-worldwide/" rel="nofollow" target="_blank">2026 Rousseeuw Prize for Statistics Awarded to R Core Team for Transforming Statistics Computing Worldwide</a> was first posted on June 18, 2026 at 5:09 am.<br />
<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://r-posts.com/2026-rousseeuw-prize-for-statistics-awarded-to-r-core-team-for-transforming-statistics-computing-worldwide/"> R-posts.com</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/2026-rousseeuw-prize-for-statistics-awarded-to-r-core-team-for-transforming-statistics-computing-worldwide/">2026 Rousseeuw Prize for Statistics Awarded to R Core Team for Transforming Statistics Computing Worldwide</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402090</post-id>	</item>
		<item>
		<title>{talib}: Interactive financial charts</title>
		<link>https://www.r-bloggers.com/2026/06/talib-interactive-financial-charts/</link>
		
		<dc:creator><![CDATA[Serkan Korkmaz]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:09:11 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://r-posts.com/?p=19168</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> {talib} is a new R package built on TA-Lib, which is now available on CRAN. The R-package is targeted at individuals and, perhaps, institutions who, in some form or the other, interacts with the financial markets using technical analysis. The library is built with minimal dependencies for long-term stability and ...</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/talib-interactive-financial-charts/">{talib}: Interactive financial charts</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://r-posts.com/talib-interactive-financial-charts/"> R-posts.com</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://github.com/serkor1/ta-lib-R" rel="nofollow" target="_blank">{talib}</a> is a new
<code>R</code> package built on <a href="https://github.com/TA-Lib/ta-lib" rel="nofollow" target="_blank">TA-Lib</a>, which is now
available on CRAN. The <code>R</code>-package is targeted at individuals
and, perhaps, institutions who, in some form or the other, interacts
with the financial markets using technical analysis.</p>
<p>The library is built with minimal dependencies for long-term
stability and freedom in mind. All functions are built around
<code>data.frame</code>– and <code>matrix</code>-classes which are
portable to all other data-containers with minimal effort.</p>
<p>Everything in the library is built ‘bottom-up’ for maximum speed and
memory efficiency. Each indicator interacts directly with R’s C API via
<code>.Call()</code>.</p>
<p>In this blog post I will give a brief introduction to the charting
interface which is built to mimick the behaviour of base
<code>R</code>’s plotting API.</p>
<div id="a-quick-introduction-to-charts" class="section level2">
<h2>A quick introduction to charts</h2>
<p>In this section I will briefly introduce the most important aspects
of the charting, ‘quality of life’-features and themes. Below is a
simple starting point; charting BTC:</p>
<pre>talib::chart(
  talib::BTC
)</pre>
<p><img decoding="async" src="https://i2.wp.com/i.imgur.com/sgaplrf.png?w=578&#038;ssl=1" data-recalc-dims="1" /><!-- --></p>
<p><code>chart()</code> returns a candlestick chart by default. Below
are the <code>formals</code>:</p>
<pre>str(formals(talib::chart))
#&gt; Dotted pair list of 5
#&gt;  $ x    : symbol 
#&gt;  $ type : chr &quot;candlestick&quot;
#&gt;  $ idx  : NULL
#&gt;  $ title: symbol 
#&gt;  $ ...  : symbol</pre>
<div id="modifying-themes" class="section level3">
<h3>Modifying themes</h3>
<pre>talib::set_theme(&quot;hawks_and_doves&quot;)

talib::chart(
  talib::BTC
)</pre>
<p><img decoding="async" src="https://i2.wp.com/i.imgur.com/EqDWSqw.png?w=578&#038;ssl=1" data-recalc-dims="1" /><!-- --></p>
</div>
</div>
<div id="charting-indicators" class="section level2">
<h2>Charting indicators</h2>
<pre>{
  talib::chart(talib::BTC)
  talib::indicator(talib::SMA, n = 7)
  talib::indicator(talib::SMA, n = 14)
  talib::indicator(talib::SMA, n = 21)
  talib::indicator(talib::SMA, n = 28)
  talib::indicator(talib::MACD)
  talib::indicator(talib::trading_volume)
}</pre>
<p><img decoding="async" src="https://i1.wp.com/i.imgur.com/ixQ9OhQ.png?w=578&#038;ssl=1" data-recalc-dims="1" /><!-- --></p>
</div>
<div id="installation" class="section level2">
<h2>Installation</h2>
<p><code>{talib}</code> is finally on CRAN, and can be installed as
follows:</p>
<pre>install.packages(&quot;talib&quot;)</pre>
<p>It can also be built from source with additional
<code>CMake</code>-flags:</p>
<pre>install.packages(
  &quot;talib&quot;,
  type = &quot;source&quot;,
  configure.args = &quot;-O3 -march=native&quot;
)</pre>
</div>
<div id="contributing-and-submitting-bug-reports" class="section level2">
<h2>Contributing and submitting bug-reports</h2>
<p><code>{talib}</code> is still in its early stage so contributions,
even if small, bug-reports, suggestions and critiques are gratefully
accepted.</p>
<p>Visit the repository here: <a href="https://github.com/serkor1/ta-lib-R" class="uri" rel="nofollow" target="_blank">https://github.com/serkor1/ta-lib-R</a>.</p>
<p><sup>Created on 2026-04-29 with <a href="https://reprex.tidyverse.org/" rel="nofollow" target="_blank">reprex v2.1.1</a></sup></p>
</div><hr style="border-top: black solid 1px" /><a href="http://r-posts.com/talib-interactive-financial-charts/" rel="nofollow" target="_blank">{talib}: Interactive financial charts</a> was first posted on June 18, 2026 at 5:09 am.<br />
<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://r-posts.com/talib-interactive-financial-charts/"> R-posts.com</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/talib-interactive-financial-charts/">{talib}: Interactive financial charts</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402092</post-id>	</item>
		<item>
		<title>Announcing shiny.webawesome: a web UI package for R/Shiny</title>
		<link>https://www.r-bloggers.com/2026/06/announcing-shiny-webawesome-a-web-ui-package-for-r-shiny/</link>
		
		<dc:creator><![CDATA[M. B. Anand]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:09:03 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://r-posts.com/?p=19179</guid>

					<description><![CDATA[<p>shiny.webawesome brings Web Awesome to R Shiny through generated wrappers, reactive bindings, and a bundled runtime. It aims for complete component support while staying close enough to upstream that the Web Awesome docs and examples are directly useful in everyday package use. CRAN &#124; R-universe &#124; Package website &#124; Source repository Background ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/announcing-shiny-webawesome-a-web-ui-package-for-r-shiny/">Announcing shiny.webawesome: a web UI package for R/Shiny</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://r-posts.com/announcing-shiny-webawesome-a-web-ui-package-for-r-shiny/"> R-posts.com</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><code>shiny.webawesome</code> brings Web Awesome to R Shiny through generated wrappers, reactive bindings, and a bundled runtime. It aims for complete component support while staying close enough to upstream that the Web Awesome docs and examples are directly useful in everyday package use.</p>
<p><a href="https://cran.r-project.org/package=shiny.webawesome" rel="nofollow" target="_blank">CRAN</a> | <a href="https://mbanand.r-universe.dev/shiny.webawesome" rel="nofollow" target="_blank">R-universe</a> | <a href="https://www.shiny-webawesome.org/" rel="nofollow" target="_blank">Package website</a> | <a href="https://github.com/mbanand/shiny.webawesome" rel="nofollow" target="_blank">Source repository</a></p>
<h2>Background</h2>
<p><code>shiny.webawesome</code> started from a perceived gap: Shiny would benefit from a UI library that feels modern, visually polished, and broad enough to support a full app coherently. Web Awesome was a strong fit because it combines rich components, layout and styling utilities, and detailed upstream documentation with a standards-based web-components structure that is straightforward to track from R. That makes it easier for the package to stay close to upstream while still fitting naturally into Shiny.</p>
<h2>The Whole Game</h2>
<p>Here’s a screenshot of a simple, complete example app using <code>shiny.webawesome</code>. The full live app and code are available in an article at: <a href="https://mbanand.github.io/ghpages/announcement/" rel="nofollow" target="_blank">https://mbanand.github.io/ghpages/announcement/</a>..</p>
<p><a href="https://mbanand.github.io/ghpages/announcement/" rel="nofollow" target="_blank"><img loading="lazy" fetchpriority="high" decoding="async" src="https://i1.wp.com/r-posts.com/wp-content/uploads/2026/05/shiny-webawesome-example-328x300.png?resize=328%2C300" alt="Screenshot of a shiny.webawesome example app showing a control sidebar and coordinated chart, summary, and details views." width="328" height="300" class="alignnone size-medium wp-image-19178" srcset_temp="https://i1.wp.com/r-posts.com/wp-content/uploads/2026/05/shiny-webawesome-example-328x300.png?resize=328%2C300 328w, http://r-posts.com/wp-content/uploads/2026/05/shiny-webawesome-example-450x412.png 450w, http://r-posts.com/wp-content/uploads/2026/05/shiny-webawesome-example-768x703.png 768w, http://r-posts.com/wp-content/uploads/2026/05/shiny-webawesome-example.png 925w" sizes="(max-width: 328px) 100vw, 328px" data-recalc-dims="1" /></a></p>
<p>This example showcases many of the facilities available in the package:</p>
<ul>
	<li>a visually rich component library</li>
	<li>direct use of Web Awesome layout utilities such as <code>wa-stack</code>, <code>wa-cluster</code>, <code>wa-gap-*</code>, and <code>wa-align-*</code> classes</li>
	<li>styling through Web Awesome design tokens and classes such as <code>--wa-color-*</code>, <code>--wa-font-*</code>, and <code>wa-body-*</code></li>
	<li>reactive Shiny input bindings</li>
	<li>helpers for calling methods on HTML elements, setting properties, and injecting simple JavaScript snippets</li>
</ul>
<h2>Design Philosophy</h2>
<p><code>shiny.webawesome</code> is designed to stay close to upstream Web Awesome. Most component wrappers are generated from Web Awesome metadata, which helps preserve upstream names, structure, and behavior while translating the interface into normal R conventions such as <code>snake_case</code>.</p>
<p>That close alignment has a practical benefit: when you want deeper details, examples, or component-specific guidance, you can usually go straight to the upstream Web Awesome documentation and apply what you find directly in <code>shiny.webawesome</code>. The package currently supports all Web Awesome components, so the upstream docs are a practical reference for day-to-day use.</p>
<p>To support the server-client model of Shiny, the package adds a small set of page and layout helpers, curated reactive bindings, and a narrow command layer for cases where browser-side interaction goes beyond the generated wrappers.</p>
<p>The result is a package with a clear default path. Use generated wrappers for ordinary UI, use bindings for meaningful reactive state, and reach for commands or small JavaScript glue when the app needs them.</p>
<h2>Shiny Bindings</h2>
<p><code>shiny.webawesome</code> does not forward every browser event and every detail of component telemetry into Shiny. Much component state and interaction detail is better handled locally in the browser rather than turned into server messages. Consequently, the package exposes only a curated set of Shiny bindings that fit Shiny’s reactive model, with an emphasis on meaningful committed state rather than low-level browser event streams.</p>
<p>In the most common case, a binding publishes a durable semantic value. A select reports its current value, a dialog can report whether it is open, and a tree can report the currently selected item ids. The key idea is that Shiny receives the state the app actually cares about, not the raw event name that happened to produce it.</p>
<p>Some components are better treated as actions than values. A button is the clearest example: in Shiny, it behaves like a Shiny action input, with each click producing a new input event. A small number of components need both action semantics and a separate value. A dropdown, for example, may need to trigger reactivity on every choice, including repeated selections of the same item, while also exposing the latest selected value.</p>
<p>This design keeps reactive messaging to the server smaller, clearer, and easier to reason about. If an interaction belongs naturally in Shiny’s input model, <code>shiny.webawesome</code> will expose it as a binding. If it is more naturally a browser-side concern, it is usually a better fit for the command layer or a small amount of JavaScript glue.</p>
<p>For the full binding categories, semantics, and examples, see the package article: <a href="https://www.shiny-webawesome.org/articles/shiny-bindings.html" rel="nofollow" target="_blank">Shiny Bindings</a>.</p>
<h2>Command API</h2>
<p><code>shiny.webawesome</code> covers the most common interaction patterns through generated wrappers, Shiny bindings, and update helpers. But sometimes an app still needs to reach into a live browser element directly: set a property, call a method, or add a small browser-local JavaScript snippet.</p>
<p>For those cases, the package provides a narrow command API. The two main server-side helpers are <code>wa_set_property()</code> and <code>wa_call_method()</code>. They let Shiny code send one-way commands to a browser element identified by <code>id</code>, either by assigning a value to a live property or invoking a browser-side method.</p>
<p>If a component already has a binding or update helper, that should usually remain the first choice. The command layer is for the cases that fall just outside those built-in paths, where the simplest solution is still to tell the existing browser component to do one specific thing.</p>
<p>The package also includes <code>wa_js()</code> for a different kind of job: small, app-local JavaScript glue. That is useful when the missing piece is browser-side logic such as listening for an event, reading live component state, or publishing a derived value back to Shiny with <code>Shiny.setInputValue()</code>.</p>
<p>For more detail and examples, see the package article: <a href="https://www.shiny-webawesome.org/articles/command-api.html" rel="nofollow" target="_blank">Command API</a>.</p>
<h2>Conclusion</h2>
<p><code>shiny.webawesome</code> brings a visually rich component library into Shiny while staying close to upstream Web Awesome. That combination gives polished components, useful layout and styling utilities, and a workflow where upstream documentation and examples remain directly relevant throughout app development.</p>
<p>For more examples, longer articles, and full reference material, see the package website: <a href="https://www.shiny-webawesome.org/" rel="nofollow" target="_blank">shiny-webawesome.org</a>.</p><hr style="border-top: black solid 1px" /><a href="http://r-posts.com/announcing-shiny-webawesome-a-web-ui-package-for-r-shiny/" rel="nofollow" target="_blank">Announcing shiny.webawesome: a web UI package for R/Shiny</a> was first posted on June 18, 2026 at 5:09 am.<br />
<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://r-posts.com/announcing-shiny-webawesome-a-web-ui-package-for-r-shiny/"> R-posts.com</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/announcing-shiny-webawesome-a-web-ui-package-for-r-shiny/">Announcing shiny.webawesome: a web UI package for R/Shiny</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402094</post-id>	</item>
		<item>
		<title>RStudio AI That Doesn’t Cost a Penny: llmcoder vs. Posit AI Assistant</title>
		<link>https://www.r-bloggers.com/2026/06/rstudio-ai-that-doesnt-cost-a-penny-llmcoder-vs-posit-ai-assistant/</link>
		
		<dc:creator><![CDATA[Shiyang Zheng]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:08:52 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://r-posts.com/?p=19190</guid>

					<description><![CDATA[<p>Introduction If you’re an R user, you’ve probably experienced these moments: You’re writing code and forgot the exact syntax for a function Your code throws an error and you’re staring at a confusing error message You have a block of code but want to understand what ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/rstudio-ai-that-doesnt-cost-a-penny-llmcoder-vs-posit-ai-assistant/">RStudio AI That Doesn’t Cost a Penny: llmcoder vs. Posit AI Assistant</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://r-posts.com/rstudio-ai-that-doesnt-cost-a-penny-llmcoder-vs-posit-ai-assistant/"> R-posts.com</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>Introduction</h2>
<p>If you’re an R user, you’ve probably experienced these moments:</p>
<ul>
	<li>You’re writing code and forgot the exact syntax for a function</li>
	<li>Your code throws an error and you’re staring at a confusing error message</li>
	<li>You have a block of code but want to understand what it does in plain English</li>
	<li>You want to chat with an AI assistant about your data analysis, but don’t want to leave RStudio</li>
</ul>
<p><strong>llmcoder</strong> is an RStudio addin that solves all of these problems by integrating Large Language Model (LLM) assistance directly into your RStudio workflow, and more importantly, it’s <strong>FREE</strong>!</p>
<p>In this post, I’ll show you how <strong>llmcoder</strong> can speed up your R coding and make your workflow smoother.</p>
<p><strong>Watch a quick demo of llmcoder in action:</strong></p>
<p><a href="https://youtu.be/SRzjaURbKCw" rel="nofollow" target="_blank">https://youtu.be/SRzjaURbKCw</a></p>
<hr />
<h2>Installation</h2>
<p>You can install llmcoder from GitHub:</p>
<pre># Install remotes if you haven't already
install.packages(&quot;remotes&quot;)

# Install llmcoder
remotes::install_github(&quot;ShiyangZheng/llmcoder&quot;)</pre>
<p>Load the package:</p>
<pre>library(llmcoder)</pre>
<hr />
<h2>Feature 1: Generate R Code from Inline Comments</h2>
<p>Ever wish you could just type what you want in plain English and get R code instantly?</p>
<p><strong>How to use:</strong></p>
<ol>
	<li>Type a comment describing what you want</li>
	<li>Place your cursor on that line</li>
	<li>Use the <strong>Addins menu</strong> and select “Generate Code from Comment”</li>
</ol>
<p><strong>Example:</strong></p>
<pre># Load the mtcars dataset and create a scatter plot of mpg vs wt, colored by number of cylinders</pre>
<p>After running the addin, the comment is replaced with:</p>
<pre>library(ggplot2)
data(mtcars)
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3, alpha = 0.8) +
  labs(
    title = &quot;Fuel Efficiency vs Weight by Cylinder Count&quot;,
    x = &quot;Weight (1000 lbs)&quot;,
    y = &quot;Miles per Gallon&quot;,
    color = &quot;Cylinders&quot;
  ) +
  theme_minimal()</pre>
<p><strong>No more switching to ChatGPT or copying code from Stack Overflow!</strong></p>
<hr />
<h2>Feature 2: Fix Console Errors with LLM Assistance</h2>
<p>We’ve all been there – a cryptic error message and you’re not sure what went wrong.</p>
<p><strong>How to use:</strong></p>
<ol>
	<li>Run code that produces an error</li>
	<li>The error appears in the console</li>
	<li>Use the <strong>Addins menu</strong> and select “Fix Error with LLM”</li>
</ol>
<p><strong>Example:</strong></p>
<pre>library(dplyr)
data %&gt;%
  filter(cyl == 4) %&gt;%
  summary()
# Error: object 'data' not found</pre>
<p>llmcoder captures the error and sends it to the LLM, which returns an explanation and suggests:</p>
<pre>mtcars %&gt;% filter(cyl == 4) %&gt;% summary()</pre>
<hr />
<h2>Feature 3: Explain Selected Code in Plain English</h2>
<p>Sometimes you inherit code from a colleague or find a Stack Overflow answer and want to understand what it does.</p>
<p><strong>How to use:</strong></p>
<ol>
	<li>Select a block of code in the editor</li>
	<li>Use the <strong>Addins menu</strong> and select “Explain Code”</li>
</ol>
<p><strong>Example:</strong></p>
<pre>mtcars %&gt;%
  group_by(cyl) %&gt;%
  summarize(
    mean_mpg = mean(mpg, na.rm = TRUE),
    sd_mpg = sd(mpg, na.rm = TRUE),
    count = n()
  ) %&gt;%
  arrange(desc(mean_mpg))</pre>
<p>llmcoder returns:</p>
<ol>
	<li>Takes the built-in <code>mtcars</code> dataset</li>
	<li>Groups the data by the number of cylinders (<code>cyl</code>)</li>
	<li>Calculates the mean and standard deviation of miles per gallon (<code>mpg</code>) for each group</li>
	<li>Arranges the results in descending order of mean fuel efficiency</li>
</ol>
<hr />
<h2>Feature 4: Multi-Turn Chat Panel with Session Context</h2>
<p>This is the flagship feature. llmcoder includes a <strong>Chat Panel</strong> that understands your current R session.</p>
<p><strong>How to open:</strong> Use the <strong>Addins menu</strong> and select “Open Chat Panel”</p>
<p><strong>What makes it special?</strong></p>
<p>The Chat Panel is <strong>session-aware</strong>:</p>
<ul>
	<li>It knows which packages you have loaded</li>
	<li>It knows what objects are in your global environment</li>
	<li>It can read the contents of your current script</li>
	<li>It has access to your recent console history</li>
</ul>
<p><strong>Example conversation:</strong></p>
<p><strong>You:</strong> What’s the correlation between mpg and wt in mtcars?</p>
<p><strong>AI:</strong> The correlation between mpg and wt in the mtcars dataset is -0.87, indicating a strong negative relationship. As weight increases, fuel efficiency decreases.</p>
<pre>cor(mtcars$mpg, mtcars$wt, use = &quot;complete.obs&quot;)</pre>
<p><strong>Want to see the Chat Panel in action?</strong> Watch this demo:<br />
<a href="https://youtu.be/zP-RuCN3q14" rel="nofollow" target="_blank">https://youtu.be/zP-RuCN3q14</a></p>
<hr />
<h2>Supported LLM Providers</h2>
<p>llmcoder supports <strong>multiple LLM providers</strong> – you can choose the one that works best for you:</p>
<table>
<tbody>
<tr>
<th>Provider</th>
<th>API Key</th>
<th>Notes</th>
</tr>
<tr>
<td>OpenAI (GPT-4/3.5)</td>
<td>Yes</td>
<td>Most popular</td>
</tr>
<tr>
<td>Anthropic (Claude)</td>
<td>Yes</td>
<td>Great for long conversations</td>
</tr>
<tr>
<td>DeepSeek</td>
<td>Yes</td>
<td>Cost-effective</td>
</tr>
<tr>
<td>Groq</td>
<td>Yes</td>
<td>Very fast inference</td>
</tr>
<tr>
<td>Together AI</td>
<td>Yes</td>
<td>Open-source models</td>
</tr>
<tr>
<td>OpenRouter</td>
<td>Yes</td>
<td>Access multiple models</td>
</tr>
<tr>
<td><strong>Ollama</strong></td>
<td><strong>No</strong></td>
<td><strong>Fully local, no API key!</strong></td>
</tr>
<tr>
<td>Custom endpoint</td>
<td>Yes</td>
<td>LM Studio, vLLM, llama.cpp</td>
</tr>
</tbody>
</table>
<p><strong>Privacy note:</strong> If you use Ollama, all processing happens <strong>locally on your machine</strong>. No data is sent to external servers.</p>
<hr />
<h2>Customization: Choose Your Prompt Style</h2>
<p>The Chat Panel allows you to select different <strong>prompt styles</strong>:</p>
<ul>
	<li><strong>General Assistant</strong>: Best for general questions</li>
	<li><strong>R Code Helper</strong>: Focuses on writing clean, idiomatic R code</li>
	<li><strong>Statistics Advisor</strong>: Helps with statistical concepts and test selection</li>
	<li><strong>Research (Psycho)</strong>: Tailored for psycholinguistics researchers</li>
</ul>
<hr />
<h2>Why llmcoder?</h2>
<p>There are many AI coding assistants out there (Copilot, Cursor, etc.), so why llmcoder?</p>
<ol>
	<li><strong>Native RStudio integration</strong>: No need to switch to another app or browser tab</li>
	<li><strong>Session-aware</strong>: The LLM knows what you’re working on</li>
	<li><strong>Multiple LLM providers</strong>: Choose the one you prefer (or use a local model for privacy)</li>
	<li><strong>Open source</strong>: MIT license, free to use and modify</li>
	<li><strong>Designed for R users</strong>: Not a generic coding assistant – it understands R-specific workflows</li>
</ol>
<hr />
<h2>Call to Action</h2>
<p>Ready to try llmcoder?</p>
<pre>remotes::install_github(&quot;ShiyangZheng/llmcoder&quot;)</pre>
<p>GitHub: <a href="https://github.com/ShiyangZheng/llmcoder" rel="nofollow" target="_blank">https://github.com/ShiyangZheng/llmcoder</a></p>
<p>If you encounter any bugs or have feature requests, please file an issue: <a href="https://github.com/ShiyangZheng/llmcoder/issues" rel="nofollow" target="_blank">https://github.com/ShiyangZheng/llmcoder/issues</a></p>
<p><strong>Star the repo</strong> if you find it useful!</p>
<hr />
<h2>About the Author</h2>
<p>Shiyang Zheng is a PhD student in Psycholinguistics at the University of Nottingham. His research focuses on idiom acquisition and computational modeling. He built llmcoder to make R coding easier for himself and the R community.</p>
<ul>
	<li>GitHub: <a href="https://github.com/ShiyangZheng" rel="nofollow" target="_blank">@ShiyangZheng</a></li>
	<li>Academic website: <a href="https://shiyangzheng.top/" rel="nofollow" target="_blank">shiyangzheng.top</a></li>
	<li>ORCID: <a href="https://orcid.org/0000-0003-0511-4683" rel="nofollow" target="_blank">0000-0003-0511-4683</a></li>
</ul><hr style="border-top: black solid 1px" /><a href="http://r-posts.com/rstudio-ai-that-doesnt-cost-a-penny-llmcoder-vs-posit-ai-assistant/" rel="nofollow" target="_blank">RStudio AI That Doesn’t Cost a Penny: llmcoder vs. Posit AI Assistant</a> was first posted on June 18, 2026 at 5:08 am.<br />
<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://r-posts.com/rstudio-ai-that-doesnt-cost-a-penny-llmcoder-vs-posit-ai-assistant/"> R-posts.com</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/rstudio-ai-that-doesnt-cost-a-penny-llmcoder-vs-posit-ai-assistant/">RStudio AI That Doesn’t Cost a Penny: llmcoder vs. Posit AI Assistant</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402096</post-id>	</item>
		<item>
		<title>New CRAN Package for sparse PCA – msPCA</title>
		<link>https://www.r-bloggers.com/2026/06/new-cran-package-for-sparse-pca-mspca/</link>
		
		<dc:creator><![CDATA[Jean Pauphilet]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:08:40 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://r-posts.com/?p=17970</guid>

					<description><![CDATA[<p>The package msPCA is now available on CRAN! <br />
It implements a new method for computing multiple sparse principal components of a dataset. Unlike other available packages, it generates PCs that are sparse and orthogonal, leading to a generally higher fra...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/new-cran-package-for-sparse-pca-mspca/">New CRAN Package for sparse PCA – msPCA</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://r-posts.com/new-cran-package-for-sparse-pca-mspca/"> R-posts.com</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>
The package <a href="https://cran.r-project.org/web/packages/msPCA/index.html" rel="nofollow" target="_blank">msPCA</a> is now available on CRAN! <br />
It implements a new method for computing <span style="text-decoration: underline">multiple</span> sparse principal components of a dataset. Unlike other available packages, it generates PCs that are sparse and orthogonal, leading to a generally higher fraction of variance explained. <br />
<img loading="lazy" decoding="async" src="https://i2.wp.com/r-posts.com/wp-content/uploads/2025/12/n_vs_orthogonality-436x300.png?resize=436%2C300" alt="" width="436" height="300" class="alignnone size-medium wp-image-17971" srcset_temp="https://i2.wp.com/r-posts.com/wp-content/uploads/2025/12/n_vs_orthogonality-436x300.png?resize=436%2C300 436w, http://r-posts.com/wp-content/uploads/2025/12/n_vs_orthogonality-450x310.png 450w, http://r-posts.com/wp-content/uploads/2025/12/n_vs_orthogonality-768x529.png 768w, http://r-posts.com/wp-content/uploads/2025/12/n_vs_orthogonality.png 1368w" sizes="(max-width: 436px) 100vw, 436px" data-recalc-dims="1" /> <img loading="lazy" decoding="async" src="https://i2.wp.com/r-posts.com/wp-content/uploads/2025/12/n_vs_variance-436x300.png?resize=436%2C300" alt="" width="436" height="300" class="alignnone size-medium wp-image-17972" srcset_temp="https://i2.wp.com/r-posts.com/wp-content/uploads/2025/12/n_vs_variance-436x300.png?resize=436%2C300 436w, http://r-posts.com/wp-content/uploads/2025/12/n_vs_variance-450x310.png 450w, http://r-posts.com/wp-content/uploads/2025/12/n_vs_variance-768x529.png 768w, http://r-posts.com/wp-content/uploads/2025/12/n_vs_variance.png 1368w" sizes="(max-width: 436px) 100vw, 436px" data-recalc-dims="1" /><hr style="border-top: black solid 1px" /><a href="http://r-posts.com/new-cran-package-for-sparse-pca-mspca/" rel="nofollow" target="_blank">New CRAN Package for sparse PCA – msPCA</a> was first posted on June 18, 2026 at 5:08 am.<br />
<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://r-posts.com/new-cran-package-for-sparse-pca-mspca/"> R-posts.com</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/new-cran-package-for-sparse-pca-mspca/">New CRAN Package for sparse PCA – msPCA</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402106</post-id>	</item>
		<item>
		<title>DIVINE: a new R package for working with a real-world COVID-19 clinical cohort</title>
		<link>https://www.r-bloggers.com/2026/06/divine-a-new-r-package-for-working-with-a-real-world-covid-19-clinical-cohort/</link>
		
		<dc:creator><![CDATA[Cristian Tebé]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 05:08:18 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://r-posts.com/?p=19290</guid>

					<description><![CDATA[<p>Clinical data are rarely as clean, compact or convenient as the examples we often use when teaching statistics or R. Real hospital datasets are usually distributed across several tables, include missing values, contain repeated structures, and require careful documentation before they can be reused. The new R package DIVINE is ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/divine-a-new-r-package-for-working-with-a-real-world-covid-19-clinical-cohort/">DIVINE: a new R package for working with a real-world COVID-19 clinical cohort</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://r-posts.com/divine-a-new-r-package-for-working-with-a-real-world-covid-19-clinical-cohort/"> R-posts.com</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><br />
Clinical data are rarely as clean, compact or convenient as the examples we often use when teaching statistics or R. Real hospital datasets are usually distributed across several tables, include missing values, contain repeated structures, and require careful documentation before they can be reused.</p>
<p>The new R package <a href="https://bruigtp.github.io/DIVINE/" rel="nofollow" target="_blank"><code>DIVINE</code> </a>is interesting precisely because it brings that reality into the R ecosystem in an accessible way.  Available on CRAN, <a href="https://cran.r-project.org/package=DIVINE" rel="nofollow" target="_blank"><code>DIVINE</code> </a>provi<img loading="lazy" decoding="async" src="https://i1.wp.com/r-posts.com/wp-content/uploads/2026/06/divine_logo1-300x300.png?resize=300%2C300" alt="" width="300" height="300" class="size-medium wp-image-19291 alignright" srcset_temp="https://i1.wp.com/r-posts.com/wp-content/uploads/2026/06/divine_logo1-300x300.png?resize=300%2C300 300w, http://r-posts.com/wp-content/uploads/2026/06/divine_logo1-450x450.png 450w, http://r-posts.com/wp-content/uploads/2026/06/divine_logo1-150x150.png 150w, http://r-posts.com/wp-content/uploads/2026/06/divine_logo1-768x768.png 768w, http://r-posts.com/wp-content/uploads/2026/06/divine_logo1-1536x1536.png 1536w, http://r-posts.com/wp-content/uploads/2026/06/divine_logo1.png 1563w" sizes="auto, (max-width: 300px) 100vw, 300px" data-recalc-dims="1" />des a curated collection of datasets from a multicentre cohort of hospitalized COVID-19 patients in the south metropolitan area of Barcelona. The package is accompanied by a recent publication in <a href="https://www.nature.com/articles/s41597-026-07479-7" rel="nofollow" target="_blank"><em>Scientific Data</em></a>, which describes the database, its structure, data collection process and potential reuse for clinical epidemiology, teaching and methodological research.</p>
<h2>A clinical dataset packaged for R</h2>
<p>The package includes 14 datasets covering different clinical domains, such as demographics, comorbidities, symptoms, vital signs, severity scores, ICU information, treatments, complications, vaccination and end-of-follow-up data.</p>
<p>This relational structure is one of the most valuable aspects of the package. Instead of providing a single pre-merged analysis file, <code>DIVINE</code> preserves the logic of a real clinical database, where information is distributed across several linked tables. This makes it especially useful for applied teaching and for demonstrating realistic data-management workflows in R.</p>
<p>For example:</p>
<pre>install.packages(&quot;DIVINE&quot;)
library(DIVINE)

data(package = &quot;DIVINE&quot;)
</pre>
<p>The datasets can then be loaded in the usual way:</p>
<pre>data(&quot;demographic&quot;)
data(&quot;vital_signs&quot;)
data(&quot;scores&quot;)
</pre>
<p>The common identifiers allow users to combine information across tables and build analysis datasets depending on the research question.</p>
<h2>More than a data package</h2>
<p>Although the datasets are the main contribution, <code>DIVINE</code> also includes helper functions for common epidemiological data workflows. These include:</p>
<pre>data_overview()
multi_join()
stats_table()
multi_plot()
impute_missing()
export_data()
</pre>
<p>These functions are not intended to replace the broader R ecosystem, but they make the package easier to use in teaching, exploratory analysis and reproducible examples.</p>
<p>A minimal workflow might look like this:</p>
<pre>library(DIVINE)

data(&quot;demographic&quot;)
data(&quot;vital_signs&quot;)
data(&quot;scores&quot;)

baseline &lt;- multi_join(
  list(demographic, vital_signs, scores),
  key = c(&quot;record_id&quot;, &quot;covid_wave&quot;, &quot;center&quot;),
  join_type = &quot;left&quot;
)

data_overview(baseline)

stats_table(
  baseline,
  vars = c(&quot;age&quot;, &quot;sex&quot;),
  by = &quot;covid_wave&quot;,
  statistic_type = &quot;median_iqr&quot;,
  pvalue = TRUE
)
</pre>
<p>This example already illustrates several important aspects of clinical data analysis: understanding table structure, joining related datasets, checking variables, and producing descriptive summaries.</p>
<h2>Why it is useful for R users</h2>
<p>For a specialised R audience, the value of <code>DIVINE</code> is not only that it provides COVID-19 data. Its main interest is that it offers a realistic, documented and reusable clinical database within a familiar R workflow.</p>
<p>The package may be useful for:</p>
<ul>
	<li>
<p>teaching data management with relational clinical datasets;</p>
</li>
	<li>
<p>preparing examples for biostatistics or epidemiology courses;</p>
</li>
	<li>
<p>demonstrating descriptive clinical analyses;</p>
</li>
	<li>
<p>exploring missing data and variable availability;</p>
</li>
	<li>
<p>developing prognostic modelling examples;</p>
</li>
	<li>
<p>validating prediction models;</p>
</li>
	<li>
<p>creating reproducible workflows using real-world health data.</p>
</li>
</ul>
<p>This makes <code>DIVINE</code> particularly attractive for applied biostatisticians, epidemiologists, clinical researchers and R instructors who want to move beyond toy datasets.</p><hr style="border-top: black solid 1px" /><a href="http://r-posts.com/divine-a-new-r-package-for-working-with-a-real-world-covid-19-clinical-cohort/" rel="nofollow" target="_blank">DIVINE: a new R package for working with a real-world COVID-19 clinical cohort</a> was first posted on June 18, 2026 at 5:08 am.<br />
<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://r-posts.com/divine-a-new-r-package-for-working-with-a-real-world-covid-19-clinical-cohort/"> R-posts.com</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/divine-a-new-r-package-for-working-with-a-real-world-covid-19-clinical-cohort/">DIVINE: a new R package for working with a real-world COVID-19 clinical cohort</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402123</post-id>	</item>
		<item>
		<title>Snapshot Testing in R: Beyond Screenshots</title>
		<link>https://www.r-bloggers.com/2026/06/snapshot-testing-in-r-beyond-screenshots/</link>
		
		<dc:creator><![CDATA[Jakub Sobolewski]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Snapshot testing is not just for screenshots. Capture console output, logs, data frames, errors, and whole data structures. Practices that keep snapshot tests trustworthy instead of brittle.</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/snapshot-testing-in-r-beyond-screenshots/">Snapshot Testing in R: Beyond Screenshots</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/snapshot-testing-beyond-screenshots"> 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://i2.wp.com/jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots/og-image.png?w=578&#038;ssl=1" alt="Snapshot Testing in R: Beyond Screenshots" data-recalc-dims="1" /></p><p>Snapshot testing is not about screenshots.</p>
<p>Most people meet it through UI regression tests: render a component, save a picture, fail the build when the picture changes. So the technique gets filed away as “the thing that compares images.” That is one use. But not the only one.</p>
<p>The mechanic underneath is general. Capture some output, save it to a file, and on every later run compare fresh output against the saved copy. The output can be a plot. It can also be console text, a log, a data frame, an error message, or a deeply nested list. Anything you can serialize, you can snapshot.</p>
<p>What makes it powerful is also what makes it dangerous: <strong>you are the test oracle.</strong> There is no <code>expect_equal(result, 42)</code> stating the answer up front. You accept the first snapshot because you read it and judged it correct. Get that review wrong, or skip it, and you have pinned a bug in place and called it a passing test.</p>
<p>In this post I want to walk through using snapshot testing for what it is good for, and the practices that make it efficient.</p>
<h2 id="what-snapshot-testing-actually-is">What snapshot testing actually is</h2>
<p>In testthat’s third edition the entry points are <code>expect_snapshot()</code> and <code>expect_snapshot_file()</code>. The first run records output into a <code>_snaps/</code> directory next to your tests, as a <code>.md</code> file named after the test file. Every run after that compares against what’s recorded. A mismatch fails the test and shows you a diff.</p>
<pre>test_that(&quot;summary prints a one-line overview&quot;, {
  expect_snapshot(print(summary(1:10)))
})</pre>
<p>The first time, testthat writes the printed output to <code>_snaps/summary.md</code> and the test passes (with a note that a new snapshot was recorded). From then on, that file is the expected value.</p>
<p>You reach for this in those situations:</p>
<ul>
<li>The output is <strong>large or tedious to assert field by field</strong>, but you can <em>recognize</em> whether it’s correct by looking at it.</li>
<li>The output is <strong>impossible to express in code</strong>: a rendered plot, a rendered table, any image.</li>
<li>The output is <strong>impractical to express in code</strong> a formatted CLI report, a full console transcript with its alignment. You can’t write <code>expect_equal()</code> for “the table is laid out correctly.” You can look at it and know.</li>
</ul>
<h2 id="five-practices-that-keep-snapshots-trustworthy">Five practices that keep snapshots trustworthy</h2>
<p>Snapshot suites rot in predictable ways: noise the diff engine flags as failures, snapshots nobody can review, tests that flake on a different machine, and (as with any other test) titles that say nothing.</p>
<p>Those practices prevent it.</p>
<h3 id="1-scope-to-exactly-what-proves-the-behavior">1. Scope to exactly what proves the behavior</h3>
<p>Capture the plot, not the page the plot lives on.</p>
<p>If you’re testing that a chart colors points correctly, snapshot the chart. Not the dashboard it’s embedded in, with its header, its sidebar, the current date in the corner, and a “last refreshed” timestamp. Every one of those is unrelated to the behavior under test, and every one is a reason for the comparison to fail when nothing you care about changed.</p>
<p>A snapshot’s diff engine is literal. It flags any difference. So don’t hand it differences that don’t matter. Scope the capture down to the smallest thing that demonstrates the behavior, and the only way the test can fail is if that behavior breaks.</p>
<p><strong>Don’t give the diff engine extra reasons to make false positives.</strong></p>
<h3 id="2-make-snapshots-human-readable">2. Make snapshots human-readable</h3>
<p>You are going to review these files by eye. So they have to be readable by eye.</p>
<p>Store snapshots as text: markdown, CSV, SVG, JSON. Never as a binary blob. I heard on the R Weekly podcast that some teams keep snapshots as <code>.rds</code> files, and I’d push back on that hard. A binary snapshot can’t be read in an editor, can’t be reviewed in a pull request, and can’t be diffed when it changes. It defeats the entire premise. The whole technique rests on a human being able to look at the recorded output and decide it’s right. You want to also help your code reviewers to do that, don’t hide the “truth” in a binary file. Especially when accepting the first snapshot as “the truth”; make it easy for your collaborators to read and judge the snapshot!</p>
<p><strong>Don’t introduce extra points of friction. Keep it simple.</strong></p>
<h3 id="3-remove-nondeterminism-or-filter-whats-left">3. Remove nondeterminism, or filter what’s left</h3>
<p>A snapshot that changes on every run is useless. Timestamps, random IDs, elapsed-time measurements, unordered query results. Any of these will make the file churn and train you to accept changes blindly.</p>
<p>Fix it at the source first. Inject the things that vary so the test controls them: pass a fixed clock instead of calling <code>Sys.time()</code>, set a seed, supply IDs rather than generating them. This is dependency injection, the same move that makes any code testable.</p>
<p>When you can’t remove the variation, filter it. testthat’s <code>expect_snapshot()</code> takes a <code>transform</code> argument: a function that cleans each line of output before it’s compared. Strip the timestamps, drop the spinner characters, normalize the paths. For data, impose a deterministic order before you serialize.</p>
<p><strong>Don’t let snapshots change when there is no reason for them to change.</strong></p>
<h3 id="4-stabilize-platform-differences">4. Stabilize platform differences</h3>
<p>A rendered snapshot depends on more than your code. Fonts render differently on macOS and Linux. A new release of a plotting or formatting dependency shifts the output by a pixel or a label. R itself changes between versions. None of that is a regression, but a literal diff engine can’t tell, so a snapshot recorded on your laptop fails the moment it runs anywhere else.</p>
<p>Two tools handle this, and they work together.</p>
<p><strong>A. Variants keep incompatible environments from overwriting each other.</strong> Both <code>expect_snapshot()</code> and <code>expect_snapshot_file()</code> take a <code>variant</code> argument. testthat stores each variant in its own subdirectory, <code>_snaps/{variant}/</code>, so the macOS render and the Linux render sit side by side instead of clobbering one another. Key the variant on whatever actually moves the output: the operating system, the R version, a specific dependency’s version, or a combination. You decide what relevant axes of variation are, and you key the snapshots to them. Maybe you want to support rendering on different platforms, and you want to support different versions of a plotting library. Then the variant should include both the platform and the library version.</p>
<pre>variant = paste(platform_variant(), packageVersion(&quot;echarts4r&quot;), sep = &quot;-&quot;)</pre>
<p><strong>B. Let one platform generate the truth, and let the whole team use it.</strong> Variants solve the storage problem. They don’t solve the contribution problem: your developers are on different operating systems, and you don’t want each regenerated snapshot to depend on whose machine produced it. If your team works on Windows, macOS and Linux, you may not want to check-into the repository 3 slightly different copies of the same thing. Nominate a single canonical environment, your CI runner, and treat the snapshots it produces as authoritative.</p>
<p>When a snapshot test fails on GitHub Actions, the files that run produced are uploaded as build artifacts. testthat gives you a helper to pull them straight into your local checkout:</p>
<pre>testthat::snapshot_download_gh(
  repository = &quot;your-org/your-package&quot;,
  run_id = &quot;47905180716&quot;
)</pre>
<p><em>This is a quite <a href="https://tidyverse.org/blog/2025/11/testthat-3-3-0/#other-new-features" rel="nofollow" target="_blank">recent addition</a> to testthat.</em> Worth knowing.</p>
<p>You rarely have to look the call up. When snapshots fail inside an <code>R CMD check</code> job, testthat prints the exact <code>snapshot_download_gh()</code> line in the CI log, ready to copy. Run it, review the downloaded files the way you’d review any first snapshot, and commit them.</p>
<p>That turns snapshot testing into a team practice. A contributor on Windows can change a plot, open a pull request, and let CI render the canonical image. The reviewer accepts the snapshot CI produced, not one tied to a particular laptop. The truth comes from one place, and everyone contributes to it through the same door.</p>
<p>You’ll notice both options have their advantages and disadvantages. <strong>It’s up to you to decide which one fits your team and workflow better.</strong></p>
<p>But now that you know your options you can test them out and see which one works best for you.</p>
<h3 id="5-name-the-test-and-the-snapshot-so-they-stand-alone">5. Name the test and the snapshot so they stand alone</h3>
<p>A test title should state the precondition and the expected output.</p>
<p>The same holds for snapshot tests. Not “reporter works.” Something like “progress reporter shows survived mutants in summary.” The title is the first thing a reviewer reads when the snapshot changes.</p>
<p>But snapshot tests aren’t self contained.</p>
<p>The assertion of a snapshot test is a file. That means you read the test and then you need to open the file to understand what is really the expected outcome. But there is also another workflow: you might also browse the snapshots directory first and get a grasp of what the code is producing.</p>
<p>With <code>expect_snapshot_file()</code> you also name the snapshot file yourself. Use that. A file called <code>scatterplot_colors_points_in_the_band.png</code> tells you what it should contain before you even read the test itself. The filename and its content should tell the story on their own, without you having to dig up the test that produced them.</p>
<hr>
<p>The rest of this post is five worked examples, each leaning on these five practices.</p>
<h2 id="example-1-plots-from-simple-to-interactive">Example 1: plots, from simple to interactive</h2>
<h3 id="ggplot-snapshot-the-svg-not-a-png">ggplot: snapshot the SVG, not a PNG</h3>
<p>For ggplot, the right tool is <a href="https://vdiffr.r-lib.org/" rel="nofollow" target="_blank">vdiffr</a>. It renders a plot to <strong>SVG</strong>, which is text, and snapshots that.</p>
<pre>test_that(&quot;points below lower threshold are green, above upper are red, inside are yellow&quot;, {
  # Arrange
  data &lt;- data.frame(
    x = seq_date(3),
    y = c(10, 20, 30)
  )

  # Act
  p &lt;- threshold_plot(data, lower = 15, upper = 25)

  # Assert
  vdiffr::expect_doppelganger(&quot;threshold_plot_below_threshold_green_above_threshold_red_inside_threshold_yellow&quot;, p)
})</pre>
<p>Two of our practices fall out of this for free. The snapshot is <strong>scoped</strong> to the plot object itself, not a Shiny page that embeds it. And it’s <strong>human-readable</strong>: the recorded <code>.svg</code> is text you can open, and vdiffr ships a Shiny app (<code>vdiffr::manage_cases()</code>) that shows the old and new render side by side when something changes. You review the picture, but the artifact under version control is inspectable text.</p>
<p>Here’s the actual plot that test captures:</p>
<p><img alt="Threshold plot: green point below 15, yellow within, red above 25" loading="lazy" decoding="async" fetchpriority="auto" width="450" src="https://jakubsobolewski.com/_astro/threshold_plot.BfKB71nF_2o73BG.webp" ></p>
<p>And here are the first lines of the SVG vdiffr would record. This is the whole point of <a href="https://jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots#2-make-snapshots-human-readable" rel="nofollow" target="_blank">practice #2</a>: the snapshot under version control is text you can read.</p>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; width=&quot;450&quot; viewBox=&quot;0 0 504 288&quot;&gt;
&lt;defs&gt;
&lt;g&gt;
&lt;g id=&quot;glyph-0-0&quot;&gt;</pre>
<p>Comparing text is easier than comparing pixels. If saving image to SVG is possible, you should always prefer it to a PNG. Not only you can view SVG both as text and the image, but also the diff engine can tell you exactly what changed in the markup, instead of just showing a pixel difference.</p>
<p>But there are plenty of cases when SVG isn’t an option.</p>
<h3 id="htmlwidgets-when-youre-forced-back-to-pixels">htmlwidgets: when you’re forced back to pixels</h3>
<p>vdiffr can’t render an htmlwidget or a Shiny tag list, because there’s no static SVG to produce. Here you fall back to rendering the thing to a real PNG and comparing images. That’s a harder problem, and it’s where scoping and determinism stop being nice-to-haves.</p>
<p>The shape of the helper: render to HTML, screenshot to PNG with <code>webshot</code>, then hand the PNG to <code>expect_snapshot_file()</code>.</p>
<pre>expect_plot &lt;- function(x, name, ...) {
  UseMethod(&quot;expect_plot&quot;)
}

expect_plot.htmlwidget &lt;- function(
  x,
  name,
  variant = shinytest2::platform_variant(),
  width = 992,
  height = 744
) {
  local_edition(3)
  html_temp &lt;- fs::path(tempdir(), name, ext = &quot;html&quot;)
  png_temp &lt;- fs::path(tempdir(), name, ext = &quot;png&quot;)
  on.exit(unlink(tempdir()))

  htmlwidgets::saveWidget(
    x,
    file = html_temp,
    selfcontained = FALSE
  )
  webshot::webshot(
    url = html_temp,
    file = png_temp,
    delay = 0.5,
    quiet = TRUE,
    vwidth = width,
    vheight = height
  )

  testthat::expect_snapshot_file(
    png_temp,
    name = fs::path(name, ext = &quot;png&quot;),
    variant = variant
  )
}</pre>
<p>I’ve used this pattern across many projects and it always worked for me very well.</p>
<p><strong>The threshold.</strong> A pixel-exact comparison of a rendered chart will fail on trivial, invisible differences in anti-aliasing. So the <code>compare</code> function allows a small per-pixel difference budget before it calls a mismatch. Locally (<code>interactive()</code>) the threshold is <code>0</code>, because you want to see every change as you work. On CI it’s relaxed, because the CI renderer isn’t identical to your laptop.</p>
<p><strong>The variant.</strong> Fonts render differently across operating systems, so a snapshot recorded on macOS will not match one produced on Linux. <code>platform_variant()</code> keys the snapshot to the platform, and <code>expect_snapshot_file()</code> keeps a separate recorded file per variant (<code>_snaps/mac/...</code>, <code>_snaps/linux/...</code>), so cross-platform rendering differences never masquerade as a regression. This is the storage half of <a href="https://jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots#4-stabilize-platform-differences" rel="nofollow" target="_blank">practice #4</a>; pair it with <code>snapshot_download_gh()</code> so CI generates the canonical files the whole team commits.</p>
<p>The test that uses it reads like any other, with a descriptive title and a named snapshot:</p>
<pre>it(&quot;colors timepoints between thresholds&quot;, {
  # Arrange
  data &lt;- data.frame(
    x = seq_date(3),
    y = c(10, 20, 30)
  )

  # Act
  plot &lt;- threshold_plot(data, lower = 15, upper = 25)

  # Assert
  expect_plot(
    plot,
    name = &quot;colors_timepoints_between_thresholds&quot;
  )
})</pre>
<h3 id="interactive-plots-you-can-even-snapshot-an-interaction">Interactive plots: you can even snapshot an interaction</h3>
<p>An htmlwidget is just HTML and JavaScript. That means you can drive it into a specific state and snapshot <em>that</em>. Here’s an <code>echarts4r</code> line chart where the behavior under test is the tooltip content.</p>
<p>A small helper dispatches the chart’s own “show tooltip” action when the widget loads, it simulates user interacting with the plot:</p>
<pre>#' tests/testthat/setup-trigger_tooltip.R
trigger_tooltip &lt;- function(x, series_index, data_index) {
  htmlwidgets::onRender(
    x,
    sprintf(
      &quot;function(el, x, data) {
        const chart = echarts.getInstanceByDom(el);
        chart.dispatchAction({
          type: 'showTip',
          seriesIndex: %s,
          dataIndex: %s,
        });
      }&quot;,
      series_index,
      data_index
    )
  )
}</pre>
<hr>
<p>Then the test renders the chart, triggers the tooltip, and snapshots the result (notice that the # Act is triggering the tooltip, not creating the plot):</p>
<pre>it(&quot;shows tooltip content from the specified tooltip column&quot;, {
  # Arrange
  data &lt;- data.frame(
    date = as.Date(
      c(&quot;2020-01-01&quot;, &quot;2020-02-01&quot;, &quot;2020-03-01&quot;)
    ),
    value = c(1, 3, 2),
    tooltip = &quot;TOOLTIP&quot;
  )
  plot &lt;- line_plot(
    data,
    date = &quot;date&quot;,
    value = &quot;value&quot;,
    tooltip = &quot;tooltip&quot;
  )

  # Act
  result &lt;- plot %&gt;%
    trigger_tooltip(series_index = 0, data_index = 1)

  # Assert
  expect_plot(result, &quot;line_plot_tooltip&quot;)
})</pre>
<p>The tooltip text is doing double duty. It’s the data the test checks, and it’s a human-readable marker that tells the reviewer exactly what to look for when accepting the snapshot. Here’s the PNG that snapshot captures:</p>
<p><img alt="echarts line chart with the tooltip ‘TOOLTIP’ shown" loading="lazy" decoding="async" fetchpriority="auto" width="450" src="https://jakubsobolewski.com/_astro/line_plot_tooltip.Bye5hnIf_ZKTGFd.webp" ></p>
<h2 id="example-2-printed-output-reporters-loggers-cli">Example 2: printed output (reporters, loggers, CLI)</h2>
<p>Some objects exist to print.</p>
<p>Test reporters, loggers, CLI tools. Their whole job is to render formatted text to the console.</p>
<p>That text <em>is</em> the behavior, and <code>expect_snapshot()</code> captures it verbatim into a readable <code>.md</code> file.</p>
<p>I use this in <a href="https://github.com/jakubsob/muttest" rel="nofollow" target="_blank">muttest</a>, a mutation testing package, to pin down exactly what the progress reporter prints. The test is small:</p>
<pre>test_that(&quot;progress reporter shows all killed&quot;, {
  .with_example_dir(&quot;shipping/&quot;, {
    mutators &lt;- list(operator(&quot;&gt;&quot;, &quot;&lt;&quot;))
    plan &lt;- muttest_plan(mutators, fs::dir_ls(&quot;R&quot;))
    .expect_snapshot(
      muttest(
        plan,
        reporter = ProgressMutationReporter$new(
          min_time = Inf,
          survived_detail = &quot;none&quot;
        )
      )
    )
  })
})</pre>
<p>But a reporter’s output is full of nondeterminism: spinner frames, blank lines, and per-step timings like <code>[0.3s]</code> and <code>Duration: 1.2s</code>. Snapshot that raw and it fails on every run. The fix is <a href="https://jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots#3-remove-nondeterminism-or-filter-whats-left" rel="nofollow" target="_blank">practice #3</a>: a <code>transform</code> that strips the noise before comparison. I wrap <code>expect_snapshot()</code> once, in <code>setup.R</code>, so every test in the suite gets the cleaning for free:</p>
<pre>.expect_snapshot &lt;- purrr::partial(
  testthat::expect_snapshot,
  transform = function(lines) {
    lines |&gt;
      stringr::str_subset(&quot;^[\\|/\\-\\\\] \\|&quot;, negate = TRUE) |&gt;
      stringr::str_subset(&quot;^$&quot;, negate = TRUE) |&gt;
      stringr::str_remove_all(&quot;\\s\\[\\d+.\\d+s\\]&quot;) |&gt;
      stringr::str_remove_all(&quot;Duration:\\s\\d+.\\d+\\ss&quot;) |&gt;
      stringr::str_trim()
  }
)</pre>
<p>The first two <code>str_subset</code> calls drop spinner lines and blanks. The two <code>str_remove_all</code> calls delete the timing fragments. What’s left is the stable, meaningful part of the output, and that’s what lands in the snapshot:</p>
<pre># progress reporter shows all killed

    Code
      muttest(plan, reporter = ProgressMutationReporter$new(min_time = Inf,
        survived_detail = &quot;none&quot;))
    Output
      i Mutation Testing
        |   K |   S |   E |   T |   % | Mutator  | File
      v |   1 |   0 |   0 |   1 | 100 | &gt; → &lt;    | shipping.R
      -- Results ---------------------------------------------------------------------
      [ KILLED 1 | SURVIVED 0 | ERRORS 0 | TOTAL 1 | SCORE 100.0% ]</pre>
<p>This is a snapshot doing exactly what it should. The table alignment, the symbols, the score line. None of that is pleasant to assert by hand, but all of it is obviously correct (or obviously wrong) at a glance. The full reporter source, setup, and recorded snapshots are in the muttest repo: <a href="https://github.com/jakubsob/muttest/blob/main/tests/testthat/setup.R" rel="nofollow" target="_blank">setup.R</a>, <a href="https://github.com/jakubsob/muttest/blob/main/tests/testthat/test-test_reporter-progress.R" rel="nofollow" target="_blank">the test</a>, and <a href="https://github.com/jakubsob/muttest/blob/main/tests/testthat/_snaps/test_reporter-progress.md" rel="nofollow" target="_blank">the snapshot</a>.</p>
<h2 id="example-3-data-frames-as-csv">Example 3: data frames as CSV</h2>
<p>Data frames are a classic snapshot candidate, and a classic way to get it wrong.</p>
<p>The tempting move is <code>expect_snapshot(print(df))</code>. Don’t. Printed data frames are truncated past a certain size, formatted to your console width, and shown in whatever order the rows happen to be in. You’re snapshotting the print method, not the data.</p>
<p>Write the data frame to CSV. CSV is text, it diffs cleanly, and it’s the obvious human-readable representation of tabular data.</p>
<p><strong>I find snapshotting tables especially useful when you need signoff of business logic calculation from a business expert.</strong> Then instead of showing a table created in code you can print hand over the CSV or even a formatted markdown table for review. The expert can then sign off on the calculation without needing to read the code.</p>
<p>Following the same S3 pattern as <code>expect_plot</code>, here’s a custom expectation. The comparison is the part worth getting right, so it lives in its own named function:</p>
<pre>compare_df &lt;- function(old, new) {
  # Compare parsed data frames, not raw CSV text
  # Notice you can use custom comparison functions here
  isTRUE(all.equal(
    read.csv(old, stringsAsFactors = FALSE),
    read.csv(new, stringsAsFactors = FALSE)
  ))
}
expect_snap &lt;- function(x, name, ...) {
  UseMethod(&quot;expect_snap&quot;)
}

expect_snap.data.frame &lt;- function(x, name, ...) {
  local_edition(3)

  # Practice #3: deterministic order so row shuffling never breaks the test
  x &lt;- x[do.call(order, x), , drop = FALSE]
  rownames(x) &lt;- NULL

  path &lt;- fs::path(tempdir(), name, ext = &quot;csv&quot;)
  on.exit(unlink(path))

  expect_snapshot_file(
    path = local({
      write.csv(x, path, row.names = FALSE)
      path
    }),
    name = fs::path(name, ext = &quot;csv&quot;),
    compare = compare_df
  )
}</pre>
<p>Two choices make this robust. Sorting on all columns before writing (that’s <a href="https://jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots#3-remove-nondeterminism-or-filter-whats-left" rel="nofollow" target="_blank">practice #3</a>) makes the snapshot order-independent. And <code>compare_df</code> reads both files back into data frames and compares <em>those</em>, not the raw text, so a trailing newline, a quoting difference, or an integer written as <code>1</code> versus <code>1.0</code> never fails the test.</p>
<p>That second claim is the one worth verifying, and <code>compare_df</code> is an ordinary function, so it gets an ordinary unit test. Two files holding the same data but different CSV text must compare equal; a genuine change must not:</p>
<pre>test_that(&quot;compare_df ignores CSV formatting but catches value changes&quot;, {
  # Arrange
  recorded    &lt;- tempfile(fileext = &quot;.csv&quot;)
  reformatted &lt;- tempfile(fileext = &quot;.csv&quot;)
  changed     &lt;- tempfile(fileext = &quot;.csv&quot;)
  writeLines(c(&quot;x,y&quot;,     &quot;1,10.5&quot;, &quot;2,20.1&quot;),     recorded)
  # quoted, trailing newline
  writeLines(c('&quot;x&quot;,&quot;y&quot;', &quot;1,10.5&quot;, &quot;2,20.1&quot;, &quot;&quot;), reformatted)
  # a real change
  writeLines(c(&quot;x,y&quot;,     &quot;1,10.5&quot;, &quot;2,99.9&quot;),     changed)

  # Act &#038; Assert
  expect_true(compare_df(recorded, reformatted))
  expect_false(compare_df(recorded, changed))
})
Test passed with 2 successes &#x1f600;.</pre>
<p>The test passes: formatting noise doesn’t fail the comparison, a changed value does. You snapshot to a readable format, but you compare on meaning.</p>
<h2 id="example-4-errors-and-conditions">Example 4: errors and conditions</h2>
<p>User-facing messages are a contract. When a function fails, the error text is part of its behavior, and <code>expect_snapshot()</code> pins it.</p>
<pre>test_that(&quot;withdrawing more than the balance reports the shortfall&quot;, {
  expect_snapshot(
    withdraw(account(balance = 50), amount = 80),
    error = TRUE
  )
})</pre>
<p>The <code>error = TRUE</code> tells testthat the code is expected to throw and to capture the condition instead of failing the test. The message goes into the snapshot:</p>
<pre># withdrawing more than the balance reports the shortfall

    Code
      withdraw(account(balance = 50), amount = 80)
    Condition
      Error in `withdraw()`:
      ! Cannot withdraw 80 from an account with balance 50.
      i Available to withdraw: 50.</pre>
<p>Now if someone changes that message (softens it, drops the available balance, mangles the formatting), the test fails and shows the diff. The same works for warnings and messages. It’s the cleanest way to keep error messages from silently degrading. (The flip side: a message worth snapshotting is a message worth writing carefully. See the <a href="https://jakubsobolewski.com/blog/test-smells-in-r/" rel="nofollow" target="_blank">Mystery Guest and Overspecification smells</a> for the failure modes nearby.)</p>
<h2 id="example-5-nested-data-structures">Example 5: nested data structures</h2>
<p>Some outputs are big nested lists: a parsed config, an API response, a model object’s metadata. Asserting them field by field is miserable:</p>
<pre>expect_equal(result$user$name, &quot;Ada&quot;)
expect_equal(result$user$roles, c(&quot;admin&quot;, &quot;editor&quot;))
expect_equal(result$settings$theme, &quot;dark&quot;)
expect_equal(result$settings$notifications$email, TRUE)
# ... twenty more lines</pre>
<p>Each line is a place to make a typo, and together they still might not cover every field. Snapshot the whole structure once, review it once.</p>
<p>The only real decision is the serialization format, and <a href="https://jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots#2-make-snapshots-human-readable" rel="nofollow" target="_blank">practice #2</a> decides it. Don’t use <code>dput()</code>; its output is valid R but painful to read. Serialize to pretty JSON or YAML, which a human can actually scan:</p>
<pre>result &lt;- list(
  user = list(name = &quot;Ada&quot;, roles = c(&quot;admin&quot;, &quot;editor&quot;)),
  settings = list(
    theme = &quot;dark&quot;,
    notifications = list(email = TRUE, sms = FALSE)
  )
)

cat(jsonlite::toJSON(result, pretty = TRUE, auto_unbox = TRUE))
{
  &quot;user&quot;: {
    &quot;name&quot;: &quot;Ada&quot;,
    &quot;roles&quot;: [&quot;admin&quot;, &quot;editor&quot;]
  },
  &quot;settings&quot;: {
    &quot;theme&quot;: &quot;dark&quot;,
    &quot;notifications&quot;: {
      &quot;email&quot;: true,
      &quot;sms&quot;: false
    }
  }
}</pre>
<p>Wrap that in a snapshot expectation and the recorded file is a clean, indented JSON document. One review covers the entire structure, and any change to any field shows up as a precise diff.</p>
<p>Use it sparingly, not every big output needs a snapshot test, sometimes it’s better to assert on the shape and values that actually matter.</p>
<h2 id="the-responsibility">The responsibility</h2>
<p>Here is where snapshot testing lives or dies.</p>
<p><strong>The first snapshot is a decision, not a fact.</strong> When testthat records a new snapshot, the test passes. That green check does not mean the output is correct. It means the output now exists. The only thing that makes it correct is <em>you reading it and deciding it is.</em> Accept a snapshot without reading it and you’ve written a test that asserts “the code does whatever it currently does,” which is no test at all.</p>
<p>When a snapshot changes, testthat tells you and gives you tools to review:</p>
<pre># opens a diff app for changed snapshots
testthat::snapshot_review()
# accept changes once you've reviewed them
testthat::snapshot_accept()</pre>
<p><code>snapshot_review()</code> is the honest path: it shows you old versus new and makes you look. <code>snapshot_accept()</code> without looking is how snapshot suites become worthless. The reason <a href="https://jakubsobolewski.com/blog/snapshot-testing-beyond-screenshots#2-make-snapshots-human-readable" rel="nofollow" target="_blank">practice #2</a> (human-readable) matters so much is that it’s what makes this review <em>possible</em>. A binary blob can’t be reviewed, so you’d rubber-stamp it by necessity.</p>
<p>And snapshots are your dependencies. They’re checked into version control and they show up in pull requests. A changed snapshot in a diff deserves the same scrutiny as a changed function, often more, because it’s the line where “the behavior changed” becomes visible.</p>
<h2 id="build-your-own-snapshot-expectations">Build your own snapshot expectations</h2>
<p>Notice what <code>expect_plot</code>, <code>expect_snap.data.frame</code>, and the <code>transform</code>-wrapped <code>.expect_snapshot</code> have in common. Each one is a <strong>domain-specific expectation</strong> that bakes the four practices into a reusable function:</p>
<ul>
<li><code>expect_plot</code> handles scoping, the difference threshold, and platform variants.</li>
<li><code>expect_snap.data.frame</code> handles deterministic ordering and meaning-based comparison.</li>
<li><code>.expect_snapshot</code> handles filtering nondeterministic console output.</li>
</ul>
<p>You decide <em>once</em> how a given kind of output should be captured, made readable, and made deterministic. Then every test that uses the expectation gets it right for free. That’s the real payoff. You can use <code>expect_snapshot()</code> as is, but you can also tailor available <code>testthat</code> functions to better fit your needs and make them more reusable, more expressive. The expectations you build on top of it are where snapshot testing becomes a tool your whole suite can lean on.</p>
<h2 id="cheat-sheet">Cheat sheet</h2>













































<table><thead><tr><th>Output type</th><th>Capture as</th><th>How to keep it deterministic</th></tr></thead><tbody><tr><td>ggplot</td><td>SVG (<code>vdiffr</code>)</td><td>Fixed data; vdiffr handles rendering</td></tr><tr><td>htmlwidget / Shiny</td><td>PNG (<code>webshot</code>)</td><td>Difference threshold + platform variant</td></tr><tr><td>Interactive widget</td><td>PNG of a state</td><td>Drive a deterministic action, then shot</td></tr><tr><td>Console / reporter</td><td><code>.md</code> text</td><td><code>transform</code> to strip timing/spinners</td></tr><tr><td>Data frame</td><td>CSV</td><td>Sort rows; compare parsed frames</td></tr><tr><td>Error / warning</td><td><code>.md</code> text</td><td><code>error = TRUE</code>; message is already fixed</td></tr><tr><td>Nested structure</td><td>pretty JSON/YAML</td><td>Stable key order from the serializer</td></tr></tbody></table>
<p>The technique is the same everywhere: capture, save, compare. What changes is the format and how you tame the noise. Keep snapshots <strong>scoped</strong> so failures mean something, <strong>readable</strong> so you can review them, <strong>deterministic</strong> so they don’t flake, and <strong>well-named</strong> so they stand on their own.</p>
<p>Do that, and snapshot testing covers far more ground than the screenshots it’s famous for.</p>
<h2 id="apply-this">Apply this</h2>
<p>Reading about the practices is easy. Applying them to a real suite is the work. The fastest way to internalize them is to point an AI agent at your own snapshot tests, have it find where they drift from the five practices, then fix the worst few yourself; that’s how you learn.</p>
<p>Open your test files in your AI coding agent (Claude Code, Cursor, Copilot Chat) and paste this prompt:</p>
<pre>You are a senior R engineer reviewing a test suite's use of snapshot testing (testthat 3rd edition: expect_snapshot / expect_snapshot_file, plus vdiffr for plots).

Scope: audit the test file(s) I've shared AND the production code they exercise. Snapshot quality usually can't be judged from the test alone — you need to see what's being captured and where any nondeterminism comes from.

Judge every snapshot against five practices, and flag where each is violated:

1. Scoped — the snapshot captures exactly the behavior under test, nothing more. A test for one chart that snapshots a whole dashboard (header, sidebar, &quot;last refreshed&quot; timestamp) fails on unrelated changes. Fix: capture the smallest artifact that proves the behavior.
2. Human-readable — the snapshot is text a reviewer can read in a diff: .md, .csv, .svg, pretty JSON/YAML. Flag binary or .rds snapshots; they can't be reviewed, so they get rubber-stamped. Fix: serialize to a text format.
3. Deterministic — the snapshot is identical run to run. Flag captured timestamps, random IDs, elapsed-time/durations, unordered query results, locale-dependent formatting. Fix at the source first (inject a fixed clock / seed / IDs — dependency injection); if you can't, filter with the `transform` argument, or sort rows before serializing.
4. Platform-stable — rendered (image) snapshots use a `variant` keyed on OS / R version / key dependency version, and image comparison allows a tolerance instead of pixel-exact equality. Flag a single _snaps file shared across platforms, or a zero-threshold pixel compare on CI. Mention the CI-as-source-of-truth workflow (testthat::snapshot_download_gh) where relevant.
5. Well-named — the test title states the precondition and the expected output (not &quot;&lt;fn&gt; works&quot;), and expect_snapshot_file snapshots get an explicit, descriptive name. The filename and title should explain the artifact without opening the test.

Also check suitability and reuse:
- Wrong tool: a snapshot of a single scalar or boolean should be expect_equal(); a 40-line field-by-field expect_equal() on a big nested object could be a snapshot. Flag both directions.
- Missing abstraction: if the same capture / clean / serialize logic repeats across tests, recommend extracting a domain-specific expectation (e.g. expect_plot(), expect_snap.data.frame()) that bakes the practices in once.

Rules:
- Only flag clear instances. Don't invent issues to look thorough.
- 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 dependency injection (a signature change), say so and describe the new interface.

Output:
1. A triage table: practice violated | 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 problem. Stop after three and ask before continuing if more remain.
3. Tell me how to re-run just these tests, and which snapshots I'll need to review and accept.</pre>
<p>Before you accept a snapshot, run it past this checklist:</p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled> You read the recorded output and decided it’s correct — you didn’t just accept the green check.</li>
<li class="task-list-item"><input type="checkbox" disabled> The snapshot captures only the behavior under test, so a failure means something.</li>
<li class="task-list-item"><input type="checkbox" disabled> It’s stored in human-readable format, not binary, so you can read and diff it in a pull request.</li>
<li class="task-list-item"><input type="checkbox" disabled> Nothing in it changes run to run — no timestamps, random IDs, or incidental ordering.</li>
<li class="task-list-item"><input type="checkbox" disabled> The test title and snapshot filename state the behavior on their own.</li>
</ul>
<p>Want to turn these habits into a path you can work through for your whole suite? The <a href="https://jakubsobolewski.com/get-roadmap" rel="nofollow" target="_blank">R testing roadmap</a> lays out the steps.</p>
<h2 id="references">References</h2>
<ol>
<li>testthat — <a href="https://testthat.r-lib.org/articles/snapshotting.html" rel="nofollow" target="_blank">Snapshot tests</a></li>
<li>vdiffr — <a href="https://vdiffr.r-lib.org/" rel="nofollow" target="_blank">Visual regression testing for ggplot2</a></li>
<li>muttest — <a href="https://github.com/jakubsob/muttest/blob/main/tests/testthat/test-test_reporter-progress.R" rel="nofollow" target="_blank">progress reporter snapshot tests</a></li>
<li><a href="https://jakubsobolewski.com/blog/test-smells-in-r/" rel="nofollow" target="_blank">11 Test Smells That Make Your Tests Lie to You</a></li>
</ol>
<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/snapshot-testing-beyond-screenshots"> 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/snapshot-testing-in-r-beyond-screenshots/">Snapshot Testing in R: Beyond Screenshots</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402125</post-id>	</item>
		<item>
		<title>TheseusPlot 0.3.0: Visualizing the Decomposition of Differences in Rate Metrics</title>
		<link>https://www.r-bloggers.com/2026/06/theseusplot-0-3-0-visualizing-the-decomposition-of-differences-in-rate-metrics/</link>
		
		<dc:creator><![CDATA[Koji Makiyama]]></dc:creator>
		<pubDate>Wed, 17 Jun 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>TheseusPlot is an R package that decomposes differences in a rate metric between two groups into subgroup-level contributions and visualizes the results as a “Theseus Plot”.<br />
For example, when a click-through rate, conversion rate, or retention r...</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/theseusplot-0-3-0-visualizing-the-decomposition-of-differences-in-rate-metrics/">TheseusPlot 0.3.0: Visualizing the Decomposition of Differences in Rate Metrics</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://hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/"> HOXO-M 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><strong>TheseusPlot</strong> is an R package that decomposes differences in a rate metric between two groups into subgroup-level contributions and visualizes the results as a “Theseus Plot”.</p>
<p>For example, when a click-through rate, conversion rate, or retention rate differs between two time periods or groups, TheseusPlot helps answer questions such as: which subgroup contributed most to the difference?</p>
<p>Suppose that the click-through rate (CTR) was 6.2% in 2024 and 5.2% in 2025, a decrease of 1.0 percentage point. A Theseus Plot can show how this decrease is decomposed: in this example, 0.8 percentage points are assigned to male users and 0.2 percentage points to female users under the decomposition.</p>
<div class="cell">
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i1.wp.com/hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/figures/example-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>Version 0.3.0 is now available on CRAN. This release fixes a compatibility issue with waterfalls 1.1.4, improves subgroup size bar rendering, and refines several plot defaults.</p>
<section id="whats-new-in-0.3.0" class="level2">
<h2 class="anchored" data-anchor-id="whats-new-in-0.3.0">What’s new in 0.3.0</h2>
<section id="cleaner-plot-labels" class="level3">
<h3 class="anchored" data-anchor-id="cleaner-plot-labels">Cleaner plot labels</h3>
<p>In earlier versions, TheseusPlot automatically displayed the analyzed column name as a subtitle. However, this was not always useful, especially when the plot was already used in a document or presentation where the context was clear.</p>
<p>In version 0.3.0, the automatic column-name subtitle has been removed. This makes the resulting plots cleaner and easier to combine with custom titles, captions, and surrounding text.</p>
<p>This release also adds an <code>xlab</code> argument to <code>create_ship()</code>, so you can customize the x-axis label used by <code>plot()</code> and <code>plot_flip()</code>.</p>
<p>For example:</p>
<div class="cell">
<pre>ship &lt;- create_ship(
  data_2024,
  data_2025,
  y = clicked,
  labels = c(&quot;2024&quot;, &quot;2025&quot;),
  xlab = &quot;Gender&quot;,
  ylab = &quot;CTR (%)&quot;
)

ship$plot(gender)</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i2.wp.com/hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/figures/xlab-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>This is useful when the column name in the data is short or technical, but you want a more readable label in the plot.</p>
</section>
<section id="better-default-labels" class="level3">
<h3 class="anchored" data-anchor-id="better-default-labels">Better default labels</h3>
<p>The default group labels have been changed from <code>&quot;Original&quot;</code> and <code>&quot;Refitted&quot;</code> to <code>&quot;Baseline&quot;</code> and <code>&quot;Comparison&quot;</code>.</p>
<div class="cell">
<pre>ship &lt;- create_ship(
  data_2024,
  data_2025,
  y = clicked
)

ship$plot(gender)</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i2.wp.com/hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/figures/no-labels-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The previous labels reflected the internal idea of replacing one group with another, but they were not always intuitive for users. The new defaults better match common comparison scenarios, such as year-over-year comparisons, control versus treatment, and before-and-after analyses.</p>
<p>Of course, you can still specify your own labels:</p>
<div class="cell">
<pre>ship &lt;- create_ship(
  data_Nov,
  data_Dec,
  y = on_time,
  labels = c(&quot;November&quot;, &quot;December&quot;)
)</pre>
</div>
</section>
<section id="simpler-numeric-display" class="level3">
<h3 class="anchored" data-anchor-id="simpler-numeric-display">Simpler numeric display</h3>
<p>The default number of displayed decimal places has been changed from 3 to 1.</p>
<p>In many plots, three decimal places made the labels more detailed than necessary. Since TheseusPlot is mainly intended to help users understand the structure of a metric difference, one decimal place is often enough for visual interpretation.</p>
<p>You can still control the precision with the <code>digits</code> argument when needed.</p>
<div class="cell">
<pre>ship &lt;- create_ship(
  data_2024,
  data_2025,
  y = clicked,
  labels = c(&quot;2024&quot;, &quot;2025&quot;),
  digits = 2
)

ship$plot(gender)</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/figures/digits-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
</section>
</section>
<section id="plot-improvements-and-bug-fixes" class="level2">
<h2 class="anchored" data-anchor-id="plot-improvements-and-bug-fixes">Plot improvements and bug fixes</h2>
<p>Version 0.3.0 also includes several improvements and bug fixes related to plot rendering.</p>
<p>First, missing subgroup size bars in <code>plot()</code> and <code>plot_flip()</code> with waterfalls 1.1.4 have been fixed. Subgroup size bars are an important part of Theseus Plots because they show the sample size of each subgroup in both groups. Without them, it becomes harder to judge whether a large contribution comes from a large subgroup, a large rate difference, or both.</p>
<p>Second, subgroup size bar scaling has been improved. Bar heights are now computed consistently from the maximum plot score in both <code>plot()</code> and <code>plot_flip()</code>. This makes visual comparisons more stable across plot directions. The maximum height of these bars can still be controlled with the <code>bar_max_value</code> argument.</p>
<p>Third, <code>text_size</code> handling has been fixed when applying the current ggplot2 theme. This makes text scaling more predictable when users customize plot themes.</p>
<div class="cell">
<pre>ship &lt;- create_ship(
  data_2024,
  data_2025,
  y = clicked,
  labels = c(&quot;2024&quot;, &quot;2025&quot;),
  text_size = 1.5
)

ship$plot(gender)</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i2.wp.com/hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/figures/text_size-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
</section>
<section id="installation" class="level2">
<h2 class="anchored" data-anchor-id="installation">Installation</h2>
<p>You can install TheseusPlot from CRAN with:</p>
<pre>install.packages(&quot;TheseusPlot&quot;)</pre>
</section>
<section id="try-it-out" class="level2">
<h2 class="anchored" data-anchor-id="try-it-out">Try it out</h2>
<p>TheseusPlot is useful when you want to understand why rate metrics differ between two groups.</p>
<p>Typical examples include:</p>
<ul>
<li>click-through rate</li>
<li>conversion rate</li>
<li>retention rate</li>
<li>success rate</li>
<li>error rate</li>
</ul>
<p>For details, please see the package website:</p>
<ul>
<li><a href="https://hoxo-m.github.io/TheseusPlot/" rel="nofollow" target="_blank">https://hoxo-m.github.io/TheseusPlot/</a></li>
</ul>


</section>

 
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/"> HOXO-M 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/theseusplot-0-3-0-visualizing-the-decomposition-of-differences-in-rate-metrics/">TheseusPlot 0.3.0: Visualizing the Decomposition of Differences in Rate Metrics</a>]]></content:encoded>
					
		
		<enclosure url="https://hoxo-m.github.io/blog/posts/TheseusPlot-0-3-0/figures/example-1.png" length="0" type="image/png" />

		<post-id xmlns="com-wordpress:feed-additions:1">402085</post-id>	</item>
		<item>
		<title>How I Used One-Way ANOVA in R to Analyze Crop Yield Data for a PhD Student (Real Case Study)</title>
		<link>https://www.r-bloggers.com/2026/06/how-i-used-one-way-anova-in-r-to-analyze-crop-yield-data-for-a-phd-student-real-case-study/</link>
		
		<dc:creator><![CDATA[Unknown]]></dc:creator>
		<pubDate>Wed, 17 Jun 2026 10:12:31 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://www.r-bloggers.com/?guid=41ca7943e3b1e71e5114c5fe09327503</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> My client's supervisor had rejected Chapter 4 twice. Not because the data was bad — the field trial was clean, the yield measurements precise. The problem was the statistics. And until I looked at the file, the student had no idea what was actually wro...</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/how-i-used-one-way-anova-in-r-to-analyze-crop-yield-data-for-a-phd-student-real-case-study/">How I Used One-Way ANOVA in R to Analyze Crop Yield Data for a PhD Student (Real Case Study)</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.rstudiodatalab.com/2026/06/how-i-used-anova-in-r-crop-yield-phd-thesis.html"> RStudioDataLab</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 style="text-align: justify;"><span style="font-family: inherit;">My client&#8217;s supervisor had rejected Chapter 4 twice. Not because the data was bad — the field trial was clean, the yield measurements precise. The problem was the statistics. And until I looked at the file, the student had no idea what was actually wrong.</span></p><p style="text-align: justify;"><span style="font-family: inherit;">This is the full story of how I ran one-way ANOVA in R to analyze wheat yield data from a three-treatment fertilizer trial, checked every assumption, wrote the APA results section, and delivered it in under 24 hours. I have helped over 500 researchers through this exact kind of problem. Here is what the process actually looks like.</span></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: justify;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdkk8AXDwU-LGNctZsKJmu6gI2Y-ouBIae3CZkM_MHcFNlu3vdMR1UNNxjpj3M7kkEK11rRSOpD3uMBV-nMhzsNvKU6M_c3FH6R0xtkbgktSvzJtea7M7hv8LAH0hYFHlJ-7Z8ONJyv46wXTK1N-YpYDTCuAudz92Pyx4FfSYq4fc9TWdC_60PRChN_G4/s1200/How%20I%20Used%20One-Way%20ANOVA%20in%20R%20to%20Analyze%20Crop%20Yield%20Data%20for%20a%20PhD%20Student%20(Real%20Case%20Study).jpg" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="How I Used One-Way ANOVA in R to Analyze Crop Yield Data for a PhD Student (Real Case Study)" border="0" data-original-height="630" data-original-width="450" src="https://i1.wp.com/blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdkk8AXDwU-LGNctZsKJmu6gI2Y-ouBIae3CZkM_MHcFNlu3vdMR1UNNxjpj3M7kkEK11rRSOpD3uMBV-nMhzsNvKU6M_c3FH6R0xtkbgktSvzJtea7M7hv8LAH0hYFHlJ-7Z8ONJyv46wXTK1N-YpYDTCuAudz92Pyx4FfSYq4fc9TWdC_60PRChN_G4/s16000/How%20I%20Used%20One-Way%20ANOVA%20in%20R%20to%20Analyze%20Crop%20Yield%20Data%20for%20a%20PhD%20Student%20(Real%20Case%20Study).jpg?resize=450%2C630&#038;ssl=1" title="How I Used One-Way ANOVA in R to Analyze Crop Yield Data for a PhD Student (Real Case Study)" data-recalc-dims="1" /></a></div><div style="text-align: justify;"><br /></div><span><div style="text-align: justify;"><br /></div></span><p></p><p style="text-align: justify;"><span style="font-family: inherit;">The dataset had three fertilizer treatments measured across three growing seasons at a UK agricultural research site. The student needed to know whether treatment type significantly affected crop yield — and which specific treatments differed from each other. That question calls for a<a href="https://www.rstudiodatalab.com/2023/06/ANOVA-Assumptions-Violated-Types-Methods-2023.html" rel="nofollow" target="_blank"> perform ANOVA</a>, followed by a post-hoc comparison.</span></p><div class="alert info" style="text-align: justify;"><span style="font-family: inherit;"><b>Info!</b><span id="docs-internal-guid-f37839e4-7fff-5dc7-37de-ae513729c2c7"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Sound familiar?</span><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> I can run this exact analysis on your data and deliver the full APA results section in 24 hours —</span><a href="https://wa.me/message/J6ELCCB6EW7YC1" style="text-decoration: none;" rel="nofollow" target="_blank"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span face="Arial, sans-serif" style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">WhatsApp me now →</span></a></span></span></div><span id="docs-internal-guid-929a3bad-7fff-0b2f-baea-1a4de4ab1ab9">
  <details class="sp toc" open="" style="font-family: inherit;"><summary data-hide="Hide all" data-show="Show all" style="text-align: justify;">Table of Contents</summary><div class="aToc"></div></details>
  
  <h2 dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 4pt; margin-top: 18pt; text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 17pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">The Client&#8217;s Problem (Chapter 4 Rejected Twice)</span></h2><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">The student — a third-year PhD candidate at a leading UK university — was studying the effect of fertilizer type on wheat yield. She had collected three years of field trial data, cleaned it carefully, and handed it to her supervisor with a results section she had written herself.</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"><b>First rejection</b>. The supervisor&#8217;s comment: </span><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-style: italic; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">&#8220;You have used three separate t-tests to compare each pair of treatments. It inflates your Type I error rate. A single ANOVA is the correct method.&#8221;</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">She corrected it and resubmitted. <b>Second rejection</b>. This time: </span><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-style: italic; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">&#8220;You have run the ANOVA but not reported any assumption checks. I need to see normality and homogeneity of variance tests before I can accept these results.&#8221;</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">That is when she contacted me. She was eight months from her final submission date</span><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> and her statistical </span><a href="https://www.rstudiodatalab.com/2023/09/Exploratory-Factor-Analysis.html" style="text-decoration: none;" rel="nofollow" target="_blank"><span face="Arial, sans-serif" style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">analysis</span></a><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> — supposedly the most routine part of Chapter 4 — was stalling her progress.</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">The core problem was not unusual. Running three separate t-tests instead of a one-way ANOVA is one of the most common mistakes I see in thesis data analysis. Each individual t-test sets α = .05, but when you run three, the family-wise error rate climbs to roughly 14%. The supervisor was right to reject it. And skipping assumption checks is equally serious — reviewers and examiners expect to see that the model&#8217;s conditions were verified, not assumed.</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">I asked her to send the raw data file. Within 20 minutes of opening it, I had identified exactly what needed to be done: </span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"></p><ul style="font-family: inherit;"><li style="text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"><a href="https://www.rstudiodatalab.com/2024/10/shapiro-wilk-normality-test-shapirotest.html" rel="nofollow" target="_blank">Shapiro-Wilk normality test</a> on the residuals, </span></li><li style="text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"><a href="https://www.rstudiodatalab.com/2025/02/levene-test-in-r-for-homogeneity-of.html" rel="nofollow" target="_blank">Levene&#8217;s test</a> for homogeneity of variance, </span></li><li style="text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">the main <a href="https://www.rstudiodatalab.com/2023/06/ANOVA-Assumptions-Violated-Types-Methods-2023.html" rel="nofollow" target="_blank">one-way ANOVA</a> using </span><span style="color: #188038; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">aov()</span><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">, </span></li><li style="text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"><a href="https://www.rstudiodatalab.com/2023/06/Tukey-HSD-test-Parametric-2023.html" rel="nofollow" target="_blank">Tukey HSD post-hoc</a> to identify which specific treatments differed. </span></li><li style="text-align: justify;"><span face="Arial, sans-serif" style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Then a clean APA write-up of all four results.</span></li></ul><p style="font-family: inherit;"></p>
  
  <div class="pRelate" style="font-family: inherit;"><div style="text-align: justify;"><b style="font-family: inherit;">Before We start Make sure you Have:</b></div><ul>
  <li style="text-align: justify;"><a href="https://www.rstudiodatalab.com/2023/06/a-comprehensive-guide-to-rstudio.html" rel="nofollow" target="_blank">Comprehensive Guide: How to install RStudio</a></li>
  <li style="text-align: justify;"><a href="https://www.rstudiodatalab.com/2023/07/how-to-import-install-packages-r.html" rel="nofollow" target="_blank">How to Import and Install Packages in R: A Comprehensive Guide</a></li>
  <li style="text-align: justify;"><a href="https://www.rstudiodatalab.com/2024/09/how-to-import-data-into-r-load-data.html" rel="nofollow" target="_blank">How to Import Data into R | Load Data file in R Programming</a></li>
  <li style="text-align: justify;"><a href="https://www.rstudiodatalab.com/search/label/Data%20Preprocessing" rel="nofollow" target="_blank">Preprocess the data</a></li></ul></div>
  <div><span face="Arial, sans-serif" style="color: #0e101a; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span id="docs-internal-guid-b4233d6e-7fff-9b60-0c82-a8768306f15b"><h2 dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 4pt; margin-top: 18pt; text-align: justify; white-space: pre-wrap;"><span style="font-size: 17pt; font-variant: normal; vertical-align: baseline;">The Dataset (Crop Yield, 3 Treatments × 3 Years)</span></h2><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;"><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline;">The trial measured wheat yield in kilograms per hectare (kg/ha) across three fertilizer treatments applied to field plots in Yorkshire from 2021 to 2023. Each treatment had three replicated plots per season, giving nine observations per group and 27 total.</span></p><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;">  <span style="font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline;">Treatments:</span></p><ul style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px; white-space: pre-wrap;"><li aria-level="1" dir="ltr" style="font-size: 11pt; font-variant: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 12pt; text-align: justify;"><span style="font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Control</span><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> — no fertilizer applied</span></p></li><li aria-level="1" dir="ltr" style="font-size: 11pt; font-variant: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: justify;"><span style="font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Organic</span><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> — composted farmyard manure (25 t/ha)</span></p></li><li aria-level="1" dir="ltr" style="font-size: 11pt; font-variant: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 0pt; text-align: justify;"><span style="font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Chemical</span><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> — NPK granular fertilizer (120:60:60 kg/ha)</span></p></li></ul><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;"><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline;">Before running any test, I summarised the</span><a href="https://www.rstudiodatalab.com/2023/06/RStudio-Documentation-Your-Essential-Guide-to-Descriptive-Statistics.html" style="text-decoration: none;" rel="nofollow" target="_blank"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline;"> </span><span style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline;">descriptive statistics</span></a><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline;"> for each group:</span></p><div align="left" dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; margin-left: 0pt; white-space: pre-wrap;"><table style="border-collapse: collapse; border-color: currentcolor; border-image: initial; border-style: none; border-width: medium; border: none; text-align: justify;"><colgroup><col width="86"></col><col width="24"></col><col width="107"></col><col width="88"></col><col width="47"></col><col width="47"></col></colgroup><tbody><tr style="height: 26.5pt;"><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: center;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Treatment</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: center;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">n</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: center;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Mean (kg/ha)</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: center;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">SD (kg/ha)</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: center;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Min</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: center;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">Max</span></p></td></tr><tr style="height: 26.5pt;"><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Control</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">9</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">2900.00</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">321.10</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">2393</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">3339</span></p></td></tr><tr style="height: 26.5pt;"><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Organic</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">9</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">3400.00</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">276.97</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">3036</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">3933</span></p></td></tr><tr style="height: 26.5pt;"><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;">
    <span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Chemical</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">9</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">3900.00</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">353.52</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">3191</span></p></td><td style="overflow-wrap: break-word; overflow: hidden; padding: 5pt; vertical-align: top;"><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">4474</span></p></td></tr></tbody></table></div><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;"><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline;">The 500 kg/ha step between each group looked substantively meaningful even before testing. The real question was whether that separation exceeded natural within-plot variability.</span></p><div style="font-family: inherit;"><h2 style="text-align: justify;"><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span style="font-size: large;">Step 1: Checking Normality (Shapiro-Wilk + Q-Q Plot)</span></span></h2><p style="text-align: left;"></p><div style="text-align: justify;"><span style="font-family: inherit; font-size: 14.6667px; white-space-collapse: preserve;">One-way ANOVA assumes that the residuals follow a normal distribution. I always run this check on the model residuals — not on raw group values — because that is what the assumption actually refers to.</span></div><span style="font-size: 14.6667px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><div style="text-align: justify;"><span style="font-family: inherit; font-size: 14.6667px;">I used the shapiro.test function from base R alongside a Q-Q plot.</span></div></span><p></p></div><h3 style="font-family: inherit; text-align: justify;"><span style="font-size: 14.6667px; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Step 1: Load packages </span></h3>
<pre>library(car)      # leveneTest()library(ggplot2)  # Q-Q plottreatment &lt;- factor(rep(c(&quot;Control&quot;, &quot;Organic&quot;, &quot;Chemical&quot;), each = 9),                       levels = c(&quot;Control&quot;, &quot;Organic&quot;, &quot;Chemical&quot;))yield &lt;-c(  # Control: no fertilizer (kg/ha)  2985, 2973, 3339, 3282, 3021, 2467, 2393, 2849, 2791,  # Organic: composted farmyard manure (kg/ha)  3310, 3435, 3143, 3401, 3299, 3933, 3724, 3319, 3036,  # Chemical: NPK granular fertilizer (kg/ha)  3848, 3913, 3876, 4149, 3191, 3967, 4054, 3628, 4474)crop_data &lt;- data.frame(treatment, yield)

# Fit model first so residuals are available 
model &lt;- aov(yield ~ treatment, data = crop_data)
#  Shapiro-Wilk on residuals 
shapiro.test(residuals(model))

#  Q-Q plot 
qqnorm(residuals(model), main = &quot;Normal Q-Q Plot of Residuals&quot;)
qqline(residuals(model), col = &quot;red&quot;, lwd = 2)

</pre><span><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; font-family: inherit; text-align: justify;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgEZ41UZGqpOfo5eJNh5ziBeMK-_PNIA0u3gwDbgUC88LgxN8cVWnlyBn5RB5H6mvB_484NhrNjriTRPi23gCAASQMOZEm_D4acIPL2uC7ieeqOiejI4ZIVpdrNi4JqTcsaJaBimebU4HcHM2zryQc_7wJ5qoYzU6MAkMXja125tI1tmC-vU1mX_3TBbJw" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="Fit Anova model, Shapiro-wilk on residuals and Q-Q Plot working code" data-original-height="529" data-original-width="450" src="https://blogger.googleusercontent.com/img/a/AVvXsEgEZ41UZGqpOfo5eJNh5ziBeMK-_PNIA0u3gwDbgUC88LgxN8cVWnlyBn5RB5H6mvB_484NhrNjriTRPi23gCAASQMOZEm_D4acIPL2uC7ieeqOiejI4ZIVpdrNi4JqTcsaJaBimebU4HcHM2zryQc_7wJ5qoYzU6MAkMXja125tI1tmC-vU1mX_3TBbJw=s16000" title="Fit Anova model, Shapiro-wilk on residuals and Q-Q Plot working code" /></a></div><div class="separator" style="clear: both; font-family: inherit; text-align: justify;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhibmzfWHo7lAVkXeuQTsNaozyv41ZIxIqzwW9_vvXFlGwMfj--cpN_siAp0C6lfnaj6uQoJWP3n-kgXrcbXGM9xqOUFkKQ58QUW2QMcoutfzMTrUy5zKMKiyaqruijbI2XKuiJldMYzZvsCsS9AOS3K_zYCGr1810A8z4cmNx_6yDQN3NnoGiryQORUI0" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="Normal Q-Q Plot for residual in R" data-original-height="360" data-original-width="450" src="https://blogger.googleusercontent.com/img/a/AVvXsEhibmzfWHo7lAVkXeuQTsNaozyv41ZIxIqzwW9_vvXFlGwMfj--cpN_siAp0C6lfnaj6uQoJWP3n-kgXrcbXGM9xqOUFkKQ58QUW2QMcoutfzMTrUy5zKMKiyaqruijbI2XKuiJldMYzZvsCsS9AOS3K_zYCGr1810A8z4cmNx_6yDQN3NnoGiryQORUI0=s16000" title="Normal Q-Q Plot for residual in R" /></a></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><br /></div><h4 style="font-family: inherit; text-align: justify;">SPSS equivalent:</h4><pre>* After entering data in SPSS Data View:EXAMINE VARIABLES = yield BY treatment  /PLOT NPPLOT  /STATISTICS DESCRIPTIVES  /CINTERVAL 95  /MISSING LISTWISE  /NOTOTAL.</pre></span></span></span><p style="font-family: inherit; text-align: justify;"><span style="color: #0e101a;">The Output
	Shapiro-Wilk normality test

data:  residuals(model)
W = 0.97692, p-value = 0.787

The output shows W = 0.977 and p = .787. The Q-Q plot showed points tracking closely along the reference line with no systematic departures.
What It Means
A Shapiro-Wilk p-value above .05 means we retain the null hypothesis that the residuals are normally distributed. With p = .787, there is no evidence of non-normality. The normality assumption is satisfied, and we can proceed to Levene's test.
APA-formatted result: W(27) = 0.977, p = .787</span></p><h3 dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 4pt; margin-top: 14pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 13pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">What It Means</span></h3><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">A Shapiro-Wilk p-value above .05 means we retain the </span><a href="https://www.rstudiodatalab.com/2023/06/hypothesis-testing-step-by-step-guide.html" style="text-decoration: none;" rel="nofollow" target="_blank"><span style="background-color: transparent; color: #1155cc; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">null hypothesis</span></a><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> that the residuals are normally distributed. With p = .787, there is no evidence of non-normality. The normality assumption is satisfied, and we can proceed to Levene's test.</span></p><p style="font-family: inherit; text-align: justify;"><span id="docs-internal-guid-95976b09-7fff-57c5-c4b9-3eb4e838cd0e"></span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">APA-formatted result:</span><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> W(27) = 0.977, p = .787</span></p></div><div><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><h2 dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 4pt; margin-top: 18pt; text-align: justify; white-space: pre-wrap;"><span style="color: #0e101a; font-size: 17pt; font-variant: normal; vertical-align: baseline;">Levene's Test (Homogeneity of Variance)</span></h2><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline;">ANOVA also requires that variance is roughly equal across groups — this is the homogeneity of variance assumption. I use Levene's test via the </span><span style="color: #188038; font-size: 11pt; font-variant: normal; vertical-align: baseline;">car</span><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline;"> package because it is more robust to for normality than Bartlett's test. A full guide to</span><a href="https://www.rstudiodatalab.com/2025/02/levene-test-in-r-for-homogeneity-of.html" style="text-decoration: none;" rel="nofollow" target="_blank"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline;"> </span><span style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline;">homogeneity of variances</span></a><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline;"> testing is available on this blog.</span></p><h3 dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 4pt; margin-top: 14pt; text-align: justify; white-space: pre-wrap;"><span style="color: #0e101a; font-size: 13pt; font-variant: normal; vertical-align: baseline;">The Code</span></h3><pre># Levene's test (center = median, Brown-Forsythe variant) leveneTest(yield ~ treatment, data = crop_data, center = median)</pre><span style="font-size: medium;"><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; font-family: inherit; font-variant-caps: normal; font-variant-ligatures: normal; text-align: justify; white-space: normal;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEin6ZA5eYaF66HwBBku8LRkC8cFT-kILZHZbHPlQs2U10LGDShlWPUNui9NdB70GZ9Mr3gloTnPUO1o4O1CYTV1BuopJD5nBn-4HCsJLitdRz5Nxilf_RI7AavlfblmhvPnqU5aHRKOf4a2iUhBEAgRT7S8gG6qVw7Wj_lpE5eyouQOdALMkutcn1p7ylw" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="Levene's test (center = median, Brown-Forsythe variant)" data-original-height="100" data-original-width="450" src="https://blogger.googleusercontent.com/img/a/AVvXsEin6ZA5eYaF66HwBBku8LRkC8cFT-kILZHZbHPlQs2U10LGDShlWPUNui9NdB70GZ9Mr3gloTnPUO1o4O1CYTV1BuopJD5nBn-4HCsJLitdRz5Nxilf_RI7AavlfblmhvPnqU5aHRKOf4a2iUhBEAgRT7S8gG6qVw7Wj_lpE5eyouQOdALMkutcn1p7ylw=s16000" title="Levene's test (center = median, Brown-Forsythe variant)" /></a></div></span><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;"><span style="color: #0e101a; font-size: 11pt; font-weight: 700;">SPSS equivalent:</span></p><pre>* Levene's test is reported automatically within:
ONEWAY yield BY treatment
  /STATISTICS HOMOGENEITY.</pre></span><p style="font-family: inherit; text-align: left;"><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span style="color: #0e101a; text-align: justify; white-space: pre-wrap;">The output shows F(2, 24) = 0.12, p = .886. The group variances — Control SD = 321, Organic SD = 277, Chemical SD = 354 — are clearly within an acceptable range of each other.</span></span></p><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><h3 dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 4pt; margin-top: 14pt; text-align: justify; white-space: pre-wrap;"><span style="color: #0e101a; font-size: 13pt; font-variant: normal; vertical-align: baseline;">What It Means</span></h3><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline;">With p = .886, we retain the null hypothesis of equal variances across the three fertilizer groups. The homogeneity assumption holds. Both assumption checks are cleared; the ANOVA result is defensible.</span></p><p dir="ltr" style="font-family: inherit; font-size: 11pt; font-variant-caps: normal; font-variant-ligatures: normal; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify; white-space: pre-wrap;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline;">APA-formatted result:</span><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline;"> F(2, 24) = 0.12, p = .886</span></p><h2 dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 4pt; margin-top: 18pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 17pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">One-Way ANOVA in R (aov() Full Code + Output)</span></h2><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">With both assumptions confirmed, I ran the one way ANOVA in R using </span><span style="background-color: transparent; color: #188038; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">aov()</span><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">. This is the function that fits the ANOVA model and partitions total variance into treatment variance (between groups) and residual variance (within groups). The F statistic is the ratio of those two quantities.</span></p><pre># One-way ANOVA model &lt;- aov(yield ~ treatment, data = crop_data)summary(model)# Effect size: eta-squared ss   &lt;- summary(model)[[1]][, &quot;Sum Sq&quot;]eta2 &lt;- ss[1] / sum(ss)        # SS_treatment / SS_totalcat(&quot;eta-squared =&quot;, round(eta2, 3), &quot;\n&quot;)# Visualise group means (box plot) ggplot(crop_data, aes(x = treatment, y = yield, fill = treatment)) +  geom_boxplot(alpha = 0.7) +  labs(title = &quot;Wheat Yield by Fertilizer Treatment&quot;,       x = &quot;Treatment&quot;, y = &quot;Yield (kg/ha)&quot;) +  theme_minimal() +  theme(legend.position = &quot;none&quot;)</pre><div style="text-align: justify;"><br /></div><h3 dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 4pt; margin-top: 14pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; white-space: pre-wrap;">SPSS equivalent:</span></h3><pre>ONEWAY yield BY treatment  /STATISTICS DESCRIPTIVES HOMOGENEITY EFFECTS  /POSTHOC TUKEY ALPHA(0.05)  /PLOT MEANS.</pre><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 13pt; font-weight: 700; white-space: pre-wrap;">The Output</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; white-space: pre-wrap;"></span></p><div class="separator" style="clear: both; font-family: inherit; text-align: justify;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhM9rOHCxSHqMEFP_HSlr2QMC5kKg5l8aj4B_Nz251BCjbK5jq8UdLspjmljOc-L4tnrdpMngOgO40E2UneGWyrTLz35NsL6uIEQKKEERJm9FKl52JTyrk-QYj3ZI2XCpm9oaBmgIHgDtWG1OoKirNdiaMz5qJwblOS19tqlbl67bgmFjnMhwkZ5i7S3VA" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="One way anova by using the rstudio" data-original-height="349" data-original-width="450" src="https://blogger.googleusercontent.com/img/a/AVvXsEhM9rOHCxSHqMEFP_HSlr2QMC5kKg5l8aj4B_Nz251BCjbK5jq8UdLspjmljOc-L4tnrdpMngOgO40E2UneGWyrTLz35NsL6uIEQKKEERJm9FKl52JTyrk-QYj3ZI2XCpm9oaBmgIHgDtWG1OoKirNdiaMz5qJwblOS19tqlbl67bgmFjnMhwkZ5i7S3VA=s16000" title="One way anova by using the rstudio" /></a></div><div class="separator" style="clear: both; font-family: inherit; text-align: justify;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgaubV7OaxcANgVMxtImZCedyjj_DwU-rZU9JJYN_J-v4cn-xyV0PVVMydzO98p5YRxIOjy4adPp7jUqOPyGe0FhvFhrm4iBxMTL4m33fUYfoQiiMuZ8Jjs1p3ECFHc5Pai0JQ_1xcWtIb9Iz9e_hzduSGukfoUvOoT1DysBbWExFRW7V8fUROqj90gfR4" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="Wheat Yield by Fertilizer Treatment" data-original-height="360" data-original-width="450" src="https://blogger.googleusercontent.com/img/a/AVvXsEgaubV7OaxcANgVMxtImZCedyjj_DwU-rZU9JJYN_J-v4cn-xyV0PVVMydzO98p5YRxIOjy4adPp7jUqOPyGe0FhvFhrm4iBxMTL4m33fUYfoQiiMuZ8Jjs1p3ECFHc5Pai0JQ_1xcWtIb9Iz9e_hzduSGukfoUvOoT1DysBbWExFRW7V8fUROqj90gfR4=s16000" title="Wheat Yield by Fertilizer Treatment" /></a></div><p style="font-family: inherit;"></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; white-space: pre-wrap;">The output shows that treatment accounts for SS = 4,500,000 with MS = 2,250,000. The residual MS (within-group variance) is 101,598. Dividing those gives F = 22.15.</span></p><h3 dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 4pt; margin-top: 14pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 13pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">What It Means</span></h3><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">The p-value (.000004) falls far below any conventional significance threshold. There is a statistically significant difference in mean wheat yield across the three fertilizer treatments. The η² = .65 means that fertilizer treatment alone explains 65% of all variance in yield — a large effect by any standard. This was the number the supervisor had been waiting to see justified properly.</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">APA-formatted result:</span><span style="background-color: transparent; color: #0e101a; font-size: 11pt; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> F(2, 24) = 22.15, p = .000004, η² = .65, 95% CI [.35, .76]</span></p><div class="alert success" style="font-family: inherit;"><span><span style="color: #0e101a; font-size: 14.6667px; font-weight: 700; text-align: justify; white-space: pre-wrap;">Need these results written in APA format for your thesis? </span><span style="color: #0e101a; font-size: 11pt; font-variant: normal; text-align: justify; vertical-align: baseline; white-space: pre-wrap;">That is exactly what I deliver with every project. Visit my</span><a href="https://www.rstudiodatalab.com/p/thesis-data-analysis-service.html" style="text-align: justify; text-decoration: none;" rel="nofollow" target="_blank"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">thesis data analysis service</span></a><span style="color: #0e101a; font-size: 11pt; font-variant: normal; text-align: justify; vertical-align: baseline; white-space: pre-wrap;"> to see what is included.</span></span></div><span id="docs-internal-guid-3f740b6c-7fff-2f2b-dba9-d97acb04f3ae" style="font-family: inherit;"><h2 dir="ltr" style="line-height: 1.38; margin-bottom: 4pt; margin-top: 18pt; text-align: justify;"><span style="color: #0e101a; font-size: 17pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Tukey HSD Post-Hoc (Which Treatments Differed)</span></h2><p dir="ltr" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">A significant ANOVA </span><a href="https://www.rstudiodatalab.com/2024/06/how-to-do-f-test-in-r-compare-variances.html" style="text-decoration: none;" rel="nofollow" target="_blank"><span style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">F-test</span></a><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> only tells you that at least one group mean differs. It does not say which ones. For that, I ran a Tukey Honestly Significant Difference (HSD) post-hoc test — the appropriate choice when comparing all three pairs simultaneously, as it controls the family-wise error rate. For a full discussion of</span><a href="https://www.rstudiodatalab.com/2023/06/post-hoc-test-types-software-data.html" style="text-decoration: none;" rel="nofollow" target="_blank"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">post-hoc test types</span></a><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> and when each applies, see the linked guide.</span></p><h3 dir="ltr" style="line-height: 1.38; margin-bottom: 4pt; margin-top: 14pt; text-align: justify;"><span style="color: #0e101a; font-size: 13pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">The Code</span></h3><pre>tukey_result &lt;- TukeyHSD(model, conf.level = 0.95)print(tukey_result)# ── Plot the confidence intervals ──────────────────────────────────────────plot(tukey_result, las = 1, col = &quot;steelblue&quot;)</pre></span></span><h3 style="font-family: inherit; text-align: left;"><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span><span style="color: #0e101a; font-family: inherit; font-size: 11pt; font-weight: 700; text-align: justify; white-space: pre-wrap;">SPSS equivalent:</span></span></span></h3><span style="font-family: inherit; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span><pre>* Already requested in the ONEWAY syntax above via /POSTHOC TUKEY.* Results appear in the &quot;Multiple Comparisons&quot; output table.</pre></span></span><h3 style="font-family: inherit; text-align: left;"><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span style="color: #0e101a; font-family: inherit; font-size: 13pt; text-align: justify; white-space: pre-wrap;">The Output</span></span></h3><span style="font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline;"><span><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-family: inherit; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"></span></p><div class="separator" style="clear: both; font-family: inherit; text-align: justify;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjW4NREPgN4nUbq5fPB2cu0RoKpk7OPjHO1xilpdKvnaG4AwAHUVN5lQMUJIj8YmWd0XkF_jNMcctEFtbvJLZrFW5wYQ4vwgri8Ls7-TMeWELcGgXUQ7FCLYscJDg6_wH6LuaIy-v-ZMRm-q5nRdH3DTCt8AsC4S54S_yiJKpnB7qZKJm8rFscAHYeyx8g" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="tukey test to compare the yield by treatment" data-original-height="265" data-original-width="450" src="https://blogger.googleusercontent.com/img/a/AVvXsEjW4NREPgN4nUbq5fPB2cu0RoKpk7OPjHO1xilpdKvnaG4AwAHUVN5lQMUJIj8YmWd0XkF_jNMcctEFtbvJLZrFW5wYQ4vwgri8Ls7-TMeWELcGgXUQ7FCLYscJDg6_wH6LuaIy-v-ZMRm-q5nRdH3DTCt8AsC4S54S_yiJKpnB7qZKJm8rFscAHYeyx8g=s16000" title="tukey test to compare the yield by treatment" /></a></div><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; font-family: inherit; text-align: justify;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh92FBrN-vbZ1_VmH6S3FYWWOIrOBlnJ1-NALUf7db7al7HJwj3ruF5FM9-8OqXLzqqHyCTWJThpdtO8V3vU-haXWAC1MzXDA1gyafprJpDk8gFdMhWSAWsOlRN5NPcU7SA9c-YMFlqXQA-C3vZ2yEKGhgHXDsTrzYGytQCMoIhdPUhQgClsswzBYyZDmM" style="margin-left: 1em; margin-right: 1em;" rel="nofollow" target="_blank"><img alt="Difference in mean level by treatment in rstudio" data-original-height="360" data-original-width="450" src="https://blogger.googleusercontent.com/img/a/AVvXsEh92FBrN-vbZ1_VmH6S3FYWWOIrOBlnJ1-NALUf7db7al7HJwj3ruF5FM9-8OqXLzqqHyCTWJThpdtO8V3vU-haXWAC1MzXDA1gyafprJpDk8gFdMhWSAWsOlRN5NPcU7SA9c-YMFlqXQA-C3vZ2yEKGhgHXDsTrzYGytQCMoIhdPUhQgClsswzBYyZDmM=s16000" title="Difference in mean level by treatment in rstudio" /></a></div></span><p style="font-family: inherit;"></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-family: inherit; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">The output shows all three pairwise</span><a href="https://www.rstudiodatalab.com/2023/10/confidence-intervals-in-r.html" style="font-family: inherit; text-decoration: none;" rel="nofollow" target="_blank"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">confidence intervals</span></a><span style="color: #0e101a; font-family: inherit; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> are entirely above zero, confirming that none of the treatment differences are compatible with a null effect.</span></p><h3 dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 4pt; margin-top: 14pt; text-align: justify;"><span style="color: #0e101a; font-size: 13pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">What It Means</span></h3><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Every pairwise comparison is statistically significant. Organic fertilizer produced 500 kg/ha more yield than the control (p = .008). Chemical fertilizer produced 1,000 kg/ha more than the control (p = .000002). And chemical outperformed organic by another 500 kg/ha (p = .008). Each successive step in the treatment sequence produced a meaningful, detectable gain.</span></p><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; font-weight: 700; vertical-align: baseline; white-space: pre-wrap;">APA-formatted results:</span></p><ul style="font-family: inherit; margin-bottom: 0px; margin-top: 0px; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: #0e101a; font-size: 11pt; font-variant: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 12pt; text-align: justify;"><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Organic vs. Control: diff = 500.00 kg/ha, 95% CI [124.76, 875.24], p = .008</span></p></li><li aria-level="1" dir="ltr" style="color: #0e101a; font-size: 11pt; font-variant: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt; text-align: justify;"><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Chemical vs. Control: diff = 1000.00 kg/ha, 95% CI [624.76, 1375.24], p = .000002</span></p></li><li aria-level="1" dir="ltr" style="color: #0e101a; font-size: 11pt; font-variant: normal; list-style-type: disc; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 12pt; margin-top: 0pt; text-align: justify;"><span style="font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">Chemical vs. Organic: diff = 500.00 kg/ha, 95% CI [124.76, 875.24], p = .008</span></p></li></ul><p dir="ltr" style="font-family: inherit; line-height: 1.38; margin-bottom: 12pt; margin-top: 12pt; text-align: justify;"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">The</span><a href="https://www.rstudiodatalab.com/2023/06/Tukey-HSD-test-Parametric-2023.html" style="text-decoration: none;" rel="nofollow" target="_blank"><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="color: #1155cc; font-size: 11pt; font-variant: normal; text-decoration-skip-ink: none; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Tukey HSD test</span></a><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> is implemented in base R via </span><span style="color: #188038; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">TukeyHSD()</span><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> and requires no additional packages after </span><span style="color: #188038; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;">aov()</span><span style="color: #0e101a; font-size: 11pt; font-variant: normal; vertical-align: baseline; white-space: pre-wrap;"> has been fitted.</span></p><div style="font-family: inherit;">
  
  <h2 style="text-align: justify;">ANOVA Results Interpretation: How I Wrote the APA Results Section</h2><p></p><p style="text-align: justify;">This is where most students get stuck — not the test itself but translating the output into the precise language a supervisor or examiner expects. I wrote the following paragraph for her Chapter 4 and she submitted it verbatim after minor phrasing adjustments.</p><p style="text-align: justify;"><i>A one-way ANOVA was conducted to examine the effect of fertilizer treatment (Control, Organic, Chemical) on wheat yield (kg/ha). Prior to analysis, residuals were assessed for normality using the Shapiro-Wilk test, W(27) = 0.977, p = .787, and homogeneity of variance was confirmed via Levene's test, F(2, 24) = 0.12, p = .886. Both assumptions were satisfied. The ANOVA revealed a statistically significant effect of treatment on yield, F(2, 24) = 22.15, p = .000004, η² = .65, 95% CI [.35, .76], indicating a large effect. Post-hoc comparisons using the Tukey HSD procedure showed that Chemical fertilizer produced significantly greater yield than Control (diff = 1000.00 kg/ha, 95% CI [624.76, 1375.24], p = .000002) and than Organic (diff = 500.00 kg/ha, 95% CI [124.76, 875.24], p = .008). Organic treatment also significantly outperformed Control (diff = 500.00 kg/ha, 95% CI [124.76, 875.24], p = .008). Mean yields were M = 2900.00 (SD = 321.10), M = 3400.00 (SD = 276.97), and M = 3900.00 (SD = 353.52) kg/ha for Control, Organic, and Chemical treatments, respectively.</i></p><p style="text-align: justify;"><b>Notice the structure</b>: </p><p></p><ul><li style="text-align: justify;">State the test, </li><li style="text-align: justify;">Report the assumption checks with exact statistics, give the main F result with all required elements, </li><li style="text-align: justify;">Then report each post-hoc comparison on its own with its 95% CI. </li></ul><div style="text-align: justify;"><span style="font-family: inherit;">Supervisors who know APA format check for every one of those components. Missing the </span><a href="https://www.rstudiodatalab.com/2023/08/p-value-less-than-0.05.html" style="font-family: inherit;" rel="nofollow" target="_blank">p-value</a><span style="font-family: inherit;"> interpretation detail, omitting effect size, or writing "p < .05" instead of the exact value — any of these will draw a comment.</span></div><p></p><p style="text-align: justify;">The write-up also deliberately leads with the <a href="https://www.rstudiodatalab.com/2023/07/Correlation-Assumptions-Types-Example.html" rel="nofollow" target="_blank">assumption check </a>results, not the ANOVA result. That ordering signals to the examiner that you know what must be verified before the main test can be trusted.</p><p class="note"></p><div style="text-align: justify;"><b style="font-family: inherit;">Complete Analysis!</b></div><div style="text-align: justify;"><span style="font-family: inherit;">I delivered the complete analysis — assumption checks, ANOVA output, Tukey post-hoc, APA write-up, and annotated R script — within 22 hours of receiving the data.</span></div><p></p><p></p><p></p><h2 style="text-align: justify;">The Outcome (Supervisor's Response)</h2><p style="text-align: justify;">The student submitted the revised Chapter 4 three days later. Her supervisor approved the statistical section without further comment and cleared her to move forward with Chapter 5. She messaged me the following week to say the thesis had been submitted to the internal examiner on schedule.</p><p style="text-align: justify;">The turnaround from a second rejection to supervisor approval: four days. The statistical work itself: 22 hours from data receipt to delivery.</p><p></p><ul><li style="text-align: justify;">What changed was <b>not the data </b>— none of it had been re-collected or altered. </li><li style="text-align: justify;">What changed was the <b>analysis structure</b>: correct test for the design, documented assumption checks, exact APA formatting throughout, and a post-hoc comparison method that actually controls the error rate when making multiple comparisons.</li></ul><p></p><p style="text-align: justify;">Her supervisor's specific feedback on the revised section: "<i>Statistical analysis is now reported correctly and in full.</i>" That is the language that closes a Chapter 4.</p><h2 style="text-align: justify;">Need the Same Done for Your Data?</h2><p style="text-align: justify;">If your supervisor has flagged your statistical analysis, or you're not confident your current approach is defensible, here is what I deliver:</p><p></p><ol class="steps"><li style="text-align: justify;">Complete analysis in R, SPSS, or Minitab — your choice of software</li><li style="text-align: justify;">APA-formatted results section written and ready to paste into your thesis</li><li style="text-align: justify;">Publication-quality figures — box plots, Q-Q plots, means plots with error bars</li><li style="text-align: justify;">Unlimited revisions until your supervisor approves</li></ol><p></p><h3 style="text-align: justify;">Turnaround times:</h3><p></p><ol><li style="text-align: justify;">Thesis chapter (like the one above): 24–48 hours</li><li style="text-align: justify;">Individual assignment or coursework: 6–12 hours</li><li style="text-align: justify;">Pricing from: $25 (assignments) · $150 (full thesis chapter)</li><li style="text-align: justify;">Primary action: WhatsApp me now — free consultation →</li><li style="text-align: justify;">Also available on: Fiverr · Upwork</li></ol><p></p><p style="text-align: justify;">Or visit the service page: <a href="https://www.rstudiodatalab.com/p/thesis-data-analysis-service.html" rel="nofollow" target="_blank">thesis data analysis service</a></p><p style="text-align: justify;">I have worked with PhD and Master's students from institutions across the UK, Pakistan, Australia, and the US. My rating is 4.9/5 across 500+ completed projects. If your Chapter 4 has been rejected — or if you want to submit it knowing it will not be — send me your data and let me show you what the analysis should look like.</p><h2 style="text-align: justify;">Frequently Asked Question</h2>
  <h3 style="text-align: justify;"><span style="font-family: Lora, serif; font-size: 17.5px;">How do I run a one-way ANOVA in R?</span></h3><p style="text-align: justify;">Use the aov() function from base R: model <- aov(yield ~ treatment, data = your_data), then inspect the result with summary(model). Before trusting the output, check normality of residuals with shapiro.test(residuals(model)) and homogeneity of variance with leveneTest() from the car package.</p><h3 style="text-align: justify;">How do I report one-way ANOVA results in APA format?</h3><p style="text-align: justify;">APA format requires: F(df_between, df_within) = value, p = exact_value, eta-squared = value, 95% CI [lower, upper]. For example: F(2, 24) = 22.15, p = .000004, eta-squared = .65, 95% CI [.35, .76]. Always report the exact p-value — never write p < .05. Also report Tukey post-hoc comparisons with pairwise differences, confidence intervals, and adjusted p-values.</p><h3 style="text-align: justify;">What post-hoc test should I use after a significant one-way ANOVA in R?</h3><p style="text-align: justify;">Use Tukey HSD (Honestly Significant Difference) when comparing all possible group pairs, as it controls the family-wise error rate. In R, run TukeyHSD(model, conf.level = 0.95) after fitting the aov() model. Tukey HSD is the most widely accepted post-hoc test for balanced ANOVA designs and is the method most PhD supervisors and journal reviewers expect.</p><h3 style="text-align: justify;">Should I check normality before running ANOVA in R?</h3><p style="text-align: justify;">Yes. Run the Shapiro-Wilk test on the model residuals using shapiro.test(residuals(model)) — not on the raw data values. A p-value above .05 indicates normality is satisfied. Supplement with a Q-Q plot using qqnorm() and qqline(). Also run Levene's test for homogeneity of variance using leveneTest() from the car package.</p><h3 style="text-align: justify;">What is eta-squared and how do I calculate it in R?</h3><p style="text-align: justify;">Eta-squared (eta²) measures effect size for ANOVA — the proportion of total variance explained by the treatment factor. Calculate it in R as: ss <- summary(model)[[1]][, 'Sum Sq']; eta2 <- ss[1] / sum(ss). Values of .01, .06, and .14 are typically interpreted as small, medium, and large effects. Always report eta² with its 95%.</p><h3 style="text-align: justify;">Why was my Chapter 4 ANOVA rejected by my supervisor?</h3><p style="text-align: justify;">The two most common reasons are: (1) using multiple t-tests instead of a single ANOVA, which inflates the Type I error rate; and (2) running the ANOVA without first reporting assumption checks (Shapiro-Wilk normality test and Levene's homogeneity test). A third frequent issue is reporting 'p < .05' instead of the exact p-value, which does not meet APA standards. Each of these will typically prompt a supervisor rejection.</p>
  

<hr style="text-align: justify;" /><p class="note" style="text-align: justify;"><span style="font-family: inherit;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;">Transform your raw data into actionable insights. Let my expertise in R and advanced data analysis techniques unlock the power of your information. Get a personalized consultation and see how I can streamline your projects, saving you time and driving better decision-making. Contact me today at contact@rstudiodatalab.com or </span><a class="editor-rtfLink" href="https://www.rstudiodatalab.com/p/order-now.html" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #4a6ee0; margin-bottom: 0pt; margin-top: 0pt;" rel="nofollow" target="_blank"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">visit</span></a><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"> to schedule your discovery call.</span></span></p>
<div style="text-align: justify;"><span style="font-family: inherit;"><a class="button" href="https://www.rstudiodatalab.com/p/join-our-community.html" rel="nofollow" target="_blank"><i class="icon demo"></i>Join Our Community</a> 
<a class="button" href="https://www.rstudiodatalab.com/p/order-now.html" rel="nofollow" target="_blank">
  <svg class="line" style="margin-right: 12px; stroke: rgb(255, 255, 255);" viewbox="0 0 24 24"><g transform="translate(2.000000, 2.500000)"><path d="M0.7501,0.7499 L2.8301,1.1099 L3.7931,12.5829 C3.8701,13.5199 4.6531,14.2389 5.5931,14.2359094 L16.5021,14.2359094 C17.3991,14.2379 18.1601,13.5779 18.2871,12.6899 L19.2361,6.1319 C19.3421,5.3989 18.8331,4.7189 18.1011,4.6129 C18.0371,4.6039 3.1641,4.5989 3.1641,4.5989"></path><line x1="12.1251" x2="14.8981" y1="8.2948" y2="8.2948"></line><path d="M5.1544,17.7025 C5.4554,17.7025 5.6984,17.9465 5.6984,18.2465 C5.6984,18.5475 5.4554,18.7915 5.1544,18.7915 C4.8534,18.7915 4.6104,18.5475 4.6104,18.2465 C4.6104,17.9465 4.8534,17.7025 5.1544,17.7025 Z"></path><path d="M16.4347,17.7025 C16.7357,17.7025 16.9797,17.9465 16.9797,18.2465 C16.9797,18.5475 16.7357,18.7915 16.4347,18.7915 C16.1337,18.7915 15.8907,18.5475 15.8907,18.2465 C15.8907,17.9465 16.1337,17.7025 16.4347,17.7025 Z"></path></g></svg>
  <span>Book a free call</span></a></span></div>



<script type="application/ld+json">
 {
 "@context": "http://schema.org",
 "@type": "Article",
 "@id": "<data:post.url/>#post-body-<data:post.id/>",
 "mainEntityOfPage": "<data:post.url/>",
 "headline": "<data:post.title/>",
 "name": "<data:post.title/>",
 "url": "<data:post.url/>",
 "description": "<data:blog.metaDescription/>",
 "image": "<data:post.featuredImage/>",
 "datePublished": "<data:post.date.iso8601/>",
 "dateModified": "<data:post.date.iso8601/>",
 "author": {
  "@type": "Person",
  "name": "<data:post.author.name/>",
  "url": "<data:blog.homepageUrl.jsonEscaped/>"
 },
 "publisher": {
  "@type": "Organization",
  "name": "<data:blog.homepageUrl.jsonEscaped/>",
  "description": "Unlock the secrets of data analysis with our comprehensive RStudio tutorials. From mastering the basics to tackling complex challenges, our blog provides the tools and knowledge you need to take your data analysis skills to the next level.",
  "logo": {
   "@type": "ImageObject",
   "url": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP_jQ9kbWVkjZ1T-5_osDo_JBuq2RAOB4_9Z726e3GPurZSUICYi5U_70kzDHQXZXzkgvhskpoXgTPeaolBDTZpz0qouYLOB8k5ge142uh5cIyJpVLYNvJ17V1wwNVxWKfX5LWq_WvU7nKpSTPvSGxgOQOSbJuXZEo1ylOsD7WJcIuTtx41Ofwo4cjwo0/s500/RStudioDataLab%20500%20x500.png",
   "width": 500,
   "height": 500
  }
 }
}
</script>

<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "Product",
  "name": "<data:post.title/>",
  "image": "<data:post.featuredImage/>",
  "description": "Welcome to RStudioDataLab, your go-to resource for mastering data analysis techniques in R. Dive into advanced topics such as logistic regression with categorical variables in R and explore p-values less than 0.05 for statistical significance. Understand the EFA vs CFA debate, create a correlation heatmap in R, and get insights into LDA r. Learn ridge regression and lasso regression in R.",
  "brand": "RStudioDatalab",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.8",
    "bestRating": "5",
    "worstRating": "1",
    "ratingCount": "5000"
  }
}
</script>


<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "name": "RStudiodatalab",
  "image": "https://blogger.googleusercontent.com/img/a/AVvXsEigqr2QHzVWYRP6N89q6Bu2cNjCvN7g8P5pWqQVmHfLWVUb2nrXfp7Qo64bmJN9M9rD8brW5SpBcLUTsAiT70iC0JCz1FXGNgN0GuylxoHsV18t19GD-s_tieNOwa36_bQ3vU9UN8X7GeGJD3SGQfSnDko4OV_cogw2fbliLPZgjPAOSwOpGI_Z9C3B8_DU=w150-h150-p-k-no-nu-rw-e90",
  "@id": "https://www.rstudiodatalab.com/",
  "url": "https://www.rstudiodatalab.com/",
  "telephone": "+923106367532",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "Near Chaze up",
    "addressLocality": "Multan",
    "postalCode": "60700",
    "addressCountry": "PK",
    "addressRegion": "PU"
  },
  "priceRange": "50",
  "sameAs": [
    "https://www.facebook.com/RStudioDataLab",
    "https://www.instagram.com/rstudiodatalab/",
    "https://twitter.com/rstudiodatalab",
    "https://youtube.com/@rstudiodatalab",
    "https://www.linkedin.com/company/rstudiodatalabs",
    "https://www.tiktok.com/@rstudiodatalab",
    "https://whatsapp.com/channel/0029VaBzfy80G0XbCXhGGA16"
  ],
  "openingHoursSpecification": {
    "@type": "OpeningHoursSpecification",
    "dayOfWeek": [
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday",
      "Saturday",
      "Sunday"
    ],
    "opens": "00:00",
    "closes": "23:59"
  }
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Rstudiodatalab",
  "url": "https://www.rstudiodatalab.com/",
  "logo": "https://blogger.googleusercontent.com/img/a/AVvXsEigqr2QHzVWYRP6N89q6Bu2cNjCvN7g8P5pWqQVmHfLWVUb2nrXfp7Qo64bmJN9M9rD8brW5SpBcLUTsAiT70iC0JCz1FXGNgN0GuylxoHsV18t19GD-s_tieNOwa36_bQ3vU9UN8X7GeGJD3SGQfSnDko4OV_cogw2fbliLPZgjPAOSwOpGI_Z9C3B8_DU=w150-h150-p-k-no-nu-rw-e90",
  "alternateName": "Rstudiodatalab",
  "sameAs": [
    "https://www.facebook.com/RStudioDataLab",
    "https://www.instagram.com/rstudiodatalab/",
    "https://twitter.com/rstudiodatalab",
    "https://youtube.com/@rstudiodatalab",
    "https://www.linkedin.com/company/rstudiodatalabs",
    "https://www.tiktok.com/@rstudiodatalab",
    "https://whatsapp.com/channel/0029VaBzfy80G0XbCXhGGA16"
  ]
}
</script>
  
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://www.rstudiodatalab.com/2026/06/how-i-used-anova-in-r-crop-yield-phd-thesis.html"
  },
  "headline": "How I Used One-Way ANOVA in R to Analyze Crop Yield Data for a PhD Student (Real Case Study)",
  "description": "Real case study: How I ran one-way ANOVA in R for a PhD student's agricultural thesis. Full code, Tukey HSD post-hoc, APA results, and how the supervisor approved it.",
  "image": {
    "@type": "ImageObject",
    "url": "https://www.rstudiodatalab.com/[PATH-TO-FEATURED-IMAGE].png",
    "width": 1200,
    "height": 630,
    "caption": "One-way ANOVA in R — crop yield analysis for PhD thesis (alt: one way anova in r crop yield agricultural trial)"
  },
  "author": {
    "@type": "Person",
    "name": "Dr. Zubair Goraya",
    "jobTitle": "Statistical Data Analyst",
    "description": "Expert in R, SPSS, and Minitab statistical analysis for agricultural and biological research. 500+ researchers helped. 4.9/5 rating.",
    "url": "https://www.rstudiodatalab.com",
    "sameAs": [
      "https://www.fiverr.com/drzubair11010"
    ]
  },
  "publisher": {
    "@type": "Organization",
    "name": "RStudio Data Lab",
    "url": "https://www.rstudiodatalab.com",
    "logo": {
      "@type": "ImageObject",
      "url": "https://blogger.googleusercontent.com/img/a/AVvXsEigqr2QHzVWYRP6N89q6Bu2cNjCvN7g8P5pWqQVmHfLWVUb2nrXfp7Qo64bmJN9M9rD8brW5SpBcLUTsAiT70iC0JCz1FXGNgN0GuylxoHsV18t19GD-s_tieNOwa36_bQ3vU9UN8X7GeGJD3SGQfSnDko4OV_cogw2fbliLPZgjPAOSwOpGI_Z9C3B8_DU=w150-h150-p-k-no-nu-rw-e90",
      "width": 512,
      "height": 512
    }
  },
  "datePublished": "2026-06-17",
  "dateModified": "2026-06-17",
  "url": "https://www.rstudiodatalab.com/2026/06/how-i-used-anova-in-r-crop-yield-phd-thesis.html",
  "inLanguage": "en-US",
  "articleSection": "Statistical Analysis",
  "keywords": [
    "one way anova in r",
    "ANOVA results interpretation",
    "how to report ANOVA results APA",
    "tukey hsd test r",
    "thesis data analysis service",
    "aov() r",
    "TukeyHSD r",
    "p value ANOVA",
    "treatment comparison",
    "agricultural trial",
    "crop science",
    "F statistic",
    "eta squared effect size",
    "Shapiro-Wilk test",
    "Levene test",
    "homogeneity of variance"
  ],
  "wordCount": 3000,
  "about": [
    {
      "@type": "Thing",
      "name": "Analysis of Variance",
      "sameAs": "https://en.wikipedia.org/wiki/Analysis_of_variance"
    },
    {
      "@type": "Thing",
      "name": "R (programming language)",
      "sameAs": "https://en.wikipedia.org/wiki/R_(programming_language)"
    },
    {
      "@type": "Thing",
      "name": "Tukey's range test",
      "sameAs": "https://en.wikipedia.org/wiki/Tukey%27s_range_test"
    },
    {
      "@type": "Thing",
      "name": "Crop yield",
      "sameAs": "https://en.wikipedia.org/wiki/Crop_yield"
    }
  ],
  "mentions": [
    {
      "@type": "SoftwareApplication",
      "name": "R",
      "operatingSystem": "Windows, macOS, Linux",
      "applicationCategory": "Statistical Software"
    },
    {
      "@type": "SoftwareApplication",
      "name": "SPSS",
      "operatingSystem": "Windows, macOS",
      "applicationCategory": "Statistical Software"
    },
    {
      "@type": "SoftwareApplication",
      "name": "Minitab",
      "operatingSystem": "Windows",
      "applicationCategory": "Statistical Software"
    }
  ],
  "isPartOf": {
    "@type": "Blog",
    "name": "RStudio Data Lab",
    "url": "https://www.rstudiodatalab.com"
  }
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "How to Run One-Way ANOVA in R for a PhD Thesis (With Assumption Checks and APA Write-Up)",
  "description": "Step-by-step guide to running one-way ANOVA in R: Shapiro-Wilk normality test, Levene's test, aov() ANOVA, TukeyHSD post-hoc, and APA-format results reporting.",
  "image": {
    "@type": "ImageObject",
    "url": "https://www.rstudiodatalab.com/[PATH-TO-FEATURED-IMAGE].png",
    "width": 1200,
    "height": 630
  },
  "estimatedCost": {
    "@type": "MonetaryAmount",
    "currency": "USD",
    "value": "0",
    "description": "Free if you run it yourself in R. From $150 if you need it done for you."
  },
  "tool": [
    {
      "@type": "HowToTool",
      "name": "R (base package — aov, shapiro.test, TukeyHSD)"
    },
    {
      "@type": "HowToTool",
      "name": "car package (leveneTest)"
    },
    {
      "@type": "HowToTool",
      "name": "ggplot2 (Q-Q plot visualization)"
    }
  ],
  "supply": [
    {
      "@type": "HowToSupply",
      "name": "Crop yield dataset — one numeric response column and one treatment factor column"
    }
  ],
  "totalTime": "PT2H",
  "step": [
    {
      "@type": "HowToStep",
      "position": 1,
      "name": "Check Normality with the Shapiro-Wilk Test",
      "text": "Fit the ANOVA model first with aov(), then pass the residuals to shapiro.test(). A p-value above .05 confirms residuals are normally distributed. Also produce a Q-Q plot with qqnorm() and qqline() to inspect the distribution visually.",
      "url": "https://www.rstudiodatalab.com/2026/06/how-i-used-anova-in-r-crop-yield-phd-thesis.html#step-1",
      "image": {
        "@type": "ImageObject",
        "url": "https://blogger.googleusercontent.com/img/a/AVvXsEgEZ41UZGqpOfo5eJNh5ziBeMK-_PNIA0u3gwDbgUC88LgxN8cVWnlyBn5RB5H6mvB_484NhrNjriTRPi23gCAASQMOZEm_D4acIPL2uC7ieeqOiejI4ZIVpdrNi4JqTcsaJaBimebU4HcHM2zryQc_7wJ5qoYzU6MAkMXja125tI1tmC-vU1mX_3TBbJw"
      }
    },
    {
      "@type": "HowToStep",
      "position": 2,
      "name": "Test Homogeneity of Variance with Levene's Test",
      "text": "Use leveneTest() from the car package with center = median (the Brown-Forsythe variant). A p-value above .05 means variances are equal across groups and the ANOVA assumption holds.",
      "url": "https://www.rstudiodatalab.com/2026/06/how-i-used-anova-in-r-crop-yield-phd-thesis.html#step-2",
      "image": {
        "@type": "ImageObject",
        "url": "https://blogger.googleusercontent.com/img/a/AVvXsEin6ZA5eYaF66HwBBku8LRkC8cFT-kILZHZbHPlQs2U10LGDShlWPUNui9NdB70GZ9Mr3gloTnPUO1o4O1CYTV1BuopJD5nBn-4HCsJLitdRz5Nxilf_RI7AavlfblmhvPnqU5aHRKOf4a2iUhBEAgRT7S8gG6qVw7Wj_lpE5eyouQOdALMkutcn1p7ylw"
      }
    },
    {
      "@type": "HowToStep",
      "position": 3,
      "name": "Run the One-Way ANOVA Using aov()",
      "text": "Fit the model with aov(yield ~ treatment, data = crop_data) and inspect the output with summary(). Record the F statistic, degrees of freedom, and p-value. Compute eta-squared manually as SS_treatment divided by SS_total.",
      "url": "https://www.rstudiodatalab.com/[YEAR]/[MONTH]/how-i-used-anova-in-r-crop-yield-phd-thesis.html#step-3",
      "image": {
        "@type": "ImageObject",
        "url": "https://www.rstudiodatalab.com/[PATH-TO-ANOVA-OUTPUT-IMAGE].png"
      }
    },
    {
      "@type": "HowToStep",
      "position": 4,
      "name": "Run Tukey HSD Post-Hoc to Identify Which Groups Differ",
      "text": "Pass the fitted aov model to TukeyHSD(model, conf.level = 0.95). The output gives pairwise mean differences, 95% confidence intervals, and Tukey-adjusted p-values for every group combination.",
      "url": "https://www.rstudiodatalab.com/2026/06/how-i-used-anova-in-r-crop-yield-phd-thesis.html#step-4",
      "image": {
        "@type": "ImageObject",
        "url": "https://blogger.googleusercontent.com/img/a/AVvXsEhM9rOHCxSHqMEFP_HSlr2QMC5kKg5l8aj4B_Nz251BCjbK5jq8UdLspjmljOc-L4tnrdpMngOgO40E2UneGWyrTLz35NsL6uIEQKKEERJm9FKl52JTyrk-QYj3ZI2XCpm9oaBmgIHgDtWG1OoKirNdiaMz5qJwblOS19tqlbl67bgmFjnMhwkZ5i7S3VA"
      }
    },
    {
      "@type": "HowToStep",
      "position": 5,
      "name": "Write the APA Results Section",
      "text": "Report: (1) the assumption check results with exact W and F statistics and p-values; (2) the main ANOVA result as F(df_between, df_within) = value, p = exact, eta-squared, and 95% CI; (3) each Tukey pairwise comparison with difference, 95% CI, and adjusted p-value. Never write p < .05 — always give the exact p-value.",
      "url": "https://blogger.googleusercontent.com/img/a/AVvXsEgaubV7OaxcANgVMxtImZCedyjj_DwU-rZU9JJYN_J-v4cn-xyV0PVVMydzO98p5YRxIOjy4adPp7jUqOPyGe0FhvFhrm4iBxMTL4m33fUYfoQiiMuZ8Jjs1p3ECFHc5Pai0JQ_1xcWtIb9Iz9e_hzduSGukfoUvOoT1DysBbWExFRW7V8fUROqj90gfR4#step-5"
    }
  ]
}
</script>

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "How do I run a one-way ANOVA in R?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use the aov() function from base R: model <- aov(yield ~ treatment, data = your_data), then inspect the result with summary(model). Before trusting the output, check normality of residuals with shapiro.test(residuals(model)) and homogeneity of variance with leveneTest() from the car package."
      }
    },
    {
      "@type": "Question",
      "name": "How do I report one-way ANOVA results in APA format?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "APA format requires: F(df_between, df_within) = value, p = exact_value, eta-squared = value, 95% CI [lower, upper]. For example: F(2, 24) = 22.15, p = .000004, eta-squared = .65, 95% CI [.35, .76]. Always report the exact p-value — never write p < .05. Also report Tukey post-hoc comparisons with pairwise differences, confidence intervals, and adjusted p-values."
      }
    },
    {
      "@type": "Question",
      "name": "What post-hoc test should I use after a significant one-way ANOVA in R?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use Tukey HSD (Honestly Significant Difference) when comparing all possible group pairs, as it controls the family-wise error rate. In R, run TukeyHSD(model, conf.level = 0.95) after fitting the aov() model. Tukey HSD is the most widely accepted post-hoc test for balanced ANOVA designs and is the method most PhD supervisors and journal reviewers expect."
      }
    },
    {
      "@type": "Question",
      "name": "Should I check normality before running ANOVA in R?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Run the Shapiro-Wilk test on the model residuals using shapiro.test(residuals(model)) — not on the raw data values. A p-value above .05 indicates normality is satisfied. Supplement with a Q-Q plot using qqnorm() and qqline(). Also run Levene's test for homogeneity of variance using leveneTest() from the car package."
      }
    },
    {
      "@type": "Question",
      "name": "What is eta-squared and how do I calculate it in R?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Eta-squared (eta²) measures effect size for ANOVA — the proportion of total variance explained by the treatment factor. Calculate it in R as: ss <- summary(model)[[1]][, 'Sum Sq']; eta2 <- ss[1] / sum(ss). Values of .01, .06, and .14 are typically interpreted as small, medium, and large effects. Always report eta² with its 95% confidence interval in your APA results."
      }
    },
    {
      "@type": "Question",
      "name": "Why was my Chapter 4 ANOVA rejected by my supervisor?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The two most common reasons are: (1) using multiple t-tests instead of a single ANOVA, which inflates the Type I error rate; and (2) running the ANOVA without first reporting assumption checks (Shapiro-Wilk normality test and Levene's homogeneity test). A third frequent issue is reporting 'p < .05' instead of the exact p-value, which does not meet APA standards. Each of these will typically prompt a supervisor rejection."
      }
    }
  ]
}
</script>

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://www.rstudiodatalab.com/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Statistical Analysis",
      "item": "https://www.rstudiodatalab.com/search/label/Statistical%20Analysis"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "ANOVA in R",
      "item": "https://www.rstudiodatalab.com/search/label/ANOVA"
    },
    {
      "@type": "ListItem",
      "position": 4,
      "name": "How I Used One-Way ANOVA in R to Analyze Crop Yield Data for a PhD Student",
      "item": "https://www.rstudiodatalab.com/[YEAR]/[MONTH]/how-i-used-anova-in-r-crop-yield-phd-thesis.html"
    }
  ]
}
</script>

</div></span></div></span>
<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.rstudiodatalab.com/2026/06/how-i-used-anova-in-r-crop-yield-phd-thesis.html"> RStudioDataLab</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/how-i-used-one-way-anova-in-r-to-analyze-crop-yield-data-for-a-phd-student-real-case-study/">How I Used One-Way ANOVA in R to Analyze Crop Yield Data for a PhD Student (Real Case Study)</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402072</post-id>	</item>
		<item>
		<title>Auditing LLM Trading: Bridging Theory and Market Reality with the GT table in R</title>
		<link>https://www.r-bloggers.com/2026/06/auditing-llm-trading-bridging-theory-and-market-reality-with-the-gt-table-in-r/</link>
		
		<dc:creator><![CDATA[Selcuk Disci]]></dc:creator>
		<pubDate>Wed, 17 Jun 2026 08:05:04 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://datageeek.com/?p=12211</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Introduction: The Laboratorial Illusion In quantitative finance, Large Language Model (LLM) multi-agent systems are frequently celebrated for their theoretical intelligence. Financial data scientists spend months refining prompt semantics, building complex reasoning frameworks, and engineering multi-turn debate loops between specialized agent nodes. On paper—and within simulated environments—these networks demonstrate ...</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/auditing-llm-trading-bridging-theory-and-market-reality-with-the-gt-table-in-r/">Auditing LLM Trading: Bridging Theory and Market Reality with the GT table in R</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://datageeek.com/2026/06/17/auditing-llm-trading-bridging-theory-and-market-reality-with-the-gt-table-in-r/"> 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">Introduction: The Laboratorial Illusion</h2>



<p class="wp-block-paragraph">In quantitative finance, Large Language Model (LLM) multi-agent systems are frequently celebrated for their theoretical intelligence. Financial data scientists spend months refining prompt semantics, building complex reasoning frameworks, and engineering multi-turn debate loops between specialized agent nodes. On paper—and within simulated environments—these networks demonstrate flawless predictive capabilities, capturing theoretical alpha with pristine efficiency.</p>



<p class="wp-block-paragraph">However, this laboratorial success cloaks a fatal vulnerability exposed by <strong><em><a href="https://arxiv.org/abs/2606.08285" rel="nofollow" target="_blank">Yao &#038; Zheng (2026)</a></em></strong>: traditional backtests systematically ignore execution semantics and market microstructure realities.</p>



<p class="wp-block-paragraph">In AI-driven trading systems, the primary risk is no longer the raw quality of the agent’s alpha signal; it is the <strong>cognitive latency</strong> required to generate that signal. While classical high-frequency algorithms fight a war of microseconds, LLM multi-agent networks engage in multi-second internal debates. When this cognitive inertia is forced to execute within highly volatile regimes, it transforms directly into a silent alpha killer. Yao &#038; Zheng (2026) forces us to stop judging agent architectures by their abstract zekası, and start auditing them by the brutal financial reality of their execution timing.</p>



<p class="wp-block-paragraph">To dismantle this illusion, this article implements a validation framework in R designed to audit multi-agent trading decisions against empirical market constraints. Rather than viewing transaction costs as a passive post-trade deduction, our framework forces execution slippage directly into the core ranking layer of the portfolio generation process, as demonstrated in our finalized <strong>Targeted Reproducibility &#038; Execution Realism Matrix</strong> below:</p>



<p class="wp-block-paragraph">Let’s break down the code block by block to see exactly how this audit engine operates, starting with the core dependencies and temporal isolation logic.</p>



<h2 class="wp-block-heading">Part 2: Environment Setup &#038; The Auditing Interface</h2>



<p class="wp-block-paragraph">The first step of our script loads the required quantitative packages and defines our core auditing function.</p>


<pre>
library(tidyquant)
library(dplyr)
library(tibble)
library(purrr)
library(gt)

audit_execution_assumptions &lt;- function(ticker, action, trade_date, order_size, latency_seconds, base_fee_bps = 10, ideal_rank = NA, audited_rank = NA) {
</pre>


<h3 class="wp-block-heading">Deconstructing the Operational Parameters</h3>



<p class="wp-block-paragraph">To test how an LLM agent’s decisions survive real market microstructure, our <code>audit_execution_assumptions</code> function requires explicit operational parameters. Here is the practical quantitative intuition behind each input:</p>



<ul class="wp-block-list">
<li><strong><code>ticker</code>:</strong> The asset symbol being audited (e.g., <code>&quot;AMD&quot;</code>, <code>&quot;TSLA&quot;</code>). It tells the engine exactly which market pricing stream to fetch.</li>



<li><strong><code>action</code>:</strong> The order side generated by the multi-agent system—strictly <code>&quot;BUY&quot;</code> or <code>&quot;SELL&quot;</code>. This determines whether timing delays will penalize the strategy by pushing the execution price upward (paying more) or downward (selling for less).</li>



<li><strong><code>trade_date</code>:</strong> The exact calendar day of the intended trade (<code>&quot;YYYY-MM-DD&quot;</code>). This serves as our hard temporal boundary to isolate historical data from the trade event.</li>



<li><strong><code>order_size</code>:</strong> The volume of shares being transacted. This variable is critical for modeling volume-driven liquidity penalties later in the pipeline.</li>



<li><strong><code>latency_seconds</code>:</strong> The time (in seconds) the LLM spent running its internal reasoning chains and debate loops. This is the master variable driving our time-based slippage penalty.</li>



<li><strong><code>base_fee_bps</code>:</strong> Fixed institutional transaction and clearing costs, measured in basis points (1 bp = 0.01%). It defaults to a standard institutional rate of 10 bps.</li>



<li><strong><code>ideal_rank</code> & <code>audited_rank</code>:</strong> Placeholders passed directly into the data matrix layer. <code>ideal_rank</code> maps the agent’s raw theoretical preference, while <code>audited_rank</code> identifies the asset’s real priority after market frictions are applied.</li>
</ul>



<h2 class="wp-block-heading">Part 3: Point-in-Time Control & Temporal Split Discipline</h2>



<p class="wp-block-paragraph">Now that our environment is ready, the function’s first critical task is to draw a strict line in time. It isolates historical data from the execution day data to ensure that future prices cannot leak into our calculations.</p>


<pre>
# 1. Point-in-Time Control & Temporal Split Discipline
  end_date &lt;- as.Date(trade_date)
  start_date &lt;- end_date - 45
  
  market_data &lt;- tq_get(ticker, from = start_date, to = end_date + 1)
  
  if (nrow(market_data) == 0) {
    stop(&quot;Audit Halted: Live data provenance check failed. Verify market calendar.&quot;)
  }
  
  execution_day_data &lt;- market_data %&gt;% filter(date == end_date)
  historical_series  &lt;- market_data %&gt;% filter(date &lt; end_date)
  
  if (nrow(execution_day_data) == 0) {
    stop(&quot;Audit Halted: Target trade date appears to be a market holiday/weekend.&quot;)
  }
  
  arrival_price &lt;- execution_day_data$open[1]
</pre>


<h3 class="wp-block-heading">Understanding the Internal Compliance Variables</h3>



<p class="wp-block-paragraph">To understand how this block enforces strict backtesting rules, let’s look at what each internal variable does:</p>



<ul class="wp-block-list">
<li><strong><code>end_date</code> & <code>start_date</code>:</strong> These variables convert the character <code>trade_date</code> into an R Date object and establish a rolling 45-day baseline window prior to the trade execution. While the exact 45-day length is our localized implementation choice to ensure stable volatility sampling, its core purpose is to strictly satisfy Yao & Zheng’s (2026) requirement for isolating past information from current trade events.</li>



<li><strong><code>market_data</code>:</strong> The raw data table downloaded via <code>tidyquant</code>. It fetches prices up to <code>end_date + 1</code> to ensure we capture the full trading session of our target date.</li>



<li><strong><code>historical_series</code>:</strong> A clean pricing array containing data strictly <em>before</em> the trade date. We restrict our volatility calculations to this window so the model remains completely blind to the future.</li>



<li><strong><code>execution_day_data</code>:</strong> Filters market activity down to the exact day of the trade. If this data frame turns up empty—meaning the agent tried to submit a trade on a weekend or a market holiday—the engine calls a hard <code>stop()</code> and terminates the run.</li>



<li><strong><code>arrival_price</code>:</strong> The stock’s <code>open</code> price on the execution day. This represents the pristine price available at the exact second the agent finishes its logic, serving as our baseline anchor before any market frictions are calculated.</li>
</ul>



<h2 class="wp-block-heading">Part 4: Mathematical Volatility & Timing Slippage Modeling</h2>



<p class="wp-block-paragraph">Once we have our clean data partitions, we scale the asset’s historical volatility down to a per-second level. This allows us to convert the agent’s cognitive delay directly into a financial price penalty.</p>


<pre>
# 2. Mathematical Volatility Modeling
  historical_vol &lt;- historical_series %&gt;%
    mutate(log_ret = log(close / lag(close))) %&gt;%
    summarise(vol = sd(log_ret, na.rm = TRUE) * sqrt(252)) %&gt;%
    pull(vol)
  
  volatility_per_second &lt;- (historical_vol / sqrt(252)) / 23400
  
  # 3. Execution Timing Latency (Timing Slippage)
  timing_slippage_dist &lt;- arrival_price * volatility_per_second * latency_seconds
  
  if (action == &quot;BUY&quot;) {
    execution_price &lt;- arrival_price + timing_slippage_dist
  } else if (action == &quot;SELL&quot;) {
    execution_price &lt;- arrival_price - timing_slippage_dist
  } else {
    stop(&quot;Audit Halted: Invalid execution semantics. Side must be BUY or SELL.&quot;)
  }
</pre>


<h3 class="wp-block-heading">Deconstructing the Mathematical Variables</h3>



<ul class="wp-block-list">
<li><strong><code>historical_vol</code>:</strong> The standard annualized volatility calculated from log returns. It represents the asset’s baseline speed of movement over a normal trading year.</li>



<li><strong><code>volatility_per_second</code>:</strong> This variable scales the annualized risk down to a single trading second. It divides the daily volatility by 23,400, which is the exact number of seconds in a standard 6.5-hour US market session (6.5 x 3600$).</li>



<li><strong><code>timing_slippage_dist</code>:</strong> The absolute dollar penalty caused by the agent’s delay. It multiplies our per-second volatility by <code>latency_seconds</code>.</li>



<li><strong><code>execution_price</code>:</strong> The real, degraded price our trade hits. If the action is <code>&quot;BUY&quot;</code>, the timing delay forces us to pay <em>more</em> (<code>arrival_price + timing_slippage_dist</code>). If the action is <code>&quot;SELL&quot;</code>, the delay forces us to sell for <em>less</em> (<code>arrival_price - timing_slippage_dist</code>).</li>
</ul>



<h2 class="wp-block-heading">Part 5: Institutional Friction & Turnover Cost Modeling</h2>



<p class="wp-block-paragraph">With the timing-degraded execution price established, the framework applies structural volume frictions. This step calculates fixed brokerage costs alongside non-linear market impact caused by our position size.</p>


<pre>
# 4. Institutional Friction & Turnover Cost Modeling (Volume Slippage)
  commission_cost     &lt;- execution_price * order_size * (base_fee_bps / 10000)
  liquidity_slippage  &lt;- execution_price * order_size * (order_size * 0.000001) 
  total_friction_cost &lt;- commission_cost + liquidity_slippage
  
  # Aggregating absolute slippage profiles for matrix visibility
  total_slippage_usd &lt;- (abs(execution_price - arrival_price) * order_size) + liquidity_slippage
  slippage_bps       &lt;- (total_slippage_usd / (arrival_price * order_size)) * 10000
</pre>


<h3 class="wp-block-heading">Deconstructing the Friction Variables</h3>



<ul class="wp-block-list">
<li><strong><code>commission_cost</code>:</strong> The baseline institutional clearing and exchange fee. It converts your fixed basis points (<code>base_fee_bps</code>) into a hard dollar cost based on the total value of the executed position.</li>



<li><strong><code>liquidity_slippage</code>:</strong> A non-linear market impact model. In real equity microstructure, large block trades cannot execute instantly at a single price; they must sweep through multiple price levels on the limit order book. The formula multiplying <code>order_size</code> by <code>0.000001</code> serves as our localized impact multiplier to penalize large trade volumes.</li>



<li><strong><code>total_friction_cost</code>:</strong> The sum of broker fees and physical market impact, representing the absolute overhead deducted from the position.</li>



<li><strong><code>total_slippage_usd</code>:</strong> The total dollar amount lost to market mechanics. It adds the money lost from the agent’s thinking delay (<code>abs(execution_price - arrival_price) * order_size</code>) to the money lost from sweeping the order book (<code>liquidity_slippage</code>).</li>



<li><strong><code>slippage_bps</code>:</strong> Standardizes the total dollar slippage back into basis points relative to the original intended position size. This allows us to compare execution damage cleanly across symbols with entirely different stock prices.</li>
</ul>



<h2 class="wp-block-heading">Part 6: Reproducibility Grading & Data Ingestion Matrix Output</h2>



<p class="wp-block-paragraph">Before returning any data, the function evaluates the structural integrity of its own audit parameters. It grades the calculation setup out of 100% to ensure the backtest is completely realistic, and then outputs a clean data row.</p>


<pre>
# 5. Reproducibility & Interpretability Score Evaluation
  reproducibility_score &lt;- 100
  if (liquidity_slippage == 0) reproducibility_score &lt;- reproducibility_score - 40
  if (base_fee_bps == 0)       reproducibility_score &lt;- reproducibility_score - 30
  
  evaluation_status &lt;- case_when(
    reproducibility_score &gt;= 85 ~ &quot;EXCELLENT / Economically Interpretable&quot;,
    reproducibility_score &gt;= 50 ~ &quot;PASS / Limited Realism&quot;,
    TRUE                         ~ &quot;FAIL / Methodological Illusion&quot;
  )
  
  # 6. Construct Raw Data Frame for gt Engine with exact mathematical parameters
  raw_matrix_df &lt;- tibble(
    Strategy      = paste0(&quot;Agent on &quot;, ticker),
    Ideal_Rank    = as.integer(ideal_rank),
    Audited_Rank  = as.integer(audited_rank),
    PIT_Control   = &quot;PASSED (Zero Look-Ahead)&quot;,
    Leakage_Guard = &quot;SECURE (Discipline Enforced)&quot;,
    Slip_BPs      = slippage_bps,
    Slip_USD      = total_slippage_usd,
    Friction_Mod  = paste0(&quot;Dynamic (&quot;, base_fee_bps, &quot; bps + Volume)&quot;),
    Turnover_Tr   = &quot;Penalized Alpha Decay&quot;,
    Latency_Mod   = paste0(&quot;Empirical Vol (&quot;, latency_seconds, &quot;s)&quot;),
    Score         = reproducibility_score,
    Status        = evaluation_status
  )
  
  return(raw_matrix_df)
}
</pre>


<h3 class="wp-block-heading">Understanding the Structural Matrix Variables</h3>



<ul class="wp-block-list">
<li><strong><code>reproducibility_score</code> & <code>evaluation_status</code>:</strong> A self-policing diagnostic mechanism. If a user tries to run a backtest with no fees or no volume penalties, the engine deducts points. A score below 50 flags the setup as a <code>Methodological Illusion</code>, warning you that the strategy looks profitable simply because it is ignoring real-world trading costs.</li>



<li><strong><code>raw_matrix_df</code>:</strong> The core data frame returned by the function. Notice that <code>Ideal_Rank</code> and <code>Audited_Rank</code> are forced into the data layer as standard integer variables. This ensures our portfolio analytics are handled strictly at the data layer before any styling or formatting takes place.</li>
</ul>



<h2 class="wp-block-heading">Part 7: High-Density Portfolio Execution Flow (The Simulation Sandbox)</h2>



<p class="wp-block-paragraph">Now that our core auditing function is defined, <strong>we need to build a simulation environment to stress-test it.</strong> In live trading, an investor relies on a priority ranking to decide capital allocation.</p>



<p class="wp-block-paragraph">To see exactly how cognitive latency disrupts this priority list, our script implements a <strong>Two-Pass Simulation Pipeline</strong> via <code>purrr::pmap_dfr</code>. Pass 1 runs a localized sweep to gather raw market frictions across a simulated portfolio, and Pass 2 injects those generated frictions back into the function to establish the final, adjusted priority order.</p>


<pre>
# ==============================================================================
# HIGH-DENSITY PORTFOLIO EXECUTION FLOW WITH STRUCTURAL RAW PARAMETERS
# ==============================================================================

# 1. Define ideal agent priority ranking inside map database
ideal_agent_ranks &lt;- tibble(
  ticker     = c(&quot;AMD&quot;, &quot;META&quot;, &quot;TSLA&quot;, &quot;MSFT&quot;, &quot;NFLX&quot;, &quot;GOOGL&quot;, &quot;NVDA&quot;, &quot;AAPL&quot;, &quot;AMZN&quot;, &quot;AVGO&quot;),
  Ideal_Rank = 1:10
)

# 2. Phase 1: Temporary execution execution mapping to capture raw slippage arrays
set.seed(42)
initial_inputs &lt;- tibble(
  ticker          = ideal_agent_ranks$ticker,
  action          = sample(c(&quot;BUY&quot;, &quot;SELL&quot;), nrow(ideal_agent_ranks), replace = TRUE, prob = c(0.6, 0.4)),
  trade_date      = &quot;2026-05-12&quot;,
  order_size      = 2500,
  latency_seconds = round(runif(nrow(ideal_agent_ranks), 3.5, 7.5), 1),
  base_fee_bps    = 10,
  ideal_rank      = ideal_agent_ranks$Ideal_Rank
)

# Run a localized sweep to compute absolute slippage values for explicit rank calculation
audited_ranks_map &lt;- pmap_dfr(initial_inputs, function(...) {
  args &lt;- list(...)
  audit_execution_assumptions(
    ticker          = args$ticker, 
    action          = args$action, 
    trade_date      = args$trade_date, 
    order_size      = args$order_size, 
    latency_seconds = args$latency_seconds, 
    base_fee_bps    = args$base_fee_bps,
    ideal_rank      = args$ideal_rank
  )
}) %&gt;%
  mutate(ticker = stringr::str_remove(Strategy, &quot;Agent on &quot;)) %&gt;%
  mutate(Calculated_Audited_Rank = min_rank(desc(Slip_BPs))) %&gt;%
  select(ticker, Calculated_Audited_Rank)

# 3. Phase 2: Inject both explicit ranks into the pipeline structure
portfolio_inputs &lt;- initial_inputs %&gt;%
  left_join(audited_ranks_map, by = &quot;ticker&quot;) %&gt;%
  rename(audited_rank = Calculated_Audited_Rank)

# 4. Generate final portfolio data matrix with dual ranking embedded in the raw layer
portfolio_matrix_df &lt;- pmap_dfr(portfolio_inputs, audit_execution_assumptions) %&gt;%
  mutate(Rank_Shift = Ideal_Rank - Audited_Rank) %&gt;%
  mutate(Ranking_Perturbation = paste0(&quot;Rank Decay: Node &quot;, Audited_Rank, &quot; (Shift: &quot;, Rank_Shift, &quot;)&quot;)) %&gt;%
  arrange(Audited_Rank)
</pre>


<h3 class="wp-block-heading">Deconstructing the Simulation Logic & Generated Variables</h3>



<p class="wp-block-paragraph">To keep things transparent, it is important to note that <strong>the code above does not represent a live execution engine; it is a synthetic playground</strong> built to show how the math behaves across a mock 10-stock universe:</p>



<ul class="wp-block-list">
<li><strong><code>ideal_agent_ranks</code>:</strong> This is our baseline control vector. It represents a mock scenario where an LLM agent has already ranked 10 stocks from best (<code>Ideal_Rank = 1</code> for AMD) to worst (<code>Ideal_Rank = 10</code> for AVGO) based purely on theoretical signals.</li>



<li><strong><code>initial_inputs</code> (The Environment Matrix):</strong> This table creates our simulated trade parameters. It forces every stock to trade an identical block of <code>2500</code> shares on a fixed historical date (<code>2026-05-12</code>). Crucially, we use <code>runif(..., 3.5, 7.5)</code> to <strong>simulate a random cognitive delay between 3.5 and 7.5 seconds</strong>—perfectly mimicking the time an LLM spends traversing multi-turn debate loops or long reasoning chains before hitting the market.</li>



<li><strong><code>audited_ranks_map</code> (The First Pass):</strong> This acts as our pre-trade exploratory sweep. Because we cannot rank the stocks by execution damage until we know what that damage is, this pass calls our function to calculate the raw absolute <code>Slip_BPs</code> for each asset. It then uses <code>min_rank(desc(Slip_BPs))</code> to generate <code>Calculated_Audited_Rank</code>—sorting the stocks based on how well they survived slippage.</li>



<li><strong><code>portfolio_inputs</code> & <code>portfolio_matrix_df</code> (The Second Pass):</strong> This forms our final consolidation loop. We combine our initial trade parameters with the newly simulated audited ranks using a standard <code>left_join</code>. Then, we run the auditing function one final time to bake both ranking layers cleanly into the final output.</li>



<li><strong><code>Rank_Shift</code> & <code>Ranking_Perturbation</code>:</strong> The ultimate diagnostic variables of our simulation. By subtracting the final audited position from the agent’s initial ideal position, these fields explicitly capture <strong>Rank Decay</strong>—showing the reader exactly how many slots an asset fell due to the toxic combination of its own volatility and the agent’s processing delay.</li>
</ul>



<h2 class="wp-block-heading">Part 8: The Professional Visualization Layer (Renderer)</h2>



<p class="wp-block-paragraph">With our data matrix fully computed inside the simulation sandbox, the final segment of our script passes the raw data frame directly into the <code>gt</code> visualization package. This block formats numbers, colors labels, and applies conditional logic to transform our raw tibble into the high-density corporate matrix seen in our audit results.</p>


<pre>
# ==============================================================================
# PROFESSIONAL VISUALIZATION LAYER (RENDERER)
# ==============================================================================
gt_audit_report &lt;- portfolio_matrix_df %&gt;%
  select(Strategy, Ideal_Rank, Audited_Rank, Ranking_Perturbation, PIT_Control, Leakage_Guard, 
         Slip_BPs, Slip_USD, Friction_Mod, Turnover_Tr, Latency_Mod, Score, Status) %&gt;%
  gt() %&gt;%
  tab_header(
    title = md(&quot;**Targeted Reproducibility & Execution Realism Matrix**&quot;),
    subtitle = paste0(&quot;Methodological Rigor Audit inspired by Yao & Zheng (2026) | Generated: &quot;, Sys.Date())
  ) %&gt;%
  cols_label(
    Strategy             = &quot;Audited LLM Strategy&quot;,
    Ideal_Rank           = &quot;Ideal Rank&quot;,
    Audited_Rank         = &quot;Audited Rank&quot;,
    Ranking_Perturbation = &quot;Ranking Perturbation&quot;,
    PIT_Control          = &quot;Point-in-Time Control&quot;,
    Leakage_Guard        = &quot;Data Leakage Guard&quot;,
    Slip_BPs             = &quot;Slippage (BPs)&quot;,
    Slip_USD             = &quot;Slippage (USD)&quot;,
    Friction_Mod         = &quot;Transaction-Cost Modeling&quot;,
    Turnover_Tr          = &quot;Turnover Treatment&quot;,
    Latency_Mod          = &quot;Execution Timing Latency&quot;,
    Score                = &quot;Rigor Score&quot;,
    Status               = &quot;Evaluation Status&quot;
  ) %&gt;%
  fmt_currency(columns = Slip_USD, currency = &quot;USD&quot;, decimals = 2) %&gt;%
  fmt_number(columns = Slip_BPs, decimals = 2) %&gt;%
  fmt_number(columns = c(Ideal_Rank, Audited_Rank), decimals = 0) %&gt;%
  fmt_number(columns = Score, decimals = 0, pattern = &quot;{x}%&quot;) %&gt;%
  tab_options(
    heading.title.font.size = px(18),
    heading.subtitle.font.size = px(13),
    column_labels.font.weight = &quot;bold&quot;,
    column_labels.background.color = &quot;#F4F6F7&quot;,
    table.font.names = &quot;Arial, sans-serif&quot;,
    data_row.padding = px(6),
    table.width = pct(100)
  ) %&gt;%
  tab_style(
    style = cell_text(color = &quot;#C0392B&quot;, weight = &quot;bold&quot;),
    locations = cells_body(columns = Ranking_Perturbation)
  ) %&gt;%
  tab_style(
    style = cell_text(color = &quot;#27AE60&quot;, weight = &quot;bold&quot;),
    locations = cells_body(columns = Status, rows = Score &gt;= 85)
  ) %&gt;%
  tab_style(
    style = cell_text(color = &quot;#C0392B&quot;, weight = &quot;bold&quot;),
    locations = cells_body(columns = Status, rows = Score &lt; 50)
  ) %&gt;%
  opt_row_striping()

# Display the multi-asset audited dashboard inside the RStudio Viewer pane
gt_audit_report
</pre>


<h3 class="wp-block-heading">Deconstructing the Presentation & Formatting Variables</h3>



<p class="wp-block-paragraph">The final rendering sequence leverages the <code>gt</code> package to map raw numerical matrices into a standardized institutional report. The formatting layer operates under strict visual rules to maximize data density and audit clarity:</p>



<ul class="wp-block-list">
<li><strong><code>cols_label()</code>:</strong> This function swaps out our machine-readable data names for human-friendly table headers. For example, it maps the raw variable <code>Slip_BPs</code> to <code>&quot;Slippage (BPs)&quot;</code> so institutional readers can scan the table without guessing what the column fields represent.</li>



<li><strong><code>fmt_currency()</code> & <code>fmt_number()</code>:</strong> These are our value formatters. They intercept raw floating-point numbers in the data frame and append standard financial currency tags (<code>$</code>) or trailing percentage signs (<code>%</code>) directly to the rendered output.</li>



<li><strong><code>tab_options()</code>:</strong> Controls the structural design and geometry of the table. It formats header font sizes, tightens row padding to increase information density, and sets a clean, professional background color (<code>#F4F6F7</code>) for the column header labels.</li>



<li><strong><code>tab_style()</code>:</strong> Enforces data-driven visual rules. It scans our data and automatically formats text color based on execution metrics:
<ul class="wp-block-list">
<li>It isolates the <code>Ranking_Perturbation</code> messages and renders them in bold crimson text to instantly draw focus to rank decay nodes.</li>



<li>It dynamically styles the <code>Status</code> column, turning rows green for secure runs (<code>Score &gt;= 85</code>) or red for unrealistic backtest assumptions (<code>Score &lt; 50</code>).</li>
</ul>
</li>



<li><strong><code>opt_row_striping()</code>:</strong> Generates alternating zebra striping across rows, allowing readers to track complex metrics across broad horizonal rows seamlessly.</li>
</ul>



<figure data-wp-context="{"imageId":"6a325cc3a2d7b"}" data-wp-interactive="core/image" data-wp-key="6a325cc3a2d7b" class="wp-block-image size-large wp-lightbox-container"><img loading="lazy" data-attachment-id="12237" data-permalink="https://datageeek.com/2026/06/17/auditing-llm-trading-bridging-theory-and-market-reality-with-the-gt-table-in-r/evidence_matrix/" data-orig-file="https://datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png" data-orig-size="1907,710" 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="evidence_matrix" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/datageeek.com/wp-content/uploads/2026/06/evidence_matrix.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://i0.wp.com/datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png?w=450&#038;ssl=1" alt="" class="wp-image-12237" srcset_temp="https://i0.wp.com/datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png?w=450&#038;ssl=1 1024w, https://datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png?w=150 150w, https://datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png?w=300 300w, https://datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png?w=768 768w, https://datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png?w=1440 1440w, https://datageeek.com/wp-content/uploads/2026/06/evidence_matrix.png 1907w" 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">Conclusion: Reclaiming Empirical Rigor</h2>



<p class="wp-block-paragraph">The output matrix generated by this R script proves a sobering fact: <strong>optimizing an LLM agent’s internal intelligence while ignoring its physical timing footprint is a zero-sum game.</strong> When cognitive latency meets volatile market microstructure, theoretical priority hierarchies collapse.</p>



<p class="wp-block-paragraph">By pushing dynamic slippage parameters directly into your research data layer rather than treats them as a post-trade footnote, you can accurately strip away laboratorial illusion. Quantitative researchers must stop asking how smart their financial agents are, and start measuring how fast those agents’ decisions decay on the trade desk.</p>



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

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://datageeek.com/2026/06/17/auditing-llm-trading-bridging-theory-and-market-reality-with-the-gt-table-in-r/"> 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/auditing-llm-trading-bridging-theory-and-market-reality-with-the-gt-table-in-r/">Auditing LLM Trading: Bridging Theory and Market Reality with the GT table in R</a>]]></content:encoded>
					
		
		<enclosure url="https://datageeek.com/wp-content/uploads/2026/06/datageeek-6a32534c9d982.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/evidence_matrix.png?w=1024" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">402068</post-id>	</item>
		<item>
		<title>Bioconductor-centric hackathon on spatial omics and image-derived data</title>
		<link>https://www.r-bloggers.com/2026/06/bioconductor-centric-hackathon-on-spatial-omics-and-image-derived-data/</link>
		
		<dc:creator><![CDATA[Davide Risso]]></dc:creator>
		<pubDate>Wed, 17 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://blog.bioconductor.org/posts/2026-06-17-venice/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>A Bioconductor-centric hackathon dedicated to spatial omics was organized by members of the Bioconductor community – Davide Risso (University of Padua, Italy), Helena Crowell (CNAG Barcelona, Spain), and Wolfgang Huber (EMBL) – on 19-22 April on...</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/bioconductor-centric-hackathon-on-spatial-omics-and-image-derived-data/">Bioconductor-centric hackathon on spatial omics and image-derived data</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://blog.bioconductor.org/posts/2026-06-17-venice/"> Bioconductor community blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 





<p>A Bioconductor-centric hackathon dedicated to spatial omics was organized by members of the Bioconductor community – Davide Risso (University of Padua, Italy), Helena Crowell (CNAG Barcelona, Spain), and Wolfgang Huber (EMBL) – on <strong>19-22 April on San Servolo, Italy</strong>, an island off the coast of Venice, facing the Campanile of St. Mark’s Square.</p>
<p>The hackathon brought together <strong>27 researchers and software developers</strong> – from Germany, Switzerland, Italy, Spain, and the USA – to advance Bioconductor capabilities in spatial data handling and analysis, as well as the related topic of image analysis.</p>
<p>Participants were invited based on their experience with the hackathon’s research themes and software development, followed by an open call to the Bioconductor community (and beyond). The final group of participants included a mix of early-career and senior researchers, including two <a href="https://scverse.org/" rel="nofollow" target="_blank">scverse</a> members and one industry researcher, with a range of expertise in spatial omics, image analysis, and software development.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i0.wp.com/blog.bioconductor.org/posts/2026-06-17-venice/terrace.jpeg?w=578&#038;ssl=1" class="img-fluid figure-img" data-recalc-dims="1"></p>
<figcaption>Picture time on a terrace overlooking St. Mark’s Square from San Servolo island. (Back:) Elisabeth Purdom, Wolfgang Huber, Pere Moles Serò, Rafael Irizarry, Helena Crowell, Martin Emons, Dario Righelli, Juan Henao, Sean Davis, Gabriele Sales, Mike Smith, Ilaria Billato, Patrick Danaher, Hugo Gruson, Carissa Chen, Daria Lazic, Luca Marconato, Artür Manukyan. (Front:) Davide Risso, Sviatoslav Kharuk, Michael Stadler, Samuel Gunz, Robert Castelo, Charlotte Soneson, Matteo Calgaro, Gabriel Grajeda, Riccardo Ceccaroni.</figcaption>
</figure>
</div>
<p>The hackathon centered on spatial omics and other bioimaging data, with emphasis on data representation, interoperable serialization, scalable data handling, Python interoperability, interactive visualization. The hackathon ran over three days with the majority of the time spent in teams who independently developed and implemented a plan that addressed a challenge or met a goal important to team members.</p>
<p>On the first day, the participants organized themselves into four major themes:</p>
<ul>
<li><strong>Spatially stratified differential expression analysis</strong><br>
(Matteo Calgaro, Robert Castelo, Patrick Danaher, Pere Moles Serò)</li>
<li><strong>Image and segmentation data manipulation and visualization</strong><br>
(Riccardo Ceccaroni, Carissa Chen, Davide Risso, Mike Smith)</li>
<li><strong>Infrastructure and interoperability of spatial data in Bioconductor</strong><br>
(Helena Crowell, Martin Emons, Gabriel Grajeda, Hugo Gruson, Samuel Gunz, Rafael Irizarry, Daria Lazic, Luca Marconato, Charlotte Soneson, Michael Stadler)</li>
<li><strong>Facilitating use of foundation models for the Bioconductor community</strong><br>
(Ilaria Billato, Juan Henao, Wolfgang Huber, Sviatoslav Kharuk, Artür Manukyan, Elisabeth Purdom, Dario Righelli, Gabriele Sales)</li>
</ul>
<p><img src="https://i0.wp.com/blog.bioconductor.org/posts/2026-06-17-venice/working.jpeg?w=578&#038;ssl=1" class="img-fluid" data-recalc-dims="1"></p>
<p>Each day started with a brief session in which each team set up goals for the day. Day 1 also included a single slide, five-minute <strong>project plan presentation</strong> right after lunch. This presentation mid-day served to help teams develop a focused project quickly, with the understanding that the project plan would likely change over the next 2 days.</p>
<p>Days 1 and 2 ended with the opportunity for each team to present their work and challenges they faced that day, again with a one-slide presentation. These <strong>daily afternoon summaries</strong> were helpful to identify shared challenges, crystallize work from the day, and to provide visibility across project teams.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-17-venice/marco.jpeg?w=578&#038;ssl=1" class="img-fluid figure-img" data-recalc-dims="1"></p>
<figcaption>On the second day, the group journeyed across the water for a stroll through the streets of Venice towards Italian dinner. This group picture was taken on St. Mark’s Square (Piazza San Marco), featuring St. Mark’s Basilica and Campanile (bell tower) in the background.</figcaption>
</figure>
</div>
<p>The hackathon ended with a <strong>concluding showcase</strong> where each team presented their progress and demonstrated their technical achievements. To ensure these developments remain accessible to the community, teams documented their work (code, vignettes, and resources) in a dedicated <strong>GitHub repository</strong>. These results have been synthesized into a <strong>collaborative preprint</strong>, with each group contributing a detailed section summarizing their specific theme and findings.</p>
<ul>
<li><a href="https://github.com/BiocCodingCollaborations/VeniceHackathon2026" rel="nofollow" target="_blank">GitHub repository</a> housing code and resources developed during the hackathon.</li>
<li><a href="https://doi.org/10.37044/osf.io/9ej32_v1" rel="nofollow" target="_blank">Collaborative preprint</a> summarizing the format, themes, and outputs of the hackathon.</li>
</ul>
<hr>
<p>The event was organized by the Department of Statistical Sciences of the University of Padova in collaboration with EMBL and Venice International University, funded in part by the European Research Council (ERC) Grant CoG 101171662, and supported by EMBL’s Transversal Theme Theory@EMBL.</p>
<hr>



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

		<post-id xmlns="com-wordpress:feed-additions:1">402070</post-id>	</item>
		<item>
		<title>Bioconductor Maintainer Validation</title>
		<link>https://www.r-bloggers.com/2026/06/bioconductor-maintainer-validation/</link>
		
		<dc:creator><![CDATA[Lori Shepherd-Kern]]></dc:creator>
		<pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://blog.bioconductor.org/posts/2026-06-16-maintainer-validation/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Introduction<br />
Bioconductor policies include being an active and reachable maintainer. Maintainer emails in the DESCRIPTION of packages often go stale as maintainers change positions. There is also a necessity to have maintainers opt into Biocond...</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/bioconductor-maintainer-validation/">Bioconductor Maintainer Validation</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://blog.bioconductor.org/posts/2026-06-16-maintainer-validation/"> Bioconductor community blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 





<section id="introduction" class="level2">
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
<p>Bioconductor policies include being an active and reachable maintainer. Maintainer emails in the DESCRIPTION of packages often go stale as maintainers change positions. There is also a necessity to have maintainers opt into Bioconductor policies and procedures as they change over time.</p>
<p>We have created an application that uses Amazon Simple Email Service (SES) to send periodic emails to maintainers to check if the endpoint is reachable and to send a verification opt-in of Bioconductor current policies and procedures and code of conduct once a year.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i1.wp.com/blog.bioconductor.org/posts/2026-06-16-maintainer-validation/MaintainerEmail.jpg?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" alt="Photo of Email" data-recalc-dims="1"></p>
</figure>
</div>
<p>Initial feedback is that this email is “spammy” and may be marked as such by institutions, but it is an initial attempt at compliance. We will look at alternatives to emails like specialized maintainer account access at a future date.</p>
<section id="access-to-information" class="level4">
<h4 class="anchored" data-anchor-id="access-to-information">Access to Information</h4>
<p>The information is in a publicly accessible database. We do not recommend connecting directly to the webservice but instead using the accompanied Bioconductor R package <a href="https://bioconductor.org/packages/BiocMaintainerApp/" rel="nofollow" target="_blank">BiocMaintainerApp</a>. It provides a Shiny application interface for querying Bioconductor package maintainers’ information.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i0.wp.com/blog.bioconductor.org/posts/2026-06-16-maintainer-validation/feature-image.jpg?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" alt="Photo of ShinyApp" data-recalc-dims="1"></p>
</figure>
</div>
</section>
<section id="thank-you" class="level4">
<h4 class="anchored" data-anchor-id="thank-you">Thank you</h4>
<p>We appreciate maintainers’ cooperation moving forward.</p>


</section>
</section>

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

		<post-id xmlns="com-wordpress:feed-additions:1">402032</post-id>	</item>
		<item>
		<title>New Package Submission Process</title>
		<link>https://www.r-bloggers.com/2026/06/new-package-submission-process/</link>
		
		<dc:creator><![CDATA[Lori Shepherd-Kern]]></dc:creator>
		<pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://blog.bioconductor.org/posts/2026-06-15-new-submission-process-with-Runiverse/</guid>

					<description><![CDATA[<p>Introduction<br />
Bioconductor is moving towards using R-universe for its daily build system. See our previous blog post Collaborating between Bioconductor and R-universe on Development of Common Infrastructure. As we move in this direction it was a...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/new-package-submission-process/">New Package Submission Process</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://blog.bioconductor.org/posts/2026-06-15-new-submission-process-with-Runiverse/"> Bioconductor community blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 





<section id="introduction" class="level2">
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
<p>Bioconductor is moving towards using R-universe for its daily build system. See our previous blog post <a href="https://blog.bioconductor.org/posts/2026-04-08-r-universe-collaboration/" rel="nofollow" target="_blank">Collaborating between Bioconductor and R-universe on Development of Common Infrastructure</a>. As we move in this direction it was also necessary to update the submission process for Bioconductor packages. While the daily builders are still transitioning, the new submission process location is now live. The new system utilizes GitHub Actions to trigger review milestones and R-Universe as the build/check backend. The new system provides a smoother experience; it is more automated and avoids administrative steps that have historically bottlenecked the review process.</p>
</section>
<section id="information" class="level2">
<h2 class="anchored" data-anchor-id="information">Information</h2>
<section id="location" class="level4">
<h4 class="anchored" data-anchor-id="location">Location:</h4>
<p>The new location for submitting new packages to Bioconductor for review is <a href="https://github.com/Bioconductor/BiocContributions" rel="nofollow" target="_blank">BiocContributions</a>. This replaces the old location at <code>Bioconductor/Contributions</code>.</p>
</section>
<section id="documentation" class="level4">
<h4 class="anchored" data-anchor-id="documentation">Documentation</h4>
<p>There is documentation on <a href="https://github.com/Bioconductor/BiocContributions/blob/devel/docs/submitters.md" rel="nofollow" target="_blank">What to Expect</a> as well as a detailed <a href="https://docs.google.com/presentation/d/1EK2wsDoRbtVGECdYC1GU5nGtYkN-h_7R-on-CSUC6CQ/edit?slide=id.p#slide=id.p" rel="nofollow" target="_blank">Slide Deck</a>.</p>
<p>There is also a <a href="https://github.com/Bioconductor/BiocContributions/blob/devel/docs/FAQs.md" rel="nofollow" target="_blank">FAQ</a> for commonly asked questions, concerns, or troubleshooting.</p>
<p>If you need to report an issue with the new system, please open an Issue on the <a href="https://github.com/BiocStaging/BiocSubmissionProcess" rel="nofollow" target="_blank">BiocSubmissionProcess</a> GitHub repository.</p>
</section>
<section id="what-about-bioconductorcontributions" class="level4">
<h4 class="anchored" data-anchor-id="what-about-bioconductorcontributions">What about Bioconductor/Contributions</h4>
<p>The submission location at <code>Bioconductor/Contributions</code> has been frozen and will no longer accept new issues. <a href="https://github.com/Bioconductor/BiocContributions" rel="nofollow" target="_blank">BiocContributions</a> replaces this location. If you already submitted to the old location, if you are assigned a reviewer, your review will finish there. If you have not been assigned a reviewer yet, we will be posting shortly to close out your submission and move to the new location.</p>
</section>
<section id="easier-reproducibility" class="level4">
<h4 class="anchored" data-anchor-id="easier-reproducibility">Easier Reproducibility</h4>
<p>One of the frequent comments we receive is how do we reproduce the results of the build reports Bioconductor creates. The switch to using R-universe as the building and checking backend allows for a reproducible testing environment. Any maintainer can apply R-Universe checking on their personal GitHub repository for a Bioconductor package by following these <a href="https://docs.r-universe.dev/bioconductor/#debugging-the-ci" rel="nofollow" target="_blank">instructions</a>. This allows for a maintainer to test before submitting to Bioconductor and testing any future changes before pushing directly to Bioconductor.</p>
</section>
<section id="thank-you" class="level4">
<h4 class="anchored" data-anchor-id="thank-you">Thank you!</h4>
<p>We appreciate your patience and understanding as we transition to the new system.</p>


</section>
</section>

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

		<post-id xmlns="com-wordpress:feed-additions:1">402034</post-id>	</item>
		<item>
		<title>Why we still need {admiral} in an age of AI</title>
		<link>https://www.r-bloggers.com/2026/06/why-we-still-need-admiral-in-an-age-of-ai/</link>
		
		<dc:creator><![CDATA[Jeff Dickinson]]></dc:creator>
		<pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pharmaverse.github.io/blog/posts/2026-06-14_admiral_in_age_of_ai/admiral_in_age_of_ai.html</guid>

					<description><![CDATA[<p>There is a version of the AI-in-pharma story that goes like this: LLMs are trained on vast amounts of R code, so they can write ADaM programs on demand. Packages like  admiral {admiral}  become optional — a style preference rather than a requi...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/why-we-still-need-admiral-in-an-age-of-ai/">Why we still need {admiral} in an age of AI</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-14_admiral_in_age_of_ai/admiral_in_age_of_ai.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>There is a version of the AI-in-pharma story that goes like this: LLMs are trained on vast amounts of R code, so they can write ADaM programs on demand. Packages like <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> become optional — a style preference rather than a requirement. Just describe what you need and let the model figure it out.</p>
<p>The benchmark data from <a href="https://github.com/RConsortium/pharma-skills" rel="nofollow" target="_blank">pharma-skills</a> tells a different story.</p>
<section id="what-an-unskilled-agent-actually-does" class="level2">
<h2 class="anchored" data-anchor-id="what-an-unskilled-agent-actually-does">What an Unskilled Agent Actually Does</h2>
<p>When an AI coding agent is asked to derive an ADAE dataset without access to <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> skill guidance, it does not reach for <code>derive_var_trtemfl()</code> or <code>derive_vars_merged()</code> with the correct parameters. Across multiple benchmark runs, unskilled agents fell into two consistent failure modes: either generating synthetic data rather than using pharmaverse reference datasets, or writing bespoke <code>dplyr</code> pipelines that reimplemented logic <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> already provides — incorrectly.</p>
<p>One example from BDS benchmarking is particularly telling. Without skill guidance, agents consistently used <code>derive_vars_merged()</code> where <code>derive_vars_merged_lookup()</code> was required for parameter code assignment. Both functions exist in <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip>. Both execute without error. But <code>derive_vars_merged()</code> drops unmatched records silently, producing a dataset with the wrong row count. No warning. No crash. Just wrong output that passes a casual review.</p>
<p>This is not a model quality problem. It is a knowledge problem. The model does not know what the pharmaverse community knows.</p>
</section>
<section id="the-package-as-specification" class="level2">
<h2 class="anchored" data-anchor-id="the-package-as-specification">The Package as Specification</h2>
<p><bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> is more than a collection of R functions. It is a community-maintained encoding of CDISC ADaM logic — accumulated through years of collaboration across sponsors, CROs, and regulators, tested against real submissions, and versioned for traceability. When a programmer calls <code>derive_vars_dtm()</code> with the correct imputation flags, they are not just writing R code. They are implementing a specification that has been reviewed, validated, and documented.</p>
<p>An LLM trained on general R code does not reliably inherit that specification. It has seen <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> in its training data, but not with the depth or precision needed to apply it correctly across the full range of ADaM derivation scenarios. The unskilled agent that wrote a custom <code>parse_dtc_datetime()</code> function using <code>substr()</code> and <code>as.POSIXct()</code> — rather than calling <code>derive_vars_dtm()</code> — was not being lazy. It was doing its best with what it knew. Its best was not good enough, and the errors it introduced were in the edge cases that matter most in a clinical submission.</p>
</section>
<section id="what-the-skill-does" class="level2">
<h2 class="anchored" data-anchor-id="what-the-skill-does">What the Skill Does</h2>
<p>The <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> skills in pharma-skills do not replace <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip>. They connect the AI agent to it. A skill provides curated, domain-aware guidance: which functions to use for which derivations, how to structure the program for QC readability, which variables require special handling, and what assertions to include. The skill is the bridge between a capable general-purpose model and the specific, validated logic the pharmaverse community has built.</p>
<p>The benchmark results reflect this directly. Across ADSL, ADAE, ADVS, and ADLB:</p>
<ul>
<li><strong>With skill: 88–100%</strong> pass rates across domains</li>
<li><strong>Without skill: 17–59%</strong> pass rates, with high variance</li>
</ul>
<p>That variance in the unskilled condition matters as much as the mean. Inconsistent output is not a defensible process in a GxP context. A skill-guided agent produces consistent, traceable, <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip>-anchored code. An unskilled agent produces something different every time.</p>
</section>
<section id="the-accountability-anchor" class="level2">
<h2 class="anchored" data-anchor-id="the-accountability-anchor">The Accountability Anchor</h2>
<p>There is a regulatory dimension here that goes beyond code quality. A clinical submission needs to trace its derivations to validated, versioned, documented methods. A bespoke LLM-generated pipeline — however functional — has no such anchor. <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> provides it. When a submission uses <code>derive_var_trtemfl()</code> from a pinned version of <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip>, the derivation logic is documented, community-reviewed, and reproducible. The AI is most useful when it is writing code that inherits those properties, not when it is improvising around them.</p>
<p>This is why the pharma-skills project frames skills not as prompt templates, but as domain knowledge artifacts. The goal is not to make AI write more R code. It is to make AI write <bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> code — correctly, consistently, and in a form that a human reviewer can audit and a regulatory submission can defend.</p>
<p><bslib-tooltip placement="auto" bsoptions="[]" data-require-bs-version="5" data-require-bs-caller="tooltip()"> <template>admiral</template> <a href="https://pharmaverse.github.io/admiral/" class="r-link-pkg" rel="nofollow" target="_blank">{admiral}</a> </bslib-tooltip> was built for exactly this moment. The community just needs to make sure AI knows how to use it.</p>
<!--------------- appendices go here ----------------->
</section>
<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-14 18:52:36.863336</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-14_admiral_in_age_of_ai/admiral_in_age_of_ai.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{dickinson2026,
  author = {Dickinson, Jeff},
  title = {Why We Still Need \{Admiral\} in an Age of {AI}},
  date = {2026-06-14},
  url = {https://pharmaverse.github.io/blog/posts/2026-06-14_admiral_in_age_of_ai/admiral_in_age_of_ai.html},
  langid = {en}
}
</pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-dickinson2026" class="csl-entry quarto-appendix-citeas">
Dickinson, Jeff. 2026. <span>“Why We Still Need {Admiral} in an Age of
AI.”</span> June 14, 2026. <a href="https://pharmaverse.github.io/blog/posts/2026-06-14_admiral_in_age_of_ai/admiral_in_age_of_ai.html" rel="nofollow" target="_blank">https://pharmaverse.github.io/blog/posts/2026-06-14_admiral_in_age_of_ai/admiral_in_age_of_ai.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-14_admiral_in_age_of_ai/admiral_in_age_of_ai.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/why-we-still-need-admiral-in-an-age-of-ai/">Why we still need {admiral} in an age of AI</a>]]></content:encoded>
					
		
		<enclosure url="https://pharmaverse.github.io/blog/posts/2026-06-14_admiral_in_age_of_ai/admiral.png" length="0" type="image/png" />

		<post-id xmlns="com-wordpress:feed-additions:1">401927</post-id>	</item>
		<item>
		<title>Seasonal adjustment by @ellis2013nz</title>
		<link>https://www.r-bloggers.com/2026/06/seasonal-adjustment-by-ellis2013nz/</link>
		
		<dc:creator><![CDATA[free range statistics - R]]></dc:creator>
		<pubDate>Sat, 13 Jun 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://freerangestats.info/blog/2026/06/14/samoa-seasonal-adjustment</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> A reasonably straightforward post today. I wanted to look at monthly tourism numbers in Samoa. In fact I started to do this for Pacific islands in general, but the data wrangling challenges were sufficient that I only got as far as Samoa for now. There...</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/seasonal-adjustment-by-ellis2013nz/">Seasonal adjustment by @ellis2013nz</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://freerangestats.info/blog/2026/06/14/samoa-seasonal-adjustment"> free range statistics - 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 reasonably straightforward post today. I wanted to look at monthly tourism numbers in Samoa. In fact I started to do this for Pacific islands in general, but the data wrangling challenges were sufficient that I only got as far as Samoa for now. There’s interest in these at the moment because they would be a relatively timely indicator of possible economic damage from the fuel crisis related to the Iran war. Visitor numbers, inflation, and merchandise trade are part of the very select number of monthly published economic statistics in this part of the world (for a select subset of countries).</p>

<p>There’s two things happening in this post:</p>

<ul>
  <li>getting hold of the Samoa’s visitor arrivals numbers; and</li>
  <li>seasonally adjusting them.</li>
</ul>

<p>The latter is more interesting to me than the former, but unfortunately the former took most of the time.</p>

<h2 id="data-wrangling">Data wrangling</h2>

<p>Here’s what the visitor numbers look like, once we’ve got them all into one data frame:</p>

<object type="image/svg+xml" data="https://freerangestats.info/img/0322-original.svg" width="450"><img src="https://i0.wp.com/freerangestats.info/img/0322-original.png?w=450&#038;ssl=1" data-recalc-dims="1" /></object>

<p>The Samoa Bureau of Statistics has a nice Excel workbook up to May 2023, but from that date onwards the data is only available as far as I can see in PDF reports. Luckily these are all available as links from <a href="https://www.sbs.gov.ws/migration/" rel="nofollow" target="_blank">a single page</a>, but there seems to be either a skill issue on my part or some kind of block on the website that stops any systematic download of them all, so I had to download all the PDFs by hand one at a time.</p>

<p>Then Claude helped me write a parser to find the visitor arrivals number in each PDF. Actually, Claude wasn’t any good at actually finding the right number, but it did give me a pattern I could adapt, much as in the old days I would have used Stack Overflow. There are a lot of numbers in each PDF and we need the right one—visitor arrivals, not total arrivals (yes, that’s an em dash, but I write them all by hand). In this case it turns out that the trick is that the PDFs all include the sentence “Overall visitor numbers for the month under review stood at [number]”, and luckily there is no other use of the word “stood” in the document.</p>

<p>The other fiddly thing was extracting the actual date each PDF referred to. Then everything needed to be tested. In the end, it would have been quicker to just manually type the 35 numbers I needed. But here’s the code that does all the data wrangling.</p>

<figure class="highlight"><pre>library(tidyverse)
library(rvest)
library(httr2)
library(lubridate)
library(pdftools)   
library(readxl)
library(seasonal)
library(tsibble)
library(fable)
library(feasts)
library(ggtext)
library(scales)

#' Extract date from messy filenames
extract_date &lt;- function(messy_date){
  tibble(path = messy_date) |&gt;
  mutate(
    stem      = tools::file_path_sans_ext(basename(path)),
    # Remove any leading word followed by _ or - (e.g. &quot;Migration_&quot;)
    stem      = str_remove(stem, &quot;^([A-Za-z]+[_-])+(?=[A-Z])&quot;),
    month_str = str_extract(stem, &quot;[A-Za-z]{3,}&quot;),
    # Normalise non-standard abbreviations
    month_str = str_replace(month_str, &quot;^Sept$&quot;, &quot;Sep&quot;),
    year_str  = str_extract(stem, &quot;\\d{4}|\\d{2}&quot;),
    year      = if_else(nchar(year_str) == 2,
                        as.integer(paste0(&quot;20&quot;, year_str)),
                        as.integer(year_str)),
    date      = as.Date(paste(year, month_str, &quot;01&quot;), format = &quot;%Y %B %d&quot;) |&gt;
                  coalesce(as.Date(paste(year, month_str, &quot;01&quot;), format = &quot;%Y %b %d&quot;))
  ) |&gt;
  pull(date)
}

test &lt;- c(&quot;samoa_pdfs/April_25.pdf&quot;, &quot;samoa_pdfs/Feb_25.pdf&quot;, &quot;samoa_pdfs/Feb_26.pdf&quot;, 
&quot;samoa_pdfs/Jan_26.pdf&quot;, &quot;samoa_pdfs/January_25.pdf&quot;, &quot;samoa_pdfs/July_25.pdf&quot;, 
&quot;samoa_pdfs/June-25.pdf&quot;, &quot;samoa_pdfs/March_2025.pdf&quot;, &quot;samoa_pdfs/March_2026.pdf&quot;, 
&quot;samoa_pdfs/May_25.pdf&quot;, &quot;samoa_pdfs/Migration_April-2026.pdf&quot;,
&quot;samoa_pdfs/Sept-24.pdf&quot;, &quot;samoa_pdfs/Migration_Rep_June_2023.pdf&quot;)

extract_date(test)

#-------------------PDFs for recent data------------------
# For the more recent years no Excel tables are published, so need
# to use the PDFs and extract total from there
# These had to be downloaded by hand - nothing I tried was able to automate
# that. Download from https://www.sbs.gov.ws/migration/ and save
# in a subfolder /samoa_pdfs/.

pdf_dir &lt;- &quot;samoa_pdfs&quot;
tbl &lt;- tibble(local_path = list.files(pdf_dir, pattern = &quot;.pdf$&quot;, full.names = TRUE))

parse_pdf_visitors &lt;- function(path) {
  txt &lt;- tryCatch(pdf_text(path), error = function(e) {
    message(&quot;  [WARN] pdftools could not read: &quot;, basename(path))
    NULL
  })
  if (is.null(txt)) return(NA_integer_)
  full_text &lt;- paste(txt, collapse = &quot;\n&quot;)
  patterns &lt;- c(
    &quot;stood at [^0-9(]{0,10}\\(?([0-9,]+)\\)?&quot;
  )
  for (pat in patterns) {
    m &lt;- str_match(full_text, pat)[, 2]
    if (!is.na(m)) return(as.integer(str_remove_all(m, &quot;,&quot;)))
  }
  message(&quot;  [WARN] Could not parse visitor count from: &quot;, basename(path))
  NA_integer_
}

found &lt;- tbl |&gt; 
  filter(file.exists(local_path))

message(&quot;  Parsing &quot;, nrow(found), &quot; local PDFs...&quot;)
pdf_tbl &lt;- found |&gt;
  mutate(visitors = map_int(local_path, \(p) {
    message(&quot;  &quot;, basename(p))
    parse_pdf_visitors(p)
  })) |&gt;
  filter(!is.na(visitors)) |&gt;
  mutate(date = extract_date(local_path))

#-----------------Excel versions for older data------------
# For May 2023 and earlier we can get the data for multiple months
# at a time from Table 1 of the Excel tables. The May 2023 Excel
# file goes back to 2017 January (although the rows are hidden)

fn &lt;- &quot;May_23.xlsx&quot;
if(!file.exists(fn)){
  download.file(&quot;https://sbs.gov.ws/images/sbs-documents/social/Arrival/2023/May_23.xlsx&quot;,
                destfile = fn, mode = &quot;wb&quot;)
}
x &lt;- read_excel(fn, sheet = &quot;Table 1&quot;, 
                 range = &quot;D48:D130&quot;,
                 col_names = &quot;visitors&quot;) |&gt; 
  drop_na() |&gt; 
  pull(visitors)

historical &lt;- tibble(visitors = x, 
               date = seq(as.Date(&quot;2017-01-01&quot;), as.Date(&quot;2023-05-01&quot;), by = &quot;month&quot;))

#-----------combine and test-----------------------
samoa_visitors &lt;- pdf_tbl |&gt; 
  select(date, visitors) |&gt;
  bind_rows(historical) |&gt; 
  arrange(date) |&gt; 
  mutate(date_month = yearmonth(date)) |&gt; 
  as_tsibble(index = date_month)


# Test - some hand picked test cases, 4 from PDFs and 3 from the Excel
samoa_test &lt;- tribble(~date, ~correct_visitors,
                      &quot;2023-08-01&quot;, 16471,
                      &quot;2024-04-01&quot;, 12644,
                      &quot;2024-08-01&quot;, 17248,
                      &quot;2026-04-01&quot;, 14188,
                      &quot;2018-02-01&quot;, 7413,
                      &quot;2020-12-01&quot;, 195,
                      &quot;2022-06-01&quot;, 866) |&gt; 
  mutate(date = as.Date(date))

stopifnot(
  samoa_test |&gt; 
    anti_join(samoa_visitors, by = c(&quot;date&quot;, &quot;correct_visitors&quot; = &quot;visitors&quot;)) |&gt; 
    nrow() == 0
)</pre></figure>

<p>And here’s the code that draws the basic time series chart I used earlier:</p>

<figure class="highlight"><pre>the_caption = &quot;Source: Samoa Bureau of Statistics&quot;

ggplot(samoa_visitors, aes(x = date, y = visitors)) +
  geom_line() +
  scale_y_continuous(label = comma) +
  labs(x = &quot;&quot;,
       y = &quot;Visitor arrivals&quot;,
       title = &quot;Visitor arrivals per month to Samoa&quot;,
       subtitle = &quot;Unadjusted originals&quot;,
       caption = the_caption)</pre></figure>

<h2 id="modelling-seasonal-decomposition">Modelling seasonal decomposition</h2>

<p>Phew. Ok, on to the more fun part of actually modelling. I intend to use the X-13ARIMA-SEATS tool. X‑13ARIMA‑SEATS is a program developed and maintained by the <a href="https://www.census.gov/data/software/x13as.html" rel="nofollow" target="_blank">US Census Bureau</a> for seasonal adjustment and time‑series decomposition. It will automatically fit a SARIMA (seasonal autoregressive integrated moving average) time series model, has built in methods for identifying and dealing with outliers, by default adjusts for number of trading days and the moving Easter holiday, and allows the user to specify additional regression explanatory variables if you want. It’s the go-to across the world for seasonal adjustment of official statistics.</p>

<p>X-13ARIMA-SEATS is available in R through the <code>seasonal</code> package (by Christoph Sax and Dirk Eddelbuettel). Downstream of that, the <code>feasts</code> and <code>fable</code> packages by (Mitchell O’Hara-Wild, Rob Hyndman and Earo Wangmake) make it easier to work with in a tabular, tidyverse approach.</p>

<p>The Covid period is an obvious dominating factor in tourism anywhere in recent decades, and shows up in the first chart I showed above. I’m also interested in the period since the USA-Israel-Iran war began to see if there is an impact from that. There’s only two months of data (March and April 2026) since the war started, so an impact would have to be dramatic to show up, but it’s worth checking.</p>

<p>X-13ARIMA-SEATS by default will fit forecasts when you ask it to model so you need to provide values of x regressor variables to cover not only the period of the data but a few periods out ahead. I’m doing this as simple time series vectors—I haven’t yet worked out the easy way to do this in the tabular world of <code>fable</code>. Luckily, it seems I can use these vectors later even in <code>fable</code>. In a moment I’ll use both <code>seasonal</code> directly and via <code>fable</code> to make sure I get the same results. In fact, updating my dated knowledge of time series modelling to use <code>fable</code> was one of my main motivations for this whole exercise.</p>

<p>Here’s the code that makes the x regressors I’ll be using for the presence of the Covid pandemic and for the Iran war:</p>

<figure class="highlight"><pre>#----------------x regressor variables-----------------

# Covid time series indicator to use as a regressor
covid_reg &lt;- ts(
  as.numeric(seq(as.Date(&quot;2017-01-01&quot;), as.Date(&quot;2029-04-01&quot;), by = &quot;month&quot;) %in%
    seq(as.Date(&quot;2020-04-01&quot;), as.Date(&quot;2022-07-01&quot;), by = &quot;month&quot;)),
  start     = c(2017, 1),
  frequency = 12
)

# Iran war time series indicator
war_reg &lt;- ts(
  as.numeric(seq(as.Date(&quot;2017-01-01&quot;), as.Date(&quot;2029-04-01&quot;), by = &quot;month&quot;) %in%
    seq(as.Date(&quot;2026-03-01&quot;), as.Date(&quot;2026-07-01&quot;), by = &quot;month&quot;)),
  start     = c(2017, 1),
  frequency = 12
)</pre></figure>

<h3 id="directly-with-seasonal">Directly with <code>seasonal</code></h3>

<p>Ok, it’s modelling time. First, using just an old fashioned time series vector of Samoa’s visitor arrivals and the <code>seasonal</code> package directly, here’s fitting the X-13ARIMA-SEATS model with defaults using both the Covid and Iran war regressors:</p>

<figure class="highlight"><pre>sa_ts &lt;- ts(samoa_visitors$visitors, frequency = 12, start = c(2017, 1))

fit_ts_war &lt;- seas(sa_ts, xreg = cbind(covid_reg, war_reg))
summary(fit_ts_war)</pre></figure>

<p>Super simple. That gets us this output:</p>

<pre>Call:
seas(x = sa_ts, xreg = cbind(covid_reg, war_reg))

Coefficients:
                  Estimate Std. Error z value Pr(&gt;|z|)    
xreg1             -3.10069    0.14567 -21.286  &lt; 2e-16 ***
xreg2             -0.15937    0.13098  -1.217    0.224    
LS2020.Mar        -0.85036    0.14567  -5.838 5.29e-09 ***
AO2020.Dec        -0.75529    0.12350  -6.115 9.63e-10 ***
LS2021.May        -0.79012    0.13233  -5.971 2.36e-09 ***
AO2021.Jul        -1.36973    0.12378 -11.066  &lt; 2e-16 ***
LS2022.May         0.89068    0.13233   6.731 1.68e-11 ***
LS2022.Aug        -1.07689    0.19608  -5.492 3.97e-08 ***
AR-Nonseasonal-01 -0.59006    0.07041  -8.380  &lt; 2e-16 ***
MA-Seasonal-12     0.99961    0.08044  12.427  &lt; 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

SEATS adj.  ARIMA: (1 1 0)(0 1 1)  Obs.: 112  Transform: log
AICc:  1608, BIC:  1634  QS (no seasonality in final):3.295  
Box-Ljung (no autocorr.): 26.42   Shapiro (normality): 0.9721 *
</pre>

<p>Some key things to note here.</p>

<p>The data was log-transformed, which is good—I would certainly have chosen to do this, or at least a square root transform, given the way visitor arrivals variance increases as its mean does, all around the world.</p>

<p>The model eventually adopted is described as ARIMA (1 1 0)(0 1 1). This means the main series as a autoregression term on one lag, after one round of differencing; and the seasonal part has a moving average term on on lag, after one round of differencing. This is a very normal model for tourism numbers. it indicates a general trend/drive (the differencing in the main series), a tendency for the values in one month to be related one way or another to those in the month before (in this case with a negative correlation of -0.59) and a strong annual seasonality effect that changes slowly over time.</p>

<p>The Covid dummy variable (<code>xreg1</code>) is strongly negatively significant. With a coefficient of -3.1 and the log transform of the response variable, this means that during the Covid period the actual arrivals were on average <code>exp(-3.1) = 0.045</code> (ie 4.5% or down 95.5%) of during the non-Covid periods.</p>

<p>In contrast, we don’t have a statistically significant effect for the Iran war (<code>xreg2</code>). For subsequent analysis I will take out that x regressor as I don’t want it complicating the recent trend and seasonally adjusted figures.</p>

<p>Six months’ data have been singled out as outliers and controlled for appropriately. These are all in the difficult-to-model Covid period of 2020 to 2022.</p>

<p>One point to note is that we don’t have an Easter effect. I am 100% sure there is really an Easter effect in Samoa’s visitor arrivals, but 9 years of data isn’t enough to show it. Easter is sometimes in April and sometimes in March. But since 2017, it happens to have been in April every year apart from 2024. That’s just not enough variation to distinguish it from regular monthly seasonal impacts.</p>

<p>To check my recollection that Easter is indeed checked for by default in X-13ARIMA-SEATS, I fit a model to the well known Box and Jenkins airline data:</p>

<pre>&gt; # Another examplefor comparison
+ m &lt;- seas(AirPassengers)
+ summary(m)

Call:
seas(x = AirPassengers)

Coefficients:
                    Estimate Std. Error z value Pr(&gt;|z|)    
Weekday           -0.0029497  0.0005232  -5.638 1.72e-08 ***
Easter[1]          0.0177674  0.0071580   2.482   0.0131 *  
AO1951.May         0.1001558  0.0204387   4.900 9.57e-07 ***
MA-Nonseasonal-01  0.1156204  0.0858588   1.347   0.1781    
MA-Seasonal-12     0.4973600  0.0774677   6.420 1.36e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

SEATS adj.  ARIMA: (0 1 1)(0 1 1)  Obs.: 144  Transform: log
AICc: 947.3, BIC: 963.9  QS (no seasonality in final):    0  
Box-Ljung (no autocorr.): 26.65   Shapiro (normality): 0.9908 
</pre>

<p>Here we see that the number of week days in a month and the moving Easter holiday are indeed in the final model, without me having to have asked for them to be checked. Easter has a positive impact on this air passengers series (1949 to 1960), and the number of week days in a month has a smaller negative impact.</p>

<p>So after that Easter-checking digression, I refit the model for my Samoa visitor arrivals without the war regressor and get an essentially identical result:</p>

<pre>&gt; fit_ts &lt;- seas(sa_ts, xreg = covid_reg)
&gt; summary(fit_ts)

Call:
seas(x = sa_ts, xreg = covid_reg)

Coefficients:
                  Estimate Std. Error z value Pr(&gt;|z|)    
xreg              -3.10045    0.14690 -21.106  &lt; 2e-16 ***
LS2020.Mar        -0.83292    0.14617  -5.698 1.21e-08 ***
AO2020.Dec        -0.75463    0.12449  -6.062 1.35e-09 ***
LS2021.May        -0.78975    0.13356  -5.913 3.36e-09 ***
AO2021.Jul        -1.36737    0.12476 -10.960  &lt; 2e-16 ***
LS2022.May         0.89113    0.13356   6.672 2.52e-11 ***
LS2022.Aug        -1.07733    0.19782  -5.446 5.15e-08 ***
AR-Nonseasonal-01 -0.58577    0.07071  -8.284  &lt; 2e-16 ***
MA-Seasonal-12     0.99912    0.07827  12.765  &lt; 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

SEATS adj.  ARIMA: (1 1 0)(0 1 1)  Obs.: 112  Transform: log
AICc:  1607, BIC:  1631  QS (no seasonality in final):2.501  
Box-Ljung (no autocorr.): 26.63   Shapiro (normality): 0.9706 *
</pre>

<h3 id="with-fable-and-feasts">With <code>fable</code> and <code>feasts</code></h3>

<p><code>tsibble</code>, <code>fable</code> and <code>feasts</code> are a brilliant set of packages that let you work with time series in R in a more tabular and <code>tidyverse</code>-friendly way than the various older time series data structures let you. Most of my work with time series was before they were available, though, so I’m lacking confidence in how they work. Luckily it seems to be pretty straightforward.</p>

<p>I’d already done the critical step earlier with <code>as_tsibble(index = date_month)</code>, specifying my main <code>samoa_visitors</code> tibble is actually a time series tibble. Now the modelling using that tsibble is pretty simple:</p>

<figure class="highlight"><pre>#----------fable version-------
fit_fb &lt;- samoa_visitors |&gt; 
  model(X_13ARIMA_SEATS(
    visitors ~ xreg(covid_reg)
  ))

report(fit_fb)</pre></figure>

<p>Note we use <code>report()</code> rather than <code>summary()</code> to get the end result. I’m not going to print it here because it is literally identical to what we got earier with <code>summary(fit_ts)</code>.</p>

<p>The main appeal for me in using the <code>fable</code>/<code>feasts</code> approach is that it fits better with both my data wrangling workflow and my approach to <code>ggplot2</code> graphics. so here is a nice decomposition of hte original timeseries, produced with <code>autoplot(</code></p>

<figure class="highlight"><pre>fit_fb |&gt; 
  components() |&gt; 
  autoplot() </pre></figure>

<object type="image/svg+xml" data="https://freerangestats.info/img/0322-autoplot.svg" width="450"><img src="https://i0.wp.com/freerangestats.info/img/0322-autoplot.png?w=450&#038;ssl=1" data-recalc-dims="1" /></object>

<p>Note that in this decomposition, the original ‘visitors’ series and the trend are on the original scale, but the ‘seasonal’ and ‘irregular’ components are expressed as multipliers. So a seasonal value of 1.2 means in a given month the value is 20% higher as a result of the seasonality than otherwise.</p>

<p>And here is my final, presentation version of the data:</p>

<object type="image/svg+xml" data="https://freerangestats.info/img/0322-final-presentation.svg" width="450"><img src="https://i0.wp.com/freerangestats.info/img/0322-final-presentation.png?w=450&#038;ssl=1" data-recalc-dims="1" /></object>

<p>Produced with this code:</p>

<figure class="highlight"><pre>comp_data &lt;- fit_fb |&gt; 
  components() |&gt; 
  # the 'trend' that comes straight from the decomposition does
  # not adjust ofr the Covid coefficient and looks pretty weird
  # so it is more intuitive to present it after adjustment for
  # Covid, which we need to calculate by multiplying (because of
  # the log transform that SEATS used autoamtically):
  mutate(covid = as.numeric(covid_reg)[1:nrow(samoa_visitors)],
         trend_adj = trend * exp(covid * coef(fit_ts)[1])) 

comp_data|&gt; 
  ggplot(aes(x = date_month, y = season_adjust)) +
  geom_line(linewidth = 1.3) +
  geom_line(aes(y = trend_adj), colour = &quot;steelblue&quot;, alpha = 0.9, linewidth = 1.2) + 
  geom_point(aes(y = visitors), colour = &quot;grey70&quot;) +
  scale_y_continuous(label = comma) +
  labs(x = &quot;&quot;,
       y =&quot;Visitor arrivals&quot;,
       title = &quot;Visitor arrivals per month to Samoa&quot;,
       subtitle = &quot;&lt;span style='color:grey60'&gt;Original&lt;/span&gt;, seasonally adjusted &lt;span style='color:steelblue'&gt;and trend (adjusted for Covid period).&lt;/span&gt;&quot;,
       caption = &quot;Source: data from Samoa Bureau of Statistics. Seasonal adjustment by freerangestats.info.&quot;) +
  theme(plot.subtitle = element_markdown())</pre></figure>

<p>After all that, what insight do we have? Well, not a lot really, other than the blindingly obvious trends of the devastation to the industry of Covid and the slow and noisy growth trend since then. We are at least well placed to make commentary on the impact of the fuel crisis on tourism, and can say that there isn’t any evident yet. If and when we do see the impact, we’ll be able to talk about in terms of trends and random variation, after having removed the seasonal element. So that’s useful.</p>

<p>Well, that’s all. Perhaps I’ll get around in a later post to adding the other Pacific island countries with monthly tourism data—Fiji, Vanuatu, Cook Islands, French Polynesia being the key ones I’m aware of.</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://freerangestats.info/blog/2026/06/14/samoa-seasonal-adjustment"> free range statistics - 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/06/seasonal-adjustment-by-ellis2013nz/">Seasonal adjustment by @ellis2013nz</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401898</post-id>	</item>
		<item>
		<title>New CRAN Packages: signal or noise?</title>
		<link>https://www.r-bloggers.com/2026/06/new-cran-packages-signal-or-noise/</link>
		
		<dc:creator><![CDATA[Joseph Rickert]]></dc:creator>
		<pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://rworks.dev/posts/too-many-R-packages/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>If you are reading this post on R-bloggers, you will probably know that I have been publishing my selection of the “Top 40” new R packages on CRAN for quite some time. I did this first as part of my work at Revolution Analytics, then on R Views ...</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/new-cran-packages-signal-or-noise/">New CRAN Packages: signal or noise?</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/too-many-R-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>If you are reading this post on <a href="https://www.r-bloggers.com/" rel="nofollow" target="_blank">R-bloggers</a>, you will probably know that I have been publishing my selection of the “Top 40” new R packages on CRAN for quite some time. I did this first as part of my work at Revolution Analytics, then on <a href="https://rviews.rstudio.com/" rel="nofollow" target="_blank">R Views</a> for RStudio and Posit, and now here on R Works. It used to take about a day’s worth of pleasurable work spread out over a month to select forty interesting packages. For a hundred or so packages, I could look at all of the package webpages, download and play with a small number of them. Now, the “Top 40” has become a real hamster-on-the-wheel project. The following plot shows my count of the number of new packages to make it to CRAN since I began publishing on R Works.</p>
<div class="cell">
<details class="code-fold">
<summary>Show plot code</summary>
<pre>library(tidyverse)
file_path &lt;- &quot;new-cran-pkgs.csv&quot;

if (!file.exists(file_path)) {
  stop(paste(&quot;File not found! Please check the path:&quot;, file_path))
}

# Read text and numbers safely
raw_data &lt;- read.csv(file_path, colClasses = c(&quot;character&quot;, &quot;numeric&quot;), stringsAsFactors = FALSE)

plot_data &lt;- raw_data |&gt; mutate(Date = my(Month)) |&gt;
  arrange(Date)

new_pkg &lt;- ggplot(plot_data, aes(x = Date, y = Num_pkgs, group = 1)) +
  geom_line(color = &quot;#1f77b4&quot;, size = 1) +
  geom_point(color = &quot;#d62728&quot;, size = 1.2) +

  labs(
    title = &quot;Monthly Volume of New CRAN Packages&quot;,
    x = &quot;Date&quot;,
    y = &quot;Number of Packages&quot;,
    caption = &quot;Source: R Works monthly Top 40 posts&quot;
  ) +

  theme(
    plot.title = element_text(face = &quot;bold&quot;),
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

new_pkg</pre>
</details>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><a href="https://i1.wp.com/rworks.dev/posts/too-many-R-packages/index_files/figure-html/unnamed-chunk-1-1.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-1" rel="nofollow" target="_blank"><img src="https://i1.wp.com/rworks.dev/posts/too-many-R-packages/index_files/figure-html/unnamed-chunk-1-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></a></p>
</figure>
</div>
</div>
</div>
<p>Why the sharp increase? What could possibly be going on? Well, I have a guess. It is apparently now just too easy to package up some code and ship it to CRAN. That’s understandable: it’s just too easy to write and deploy any kind of software. The following plot that John Burn-Murdoch recently published in the <a href="https://www.ft.com/stream/e191658e-c66a-45bc-9bad-343bdc4210b3" rel="nofollow" target="_blank">Financial Times</a> based on <a href="https://www.nber.org/papers/w35275" rel="nofollow" target="_blank">NBER research</a> shows the explosion of apps in our Agentic AI era.</p>
<p><a href="https://i2.wp.com/rworks.dev/posts/too-many-R-packages/API.png?ssl=1" class="lightbox" data-gallery="quarto-lightbox-gallery-2" rel="nofollow" target="_blank"><img src="https://i2.wp.com/rworks.dev/posts/too-many-R-packages/API.png?w=578&#038;ssl=1" class="img-fluid" alt="App releases over time" data-recalc-dims="1"></a></p>
<p>The plot also indicates that the new apps can’t be making much of a positive contribution to people’s lives or corporate profits. They are apparently not being used, or reviewed, or even discovered.</p>
<p>So let’s ask the same kinds of questions about new R packages. Are most of them really making a contribution to R and the R Community? Are they contributing new statistical methods, extending the reach of R into new application areas, offering efficient high-performance code, or doing something else that would arguably benefit the R Community?</p>
<p>My impression as an engaged dilettante is no: most new R packages are not making a contribution. One obvious indicator of quality is documentation. A significant number of new R packages don’t provide sufficient documentation to explain what they offer. For example, in May, 40 of the 323 new CRAN packages had no README file, no vignettes, and no URL linking to a repository. To my way of thinking, with the possible exceptions of packages that have some sort of discoverable out-of-band documentation (e.g., a journal publication) or are not meant to be called by end users because they are infrastructure for some suite of packages, packages that don’t describe what, why, and how they work are not contributions.</p>
<p>As a hamster on the wheel, I would be very pleased to hear what you have to say. If you are motivated, please leave a comment at <a href="https://github.com/r-community-works/rworks-website/issues/68" rel="nofollow" target="_blank">Issue #68</a> on the R Works GitHub repository.</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://rworks.dev/posts/too-many-R-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/06/new-cran-packages-signal-or-noise/">New CRAN Packages: signal or noise?</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401823</post-id>	</item>
		<item>
		<title>Test Doubles Taxonomy for R: Dummy, Stub, Spy, Mock, Fake</title>
		<link>https://www.r-bloggers.com/2026/06/test-doubles-taxonomy-for-r-dummy-stub-spy-mock-fake/</link>
		
		<dc:creator><![CDATA[Jakub Sobolewski]]></dc:creator>
		<pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://jakubsobolewski.com/blog/test-doubles-taxonomy</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> You probably call them all "mock". What each type of test double actually is, when to reach for it in R, and what goes wrong when you mix them up.</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/test-doubles-taxonomy-for-r-dummy-stub-spy-mock-fake/">Test Doubles Taxonomy for R: Dummy, Stub, Spy, Mock, Fake</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-doubles-taxonomy"> 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://i1.wp.com/jakubsobolewski.com/blog/test-doubles-taxonomy/og-image.png?w=578&#038;ssl=1" alt="Test Doubles Taxonomy for R: Dummy, Stub, Spy, Mock, Fake" data-recalc-dims="1" /></p><p>You might call them all “mock”.</p>
<p>Mock the database. Mock the API. Mock the function. The word becomes a catch-all for any test double, any object you substitute for a real dependency in a test. Lumping them together makes it harder to choose the right tool, and the wrong choice leads to brittle, misleading tests.</p>
<p>There are five distinct types, each with a specific job. Knowing which is which is how you stop writing tests that do the wrong thing.</p>
<h2 id="the-code-under-test">The code under test</h2>
<p>All five examples use a single function: <code>process_payment</code>. It charges a card, logs the attempt, and optionally notifies the customer.</p>
<pre>process_payment &lt;- function(order, payment_gateway, logger, notifier = NULL) {
  logger$log(paste(&quot;Processing order&quot;, order$id))

  result &lt;- payment_gateway$charge(order$amount, order$card_token)

  if (!result$success) stop(&quot;Payment failed: &quot;, result$error)

  if (!is.null(notifier)) {
    notifier$send(order$customer_id, result$transaction_id)
  }

  result$transaction_id
}</pre>
<p>It has three dependencies: <code>payment_gateway</code>, <code>logger</code>, and <code>notifier</code>. Each one will be replaced with a different kind of double depending on what we’re trying to test.</p>
<h2 id="1-dummy">1. Dummy</h2>
<p><strong><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;" /> Definition:</strong> an object passed to satisfy a required parameter but never actually used by the test.</p>
<p><code>process_payment</code> always calls <code>logger$log</code>. The logger is required. But for a test that’s only checking whether the correct transaction ID is returned, we don’t care what gets logged. We just need something that won’t blow up when called.</p>
<pre>test_that(&quot;returns the transaction ID on successful payment&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-1&quot;,
    amount = 100,
    card_token = &quot;tok_visa&quot;,
    customer_id = &quot;cust-42&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(
    charge = function(amount, token) {
      list(success = TRUE, transaction_id = &quot;txn-abc&quot;)
    }
  )

  # Act
  result &lt;- process_payment(
    order,
    payment_gateway = stub_gateway,
    logger = dummy_logger
  )

  # Assert
  expect_equal(result, &quot;txn-abc&quot;)
})
Test passed with 1 success &#x1f947;.</pre>
<p><code>dummy_logger</code> accepts any call and does nothing. The test doesn’t assert on it at all. Its only job is to satisfy the function signature.</p>
<p>A dummy should be the simplest thing that compiles. Recording calls or setting expectations would make it something else. If you find yourself writing a dummy that crashes, or does something unexpected when called, the code path you’re testing actually does use the dependency.</p>
<p>Worth knowing.</p>
<h2 id="2-stub">2. Stub</h2>
<p><strong><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;" /> Definition:</strong> a replacement that returns pre-programmed responses, used to control what the code under test receives.</p>
<p>A stub lets you put the system in a specific state without involving real infrastructure. If you want to test what <code>process_payment</code> does when a card is declined, you don’t need a real payment API. You just return the response you want.</p>
<pre>test_that(&quot;throws an error when payment is declined&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-2&quot;,
    amount = 200,
    card_token = &quot;tok_declined&quot;,
    customer_id = &quot;cust-7&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(
    charge = function(amount, token) {
      list(success = FALSE, error = &quot;insufficient funds&quot;)
    }
  )

  # Act &#038; Assert
  expect_error(
    process_payment(
      order,
      payment_gateway = stub_gateway,
      logger = dummy_logger
    ),
    &quot;insufficient funds&quot;
  )
})
Test passed with 1 success &#x1f947;.</pre>
<p>The stub provides inputs <em>to</em> the system under test. You assert on what the code did <em>with</em> those inputs (in this case, that it threw the right error).</p>
<p>Notice that <code>process_payment</code> accepts <code>payment_gateway</code> as an argument. That’s dependency injection: the function doesn’t create or import its own gateway, so the test can pass in anything with the same interface. Without it you’d need a patching library to intercept the real dependency mid-call. With it, a plain list with a <code>charge</code> function is enough. Stubs work best when the code is designed this way: dependencies accepted as arguments, not hardwired inside.</p>
<p>If you practice test-first development, you’ll notice that you use this pattern all the time. You can’t write the test without it. You don’t know what to patch in a function that doesn’t exist yet! It’s only natural to inject all dependencies as you write the interface of your code.</p>
<p>When the dependency <em>isn’t</em> declared in the interface, when the function calls another function directly by name, <code>mockery::stub()</code> can patch it for the duration of a test:</p>
<pre># A function that calls charge_card() internally, with no way to inject it
process_payment_legacy &lt;- function(order) {
  result &lt;- charge_card(order$amount, order$card_token)
  if (!result$success) {
    stop(&quot;Payment failed: &quot;, result$error)
  }
  result$transaction_id
}

charge_card &lt;- function(amount, token) {
  stop(&quot;would call real payment API&quot;)
}

test_that(&quot;returns transaction ID when charge succeeds&quot;, {
  # Arrange
  order &lt;- list(amount = 100, card_token = &quot;tok_visa&quot;)
  mockery::stub(
    process_payment_legacy,
    &quot;charge_card&quot;,
    function(amount, token) {
      list(success = TRUE, transaction_id = &quot;txn-stub&quot;)
    }
  )

  # Act
  result &lt;- process_payment_legacy(order)

  # Assert
  expect_equal(result, &quot;txn-stub&quot;)
})
Test passed with 1 success &#x1f973;.</pre>
<p><code>mockery::stub()</code> replaces <code>charge_card</code> inside the scope of <code>process_payment_legacy</code> for that one test call, without touching the real function anywhere else.</p>
<p><code>mockery::stub()</code> has a catch. The stub is targeted by function name as a string, so if you rename <code>charge_card</code>, the stub silently stops working and the test passes against the real function with no warning. The test is also coupled to an implementation detail: if you refactor <code>process_payment_legacy</code> to call <code>payment_gateway$charge()</code> instead, the stub breaks even if the behavior is unchanged. That’s the <a href="https://jakubsobolewski.com/blog/test-smells-in-r/#3-over-specification" rel="nofollow" target="_blank">Overspecification smell</a>.</p>
<p>Use <code>mockery::stub()</code> when you’re working with legacy code that wasn’t built with testability in mind and you can’t refactor the interface right now. It lets you get tests in place quickly. Treat it as a stepping stone: once the characterization tests are green, refactor toward dependency injection and replace the patch with a plain stub passed as an argument.</p>
<p>To sum up: when you need to control what a dependency returns and don’t care how it was called, reach for a stub.</p>
<h2 id="3-spy">3. Spy</h2>
<p><strong><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;" /> Definition:</strong> a stub that also records calls made to it, so you can assert on them afterward.</p>
<p>Sometimes the behavior you’re testing is a side effect. A notification that should have been sent, a message that should have been logged. The code doesn’t return a value you can assert on. It calls something. A spy captures those calls.</p>
<pre>make_notifier_spy &lt;- function() {
  calls &lt;- list()
  list(
    send = function(customer_id, transaction_id) {
      calls[[length(calls) + 1]] &lt;&lt;- list(
        customer_id    = customer_id,
        transaction_id = transaction_id
      )
    },
    calls = function() calls
  )
}
test_that(&quot;notifies the customer after successful payment&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-3&quot;,
    amount = 50,
    card_token = &quot;tok_visa&quot;,
    customer_id = &quot;cust-99&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(
    charge = function(amount, token) {
      list(success = TRUE, transaction_id = &quot;txn-xyz&quot;)
    }
  )
  spy_notifier &lt;- make_notifier_spy()

  # Act
  process_payment(
    order,
    payment_gateway = stub_gateway,
    logger = dummy_logger,
    notifier = spy_notifier
  )

  # Assert
  expect_length(spy_notifier$calls(), 1)
  expect_equal(spy_notifier$calls()[[1]]$customer_id, &quot;cust-99&quot;)
  expect_equal(spy_notifier$calls()[[1]]$transaction_id, &quot;txn-xyz&quot;)
})
Test passed with 3 successes &#x1f308;.</pre>
<p>The spy is a stub with memory. You call the code, then interrogate the spy to see what happened.</p>
<p>You don’t always need to build a spy by hand. <code>mockery::mock()</code> also collects calls, so it can serve as a spy when you want the recording behaviour without writing the closure yourself:</p>
<pre>test_that(&quot;notifies the customer after successful payment (mockery spy)&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-3b&quot;,
    amount = 50,
    card_token = &quot;tok_visa&quot;,
    customer_id = &quot;cust-99&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(
    charge = function(amount, token) {
      list(success = TRUE, transaction_id = &quot;txn-xyz&quot;)
    }
  )
  spy_send &lt;- mockery::mock()

  # Act
  process_payment(
    order,
    payment_gateway = stub_gateway,
    logger = dummy_logger,
    notifier = list(send = spy_send)
  )

  # Assert
  mockery::expect_called(spy_send, 1)
  expect_equal(mockery::mock_args(spy_send)[[1]][[1]], &quot;cust-99&quot;)
  expect_equal(mockery::mock_args(spy_send)[[1]][[2]], &quot;txn-xyz&quot;)
})
Test passed with 3 successes &#x1f600;.</pre>
<p>The handwritten version is clearer when you want the recording mechanism visible to readers, useful in a codebase where not everyone knows <code>mockery</code>. <code>mockery::mock()</code> is more concise once the team is familiar with the library.</p>
<p>The difference from a mock comes down to return values. A spy records calls and nothing else. A mock records calls and can also return pre-programmed values, which makes it useful when you need the dependency to behave a specific way and want to assert on how it was used.</p>
<h2 id="4-mock">4. Mock</h2>
<p><strong><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;" /> Definition:</strong> “a double pre-programmed with expectations that form a specification of the calls it should receive. A true mock can throw if it receives a call it doesn’t expect, and is checked during verification to confirm it got all the calls it was expecting.”<sup><a href="https://jakubsobolewski.com/blog/test-doubles-taxonomy#references" rel="nofollow" target="_blank">[1, Fowler]</a></sup></p>
<p><code>mockery::mock()</code> is looser than that definition. It accepts any call without complaining and doesn’t enforce expectations upfront. It records every call it receives (the arguments, the order, the count) and returns pre-programmed values you supply. Verification is your responsibility in the Assert step.</p>
<pre>test_that(&quot;sends exactly one notification with correct arguments&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-4&quot;,
    amount = 75,
    card_token = &quot;tok_visa&quot;,
    customer_id = &quot;cust-11&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(
    charge = function(amount, token) {
      list(success = TRUE, transaction_id = &quot;txn-def&quot;)
    }
  )
  mock_notifier &lt;- list(send = mockery::mock())

  # Act
  process_payment(
    order,
    payment_gateway = stub_gateway,
    logger = dummy_logger,
    notifier = mock_notifier
  )

  # Assert
  mockery::expect_called(mock_notifier$send, 1)
  mockery::expect_args(mock_notifier$send, 1, &quot;cust-11&quot;, &quot;txn-def&quot;)
})
Test passed with 5 successes &#x1f973;.</pre>
<p>Use a mock when the <em>interaction itself</em> is what you’re testing: whether the code called the dependency in the right way, with the right arguments.</p>
<p>They’re also the easiest double to overuse. Assert on every call to every dependency and you’ve written an overspecified test, one that breaks whenever the implementation changes even when the behavior stays the same.</p>
<p>Prefer a spy when you only need to record calls. A plain list with a function that appends to a vector is often enough. Reach for a mock when you also need to control what the dependency returns. The risk is the same with any interaction-based assertion: check every call to every dependency and you end up with a test that mirrors the implementation rather than the behaviour, breaking whenever the internals change even when the outcome doesn’t.</p>
<h2 id="5-fake">5. Fake</h2>
<p><strong><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;" /> Definition:</strong> a working implementation that’s simpler than the real thing, suitable for tests but not production.</p>
<p>A fake isn’t just a pre-programmed response. It has real behavior. An in-memory database is a fake: it stores and retrieves data like the real thing, just without persistence or network overhead. It behaves correctly across multiple calls, which a stub can’t do.</p>
<pre>make_fake_payment_gateway &lt;- function() {
  transactions &lt;- list()

  list(
    charge = function(amount, token) {
      if (amount &lt;= 0) {
        return(list(success = FALSE, error = &quot;invalid amount&quot;))
      }
      if (token == &quot;tok_declined&quot;) {
        return(list(success = FALSE, error = &quot;card declined&quot;))
      }

      id &lt;- paste0(&quot;txn-&quot;, length(transactions) + 1)
      transactions[[id]] &lt;&lt;- list(
        amount = amount,
        token = token
      )
      list(success = TRUE, transaction_id = id)
    },
    find = function(transaction_id) {
      transactions[[transaction_id]]
    }
  )
}
test_that(&quot;successful charges are recorded in the gateway&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-5&quot;,
    amount = 120,
    card_token = &quot;tok_visa&quot;,
    customer_id = &quot;cust-3&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  fake_gateway &lt;- make_fake_payment_gateway()

  # Act
  txn_id &lt;- process_payment(
    order,
    payment_gateway = fake_gateway,
    logger = dummy_logger
  )

  # Assert
  recorded &lt;- fake_gateway$find(txn_id)
  expect_equal(recorded$amount, 120)
  expect_equal(recorded$token, &quot;tok_visa&quot;)
})
Test passed with 2 successes &#x1f38a;.</pre>
<p>Fakes work well when you need to test behaviour across multiple operations: place an order, query its status, refund it. A stub would need to be reprogrammed for each call. A fake just handles it.</p>
<p>They’re also a good fit for acceptance tests and manual inspection. An acceptance test exercises a full user-facing behaviour end-to-end, several layers of the application working together. At that level you don’t want stubs reprogrammed for individual calls; you want a dependency that behaves realistically across the whole flow. A fake payment gateway, a fake email sender, a fake file store: these let your acceptance test suite run in CI without connecting to external services, needing credentials, or leaving side effects behind. You can also wire the same fakes into a development mode of the app. Spin up the Shiny app pointing at the in-memory gateway and you can click through every payment scenario without touching a real API.</p>
<p>The cost is that fakes take time to build and maintain. They need to be kept in sync with the real interface they’re replacing. For a small, stable interface that’s used heavily across your test suite and in manual workflows, the investment pays off. For a dependency you only use in one unit test, a stub is simpler.</p>
<h2 id="when-to-reach-for-each-one">When to reach for each one</h2>





























































<table><thead><tr><th>Double</th><th align="center">Has behaviour</th><th align="center">Records calls</th><th align="center">Returns programmed values</th><th align="center">Rejects unexpected calls</th><th>When to use</th></tr></thead><tbody><tr><td>Dummy</td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td>Fill a required parameter you won’t touch</td></tr><tr><td>Stub</td><td align="center">Pre-programmed only</td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td>Control what the code receives</td></tr><tr><td>Spy</td><td align="center">Pre-programmed only</td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td>Assert on side effects after the fact</td></tr><tr><td>Mock (<code>mockery</code>)</td><td align="center">Pre-programmed only</td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td>Assert on calls and control what the code receives</td></tr><tr><td>Mock</td><td align="center">Pre-programmed only</td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td>Pin an exact interaction as a hard contract</td></tr><tr><td>Fake</td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td align="center"><img src="https://s.w.org/images/core/emoji/13.0.0/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td><td>Replace stateful or multi-call dependencies</td></tr></tbody></table>
<p>The key difference is between stub and mock. A stub returns values. You assert on the outcome. A mock records calls and can return pre-programmed <strong>values</strong>. Using a mock where a stub would do couples your test to implementation details. Using a stub where a mock is needed means missing the interaction you were trying to verify.</p>
<p>When in doubt: if you’re asserting on a return value or a state change, use a stub. If you’re asserting that a specific call was made, use a spy or a mock. If the dependency has real state that needs to survive across calls, build a fake.</p>
<h2 id="appendix-implementing-an-eager-mock-by-hand">Appendix: implementing an eager mock by hand</h2>
<p><em><code>mockery::mock()</code> is sufficient for everyday use. Skip this if you’re not curious about mock that throws failures during execution of code under test.</em></p>
<p>This is what a mock matching Fowler’s definition looks like in R. It takes a list of expected calls in the Arrange step, fails immediately on anything unexpected, and exposes a <code>verify()</code> function to confirm every expected call was made.</p>
<pre>make_mock_notifier &lt;- function(expected_calls) {
  received &lt;- list()

  list(
    send = function(customer_id, transaction_id) {
      call &lt;- list(
        customer_id = customer_id,
        transaction_id = transaction_id
      )
      match &lt;- any(sapply(expected_calls, identical, call))
      if (!match) {
        testthat::fail(sprintf(
          &quot;Unexpected call: send('%s', '%s')&quot;,
          customer_id,
          transaction_id
        ))
      }
      received[[length(received) + 1]] &lt;&lt;- call
    },
    verify = function() {
      for (exp in expected_calls) {
        found &lt;- any(sapply(received, identical, exp))
        if (!found) {
          testthat::fail(sprintf(
            &quot;Expected call never made: send('%s', '%s')&quot;,
            exp$customer_id, exp$transaction_id
          ))
        }
      }
      testthat::succeed()
    }
  )
}</pre>
<p>The mock rejects unexpected calls on the spot:</p>
<pre>test_that(&quot;throws immediately when called with unexpected arguments&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-4b&quot;,
    amount = 75,
    card_token = &quot;tok_visa&quot;,
    customer_id = &quot;cust-11&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(
    charge = function(amount, token) {
      list(success = TRUE, transaction_id = &quot;txn-def&quot;)
    }
  )
  mock_notifier &lt;- make_mock_notifier(
    expected_calls = list(list(
      customer_id = &quot;cust-WRONG&quot;,
      transaction_id = &quot;txn-def&quot;
    ))
  )

  # Act — throws before we even reach Assert
  process_payment(
    order,
    payment_gateway = stub_gateway,
    logger = dummy_logger,
    notifier = mock_notifier
  )
})
── Failure: throws immediately when called with unexpected arguments ───────────
Unexpected call: send('cust-11', 'txn-def')
Backtrace:
    ▆
 1. └─global process_payment(...)
 2.   └─notifier$send(order$customer_id, result$transaction_id)

Error:
! Test failed with 1 failure and 0 successes.</pre>
<p>And <code>verify()</code> catches expected calls that were never made:</p>
<pre>test_that(&quot;fails verification when an expected call was never made&quot;, {
  # Arrange
  order &lt;- list(id = &quot;ord-4c&quot;, amount = 75, card_token = &quot;tok_visa&quot;, customer_id = &quot;cust-11&quot;)
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(charge = function(amount, token) list(success = TRUE, transaction_id = &quot;txn-def&quot;))
  mock_notifier &lt;- make_mock_notifier(
    expected_calls = list(
      list(customer_id = &quot;cust-11&quot;, transaction_id = &quot;txn-def&quot;),
      list(customer_id = &quot;cust-99&quot;, transaction_id = &quot;txn-xyz&quot;)  # will never be called
    )
  )

  # Act
  process_payment(order, payment_gateway = stub_gateway, logger = dummy_logger, notifier = mock_notifier)

  # Assert
  mock_notifier$verify()
})
── Failure: fails verification when an expected call was never made ────────────
Expected call never made: send('cust-99', 'txn-xyz')

Error:
! Test failed with 1 failure and 1 success.</pre>
<p>The happy path passes both checks:</p>
<pre>test_that(&quot;passes when all expected calls are made and no unexpected ones occur&quot;, {
  # Arrange
  order &lt;- list(
    id = &quot;ord-4d&quot;,
    amount = 75,
    card_token = &quot;tok_visa&quot;,
    customer_id = &quot;cust-11&quot;
  )
  dummy_logger &lt;- list(log = function(...) invisible(NULL))
  stub_gateway &lt;- list(
    charge = function(amount, token) {
      list(success = TRUE, transaction_id = &quot;txn-def&quot;)
    }
  )
  mock_notifier &lt;- make_mock_notifier(
    expected_calls = list(list(
      customer_id = &quot;cust-11&quot;,
      transaction_id = &quot;txn-def&quot;
    ))
  )

  # Act
  process_payment(
    order,
    payment_gateway = stub_gateway,
    logger = dummy_logger,
    notifier = mock_notifier
  )

  # Assert
  mock_notifier$verify()
})
Test passed with 1 success &#x1f947;.</pre>
<h2 id="references">References</h2>
<ol>
<li>Martin Fowler — <a href="https://www.martinfowler.com/bliki/TestDouble.html" rel="nofollow" target="_blank">TestDouble</a></li>
<li>Gerard Meszaros — <a href="http://xunitpatterns.com/Test%20Double%20Patterns.html" rel="nofollow" target="_blank">Test Double Patterns</a></li>
</ol>
<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-doubles-taxonomy"> 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/test-doubles-taxonomy-for-r-dummy-stub-spy-mock-fake/">Test Doubles Taxonomy for R: Dummy, Stub, Spy, Mock, Fake</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401821</post-id>	</item>
		<item>
		<title>armadillo4r 1.0.0 is on CRAN</title>
		<link>https://www.r-bloggers.com/2026/06/armadillo4r-1-0-0-is-on-cran/</link>
		
		<dc:creator><![CDATA[https://pacha.dev/blog]]></dc:creator>
		<pubDate>Thu, 11 Jun 2026 23:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pacha.dev/blog/2026/06/12/index.html</guid>

					<description><![CDATA[<p>armadillo 1.0.0 brings enhanced sparse matrix support, reduced dependencies, and comprehensive cross-platform testing.</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/armadillo4r-1-0-0-is-on-cran/">armadillo4r 1.0.0 is on CRAN</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://pacha.dev/blog/2026/06/12/index.html"> https://pacha.dev/blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>pacha.dev/blog</title>
  
  <script>
    window.site_root = '../../..';
  </script>
  <!-- MathJax Configuration -->
  <script>
    window.MathJax = {
      tex: {
        inlineMath: [['\\(', '\\)'], ['$', '$']],
        displayMath: [['\\[', '\\]'], ['$$', '$$']],
        processEscapes: true,
        processEnvironments: true,
        packages: {'[+]': ['ams', 'newcommand', 'configmacros']}
      },
      options: {
        ignoreHtmlClass: 'tex2jax_ignore',
        processHtmlClass: 'tex2jax_process'
      }
    };
  </script>
  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
  
<!-- Smart header: libraries detected based on content -->
<!-- File: /tmp/tmp.LuxLuYPhiE/index.html -->
<script src="https://pacha.dev/blog/libs/bootstrap/bootstrap.min.js"></script>
<link href="https://pacha.dev/blog/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="https://pacha.dev/blog/libs/bootstrap/bootstrap-bb462d781dde1847d9e3ccf7736099dd.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
  
  <!-- Load custom CSS after any library CSS to ensure proper precedence -->
  <link rel="stylesheet" href="https://pacha.dev/blog/css/menu.css">
  <link rel="stylesheet" href="https://pacha.dev/blog/css/content.css">
</head>
<body>
<div class="main-container">
  <button id="menu-toggle" aria-controls="menu" aria-expanded="false" aria-label="Toggle menu">☰ Menu</button>

  <div id="menu">

<header class="site-top">
  <div class="site-controls-wrapper">
    <div class="site-controls-row bottom-row">
      <h1 style="margin-bottom: 0 !important;">Mauricio &#8220;Pachá&#8221; Vargas Sepúlveda</h1>
      <p>Blog with notes about R, Shiny, SQL, Python, Linux and C++. This blog is listed on <b>R-Bloggers</b>.</p>
    </div>
    <div class="site-controls-row top-row">
      <input type="text" id="search-box" placeholder="Search..." />
      <button id="theme-toggle" title="Toggle dark mode" aria-label="Toggle dark mode">Dark mode on</button>
      <button id="font-decrease" title="Decrease font size" aria-label="Decrease font size">A-</button>
      <button id="font-increase" title="Increase font size" aria-label="Increase font size">A+</button>
    </div>
  </div>
</header>

</br>
<center><a href="https://pacha.dev/blog/" class="home-link" rel="nofollow" target="_blank">HOME <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f3e0.png" alt="🏠" class="wp-smiley" style="height: 1em; max-height: 1em;" /></a></center>
</br>

<!-- categories are printed below this-->
 
<nav class="sidebar-nav">
<h3>Categories</h3>
<ul>
<li><a href="https://pacha.dev/blog/Armadillo/" rel="nofollow" target="_blank">Armadillo</a></li>
<li><a href="https://pacha.dev/blog/Arrow/" rel="nofollow" target="_blank">Arrow</a></li>
<li><a href="https://pacha.dev/blog/BibTeX/" rel="nofollow" target="_blank">BibTeX</a></li>
<li><a href="https://pacha.dev/blog/Blogdown/" rel="nofollow" target="_blank">Blogdown</a></li>
<li><a href="https://pacha.dev/blog/C__/" rel="nofollow" target="_blank">C++</a></li>
<li><a href="https://pacha.dev/blog/CRAN/" rel="nofollow" target="_blank">CRAN</a></li>
<li><a href="https://pacha.dev/blog/Consultancy/" rel="nofollow" target="_blank">Consultancy</a></li>
<li><a href="https://pacha.dev/blog/D3po/" rel="nofollow" target="_blank">D3po</a></li>
<li><a href="https://pacha.dev/blog/DigitalOcean/" rel="nofollow" target="_blank">DigitalOcean</a></li>
<li><a href="https://pacha.dev/blog/DuckDB/" rel="nofollow" target="_blank">DuckDB</a></li>
<li><a href="https://pacha.dev/blog/Education/" rel="nofollow" target="_blank">Education</a></li>
<li><a href="https://pacha.dev/blog/GitHub/" rel="nofollow" target="_blank">GitHub</a></li>
<li><a href="https://pacha.dev/blog/Google_Sheets/" rel="nofollow" target="_blank">Google Sheets</a></li>
<li><a href="https://pacha.dev/blog/Inkscape/" rel="nofollow" target="_blank">Inkscape</a></li>
<li><a href="https://pacha.dev/blog/International_Trade/" rel="nofollow" target="_blank">International Trade</a></li>
<li><a href="https://pacha.dev/blog/Interviews/" rel="nofollow" target="_blank">Interviews</a></li>
<li><a href="https://pacha.dev/blog/Kubernetes/" rel="nofollow" target="_blank">Kubernetes</a></li>
<li><a href="https://pacha.dev/blog/Latex/" rel="nofollow" target="_blank">Latex</a></li>
<li><a href="https://pacha.dev/blog/Linear_algebra/" rel="nofollow" target="_blank">Linear algebra</a></li>
<li><a href="https://pacha.dev/blog/Linear_models/" rel="nofollow" target="_blank">Linear models</a></li>
<li><a href="https://pacha.dev/blog/Linux/" rel="nofollow" target="_blank">Linux</a></li>
<li><a href="https://pacha.dev/blog/Manjaro/" rel="nofollow" target="_blank">Manjaro</a></li>
<li><a href="https://pacha.dev/blog/Microeconomics/" rel="nofollow" target="_blank">Microeconomics</a></li>
<li><a href="https://pacha.dev/blog/Microsoft_Excel/" rel="nofollow" target="_blank">Microsoft Excel</a></li>
<li><a href="https://pacha.dev/blog/NLP/" rel="nofollow" target="_blank">NLP</a></li>
<li><a href="https://pacha.dev/blog/Non_English_datasets/" rel="nofollow" target="_blank">Non-English datasets</a></li>
<li><a href="https://pacha.dev/blog/OS_X/" rel="nofollow" target="_blank">OS X</a></li>
<li><a href="https://pacha.dev/blog/OpenBLAS/" rel="nofollow" target="_blank">OpenBLAS</a></li>
<li><a href="https://pacha.dev/blog/Optimization/" rel="nofollow" target="_blank">Optimization</a></li>
<li><a href="https://pacha.dev/blog/PDF/" rel="nofollow" target="_blank">PDF</a></li>
<li><a href="https://pacha.dev/blog/Pelican/" rel="nofollow" target="_blank">Pelican</a></li>
<li><a href="https://pacha.dev/blog/Positron/" rel="nofollow" target="_blank">Positron</a></li>
<li><a href="https://pacha.dev/blog/PostGIS/" rel="nofollow" target="_blank">PostGIS</a></li>
<li><a href="https://pacha.dev/blog/PostgreSQL/" rel="nofollow" target="_blank">PostgreSQL</a></li>
<li><a href="https://pacha.dev/blog/Python/" rel="nofollow" target="_blank">Python</a></li>
<li><a href="https://pacha.dev/blog/Quarto/" rel="nofollow" target="_blank">Quarto</a></li>
<li><a href="https://pacha.dev/blog/R/" rel="nofollow" target="_blank">R</a></li>
<li><a href="https://pacha.dev/blog/R_Packages/" rel="nofollow" target="_blank">R Packages</a></li>
<li><a href="https://pacha.dev/blog/R_Universe/" rel="nofollow" target="_blank">R-Universe</a></li>
<li><a href="https://pacha.dev/blog/R4DS/" rel="nofollow" target="_blank">R4DS</a></li>
<li><a href="https://pacha.dev/blog/REST_API/" rel="nofollow" target="_blank">REST API</a></li>
<li><a href="https://pacha.dev/blog/RStudio/" rel="nofollow" target="_blank">RStudio</a></li>
<li><a href="https://pacha.dev/blog/RStudio_Server/" rel="nofollow" target="_blank">RStudio Server</a></li>
<li><a href="https://pacha.dev/blog/Redatam/" rel="nofollow" target="_blank">Redatam</a></li>
<li><a href="https://pacha.dev/blog/Rick_and_Morty/" rel="nofollow" target="_blank">Rick and Morty</a></li>
<li><a href="https://pacha.dev/blog/SPSS/" rel="nofollow" target="_blank">SPSS</a></li>
<li><a href="https://pacha.dev/blog/SQL/" rel="nofollow" target="_blank">SQL</a></li>
<li><a href="https://pacha.dev/blog/Selenium/" rel="nofollow" target="_blank">Selenium</a></li>
<li><a href="https://pacha.dev/blog/Shiny/" rel="nofollow" target="_blank">Shiny</a></li>
<li><a href="https://pacha.dev/blog/Spreadsheets/" rel="nofollow" target="_blank">Spreadsheets</a></li>
<li><a href="https://pacha.dev/blog/Stan/" rel="nofollow" target="_blank">Stan</a></li>
<li><a href="https://pacha.dev/blog/Stata/" rel="nofollow" target="_blank">Stata</a></li>
<li><a href="https://pacha.dev/blog/Statistics/" rel="nofollow" target="_blank">Statistics</a></li>
<li><a href="https://pacha.dev/blog/Tabler/" rel="nofollow" target="_blank">Tabler</a></li>
<li><a href="https://pacha.dev/blog/Tidyverse/" rel="nofollow" target="_blank">Tidyverse</a></li>
<li><a href="https://pacha.dev/blog/Ubuntu/" rel="nofollow" target="_blank">Ubuntu</a></li>
<li><a href="https://pacha.dev/blog/VSCode/" rel="nofollow" target="_blank">VSCode</a></li>
<li><a href="https://pacha.dev/blog/Windows/" rel="nofollow" target="_blank">Windows</a></li>
<li><a href="https://pacha.dev/blog/Zotero/" rel="nofollow" target="_blank">Zotero</a></li>
<li><a href="https://pacha.dev/blog/cpp11/" rel="nofollow" target="_blank">cpp11</a></li>
<li><a href="https://pacha.dev/blog/cpp4r/" rel="nofollow" target="_blank">cpp4r</a></li>
<li><a href="https://pacha.dev/blog/ggplot2/" rel="nofollow" target="_blank">ggplot2</a></li>
<li><a href="https://pacha.dev/blog/golem/" rel="nofollow" target="_blank">golem</a></li>
<li><a href="https://pacha.dev/blog/plotnine/" rel="nofollow" target="_blank">plotnine</a></li>
<li><a href="https://pacha.dev/blog/purrr/" rel="nofollow" target="_blank">purrr</a></li>
<li><a href="https://pacha.dev/blog/wbstats/" rel="nofollow" target="_blank">wbstats</a></li>
</ul>
</nav>
  </div>

  <div id="page">
    <div id="content">


<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title">armadillo 1.0.0 is on CRAN</h1>
  <div class="quarto-categories">
    <div class="quarto-category"><a href="https://pacha.dev//R/" rel="nofollow" target="_blank">R</a></div>
    <div class="quarto-category"><a href="https://pacha.dev//C__/" rel="nofollow" target="_blank">C++</a></div>
    <div class="quarto-category"><a href="https://pacha.dev//Armadillo/" rel="nofollow" target="_blank">Armadillo</a></div>
    <div class="quarto-category"><a href="https://pacha.dev//Linear_algebra/" rel="nofollow" target="_blank">Linear algebra</a></div>
  </div>
  </div>

<div>
  <div class="description">
    armadillo 1.0.0 brings enhanced sparse matrix support, reduced dependencies, and comprehensive cross-platform testing.
  </div>
</div>


<div class="quarto-title-meta">

    <div>
    <div class="quarto-title-meta-heading">Author</div>
    <div class="quarto-title-meta-contents">
             <p>Mauricio “Pachá” Vargas S. </p>
          </div>
  </div>
    
    <div>
    <div class="quarto-title-meta-heading">Published</div>
    <div class="quarto-title-meta-contents">
      <p class="date">June 12, 2026</p>
    </div>
  </div>
  
    
  </div>
  


</header>


<p>I’m pleased to announce that armadillo 1.0.0 is now available on CRAN. This release brings substantial improvements to performance, reduced dependencies, and widely tested cross-platform compatibility.</p>
<section id="key-improvements" class="level2">
<h2 class="anchored" data-anchor-id="key-improvements">Key Improvements</h2>
<p>The 1.0.0 release brings several major enhancements:</p>
<ul>
<li><p><em>Enhanced sparse matrix support</em>: The package now offers seamless interoperability with R’s Matrix package, providing a more robust “translation” between R and Armadillo sparse matrices.</p></li>
<li><p><em>Reduced dependencies</em>: All unit tests have been migrated from testthat to the lightweight tinytest suite, simplifying the dependency footprint.</p></li>
<li><p><em>Upgraded cpp4r dependency</em>: The underlying cpp4r library has been refined to reduce dependencies while conditionally leveraging newer C++ features where available ( C++23 on modern platforms).</p></li>
<li><p><em>Comprehensive testing</em>: The package has been validated across multiple platforms using R-Hub images for different C++ compilers and operating systems, complemented by GitHub Actions testing on macOS and Windows.</p></li>
</ul>
<p>For more information, visit the <a href="https://cran.r-project.org/package=armadillo4r" rel="nofollow" target="_blank">CRAN package page</a> or explore the <a href="https://pacha.dev/armadillo4r/" rel="nofollow" target="_blank">500+ examples</a>.</p>
<p><em>If you liked this post, please consider donating to support my Open Source work: <a href="https://buymeacoffee.com/pacha" rel="nofollow" target="_blank">https://buymeacoffee.com/pacha</a>.</em></p>
</section>

    </div>
  </div>

<footer>
  <p id="last-updated">Loading&#8230;</p>

  <div style="text-align: center;">
    <a href="https://validator.w3.org/check?uri=referer" rel="nofollow" target="_blank">
      <img loading="lazy" src="https://www.w3.org/Icons/valid-html401" alt="Valid HTML 4.01 Strict" height="31" width="88"
        style="border: 0; width:88px; height:31px; object-fit:contain;">
    </a>
  </div>
</footer>

<script src="https://pacha.dev/blog/js/footer.js"></script>
<script src="https://pacha.dev/blog/js/theme.js"></script>
<script src="https://pacha.dev/blog/js/search.js"></script>
<script src="https://pacha.dev/blog/js/fontsize.js"></script>
</div>
</div>
<script>
  (function(){
    var toggle = document.getElementById('menu-toggle');
    var menu = document.getElementById('menu');
    if(!toggle || !menu) return;
    toggle.addEventListener('click', function(){
      var isOpen = menu.classList.toggle('open');
      toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
      // preserve scroll position for menu when opening
      if(isOpen) menu.scrollTop = 0;
    });
    // close menu when clicking outside on small screens
    document.addEventListener('click', function(e){
      if(window.innerWidth > 768) return;
      if(!menu.classList.contains('open')) return;
      if(e.target === toggle || menu.contains(e.target)) return;
      menu.classList.remove('open');
      toggle.setAttribute('aria-expanded', 'false');
    });
  })();
</script>
<!-- Load shared sidebar -->
<script src="https://pacha.dev/blog/js/sidebar.js"></script>
</body>
</html>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://pacha.dev/blog/2026/06/12/index.html"> https://pacha.dev/blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/06/armadillo4r-1-0-0-is-on-cran/">armadillo4r 1.0.0 is on CRAN</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">401947</post-id>	</item>
		<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>
	</channel>
</rss>

<!-- Dynamic page generated in 1.354 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2026-06-19 10:23:14 -->

<!-- Compression = gzip -->