<?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>Sat, 22 Aug 2026 18:19:07 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.5.20</generator>

<image>
	<url>https://i0.wp.com/www.r-bloggers.com/wp-content/uploads/2016/08/cropped-R_single_01-200.png?fit=32%2C32&#038;ssl=1</url>
	<title>R-bloggers</title>
	<link>https://www.r-bloggers.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">11524731</site>	<item>
		<title>Does state life expectancy correlate with political party voting?</title>
		<link>https://www.r-bloggers.com/2026/08/does-state-life-expectancy-correlate-with-political-party-voting/</link>
		
		<dc:creator><![CDATA[Jerry Tuttle]]></dc:creator>
		<pubDate>Sat, 22 Aug 2026 18:19:07 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://www.r-bloggers.com/?guid=7e5aa54c49a9ebbb1cf1a06ef6eb3ee8</guid>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

library(ggplot2)

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

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

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

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

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

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

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

table(df$LE_RANGE)

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

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

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


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

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

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

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

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

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

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

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

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

jobs:
 deploy:
 runs-on: ubuntu-latest

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

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

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

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

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

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

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

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

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

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

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





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


</section>

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

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

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

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

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

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

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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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

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

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

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

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

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

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

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

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

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



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



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



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

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

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

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

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



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



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



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

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

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



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



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



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

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



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



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



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



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

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

rlt_codes_kept_vs_dropped</pre>



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



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



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

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



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



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



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



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



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



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



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



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

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

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

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

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

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

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





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

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

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

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

resources:
  cpus: 1
  memory_gb: 4

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


</section>

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

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

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

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

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

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


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

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

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

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

<hr />

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

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

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

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

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

<hr />

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

<p>/Henrik</p>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<pre>ODDS_API_KEY=YOUR_PRIVATE_API_KEY</pre>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  map_dfr(events, function(event) {

    map_dfr(event$bookmakers, function(bookmaker) {

      map_dfr(bookmaker$markets, function(market) {

        map_dfr(market$outcomes, function(outcome) {

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

odds_df &lt;- tidy_odds(odds_raw)

glimpse(odds_df)</pre>

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

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

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

<pre>library(lubridate)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

library(nflreadr)
library(dplyr)

pbp &lt;- load_pbp(2025)

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

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

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

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

<h3>NFL Data</h3>

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

<h3>NBA Data</h3>

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

<h3>MLB Data</h3>

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

<h3>NHL Data</h3>

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

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

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

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

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

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

<h2>Identify Potential Value Bets</h2>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<h3>Testing Too Many Strategies</h3>

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

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

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

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

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

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

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

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

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

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

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

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

<h2>Frequently Asked Questions</h2>

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

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

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

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

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

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

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

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

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

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

<h2>Conclusion</h2>

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

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

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

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



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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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

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

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

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

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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

print(metrics_summary)

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


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

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

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

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

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

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

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

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

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


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



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



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



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

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

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

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




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



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

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

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




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



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<h2>Quick Start</h2>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		<post-id xmlns="com-wordpress:feed-additions:1">403150</post-id>	</item>
		<item>
		<title>Token Maxxing</title>
		<link>https://www.r-bloggers.com/2026/08/token-maxxing/</link>
		
		<dc:creator><![CDATA[Alex Fischer]]></dc:creator>
		<pubDate>Sat, 15 Aug 2026 22:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://s3alfisc.github.io/blog/posts/token-maxxing/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Claude</p>
<p>Codex</p>
<p>Usually I don’t watch much youtube (a social phenomenon I have somewhat missed out on), but here’s an interesting video about another social phenomenon I have somewhat experienced myself .<br />
With LLM coding, I feel li...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/token-maxxing/">Token Maxxing</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://s3alfisc.github.io/blog/posts/token-maxxing/"> Alex Fischer</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 




<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i0.wp.com/s3alfisc.github.io/blog/posts/token-maxxing/claude_maxxing.png?w=578&#038;ssl=1" class="img-fluid figure-img" data-recalc-dims="1"></p>
<figcaption>Claude</figcaption>
</figure>
</div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/s3alfisc.github.io/blog/posts/token-maxxing/codex_maxxing.png?w=578&#038;ssl=1" class="img-fluid figure-img" data-recalc-dims="1"></p>
<figcaption>Codex</figcaption>
</figure>
</div>
</div>
</div>
</div>
<p>Usually I don’t watch much youtube (a social phenomenon I have somewhat missed out on), but here’s an <a href="https://www.youtube.com/watch?v=iPUn1Fnfn0k" rel="nofollow" target="_blank">interesting video</a> about another social phenomenon I have somewhat experienced myself .</p>
<p>With LLM coding, I feel like I can do anything and start working on too many things simultaneously / in multiple parallel sessions (pyfixest features and <a href="https://github.com/py-econometrics/pyfixest/pull/1379" rel="nofollow" target="_blank">refactoring</a>, <a href="https://github.com/py-econometrics/within-paper" rel="nofollow" target="_blank">a paper on our new demeaning algo</a>, <a href="https://github.com/py-econometrics/within/pull/84" rel="nofollow" target="_blank">R bindings for the new algo</a>, <a href="https://www.meetup.com/de-de/pydata-berlin/events/316084301/?eventOrigin=group_upcoming_events" rel="nofollow" target="_blank">presentations</a> on this and that, blog posts, a <a href="https://github.com/py-econometrics/pyfixest/pull/1447" rel="nofollow" target="_blank">JOSS paper draft</a>, etc etc).</p>
<p>I am paying for subscriptions with Codex, Claude, and OpenCode, and when I don’t spend all of my usage, I feel like I missed an opportunity, so sometimes I skip going to the gym to keep on working because I still have usage to spend or usage will reset in 30 minutes so I can keep on <del>working</del> prompting my agents to keep on working… This was particularly bad the weekend that Fable was temporarily available on the 20$ plan &#8211; I almost couldn’t leave my keyboard, the desire to keep on prompting was too strong. Needless to say that &#8211; because I prompted without much thinking &#8211; the results I got were not very good.</p>
<p>I usually start working on OSS after work around 21:00, and suddenly it’s 23:30 and I want to stop, but there is one last prompt to write and finally I stop and it is 00:30 but I feel no accomplishment because I haven’t been doing any deep work … but at least, I used all of my usage (#tokenmaxxing)!</p>
<p>Long story short, I think a more optimal strategy for me going forward is to reduce the number of subscriptions (fewer token limits to hit) and topics I work on in parallel and, to some degree, stop over-relying on an auto-mode-and-review workflow and switch back to a more “socratic” style of coding with LLMs.</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://s3alfisc.github.io/blog/posts/token-maxxing/"> Alex Fischer</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/token-maxxing/">Token Maxxing</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403143</post-id>	</item>
		<item>
		<title>A Tiny Iroshizuku Ink Shop in R</title>
		<link>https://www.r-bloggers.com/2026/08/a-tiny-iroshizuku-ink-shop-in-r/</link>
		
		<dc:creator><![CDATA[Chi]]></dc:creator>
		<pubDate>Sat, 15 Aug 2026 07:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://chichacha.github.io/chi-files/posts/iroshizuku/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>色彩雫<br />
I just discovered that the R script I had written while back, which was a hand-curated colour palette of Pilot Iroshizuku fountain pen inks.<br />
Pilot describes Iroshizuku as a combination of iro = colour, and shizuku = droplet. The individual ...</p></div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/a-tiny-iroshizuku-ink-shop-in-r/">A Tiny Iroshizuku Ink Shop 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://chichacha.github.io/chi-files/posts/iroshizuku/"> CHI(χ)-Files</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="色彩雫" class="level2">
<h2 class="anchored" data-anchor-id="色彩雫">色彩雫</h2>
<p>I just discovered that the R script I had written while back, which was a hand-curated colour palette of Pilot Iroshizuku fountain pen inks.</p>
<p>Pilot describes Iroshizuku as a combination of iro = colour, and shizuku = droplet. The individual inks take their names from Japanese landscapes, plants, seasons, and other bits of nature.</p>
<p>The names don’t simply tell you what colour an ink is.</p>
<ul>
<li>月夜 isn’t just dark blue. It is moonlit night.</li>
<li>花筏 isn’t just pink. It evokes cherry blossom petals floating together on water like a little raft.</li>
<li>冬将軍 &#8211; literally the Winter General &#8211; somehow becomes a cool gray.</li>
</ul>
<p>So naturally, I turned them into data!</p>
</section>
<section id="the-data" class="level2">
<h2 class="anchored" data-anchor-id="the-data">The data</h2>
<p>This is a small hand-curated dataset of 24 Iroshizuku inks. The hex colours are approximate representations rather than measurements of the physical inks. Fountain pen ink is much more complicated than a single hex value: paper, nib width, saturation, shading, sheen, and lighting all change how an ink appears.</p>
<p>The ink names and descriptions are based on Pilot’s <a href="https://www.pilotpen.eu/our-universes/fine-writing/iroshizuku-inks/" rel="nofollow" target="_blank">Iroshizuku collection</a>. The hex values are approximate colours used for this visualization rather than official measured colour values.</p>
<p>But hex is enough for a little plotting experiment!</p>
</section>
<section id="first-just-the-colours" class="level2">
<h2 class="anchored" data-anchor-id="first-just-the-colours">First: just the colours</h2>
<p>Before making bottles, it is useful to see the palette itself.</p>
<div class="cell">
<div class="cell-output-display">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i1.wp.com/chichacha.github.io/chi-files/posts/iroshizuku/index_files/figure-html/simple-palette-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
<figcaption>The Iroshizuku palette in its original order.</figcaption>
</figure>
</div>
</div>
</div>
<p>This already makes a nice colour chart, but the ordering feels fairly arbitrary. So I wanted to see what would happen if the inks were arranged by colour rather than by catalogue order.</p>
</section>
<section id="sorting-colours-perceptually" class="level2">
<h2 class="anchored" data-anchor-id="sorting-colours-perceptually">Sorting colours perceptually</h2>
<p>RGB is useful for screens, but it isn’t especially good at representing how humans perceive differences between colours. For sorting this palette, I converted the hex values into <strong>HCL colour space</strong> using the <code>colorspace</code> package.</p>
<p>HCL separates colour into:</p>
<ul>
<li><strong>Hue</strong> — roughly which colour family it belongs to</li>
<li><strong>Chroma</strong> — how colourful or saturated it feels</li>
<li><strong>Luminance</strong> — how light or dark it appears</li>
</ul>
<p>That makes it much nicer for arranging colours in a visually coherent sequence.</p>
<div class="cell">
<details class="code-fold">
<summary>Code</summary>
<pre>hcl_coords &lt;- coords(
  as(hex2RGB(iroshizuku_colors$hex), &quot;polarLUV&quot;)
) |&gt;
  as_tibble()

ink_plot &lt;- iroshizuku_colors |&gt;
  bind_cols(hcl_coords) |&gt;
  mutate(
    # Rotate the hue wheel so the sequence starts near blue / teal.
    hue_sort = (H - 220) %% 360
  ) |&gt;
  arrange(hue_sort, desc(L), desc(C)) |&gt;
  mutate(
    plot_order = row_number() - 1,
    col = plot_order %% 6,
    row_raw = plot_order %/% 6,
    row = max(row_raw) - row_raw
  )</pre>
</details>
</div>
<p>The exact order is not scientifically important. I rotated the hue wheel so that the display begins around the blue-green part of the palette, simply because it produces a calmer visual flow for this particular set of inks.</p>
</section>
<section id="the-tiny-ink-shop" class="level2">
<h2 class="anchored" data-anchor-id="the-tiny-ink-shop">The tiny ink shop</h2>
<p>I wanted the palette to look like a tiny Japanese stationery-shop display: rows of ink bottles, wooden shelves, paper labels, and little price cards.</p>
<p>The whole thing is still just <code>ggplot2</code>.</p>
<div class="cell">
<div class="cell-output-display">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/chichacha.github.io/chi-files/posts/iroshizuku/index_files/figure-html/ink-shop-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
<figcaption>A tiny Iroshizuku ink shop, arranged in perceptual colour order.</figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="the-palette-as-data" class="level2">
<h2 class="anchored" data-anchor-id="the-palette-as-data">The palette as data</h2>
<p>And because this is still a data project, here is the resulting palette in the same perceptual order.</p>
<div class="cell">
<div class="cell-output-display">
<div class="table-responsive">
<table class="table table-striped table-hover table-condensed caption-top table-sm small" data-quarto-postprocess="true">
<thead>
<tr class="header">
<th style="text-align: center;" data-quarto-table-cell-role="th">日本語</th>
<th style="text-align: left;" data-quarto-table-cell-role="th">Ink</th>
<th style="text-align: left;" data-quarto-table-cell-role="th">Meaning</th>
<th style="text-align: right;" data-quarto-table-cell-role="th">Hex</th>
<th style="text-align: right;" data-quarto-table-cell-role="th">Hue °</th>
<th style="text-align: right;" data-quarto-table-cell-role="th">Chroma</th>
<th style="text-align: center;" data-quarto-table-cell-role="th">Lightness</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">月夜</td>
<td style="text-align: left; width: 8em;">Tsukiyo</td>
<td style="text-align: left; width: 15em;">Moonlit Night</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(1, 109, 140, 255) !important;">#016D8C</span></td>
<td style="text-align: right;">228</td>
<td style="text-align: right;">44.5</td>
<td style="text-align: center;">42.5</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">立夏</td>
<td style="text-align: left; width: 8em;">Rikka</td>
<td style="text-align: left; width: 15em;">Early Summer</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(26, 125, 165, 255) !important;">#1A7DA5</span></td>
<td style="text-align: right;">233</td>
<td style="text-align: right;">52.4</td>
<td style="text-align: center;">49.0</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">冬将軍</td>
<td style="text-align: left; width: 8em;">Fuyusyogun</td>
<td style="text-align: left; width: 15em;">Winter Commander</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(106, 134, 154, 255) !important;">#6A869A</span></td>
<td style="text-align: right;">233</td>
<td style="text-align: right;">24.5</td>
<td style="text-align: center;">54.5</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">天色</td>
<td style="text-align: left; width: 8em;">Amairo</td>
<td style="text-align: left; width: 15em;">Sky Blue</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(0, 160, 223, 255) !important;">#00A0DF</span></td>
<td style="text-align: right;">237</td>
<td style="text-align: right;">76.5</td>
<td style="text-align: center;">62.1</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">紺碧</td>
<td style="text-align: left; width: 8em;">Konpeki</td>
<td style="text-align: left; width: 15em;">Deep Cerulean Blue</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(3, 104, 180, 255) !important;">#0368B4</span></td>
<td style="text-align: right;">250</td>
<td style="text-align: right;">74.5</td>
<td style="text-align: center;">43.1</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">深海</td>
<td style="text-align: left; width: 8em;">Shinkai</td>
<td style="text-align: left; width: 15em;">Deep Sea</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(28, 58, 101, 255) !important;">#1C3A65</span></td>
<td style="text-align: right;">253</td>
<td style="text-align: right;">36.9</td>
<td style="text-align: center;">24.4</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">紫陽花</td>
<td style="text-align: left; width: 8em;">Ajisai</td>
<td style="text-align: left; width: 15em;">Hydrangea</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(18, 85, 162, 255) !important;">#1255A2</span></td>
<td style="text-align: right;">254</td>
<td style="text-align: right;">70.0</td>
<td style="text-align: center;">36.4</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">朝顔</td>
<td style="text-align: left; width: 8em;">Asagao</td>
<td style="text-align: left; width: 15em;">Morning Glory</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(4, 49, 142, 255) !important;">#04318E</span></td>
<td style="text-align: right;">261</td>
<td style="text-align: right;">67.8</td>
<td style="text-align: center;">24.2</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">紫式部</td>
<td style="text-align: left; width: 8em;">Murasakishikibu</td>
<td style="text-align: left; width: 15em;">Murasaki Shikibu</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(118, 95, 168, 255) !important;">#765FA8</span></td>
<td style="text-align: right;">276</td>
<td style="text-align: right;">56.5</td>
<td style="text-align: center;">45.4</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">竹炭</td>
<td style="text-align: left; width: 8em;">Takesumi</td>
<td style="text-align: left; width: 15em;">Bamboo Charcoal</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(30, 29, 30, 255) !important;">#1E1D1E</span></td>
<td style="text-align: right;">308</td>
<td style="text-align: right;">0.6</td>
<td style="text-align: center;">10.9</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">山葡萄</td>
<td style="text-align: left; width: 8em;">Yamabudo</td>
<td style="text-align: left; width: 15em;">Wild Grape Vine</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(102, 13, 91, 255) !important;">#660D5B</span></td>
<td style="text-align: right;">317</td>
<td style="text-align: right;">47.2</td>
<td style="text-align: center;">23.2</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">花筏</td>
<td style="text-align: left; width: 8em;">Hanaikada</td>
<td style="text-align: left; width: 15em;">Floating Cherry Blossoms</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(34, 34, 34, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(237, 126, 147, 255) !important;">#ED7E93</span></td>
<td style="text-align: right;">2</td>
<td style="text-align: right;">74.6</td>
<td style="text-align: center;">65.8</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">紅葉</td>
<td style="text-align: left; width: 8em;">Momiji</td>
<td style="text-align: left; width: 15em;">Autumn Maple Leaves</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(225, 46, 44, 255) !important;">#E12E2C</span></td>
<td style="text-align: right;">12</td>
<td style="text-align: right;">140.0</td>
<td style="text-align: center;">49.7</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">春暁</td>
<td style="text-align: left; width: 8em;">Syungyo</td>
<td style="text-align: left; width: 15em;">Spring Dawn</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(103, 79, 77, 255) !important;">#674F4D</span></td>
<td style="text-align: right;">17</td>
<td style="text-align: right;">15.5</td>
<td style="text-align: center;">36.0</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">冬柿</td>
<td style="text-align: left; width: 8em;">Fuyugaki</td>
<td style="text-align: left; width: 15em;">Winter Persimmon</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(234, 90, 16, 255) !important;">#EA5A10</span></td>
<td style="text-align: right;">22</td>
<td style="text-align: right;">128.0</td>
<td style="text-align: center;">56.9</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">夕焼け</td>
<td style="text-align: left; width: 8em;">Yuyake</td>
<td style="text-align: left; width: 15em;">Sunset Glow</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(34, 34, 34, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(239, 136, 31, 255) !important;">#EF881F</span></td>
<td style="text-align: right;">35</td>
<td style="text-align: right;">104.2</td>
<td style="text-align: center;">66.6</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">山栗</td>
<td style="text-align: left; width: 8em;">Yamaguri</td>
<td style="text-align: left; width: 15em;">Wild Chestnut</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(91, 69, 50, 255) !important;">#5B4532</span></td>
<td style="text-align: right;">45</td>
<td style="text-align: right;">21.2</td>
<td style="text-align: center;">31.1</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">灯籠</td>
<td style="text-align: left; width: 8em;">Toro</td>
<td style="text-align: left; width: 15em;">Lantern Light</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(34, 34, 34, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(240, 176, 24, 255) !important;">#F0B018</span></td>
<td style="text-align: right;">55</td>
<td style="text-align: right;">92.7</td>
<td style="text-align: center;">75.9</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">蛍火</td>
<td style="text-align: left; width: 8em;">Hotarubi</td>
<td style="text-align: left; width: 15em;">Firefly Glow</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(34, 34, 34, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(217, 218, 38, 255) !important;">#D9DA26</span></td>
<td style="text-align: right;">86</td>
<td style="text-align: right;">89.9</td>
<td style="text-align: center;">84.5</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">竹林</td>
<td style="text-align: left; width: 8em;">Chikurin</td>
<td style="text-align: left; width: 15em;">Bamboo Forest</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(34, 34, 34, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(148, 189, 78, 255) !important;">#94BD4E</span></td>
<td style="text-align: right;">107</td>
<td style="text-align: right;">68.9</td>
<td style="text-align: center;">71.7</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">深緑</td>
<td style="text-align: left; width: 8em;">Shinryoku</td>
<td style="text-align: left; width: 15em;">Forest Green</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(0, 126, 79, 255) !important;">#007E4F</span></td>
<td style="text-align: right;">145</td>
<td style="text-align: right;">48.9</td>
<td style="text-align: center;">46.3</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">松露</td>
<td style="text-align: left; width: 8em;">Syoro</td>
<td style="text-align: left; width: 15em;">Dew on Pine Tree</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(7, 125, 94, 255) !important;">#077D5E</span></td>
<td style="text-align: right;">156</td>
<td style="text-align: right;">41.9</td>
<td style="text-align: center;">46.3</td>
</tr>
<tr class="odd">
<td style="text-align: center; width: 6em; font-weight: bold;">翠玉</td>
<td style="text-align: left; width: 8em;">Suigyoku</td>
<td style="text-align: left; width: 15em;">Emerald</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(3, 114, 97, 255) !important;">#037261</span></td>
<td style="text-align: right;">169</td>
<td style="text-align: right;">35.1</td>
<td style="text-align: center;">42.6</td>
</tr>
<tr class="even">
<td style="text-align: center; width: 6em; font-weight: bold;">孔雀</td>
<td style="text-align: left; width: 8em;">Kujaku</td>
<td style="text-align: left; width: 15em;">Peacock</td>
<td style="text-align: right; width: 5em;"><span style=" font-weight: bold;    color: rgba(255, 255, 255, 255) !important;border-radius: 4px; padding-right: 4px; padding-left: 4px; background-color: rgba(2, 137, 134, 255) !important;">#028986</span></td>
<td style="text-align: right;">189</td>
<td style="text-align: right;">40.4</td>
<td style="text-align: center;">51.4</td>
</tr>
</tbody>
</table>
</div>


</div>
</div>
<p>I started with an old R script containing 24 fountain pen colours.</p>
<p>Somewhere along the way I learned a little more about perceptual colour spaces and built a tiny imaginary Japanese ink shop out of geom_rect().</p>
<p>Now I just want more ink. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f58b.png" alt="🖋" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>


<!-- -->

</section>

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

		<post-id xmlns="com-wordpress:feed-additions:1">403141</post-id>	</item>
		<item>
		<title>[R] Environment Variables in R Shiny-Server Container: Problem and Solutions</title>
		<link>https://www.r-bloggers.com/2026/08/r-environment-variables-in-r-shiny-server-container-problem-and-solutions/</link>
		
		<dc:creator><![CDATA[R on Zhenguo Zhang&#039;s Blog]]></dc:creator>
		<pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/</guid>

					<description><![CDATA[<p>Zhenguo Zhang's Blog https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/ -When dockerizing an R Shiny application hosted via Shiny Server (built from shiny server docker image https://hub.docker.com/r/rocker/shiny), a common issue developers face is that environment variables set via ENV instructions in the Dockerfile (or passed at ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/r-environment-variables-in-r-shiny-server-container-problem-and-solutions/">[R] Environment Variables in R Shiny-Server Container: Problem and Solutions</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/"> R on Zhenguo Zhang&#039;s Blog</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
Zhenguo Zhang&#8217;s Blog https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/ &#8211;<p>When dockerizing an R Shiny application hosted via <strong>Shiny Server</strong> (built from shiny server docker image <a href="https://hub.docker.com/r/rocker/shiny)" rel="nofollow" target="_blank">https://hub.docker.com/r/rocker/shiny)</a>, a common issue developers face is that environment variables set via <code>ENV</code> instructions in the <code>Dockerfile</code> (or passed at runtime via <code>docker run -e</code>) are completely missing inside the R Shiny app.</p>
<p>For example, if you set the following environment variable in your <code>Dockerfile</code>:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
<table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre>1
</pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre>ENV SHOULD_IN_SHINY=&quot;From dockerfile&quot;
</pre></td></tr></table>
</div>
</div><p>When your app launches and runs <code>Sys.getenv(&quot;SHOULD_IN_SHINY&quot;)</code>, it returns an empty string <code>&quot;&quot;</code> instead of <code>&quot;From dockerfile&quot;</code>.
This is not a problem with Docker itself, but rather a consequence of how <strong>Shiny Server</strong> spawns R worker processes.</p>
<p>In this post, we will look into the root cause behind this behavior in Shiny Server and demonstrate the two recommended solutions to correctly expose environment variables to your R Shiny workers.</p>
<hr>
<h2 id="the-root-cause-how-shiny-server-spawns-r-processes">The Root Cause: How Shiny Server Spawns R Processes</h2>
<p>The reason environment variables do not carry over to your R session lies in how Shiny Server executes R worker processes inside the container.</p>
<p>Shiny Server runs as a system service (typically as <code>root</code> or <code>shiny</code>). When launching an app instance, it re-executes R as the <code>shiny</code> unprivileged user using <code>su</code>. Specifically, the execution call combines two mutually exclusive flags:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
<table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre>1
</pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre>su shiny --login --preserve-environment -c &quot;... R ...&quot;
</pre></td></tr></table>
</div>
</div><p>Let’s break down what these flags request from <code>su</code>:</p>
<ol>
<li><strong><code>--login</code> (<code>-l</code>)</strong>: Starts a <strong>login shell</strong>. This intentionally <strong>resets</strong> the environment to a minimal whitelist (<code>HOME</code>, <code>PATH</code>, <code>USER</code>, <code>TERM</code>, etc.) and sources system startup profiles (<code>/etc/profile</code>, <code>~/.profile</code>).</li>
<li><strong><code>--preserve-environment</code> (<code>-p</code>)</strong>: Explicitly asks <code>su</code> to <strong>keep</strong> the current environment inherited from the caller (which includes Docker’s <code>ENV</code> variables).</li>
</ol>
<p>Because Linux’s <code>su</code> utility cannot honor both conflicting behaviors, it chooses <code>--login</code> and prints a warning:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
<table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre>1
</pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre>su: ignoring --preserve-environment, it&#39;s mutually exclusive with --login
</pre></td></tr></table>
</div>
</div><blockquote>
<p><strong>Note:</strong> This warning is emitted by Shiny Server’s underlying process execution call, not by the R app itself. Attempting to suppress or patch it requires modifying and rebuilding Shiny Server C++ code, which is rarely practical.</p>
</blockquote>
<p>Because the <strong>login shell wins</strong>, all custom environment variables passed to the container via <code>ENV</code> or <code>docker run</code> are <strong>wiped before R ever starts</strong>.</p>
<hr>
<h2 id="solutions">Solutions</h2>
<p>Since patching Shiny Server is unnecessary, we can utilize the natural extension points provided by the login shell or R itself.</p>
<h3 id="solution-1-use-profile-recommended-for-user-level-shell-vars">Solution 1: Use <code>~/.profile</code> (Recommended for User-Level Shell Vars)</h3>
<p>Since <code>--login</code> causes the shell to source <code>~/.profile</code> for the <code>shiny</code> user, we can write our environment variables to <code>/home/shiny/.profile</code> during the Docker build phase.</p>
<p>In your <code>Dockerfile</code>:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
<table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre>1
2
</pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre># Append environment variable to the shiny user&#39;s profile
RUN echo &#39;export SHOULD_IN_SHINY=&quot;From dockerfile&quot;&#39; &gt;&gt; /home/shiny/.profile
</pre></td></tr></table>
</div>
</div><p>When Shiny Server executes <code>su shiny --login ...</code>, the login shell will read <code>/home/shiny/.profile</code> and load <code>SHOULD_IN_SHINY</code> into the environment right before starting the R process.</p>
<hr>
<h3 id="solution-2-use-renviron-recommended-for-r-specific-configs">Solution 2: Use <code>~/.Renviron</code> (Recommended for R-Specific Configs)</h3>
<p>Alternatively, R automatically inspects and loads <code>~/.Renviron</code> on startup, right after the shell environment is initialized.</p>
<p><code>~/.Renviron</code> is purpose-built for R:</p>
<ul>
<li>Scoped strictly to R processes.</li>
<li>Does not use shell <code>export</code> keywords or <code>$</code> variable expansions.</li>
<li>Uses simple <code>KEY=value</code> key-value pairs.</li>
</ul>
<p>In your <code>Dockerfile</code>, set up <code>/home/shiny/.Renviron</code>:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
<table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre>1
2
3
</pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre># Set up .Renviron for the shiny user
RUN echo &#39;SHOULD_IN_SHINY=&quot;From dockerfile&quot;&#39; &gt;&gt; /home/shiny/.Renviron \
    && chown shiny:shiny /home/shiny/.Renviron
</pre></td></tr></table>
</div>
</div><p>In your Shiny app (<code>app.R</code> or <code>server.R</code>), you can access it reliably:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
<table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre>1
2
</pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre>should_in_shiny &lt;- Sys.getenv(&quot;SHOULD_IN_SHINY&quot;)
# Returns: &quot;From dockerfile&quot;
</pre></td></tr></table>
</div>
</div><hr>
<h2 id="summary">Summary</h2>
<ul>
<li><strong>The Problem</strong>: Shiny Server calls <code>su shiny --login --preserve-environment</code>, causing <code>su</code> to discard inherited environment variables (such as Docker <code>ENV</code>) in favor of a clean login shell.</li>
<li><strong>Solution 1 (<code>~/.profile</code>)</strong>: Append <code>export KEY=&quot;value&quot;</code> to <code>/home/shiny/.profile</code> in your <code>Dockerfile</code>.</li>
<li><strong>Solution 2 (<code>~/.Renviron</code>)</strong>: Append <code>KEY=&quot;value&quot;</code> to <code>/home/shiny/.Renviron</code> in your <code>Dockerfile</code>.</li>
</ul>
<p>Both approaches integrate seamlessly with Docker builds and ensure your Shiny application receives all required configuration variables cleanly.</p>
- https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/ - 
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/"> R on Zhenguo Zhang&#039;s Blog</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/r-environment-variables-in-r-shiny-server-container-problem-and-solutions/">[R] Environment Variables in R Shiny-Server Container: Problem and Solutions</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403139</post-id>	</item>
		<item>
		<title>&#8216;Zero-Shot Probabilistic Stock Returns Forecasting with Pretrained RVFL Networks&#8217; accepted at COPA 2026 (and to appear in the Proceedings of Machine Learning Research)</title>
		<link>https://www.r-bloggers.com/2026/08/zero-shot-probabilistic-stock-returns-forecasting-with-pretrained-rvfl-networks-accepted-at-copa-2026-and-to-appear-in-the-proceedings-of-machine-learning-research/</link>
		
		<dc:creator><![CDATA[T. Moudiki]]></dc:creator>
		<pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://thierrymoudiki.github.io//blog/2026/08/15/r/metalearned-ridge2f</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> Link to the paper and the code repository.</div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/zero-shot-probabilistic-stock-returns-forecasting-with-pretrained-rvfl-networks-accepted-at-copa-2026-and-to-appear-in-the-proceedings-of-machine-learning-research/">‘Zero-Shot Probabilistic Stock Returns Forecasting with Pretrained RVFL Networks’ accepted at COPA 2026 (and to appear in the Proceedings of Machine Learning Research)</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://thierrymoudiki.github.io//blog/2026/08/15/r/metalearned-ridge2f"> T. Moudiki's Webpage - R</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p>As <a href="https://thierrymoudiki.github.io/blog/2025/09/04/r/python/copa-conf-2025" rel="nofollow" target="_blank">for last year</a>, I’ve got a poster accepted at <a href="https://copa-conference.com/" rel="nofollow" target="_blank">COPA conference</a>.</p>

<p>This year, the poster is about <em>‘Zero-Shot Probabilistic Stock Returns Forecasting with Pretrained RVFL Networks’</em>.</p>

<p>Links to the paper and the code repository are below:</p>

<ul>
  <li><strong>Paper</strong>: <a href="https://www.researchgate.net/publication/412115839_Zero-Shot_Probabilistic_Stock_Returns_Forecasting_with_Pretrained_RVFL_Networks" rel="nofollow" target="_blank">https://www.researchgate.net/publication/412115839_Zero-Shot_Probabilistic_Stock_Returns_Forecasting_with_Pretrained_RVFL_Networks</a></li>
  <li><strong>R Code</strong>: <a href="https://github.com/thierrymoudiki/2026_05_28_Pretrain_Ridge2_Stocks_Full_Pipeline" rel="nofollow" target="_blank">https://github.com/thierrymoudiki/2026_05_28_Pretrain_Ridge2_Stocks_Full_Pipeline</a></li>
</ul>

<p><img src="https://i1.wp.com/thierrymoudiki.github.io/images/2026-08-15/2026-08-15-image1.png?w=578&#038;ssl=1" alt="xxx" class="img-responsive" data-recalc-dims="1" /></p>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://thierrymoudiki.github.io//blog/2026/08/15/r/metalearned-ridge2f"> T. Moudiki's Webpage - R</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/zero-shot-probabilistic-stock-returns-forecasting-with-pretrained-rvfl-networks-accepted-at-copa-2026-and-to-appear-in-the-proceedings-of-machine-learning-research/">‘Zero-Shot Probabilistic Stock Returns Forecasting with Pretrained RVFL Networks’ accepted at COPA 2026 (and to appear in the Proceedings of Machine Learning Research)</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403129</post-id>	</item>
		<item>
		<title>Round-robin (with Claude)</title>
		<link>https://www.r-bloggers.com/2026/08/round-robin-with-claude/</link>
		
		<dc:creator><![CDATA[xi'an]]></dc:creator>
		<pubDate>Fri, 14 Aug 2026 22:26:55 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">http://xianblog.wordpress.com/?p=63619</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> A few days ago I had a coffee in Paris with my long-time friend (and former Statistics &#038; Computing editor) Gilles Celeux, and he mentioned me stopping solving and posting maths puzzles like those weekly published by Le Monde. They have indeed vanished with the retirement of the authors, but Gilles ...</div>
<div style = "width: 40%; display: inline-block; float:right;"></div>
<div style="clear: both;"></div>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/round-robin-with-claude/">Round-robin (with Claude)</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://xianblog.wordpress.com/2026/08/15/round-robin-with-claude/"> R – Xi&#039;an&#039;s Og</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"><strong><img loading="lazy" data-attachment-id="12990" data-permalink="https://xianblog.wordpress.com/2011/10/23/on-the-way-to-work/dscn0602/" data-orig-file="https://xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg" data-orig-size="2592,1483" data-comments-opened="1" data-image-meta="{"aperture":"4.2","credit":"","camera":"COOLPIX S3100","caption":"","created_timestamp":"1319173645","copyright":"","focal_length":"7.7","iso":"80","shutter_speed":"0.002","title":""}" data-image-title="Paris and la Seine, from Pont du Garigliano, Oct. 20, 2011" data-image-description="" data-image-caption="" data-large-file="https://i1.wp.com/xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?resize=450%2C257&#038;ssl=1" class="aligncenter size-large wp-image-12990" src="https://i1.wp.com/xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?resize=450%2C257&#038;ssl=1" alt="" width="450" height="257" srcset_temp="https://i1.wp.com/xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?resize=450%2C257&#038;ssl=1 450w, https://xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?w=900 900w, https://xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?w=128 128w, https://xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?w=300 300w, https://xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?w=768 768w" sizes="(max-width: 450px) 100vw, 450px" data-recalc-dims="1" />A</strong> few days ago I had a coffee in Paris with my long-time friend (and former <a href="https://xianblog.wordpress.com/2026/07/10/resigning-from-the-editorial-board-of-statistics-computing-reposted/" rel="nofollow" target="_blank">Statistics &#038; Computing</a> editor) Gilles Celeux, and he mentioned me stopping solving and <a href="https://xianblog.wordpress.com/?s=monde+puzzle" rel="nofollow" target="_blank">posting</a> maths puzzles like those <a href="https://xianblog.wordpress.com/?s=monde+puzzle" rel="nofollow" target="_blank">weekly published by Le Monde</a>. They have indeed vanished with the retirement of the authors, but Gilles added that the arrival of LLMs would have made the exercise moot. I disagreed as (i) the fun of solving the puzzle on my own  has not gone away and (ii) the pedagogical appeal of the puzzle and its resolution remains. As <a href="https://thefiddler.substack.com/p/how-lucky-can-a-baseball-team-get" rel="nofollow" target="_blank">the next Fiddler puzzle</a> arrived in my mailbox, my resolution was put to the test (contrariwise to the <a href="https://xianblog.wordpress.com/2026/07/22/broken-random-generators/" rel="nofollow" target="_blank">previous entry</a>, which did not require massive computations):</p>
<blockquote>
<p style="text-align: justify"><span style="color: #ff6600"><em>The Fiddler League consists of two teams. Over a season, they play each other 162 times. Each team has an equal chance of winning each game, and the results of games are independent. Over the season, on average, how many games would you expect the team with the better record to have won?</em></span></p>
</blockquote>
<p style="text-align: justify">I started on the wrong foot with <strong>E</strong>[X|X≥81] when X is Bin(162,½), equal to 85.77 (either directly or with a Normal approximation), which differs from my second thought, <strong>E</strong>[max(X,162-X)]=86.07 (either directly or with a Normal approximation), which is larger because of the reflection produced by max. While the first computation was manageable, the second one seemed to involve simulation and I caved in prompting Claude, which provided the answer along with the connection</p>
<p style="text-align: center"><strong>E</strong>[max(X,162−X)]=<strong>E</strong>[X∣X≥81](1+p<sub>81</sub>​)−81p<sub>81</sub>​</p>
<blockquote>
<p style="text-align: justify"><span style="color: #ff6600"><em>After some expansion, the League boasts 30 teams. Over a season, each team plays each other team five times. (Each team plays a total of 145 games.) Again, each team has an equal chance of winning each game, and the results of games are independent. Over the season, on average, how many games would you expect the team with the best record to have won?</em></span></p>
</blockquote>
<p style="text-align: justify">The best record is max(X<sub>i</sub>) with each of the 30 X<sub>i</sub>‘s a sum of 29 Y<sub>ij</sub> and the Y<sub>ij</sub>=5-Y<sub>ji</sub> distributed as Bin(5,½). The X<sub>i</sub>‘s are thus Bin (145,½) but dependent. While I could not figure out a closed form answer for the expectation, a direct Monte Carlo resolution is obviously feasible, with Claude (rather than me) running it over 400 million repetitions, but a 30 dimensional Normal approximation exploiting the correlation of 1/29 between the components leads to roughly 85 as the expected value. (Again computed by a Claudicant simulation.)</p>
<p style="text-align: justify">While the conclusion that the Normal approximation is pretty accurate with so many terms in the Binomial variates is quite unsurprising, Claude saves me coding time without ruining the puzzle altogether. (And Gemini made me aware that the name of the café where Gilles and I regularly meet, <em>L’Écir</em>, is an Auvergne noun for a local, dangerous, mountain blizzard! Thus linking the place to the foundation of the café by Auvergne expatriates…)</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://xianblog.wordpress.com/2026/08/15/round-robin-with-claude/"> R – Xi&#039;an&#039;s Og</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/round-robin-with-claude/">Round-robin (with Claude)</a>]]></content:encoded>
					
		
		<enclosure url="https://0.gravatar.com/avatar/3bddf040412784bc8ff54f0b6353b2c283c3eb7e11daccf2b3bfa95b469e4029?s=96&#038;d=https://s0.wp.com/i/mu.gif&#038;r=G" length="0" type="" />
<enclosure url="https://xianblog.wordpress.com/wp-content/uploads/2011/10/dscn0602-e1319302843575.jpg?w=450" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403122</post-id>	</item>
		<item>
		<title>Reading notes on Code That Fits in Your Head by Mark Seemann</title>
		<link>https://www.r-bloggers.com/2026/08/reading-notes-on-code-that-fits-in-your-head-by-mark-seemann/</link>
		
		<dc:creator><![CDATA[Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website]]></dc:creator>
		<pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://masalmon.eu/2026/08/14/code-fits-head-mark-seemann-reading-notes/</guid>

					<description><![CDATA[<p>Last month, Vicki Boykis recommended the book “Code that Fits in Your Head” by Mark Seemann.<br />
I was intrigued, especially by her takeaway that “Writing good software should be a slow and deliberate craft”.<br />
The book reminded of Th...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/reading-notes-on-code-that-fits-in-your-head-by-mark-seemann/">Reading notes on Code That Fits in Your Head by Mark Seemann</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="https://masalmon.eu/2026/08/14/code-fits-head-mark-seemann-reading-notes/"> Maëlle&#039;s R blog on Maëlle Salmon&#039;s personal website</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p>Last month, Vicki Boykis <a href="https://bsky.app/profile/vickiboykis.com/post/3mpk5n2velk2i" rel="nofollow" target="_blank">recommended</a> the book “Code that Fits in Your Head” by Mark Seemann.
I was intrigued, especially by her takeaway that “Writing good software should be a slow and deliberate craft”.</p>
<p>The book reminded of <a href="https://masalmon.eu/2023/12/11/reading-notes-pragmatic-programmer/" rel="nofollow" target="_blank">The Pragmatic Programmer</a> in the breadth of topics it covers.
However, I much preferred its tone.</p>
<h2 id="sustainability">Sustainability</h2>
<p>Throughout the book, the author makes it clear that he believes one should pay attention to the code one writes, to its internal quality.
The following two sentences even got their little frame:</p>
<blockquote>
<p>“The goal is not to write code fast. The goal is sustainable software.”</p>
</blockquote>
<h2 id="arrange-act-assert">Arrange Act Assert</h2>
<p>What  useful phrase!</p>
<p>You know how in a test you can have something like:</p>
<pre>test_that(&quot;Exception Bla handled&quot;, {
  withr::local_option(&quot;bla&quot; = 1)
  test_thing &lt;- 23

  x &lt;- my_function(test_thing)

  expect_equal(x, 2)
})
</pre><p>The first two lines within the <code>test_that()</code> call prepare what’s needed. That’s the <em>arrange</em> phrase.</p>
<p>The line calling <code>my_function()</code> does the thing I want to test. That’s the <em>act</em> phrase.</p>
<p>The line calling <code>expect_equal()</code> checks that thing. That’s the <em>assert</em> phrase.</p>
<p>Arrange, act, assert!
The book even mentions you can separate the three phases with empty lines.</p>
<h2 id="red-green-refactor">Red Green Refactor</h2>
<p>Another neat phrase!
It describes how you could iteratively work on a part of your software.</p>
<ul>
<li>Red: you write a failing test for e.g. a feature.</li>
<li>Green: you write the code to make the test pass.</li>
<li>Refactor: you improve that code (but don’t make the test fail again!).</li>
</ul>
<p>I appreciated how the author wrote that even the red phase isn’t easy, because you can get a passing test that you thought would fail.
I think the point is made again elsewhere in the book: you can imagine a test will pass or fail under certain conditions, but better to actually prove it by <a href="https://masalmon.eu/2024/08/29/cherrypick-test/" rel="nofollow" target="_blank">running the test</a>!</p>
<h2 id="the-devils-advocate">The Devil’s Advocate</h2>
<p>A technique presented in the book is The Devil’s Advocate, in which you write wrong code on purpose, to see whether your tests will detect it.
I suppose it’s a less random (but more difficult?) version of mutation testing, in which you evaluate how often your test suite detects mutants of your code.
In R, mutation testing is provided by e.g. the <a href="https://prl-prg.github.io/mutator/" rel="nofollow" target="_blank">mutator package</a>.</p>
<h2 id="code-reviews-that-do-not-block">Code reviews that do not block</h2>
<p>There’s a short discussion of when to do code reviews (regularly) so as to not block your team mates.
I enjoyed seeing this, as it’s important to not always be a rate-limiting factor.
It shows the book’s advice is pragmatic.</p>
<h2 id="bundle-smaller-breaking-changes">Bundle smaller breaking changes</h2>
<p>In the chapter “Augmenting code” that’s about working with existing code, the author – among other things – discusses whether to bundle or separate breaking changes into one or several releases.
The decision criterion is how much work you create for “client developers” (maintainers of reverse dependencies).</p>
<p>It’s also one of the many places in the book where the author reminds us there’s no hard rule, that it’s the “<em>art</em> of software engineering”.</p>
<h2 id="house-or-not-house">House or not House</h2>
<p>The first chapter of the book presents and criticizes analogies of software engineering, like comparing developing software to building a house.
That reminded of the talk <a href="https://resources.rstudio.com/resources/rstudioglobal-2021/maintaining-the-house-the-tidyverse-built/" rel="nofollow" target="_blank">Maintaining the house the tidyverse built</a> by Hadley Wickham, that compared tidyverse maintenance to house maintenance.</p>
<p>The chapter’s thesis is that all analogies are useful and imperfect and you shouldn’t let them limit or cloud the way you view your work.</p>
<h2 id="justify-exceptions">Justify exceptions</h2>
<p>At some point the author writes that static code analysis brings false positives, but that if you disable specific rules, you should justify why.
That’s how <a href="https://blog.r-hub.io/2026/06/02/jarl/" rel="nofollow" target="_blank">Jarl</a>, a linter for R code, makes you suppress rules: you have to explain a <a href="https://jarl.etiennebacher.com/howto/suppression-comments" rel="nofollow" target="_blank">reason</a>.</p>
<h2 id="git-is-easy">Git is easy</h2>
<p>I like these encouraging three sentences:</p>
<blockquote>
<p>“Git isn’t the most user-friendly piece of technology on the planet, but you’re a programmer. You’ve managed to learn at least one programming language. Compared to that, learning the basics of Git is easy.”</p>
</blockquote>
<h2 id="testing-against-databases">Testing against databases</h2>
<p>As an example of slow tests, the author mentions tests against databases.
I want to use this as an excuse to plug the <a href="https://docs.ropensci.org/dittodb/" rel="nofollow" target="_blank">dittodb package</a> to mock databases in tests.</p>
<h2 id="bisection-without-and-with-git">Bisection without and with Git</h2>
<p>In the chapter about Troubleshooting, the idea of bisection without Git is introduced.
The author says it’s called bisection for lack of a better word.
The idea being to go from a big piece of code with a bug to the smallest piece of code with the same bug,
I suppose a better word is <a href="https://reprex.tidyverse.org/" rel="nofollow" target="_blank">reprex</a>. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f601.png" alt="😁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<blockquote>
<p>“Being able to produce a minimal working example is a superpower in software engineering.”</p>
</blockquote>
<p>Regarding <em>Git</em> bisection, here’s your friendly reminder that you can try it out using the <a href="https://docs.ropensci.org/saperlipopette/reference/exo_bisect.html" rel="nofollow" target="_blank">saperlipopette R package</a>.</p>
<h2 id="time-boxing">Time boxing</h2>
<p>The author extols the virtues of time boxing (e.g. the Pomodoro method).
I’ve been using <a href="https://entracte.drmowinckels.io/" rel="nofollow" target="_blank">Entracte</a> and would recommend giving it a go, if you have no similar setup yet!</p>
<p>When reading the book, between chunks of reading I’d <a href="https://masalmon.eu/2026/05/19/crochet-again/" rel="nofollow" target="_blank">crochet a few rounds</a>.</p>
<h2 id="performance">Performance</h2>
<p>I enjoyed the pragmatic discussion of performance: don’t forget to check the numbers and their significance.</p>
<h2 id="behavioural-code-analysis">Behavioural code analysis</h2>
<p>I have never used behavioural code analysis, something that uses Git data, but its presentation reminded me of <a href="https://ropensci.org/blog/2026/04/30/news-april-2026/#git-commands-to-get-to-know-a-project" rel="nofollow" target="_blank">Git commands to get to know a project</a> which is related (or the same?).</p>
<h2 id="code-navigation">Code navigation</h2>
<p>Yay to the mention of learning how to navigate code in your IDE.
Such an important skill.</p>
<h2 id="conclusion">Conclusion</h2>
<p>“Code that Fits In Your Head” is an ambitious (and rather long) book.
I was glad to learn new phrases and to get the opportunity to get to know or reflect on so many topics.</p>
<p>Because the book was published in 2021, AI only makes an appearance as a brief footnote.
The only moment I really thought AI would make a point less valid was a point about some kinds of refactoring taking ages: in the igraph R package, some changes have recently become feasible in an easier way thanks to using tools like Claude.</p>
<p>Some aspects of the book might be a tad annoying like the super simple diagrams or illustrations (think: a hammer drawing to illustrate the fact that to someone with a hammer, everything looks like a nail) but I suppose those play their role of breaking up pages.</p>
<h2 id="bonus-a-reading-list">Bonus: a reading list</h2>
<p>If someone fairly new to software engineering were to ask me book recs, from looking at the stack near my desk I would recommend the following books, more specialized than “Code that Fits In Your Head”:</p>
<ul>
<li><a href="https://www.oreilly.com/library/view/the-art-of/9781449318482/" rel="nofollow" target="_blank">The Art of Readable Code by Dustin Boswell and Trevor Foucher</a></li>
<li><a href="https://www.manning.com/books/the-programmers-brain" rel="nofollow" target="_blank">The Programmer’s Brain by Felienne Hermans</a> (that I plan to re-read soon!)</li>
<li><a href="https://masalmon.eu/2023/10/19/reading-notes-philosophy-software-design/" rel="nofollow" target="_blank">A Philosophy of Software Design by John Ousterhout</a></li>
<li>A Git book, either <a href="https://masalmon.eu/2023/11/01/reading-notes-git-in-practice/" rel="nofollow" target="_blank">Git in Practice by Mike McQuaid</a> or <a href="https://masalmon.eu/2024/01/19/pro-git-scott-chacon-reading-notes/" rel="nofollow" target="_blank">Pro Git by Scott Chacon</a></li>
<li>A book about team work. Maybe <a href="https://www.routledge.com/The-Psychology-of-Software-Teams/Hicks/p/book/9781032963389" rel="nofollow" target="_blank">The Psychology of Software Teams by Cat Hicks</a>, <a href="https://colinmfisher.com/" rel="nofollow" target="_blank">The Collective Edge by Colin M. Fisher</a>, <a href="https://brenebrown.com/hubs/dare-to-lead/" rel="nofollow" target="_blank">Dare to Lead by Brené Brown</a>…</li>
<li>A book about productivity/organization, but not a sanctimonious one about profound labor or the like. I enjoyed <a href="https://maketime.blog/" rel="nofollow" target="_blank">Make Time by Jake Knapp and John Zeratsky</a> and <a href="https://lauravanderkam.com/books/tranquility-by-tuesday/" rel="nofollow" target="_blank">Tranquility by Tuesday by Laura Vanderkam</a>.</li>
<li>A book critical of AI such as <a href="https://thecon.ai/" rel="nofollow" target="_blank">The AI Con by Emily M. Bender and Alex Hanna</a> or <a href="https://en.wikipedia.org/wiki/Empire_of_AI" rel="nofollow" target="_blank">Empire of AI by Karen Hao</a>.</li>
</ul>
<p>I also believe there are other ways to learn, like watching talks, but this post is about books. <img src="https://s.w.org/images/core/emoji/13.0.0/72x72/1f638.png" alt="😸" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>

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

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