<?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>Wed, 19 Aug 2026 21:39:35 +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>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>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>
		<item>
		<title>Posit Assistant: Is it worth the switch?</title>
		<link>https://www.r-bloggers.com/2026/08/posit-assistant-is-it-worth-the-switch/</link>
		
		<dc:creator><![CDATA[The Jumping Rivers Blog]]></dc:creator>
		<pubDate>Thu, 13 Aug 2026 23:59:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.jumpingrivers.com/blog/posit-assistant-is-it-worth-the-switch/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>What Is Posit Assistant?<br />
Posit Assistant is an AI assistant built into your IDE, either Positron or RStudio. It’s built primarily for exploratory data analysis, and its pitch is session context and specialised, in-built skills.<br />
If you follow Posit’s developments, you might have heard of their previous iterations ...</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/posit-assistant-is-it-worth-the-switch/">Posit Assistant: Is it worth the switch?</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/posit-assistant-is-it-worth-the-switch/"> 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/posit-assistant-is-it-worth-the-switch/">
<img src="https://www.jumpingrivers.com/blog/posit-assistant-is-it-worth-the-switch/" width="400" style="width:400px" class="image-center" style="display: block; margin: auto;" />
</a>
</p>
<style>
.conversation-block {
background-color: var(--cream);
padding: 1rem;
}
/* Nudge the code down from the 18px body size so the widest lines of the
transcript fit the column without scrolling. */
.conversation-block pre {
font-size: 0.875em;
}
[data-speaker="Posit Assistant"] .conversation-speaker::before,
[data-speaker="Kia"] .conversation-speaker::after {
content: '';
display: inline-block;
width: 28px;
height: 28px;
background-size: contain;
background-repeat: no-repeat;
border-radius: 25%;
}
[data-speaker="Posit Assistant"] .conversation-speaker::before {
background-image: url("/images/partnering/posit-logo.svg");
transform: translateX(-4px) translateY(7px);
}
[data-speaker="Kia"] .conversation-speaker::after {
background-image: url("kia-mack-avatar.webp");
transform: translateX(4px) translateY(7px);
}
</style>
<h2 id="what-is-posit-assistant">What Is Posit Assistant?</h2>
<p>Posit Assistant is an AI assistant built into your IDE, either Positron or RStudio. It’s built primarily for exploratory data analysis, and its pitch is session context and specialised, in-built skills.</p>
<p>If you follow Posit’s developments, you might have heard of their previous iterations of an integrated AI assistant. The naming has been a moving target:</p>
<ul>
<li><strong>Positron Assistant</strong> came first, in early 2025, as a general coding assistant inside Positron</li>
<li><strong>Databot</strong> followed that August, aimed squarely at data exploration</li>
<li><strong>Posit Assistant</strong> replaces both, and is the first to be available in RStudio as well as Positron</li>
</ul>
<p>In this blog, we will</p>
<ul>
<li>Take a look at a typical chat with Posit Assistant</li>
<li>Explore what sets it apart from other integrated AI assistants</li>
<li>Introduce the <code>btw</code> R package, for those who’d rather add R session context to their existing assistant instead</li>
</ul>
<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=what-posit-assistant-can-see" rel="nofollow" target="_blank">Jumping Rivers has a training course for you</a>.
</p>
</aside>
<h2 id="a-typical-chat">A Typical Chat</h2>
<p>A great way to see it in action is to load some data and ask a broad question.</p>
<div class="conversation-block">
<style>
.conversation-turn {
padding: 0.5rem 1rem;
margin-bottom: 1rem;
border: 1px solid var(--burgundy);
}
.conversation-turn--right {
color: var(--off-white);
background-color: var(--burgundy);
margin-left: 2rem;
border-radius: 1.1rem 1.1rem 0.2rem 1.1rem;
}
.conversation-turn--left {
color: var(--dark-blue);
background-color: var(--off-white);
margin-right: 2rem;
border-radius: 1.1rem 1.1rem 1.1rem 0.2rem;
}
.conversation-turn p {
margin: 0.25rem 0;
}
.conversation-turn .conversation-speaker {
width: 100%;
font-weight: 500;
margin-bottom: 0.25rem;
}
.conversation-turn--right .conversation-speaker {
text-align: right;
}
.conversation-turn--right .conversation-body {
margin-right: 1rem;
}
.conversation-turn--left .conversation-body {
margin-left: 1rem;
}
</style>
<div class="conversation-turn conversation-turn--kia conversation-turn--right" data-speaker="Kia">
<div class="conversation-speaker">Kia</div>
<div class="conversation-body">What stands out in the happiness data?</div>
</div>
<div class="conversation-turn conversation-turn--posit assistant conversation-turn--left" data-speaker="Posit Assistant">
<div class="conversation-speaker">Posit Assistant</div>
<div class="conversation-body"><p>Let me take a look.</p>
<pre>glimpse(happy)

happy |&gt;
 summarise(across(everything(), ~sum(is.na(.)))) |&gt;
 select(where(~. &gt; 0))

drivers = select(happy,
 `GDP per capita`:`Perceptions of corruption`)

cor(happy$Score, drivers) |&gt;
 round(2) |&gt;
 t()
#&gt; [,1]
#&gt; GDP per capita 0.79
#&gt; Social support 0.78
#&gt; Healthy life expectancy 0.78
#&gt; Freedom 0.57
#&gt; Generosity 0.08
#&gt; Corruption 0.39
</pre><p>The 2019 World Happiness Report data is clean and tidy: 156 countries, 9 columns, no missing values. A few things stand out.</p>
<p>The score spread is wide. Happiness scores range from 2.85 to 7.77, with Finland on top and a median around 5.38, so the happiest country scores nearly 3x the least happy.</p>
<p>The “big three” drivers dominate. Score correlates strongly with GDP per capita (0.79), social support (0.78) and healthy life expectancy (0.78). These three move together and track closely with overall happiness.</p>
<p>Generosity is essentially uncorrelated with happiness (r = 0.08), which is a notable surprise. Freedom (0.57) and perceptions of corruption (0.39) matter, but less than the big three.</p>
<p>I’ll suggest some directions below.</p>
<ul>
<li>Visualize the relationship between GDP per capita and happiness score.</li>
<li>Look at which countries most over- or under-perform their GDP.</li>
<li>Explore why generosity is uncorrelated with the happiness score.</li>
</ul>
</div>
</div>
</div>
<p>Firstly, nothing in my request told it what the data represents, what columns existed or what the values looked like. Yet it came back with a description of the data, and a simple correlation analysis showing which variables drive happiness, and three suggestions of what you may want to explore next.</p>
<p>The other thing I like is that it answers by writing and then running code, which makes its decisions traceable. Language models are not designed to do maths (clue is in the “language” bit), but by writing code that does the maths, which we can scrutinise, copy and tweak, it gives us a much more trustworthy output.</p>
<h2 id="how-does-it-compare-to-other-integrated-assistants">How Does It Compare to Other Integrated Assistants?</h2>
<p><a href="https://claude.com/claude-code" rel="nofollow" target="_blank">Claude Code</a> is probably the most popular alternative AI coding assistant, which is available inside Positron as an extension, and available anywhere you have a terminal through its CLI tool.</p>
<p>The two overlap on most of the basics. Both Posit Assistant and Claude Code:</p>
<ul>
<li>Can run on the same underlying model (though Posit Assistant also supports other providers)</li>
<li>Can read and edit your files</li>
<li>Generate and run code, from which they come to conclusions</li>
<li>Can be given persistent project context files, through <code>CLAUDE.md</code> for Claude Code and <code>AGENTS.md</code> for Posit Assistant</li>
</ul>
<p>So what sets them apart?</p>
<p>As we know now, Posit Assistant adds the session context. It knows the variables you have defined, the data frames you have loaded and the plot you are looking at, which is what made the exchange above possible.</p>
<p>Plots are one place where that makes a big difference. Posit Assistant can see the plots you previously made in the plot viewer, interpret them, and print plots back into the chat. That is worth a great deal in exploratory work, where much of what you learn comes from looking at a chart rather than a table. Claude Code can create a plot and save it to an image file, or ingest an image file, but the process is much clunkier.</p>
<p>It is not infallible, and Posit are candid about that: their research on <a href="https://posit.co/blog/llm-plot-interpretation" rel="nofollow" target="_blank">how well LLMs interpret plots</a> found models read straightforward charts well, but stumble when what is on screen contradicts what they expect to see.</p>
<p>It also brings the skills, which are bundles of instructions and code that the assistant pulls in by itself when it judges them relevant to what you asked. Thirteen ship built in, covering things like report authoring with Quarto and Jupyter Notebooks, and predictive modelling in R and Python, so it arrives at those tasks already knowing the conventions. You do not have to wait for it to spot the right one, though. <a href="https://assistant.posit.co/docs/features/skills/" rel="nofollow" target="_blank">Most skills are also exposed as slash commands</a>, so typing <code>/report</code>, <code>/quarto-authoring</code> or <code>/shiny-bslib</code> runs that skill’s instructions there and then.</p>
<p>Claude Code is more autonomous, and will work through several steps before it checks in, whereas Posit Assistant stops for guidance more often. That is deliberate: it inherits Databot’s exploratory behaviour, so when it decides you are exploring data it runs a couple of tool calls, reports back and offers you some directions. The point is to keep you involved while the exploration is happening, rather than handing you a finished answer.</p>
<p>One thing to be aware of: Posit’s <a href="https://positron.posit.co/assistant.html" rel="nofollow" target="_blank">documentation </a> lists “console history” as available context, which is a little misleading. Line by line detail of what has been run is not part of the context in either R or Python, so if you were hoping the assistant would know what code you’ve already run, or an error message that’s popped up, it will not.</p>
<h2 id="not-ready-to-make-the-switch-add-context-to-your-assistant-with-an-mcp-instead">Not ready to make the switch? Add context to your assistant with an MCP instead</h2>
<p>If you would rather keep one assistant, it’s possible to give your AI assistant of choice some of the same session awareness with the <a href="https://posit-dev.github.io/btw/" rel="nofollow" target="_blank"><code>btw</code></a> R package, also developed by Posit. It works over the Model Context Protocol, and if MCP is new to you, Neal Richardson of Posit gave a good primer, <a href="https://www.youtube.com/watch?v=1mtWbxQE8S8" rel="nofollow" target="_blank">“MCP, or Not MCP”</a>, at our AI in Production conference.</p>
<p>The setup is straightforward, and the <a href="https://posit-dev.github.io/btw/reference/mcp.html" rel="nofollow" target="_blank">MCP setup guide</a> walks you through it.</p>
<p>Once it is running, your assistant can inspect R objects and data frames in your session, check installed packages, and search for and describe CRAN packages.</p>
<p>There are trade-offs. It covers R only, so a Python session gets nothing from it. You also lose the skills, so your assistant comes to a Quarto document, Shiny app, or a modelling task without the guidance Posit Assistant would have loaded for you.</p>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>Posit Assistant is easy to adopt, particularly if you already run the Claude Code extension in Positron or VS Code, because the workflow is familiar and the session context arrives without any configuration.</p>
<p>For exploratory data analysis it is an excellent tool. Being able to ask about the data you actually have loaded, and to read the code it wrote to answer you, changes the pace of that work more than I expected.</p>
<p>If you would rather keep to a single assistant, pair your go-to coding assistant with Posit’s <code>btw</code> package and you will get most of the way there.</p>
<p>This post grew out of “Improving Your Workflow with Positron and Claude”, a workshop we ran at <a href="https://ai-in-production.jumpingrivers.com/" rel="nofollow" target="_blank">AI in Production 2026</a>. If sessions like that appeal, come and join us next year.</p>
<h2 id="find-out-more">Find Out More</h2>
<p>From us:</p>
<ul>
<li><a href="https://www.jumpingrivers.com/blog/first-look-positron-posit-assistant/" rel="nofollow" target="_blank">A First Look at Positron and Posit Assistant</a> &#8211; our free webinar on August 13th, 2026</li>
<li><a href="https://www.jumpingrivers.com/blog/why-move-to-positron-r/" rel="nofollow" target="_blank">Positron vs RStudio &#8211; is it time to switch?</a></li>
<li><a href="https://www.youtube.com/watch?v=1mtWbxQE8S8" rel="nofollow" target="_blank">“MCP, or Not MCP”</a> &#8211; Neal Richardson (Posit) at AI in Production 2026</li>
</ul>
<p>From Posit:</p>
<ul>
<li><a href="https://opensource.posit.co/blog/2026-06-11_history-of-posit-data-science-agents/" rel="nofollow" target="_blank">A brief and biased history of Posit data science agents</a> &#8211; Joe Cheng on why there were three of them</li>
<li><a href="https://posit.co/blog/introducing-ai-in-rstudio" rel="nofollow" target="_blank">Introducing AI in RStudio</a></li>
</ul>
<aside class="advert">
<p>
Join us for our AI in Production conference! For more details, check out our <a href="https://ai-in-production.jumpingrivers.com/" rel="nofollow" target="_blank">conference website!</a>
</p>
</aside>
<p>
For updates and revisions to this article, see the <a href = "https://www.jumpingrivers.com/blog/posit-assistant-is-it-worth-the-switch/">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/posit-assistant-is-it-worth-the-switch/"> 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/posit-assistant-is-it-worth-the-switch/">Posit Assistant: Is it worth the switch?</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403160</post-id>	</item>
		<item>
		<title>Some everyday data tasks: a few hints with R</title>
		<link>https://www.r-bloggers.com/2026/08/some-everyday-data-tasks-a-few-hints-with-r-2/</link>
		
		<dc:creator><![CDATA[Andrea Onofri]]></dc:creator>
		<pubDate>Thu, 13 Aug 2026 22:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.statforbiology.com/posts/R_ShapingData.html</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>It is important to know how to reshape a dataframe into a form that might be more suitable for the analyses we intend to perform. For me, it is also important to know how to do this with base R, without using any other packages. Yes, I know that...</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/some-everyday-data-tasks-a-few-hints-with-r-2/">Some everyday data tasks: a few hints 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.statforbiology.com/posts/R_ShapingData.html"> Statforbiology</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>It is important to know how to reshape a dataframe into a form that might be more suitable for the analyses we intend to perform. For me, it is also important to know how to do this with base R, without using any other packages. Yes, I know that other packages, such as <code>dplyr</code> and <code>tidyr</code>, are much better… Still, having the skills to perform most of our everyday tasks with base R may be a good idea. In particular, there are at least four routine tasks that we need to be able to perform:</p>
<ol type="1">
<li>subsetting</li>
<li>sorting</li>
<li>casting</li>
<li>melting</li>
</ol>
<section id="subsetting-the-data" class="level1">
<h1>Subsetting the data</h1>
<p>Subsetting means selecting the records (rows) or the variables (columns) that satisfy certain criteria. In base R, we can use the <code>subset()</code> function.</p>
<p>Let’s consider the <code>students</code> dataset, which is available in the <code>statforbiology</code> package. It represents a collection of exams taken by students at my university in different subjects. Let’s load it using the <code>getAgroData()</code> function from the <code>statforbiology</code> package.</p>
<div class="cell">
<pre>library(statforbiology)
students &lt;- getAgroData(&quot;students&quot;)
head(students)
##   Id  Subject       Date Mark Year  HighSchool
## 1  1 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 2  2 AGRONOMY 08/07/2002   24 2001 AGRICULTURE
## 3  3 AGRONOMY 24/06/2002   30 2001 AGRICULTURE
## 4  4 AGRONOMY 24/06/2002   26 2001  HUMANITIES
## 5  5 AGRONOMY 23/01/2003   30 2001  HUMANITIES
## 6  6 AGRONOMY 09/09/2002   28 2001 AGRICULTURE</pre>
</div>
<p>Let’s say that we want a new dataset that contains only the records where the mark was equal to or above 28 (please note that, in Italy, an exam is passed with a minimum mark of 18, while the maximum mark is 30).</p>
<div class="cell">
<pre>subData &lt;- subset(students, Mark &gt;= 28)
head(subData)
##    Id  Subject       Date Mark Year  HighSchool
## 1   1 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 3   3 AGRONOMY 24/06/2002   30 2001 AGRICULTURE
## 5   5 AGRONOMY 23/01/2003   30 2001  HUMANITIES
## 6   6 AGRONOMY 09/09/2002   28 2001 AGRICULTURE
## 11 11 AGRONOMY 09/09/2002   28 2001  SCIENTIFIC
## 17 17 AGRONOMY 10/06/2002   30 2001  HUMANITIES</pre>
</div>
<p>Let’s make it more difficult and extract the records where the mark ranges from 26 to 28 (margins included). Look at the AND clause, which is expressed by using the <code>&</code> operator:</p>
<div class="cell">
<pre>subData &lt;- subset(students, Mark &lt;= 28 &#038; Mark &gt;= 26)
head(subData)
##    Id  Subject       Date Mark Year  HighSchool
## 4   4 AGRONOMY 24/06/2002   26 2001  HUMANITIES
## 6   6 AGRONOMY 09/09/2002   28 2001 AGRICULTURE
## 7   7 AGRONOMY 24/02/2003   26 2001  HUMANITIES
## 8   8 AGRONOMY 09/09/2002   26 2001  SCIENTIFIC
## 10 10 AGRONOMY 08/07/2002   27 2001  HUMANITIES
## 11 11 AGRONOMY 09/09/2002   28 2001  SCIENTIFIC</pre>
</div>
<p>Now we are interested in those students who got a mark ranging from 26 to 28 in MATHS (please note the equality operator, written as <code>==</code>):</p>
<div class="cell">
<pre>subData &lt;- subset(students, Mark &lt;= 28 &#038; Mark &gt;= 26 & 
                    Subject == &quot;MATHS&quot;)
head(subData)
##      Id Subject       Date Mark Year  HighSchool
## 115 115   MATHS 15/07/2002   26 2001 AGRICULTURE
## 124 124   MATHS 16/09/2002   26 2001  SCIENTIFIC
## 138 138   MATHS 04/02/2002   27 2001  HUMANITIES
## 144 144   MATHS 10/02/2003   27 2001  HUMANITIES
## 145 145   MATHS 04/07/2003   27 2002  HUMANITIES
## 146 146   MATHS 28/02/2002   28 2001 AGRICULTURE</pre>
</div>
<p>Let’s look for good students who got a mark ranging from 26 to 28 either in MATHS or in CHEMISTRY (OR clause; note the <code>|</code> operator):</p>
<div class="cell">
<pre>subData &lt;- subset(students, Mark &lt;= 28 &#038; Mark &gt;= 26 & 
                    (Subject == &quot;MATHS&quot; | 
                     Subject == &quot;CHEMISTRY&quot;))
head(subData)
##    Id   Subject       Date Mark Year   HighSchool
## 68 68 CHEMISTRY 04/06/2002   28 2001 OTHER SCHOOL
## 70 70 CHEMISTRY 04/06/2002   26 2001   ACCOUNTING
## 71 71 CHEMISTRY 04/06/2002   27 2001  AGRICULTURE
## 72 72 CHEMISTRY 23/01/2003   27 2001   SCIENTIFIC
## 75 75 CHEMISTRY 10/07/2002   27 2001  AGRICULTURE
## 81 81 CHEMISTRY 23/01/2003   28 2001  AGRICULTURE</pre>
</div>
<p>We can also select columns; for example, we may want to display only the <code>Subject</code>, <code>Mark</code>, and <code>HighSchool</code> columns:</p>
<div class="cell">
<pre>subData &lt;- subset(students, Mark &lt;= 28 &#038; Mark &gt;= 26 & 
                    (Subject == &quot;MATHS&quot; | 
                     Subject == &quot;CHEMISTRY&quot;),
                  select = c(Subject, Mark, HighSchool))
head(subData)
##      Subject Mark   HighSchool
## 68 CHEMISTRY   28 OTHER SCHOOL
## 70 CHEMISTRY   26   ACCOUNTING
## 71 CHEMISTRY   27  AGRICULTURE
## 72 CHEMISTRY   27   SCIENTIFIC
## 75 CHEMISTRY   27  AGRICULTURE
## 81 CHEMISTRY   28  AGRICULTURE</pre>
</div>
<p>We can also drop unwanted columns:</p>
<div class="cell">
<pre>subData &lt;- subset(students, Mark &lt;= 28 &#038; Mark &gt;= 26 & 
                    (Subject == &quot;MATHS&quot; | 
                     Subject == &quot;CHEMISTRY&quot;),
                  select = c(-Id, 
                             -Date,
                             -Year))
head(subData)
##      Subject Mark   HighSchool
## 68 CHEMISTRY   28 OTHER SCHOOL
## 70 CHEMISTRY   26   ACCOUNTING
## 71 CHEMISTRY   27  AGRICULTURE
## 72 CHEMISTRY   27   SCIENTIFIC
## 75 CHEMISTRY   27  AGRICULTURE
## 81 CHEMISTRY   28  AGRICULTURE</pre>
</div>
<p>The <code>subset()</code> function is very easy to use. However, we might have greater flexibility by using indices for subsetting. We already know that the notation <code>dataframe[i, j]</code> returns the element in the i-th row and j-th column of a data frame. We can, of course, replace <code>i</code> and <code>j</code> with some subsetting rules. For example, selecting the exams where the mark is between 25 and 29 is done as follows:</p>
<div class="cell">
<pre>subData &lt;- students[(students$Mark &lt;= 29 &#038; students$Mark &gt;= 25),]
head(subData)
##    Id  Subject       Date Mark Year  HighSchool
## 4   4 AGRONOMY 24/06/2002   26 2001  HUMANITIES
## 6   6 AGRONOMY 09/09/2002   28 2001 AGRICULTURE
## 7   7 AGRONOMY 24/02/2003   26 2001  HUMANITIES
## 8   8 AGRONOMY 09/09/2002   26 2001  SCIENTIFIC
## 10 10 AGRONOMY 08/07/2002   27 2001  HUMANITIES
## 11 11 AGRONOMY 09/09/2002   28 2001  SCIENTIFIC</pre>
</div>
<p>This is useful for quickly editing the data. For example, if we want to replace all marks from 25 to 29 with <code>NA</code>s (missing values), we can simply do:</p>
<div class="cell">
<pre>subData &lt;- students
subData[(subData$Mark &lt;= 29 &#038; subData$Mark &gt;= 25), &quot;Mark&quot;] &lt;- NA
head(subData)
##   Id  Subject       Date Mark Year  HighSchool
## 1  1 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 2  2 AGRONOMY 08/07/2002   24 2001 AGRICULTURE
## 3  3 AGRONOMY 24/06/2002   30 2001 AGRICULTURE
## 4  4 AGRONOMY 24/06/2002   NA 2001  HUMANITIES
## 5  5 AGRONOMY 23/01/2003   30 2001  HUMANITIES
## 6  6 AGRONOMY 09/09/2002   NA 2001 AGRICULTURE</pre>
</div>
<p>Please note that I created a new dataset to make the replacement, so as not to modify the original dataset. Of course, I can use the <code>is.na()</code> function to find missing values and edit them.</p>
<div class="cell">
<pre>subData[is.na(subData$Mark), &quot;Mark&quot;] &lt;- 0 
head(subData)
##   Id  Subject       Date Mark Year  HighSchool
## 1  1 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 2  2 AGRONOMY 08/07/2002   24 2001 AGRICULTURE
## 3  3 AGRONOMY 24/06/2002   30 2001 AGRICULTURE
## 4  4 AGRONOMY 24/06/2002    0 2001  HUMANITIES
## 5  5 AGRONOMY 23/01/2003   30 2001  HUMANITIES
## 6  6 AGRONOMY 09/09/2002    0 2001 AGRICULTURE</pre>
</div>
</section>
<section id="sorting-the-data" class="level1">
<h1>Sorting the data</h1>
<p>Sorting is very similar to subsetting by indexing. We simply need to use the <code>order()</code> function. For example, let’s sort the <code>students</code> dataset by mark:</p>
<div class="cell">
<pre>sortedData &lt;- students[order(students$Mark), ]
head(sortedData)
##    Id   Subject       Date Mark Year   HighSchool
## 51 51   BIOLOGY 01/03/2002   18 2001   HUMANITIES
## 67 67 CHEMISTRY 20/02/2003   18 2002  AGRICULTURE
## 76 76 CHEMISTRY 24/02/2003   18 2002 OTHER SCHOOL
## 79 79 CHEMISTRY 18/06/2003   18 2002  AGRICULTURE
## 82 82 CHEMISTRY 18/07/2002   18 2001  AGRICULTURE
## 83 83 CHEMISTRY 23/01/2003   18 2001   SCIENTIFIC</pre>
</div>
<p>We can also sort in decreasing order:</p>
<div class="cell">
<pre>sortedData &lt;- students[order(-students$Mark), ]
head(sortedData)
##    Id  Subject       Date Mark Year  HighSchool
## 1   1 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 3   3 AGRONOMY 24/06/2002   30 2001 AGRICULTURE
## 5   5 AGRONOMY 23/01/2003   30 2001  HUMANITIES
## 17 17 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 18 18 AGRONOMY 10/06/2002   30 2001 AGRICULTURE
## 19 19 AGRONOMY 09/09/2002   30 2001 AGRICULTURE</pre>
</div>
<p>We can obviously use multiple keys. For example, let’s sort by mark and, within each mark, by subject:</p>
<div class="cell">
<pre>sortedData &lt;- students[order(-students$Mark, students$Subject), ]
head(sortedData)
##    Id  Subject       Date Mark Year  HighSchool
## 1   1 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 3   3 AGRONOMY 24/06/2002   30 2001 AGRICULTURE
## 5   5 AGRONOMY 23/01/2003   30 2001  HUMANITIES
## 17 17 AGRONOMY 10/06/2002   30 2001  HUMANITIES
## 18 18 AGRONOMY 10/06/2002   30 2001 AGRICULTURE
## 19 19 AGRONOMY 09/09/2002   30 2001 AGRICULTURE</pre>
</div>
<p>If I want to sort a character variable (such as <code>Subject</code>) in decreasing order, I need to use the helper function <code>xtfrm()</code>:</p>
<div class="cell">
<pre>sortedData &lt;- students[order(-students$Mark, -xtfrm(students$Subject)), ]
head(sortedData)
##      Id Subject       Date Mark Year   HighSchool
## 116 116   MATHS 01/07/2002   30 2001 OTHER SCHOOL
## 117 117   MATHS 18/06/2002   30 2001   ACCOUNTING
## 118 118   MATHS 09/07/2002   30 2001  AGRICULTURE
## 121 121   MATHS 18/06/2002   30 2001   ACCOUNTING
## 123 123   MATHS 09/07/2002   30 2001   HUMANITIES
## 130 130   MATHS 07/02/2002   30 2001   SCIENTIFIC</pre>
</div>
</section>
<section id="casting-and-melting" class="level1">
<h1>Casting and melting</h1>
<p>These are two operations that we can perform on the entire dataframe, to reshape it from:</p>
<ol type="1">
<li>LONG to WIDE format (casting)</li>
<li>WIDE to LONG format (melting)</li>
</ol>
<p>In base R, we use the same function, namely <code>reshape()</code>, which was originally tailored to the needs of longitudinal data management. Although the terminology comes from this original conceptual framework (which is quite confusing at the beginning), you can use this function for any type of data that needs to be reshaped from LONG to WIDE or vice versa.</p>
</section>
<section id="casting-the-data" class="level1">
<h1>Casting the data</h1>
<p>We might be familiar with the ‘pivot table’ function in Excel, which reshapes a dataset from the LONG format to the WIDE format. For example, let’s take the <code>rimsulfuron</code> dataset in the <code>statforbiology</code> package, which contains the results of an experiment in blocks designed to compare 16 herbicides for weed control in maize. The dataset is in the LONG format, with one row for each plot and the observations made in each plot listed in different columns.</p>
<div class="cell">
<pre>rimsulfuron &lt;- getAgroData(&quot;rimsulfuron&quot;)
head(rimsulfuron)
##                      Herbicide Plot Code Block Column WeedCover Yield
## 1             Rimsulfuron (40)    1    1     1      1      27.8 85.91
## 2             Rimsulfuron (45)    2    2     1      1      27.8 93.03
## 3             Rimsulfuron (50)    3    3     1      1      23.0 86.93
## 4             Rimsulfuron (60)    4    4     1      1      42.8 52.99
## 5    Rimsulfuron (50+30 split)    5    5     1      1      15.1 71.36
## 6 Rimsulfuron + thyfensulfuron    6    6     1      1      22.9 75.28</pre>
</div>
<p>Let’s put this data frame in the WIDE format, so that we have the 16 herbicides in different rows, and the observations for each herbicide are listed in different columns, with a separate column per each block (provided that we have only one observation per herbicide in each block, which is the case here). In base R, we can use the <code>reshape()</code> function with <code>direction = &quot;wide&quot;</code>. Basically, we need to specify which variable should identify the rows (in our case, <code>idvar = &quot;Herbicide&quot;</code>) and which variable identifies the different sets of observations (in our case, <code>timevar = &quot;Block&quot;</code>). The name <code>timevar</code> shows that this function was initially tailored to the needs of longitudinal data. Initially, we have to subset the data frame to retain only the columns we need to display in the final table, although we can also use the <code>drop</code> argument to exclude the variables we do not intend to use.</p>
<div class="cell">
<pre>castData &lt;- reshape(
  rimsulfuron[, c(&quot;Herbicide&quot;, &quot;Block&quot;, &quot;Yield&quot;)], 
  direction = &quot;wide&quot;,
  idvar = &quot;Herbicide&quot;,
  timevar = &quot;Block&quot;
)
castData
##                                     Herbicide Yield.1 Yield.2 Yield.3 Yield.4
## 1                            Rimsulfuron (40)   85.91   91.09  111.42   93.15
## 2                            Rimsulfuron (45)   93.03  105.00   89.19   79.04
## 3                            Rimsulfuron (50)   86.93  105.82  110.02   89.10
## 4                            Rimsulfuron (60)   52.99  102.86  100.62   97.04
## 5                   Rimsulfuron (50+30 split)   71.36   77.57  115.91   92.16
## 6                Rimsulfuron + thyfensulfuron   75.28   82.59   94.96   85.85
## 7                        Rimsulfuron + hoeing   73.22   86.06  118.01   98.32
## 8    Pendimethalin (pre) + rimsulfuron (post)   65.51   88.72   95.52   82.39
## 9  Pendimethalin (post) + rimsuulfuron (post)   94.82   87.72  102.05  101.94
## 10                        Rimsulfuron + Atred   94.11   89.86  104.34   99.63
## 11                             Thifensulfuron   78.47   42.32   62.52   24.34
## 12         Metolachlor + terbuthylazine (pre)   51.77   52.10   49.46   34.67
## 13                  Alachlor + terbuthylazine   12.06   49.58   41.34   16.37
## 14                                Hand-Weeded   77.58   92.08   86.59   99.63
## 15                                 Unweeded 1   10.88   31.77   23.92   20.85
## 16                                 Unweeded 2   27.58   51.55   25.13   38.61</pre>
</div>
</section>
<section id="melting-the-data" class="level1">
<h1>Melting the data</h1>
<p>The <code>reshape()</code> function can also be used to transform a dataset from WIDE to LONG format by setting the <code>direction = &quot;long&quot;</code> argument. For this task, let’s use the <code>WeedPop</code> dataset in the <code>statforbiology</code> package, which reports the results of a weed survey involving six species in nine conditions (the letters from A to I in the <code>Code</code> variable). The experimental unit is the condition and, for each condition, the ground cover of the six species is reported in different columns.</p>
<p>Now, we want to reshape this data frame so that we have one row for each combination of species and condition, with the ground cover listed in a single column. In order to use the <code>reshape()</code> function, we need to think of this table as if it represented longitudinal data, with repeated measurements taken at different time points on the same subject (id). Thus, the values that change with ‘time’ (the <code>varying</code> variables) are those contained in the original dataset in columns 2 to 7. These variables will be combined into a single column in the newly created dataset, which we will name <code>WeedCover</code> (<code>v.names = &quot;WeedCover&quot;</code>).</p>
<p>Now, we have to add at least two other variables to this newly created dataset. The first one represents the subjects (<code>idvar</code>) and must contain the codes contained in the <code>WeedPop$Code</code> column (<code>ids = WeedPop$Code</code>). We will name this variable <code>Code</code> (<code>idvar = &quot;Code&quot;</code>).</p>
<p>The second variable must contain the names of the original variables (<code>times = colnames(WeedPop[2:7])</code>) corresponding to the observations taken for each subject. We can assign a name to this newly created variable by using <code>timevar = &quot;Weed name&quot;</code>.</p>
<div class="cell">
<pre>WeedPop &lt;- getAgroData(&quot;WeedPop&quot;)
WeedPop
##   Code POLLA CHEPO ECHCG AMARE XANST POLAV
## 1    A   0.1    33    11     0   0.1   0.1
## 2    B   0.1     3     3     0   0.1   0.0
## 3    C   7.0    19    19     4   7.0   1.0
## 4    D  18.0     3    28    19  12.0   6.0
## 5    E   5.0     7    28     3  10.0   1.0
## 6    F  11.0     9    33     7  10.0   6.0
## 7    G   8.0    13    33     6  15.0  15.0
## 8    H  18.0     5    33     4  19.0  12.0
## 9    I   6.0     6    38     3  10.0   6.0
mdati &lt;- reshape(
  WeedPop,
  direction = &quot;long&quot;,
  varying = colnames(WeedPop[2:7]),
  v.names = &quot;WeedCover&quot;,
  times = colnames(WeedPop[2:7]),
  timevar = &quot;Weed name&quot;,
  ids = WeedPop$Code,
  idvar = &quot;Code&quot;,
)
head(mdati)
##         Code Weed name WeedCover
## A.POLLA    A     POLLA       0.1
## B.POLLA    B     POLLA       0.1
## C.POLLA    C     POLLA       7.0
## D.POLLA    D     POLLA      18.0
## E.POLLA    E     POLLA       5.0
## F.POLLA    F     POLLA      11.0</pre>
</div>
<p>I must admit that these functions are not particularly intuitive to use and the corresponding functions in the packages ‘dplyr’ and ‘tidyr’ may be easier to use. However, I like my students to have a good command of base R before they move on to other dialects</p>
<p>Have fun working with these functions! Should you have comments, please, drop me a note at the address below.</p>
<p>And … don’t forget to check out my new book!</p>
<p>Prof. Andrea Onofri<br>
Department of Agricultural, Food and Environmental Sciences<br>
University of Perugia (Italy)<br>
Send comments to: <a href="mailto:andrea.onofri@unipg.it" rel="nofollow" target="_blank">andrea.onofri@unipg.it</a></p>
<p><a href="https://www.awin1.com/cread.php?awinmid=26429&#038;awinaffid=2675822&#038;ued=https%3A%2F%2Flink.springer.com%2Fbook%2F10.1007%2F978-3-032-08199-5" rel="nofollow" target="_blank"><img src="https://i0.wp.com/www.statforbiology.com/Figures/Email_Signature_978-3-032-08199-5.png?w=578&#038;ssl=1" alt="Book cover" class="cover" align="center" data-recalc-dims="1"></a></p>
<hr>
<p>This post was originally published on 2019-03-27</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.statforbiology.com/posts/R_ShapingData.html"> Statforbiology</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/some-everyday-data-tasks-a-few-hints-with-r-2/">Some everyday data tasks: a few hints with R</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403127</post-id>	</item>
		<item>
		<title>Ihaka Lecture</title>
		<link>https://www.r-bloggers.com/2026/08/ihaka-lecture/</link>
		
		<dc:creator><![CDATA[R on kieranhealy.org]]></dc:creator>
		<pubDate>Thu, 13 Aug 2026 16:09:27 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://kieranhealy.org/blog/archives/2026/08/13/ihaka-lecture/</guid>

					<description><![CDATA[<p>I gave the first of this year’s Ihaka Lectures at the University of Auckland this past July. It revisited and partially updated things I’ve been talking about for the past eighteen months or so on trust in data visualization (and b...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/ihaka-lecture/">Ihaka Lecture</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/13/ihaka-lecture/"> 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>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
			<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/mpj-zqkiiXQ?autoplay=0&#038;controls=1&#038;end=0&#038;loop=0&#038;mute=0&#038;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
		</div>

<p>I gave the first of this year’s <a href="https://www.auckland.ac.nz/en/science/about-the-faculty/department-of-statistics/ihaka-lecture-series.html" rel="nofollow" target="_blank">Ihaka Lectures</a> at the University of Auckland this past July. It revisited and partially updated things I’ve been talking about for the past eighteen months or so on trust in data visualization (and by extension in scientific work) in the light of the many challenges we presently face.</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/13/ihaka-lecture/"> 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/ihaka-lecture/">Ihaka Lecture</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403105</post-id>	</item>
		<item>
		<title>Announcing AI in Production 2027</title>
		<link>https://www.r-bloggers.com/2026/08/announcing-ai-in-production-2027/</link>
		
		<dc:creator><![CDATA[The Jumping Rivers Blog]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 23:59:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.jumpingrivers.com/blog/ai-in-production-2027/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>AI in Production is about the reality of running AI and machine learning systems, not just building them. The talks come from people who have shipped something and lived with what came next: what worked, what surprised them, what they would do di...</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/announcing-ai-in-production-2027/">Announcing AI in Production 2027</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/ai-in-production-2027/"> 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/ai-in-production-2027/">
<img src="https://www.jumpingrivers.com/blog/ai-in-production-2027/" width="400" style="width:400px" class="image-center" style="display: block; margin: auto;" />
</a>
</p>
<p><a href="https://ai-in-production.jumpingrivers.com/" rel="nofollow" target="_blank">AI in Production</a> is about the reality of running AI and machine learning systems, not just building them. The talks come from people who have shipped something and lived with what came next: what worked, what surprised them, what they would do differently. The second edition returns to <a href="https://www.thecatalystnewcastle.co.uk/" rel="nofollow" target="_blank">The Catalyst</a> in Newcastle upon Tyne on 10 and 11 June 2027, and the call for speakers is open until 29 January.</p>
<h2 id="what-to-expect">What to expect</h2>
<p>Hands-on workshops on Thursday 10 June, then a full day of talks and lightning talks on Friday 11 June. On the workshop day you take one morning and one afternoon session, with lunch included and a dinner and drinks reception in the Catalyst atrium afterwards. Topics across both days cover deployment, monitoring and observability, generative AI, retrieval-augmented generation, and responsible AI as it applies day to day. The talks we like best are the ones that cover what went wrong as well as what worked.</p>
<p>The <a href="https://ai-in-production.jumpingrivers.com/#schedule" rel="nofollow" target="_blank">shape of both days</a> is on the conference site, and the <a href="https://ai-in-production.jumpingrivers.com/faq" rel="nofollow" target="_blank">FAQ</a> answers most of the logistics questions.</p>
<h2 id="call-for-speakers">Call for speakers</h2>
<p>Proposals close on <strong>29 January 2027</strong>. We want talks grounded in a system that exists and gets used by someone other than its author: what worked, what surprised you, what you would do differently. Deployment, observability, incident response, evaluation and guardrails, cost and latency, governance in daily practice, and the team lessons that come with keeping any of it running.</p>
<p>If you are unsure whether your talk fits, try this. Swap “AI” for any other kind of software system. If the talk still holds up, it is probably not for us. If it falls apart because the AI-specific parts are central to the story, that is what we are looking for.</p>
<p>First-time speakers are welcome. Our <a href="https://www.jumpingrivers.com/blog/beginners-guide-conference-abstracts/" rel="nofollow" target="_blank">guide to submitting conference abstracts</a> walks through the writing, and <a href="https://www.jumpingrivers.com/blog/why-submit-ai-in-production/" rel="nofollow" target="_blank">why submit to AI in Production</a> addresses the usual worry about whether your work is ready. Send a title and up to 300 words through the <a href="https://form.asana.com/?k=qWOyHlHkTlfHXwNivZpkAA&#038;d=741949310572673" rel="nofollow" target="_blank">submission form</a>.</p>
<aside class="advert">
<p>
Join us for our AI in Production conference! For more details, check out our <a href="https://ai-in-production.jumpingrivers.com/" rel="nofollow" target="_blank">conference website!</a>
</p>
</aside>
<h2 id="key-dates">Key dates</h2>
<ul>
<li><strong>8 January 2027:</strong> Super early bird deadline</li>
<li><strong>29 January 2027:</strong> Call for speakers closes</li>
<li><strong>5 March 2027:</strong> Early bird deadline</li>
<li><strong>4 June 2027:</strong> General registration deadline</li>
<li><strong>10 June 2027:</strong> Conference begins</li>
</ul>
<h2 id="tickets">Tickets</h2>
<p>Book the conference day on its own, or a combined ticket that includes the workshop day.</p>
<table>
<thead>
<tr>
<th>Tier</th>
<th>Deadline</th>
<th>Conference</th>
<th>With workshop day</th>
</tr>
</thead>
<tbody>
<tr>
<td>Super early bird</td>
<td>8 January 2027</td>
<td>£141</td>
<td>£260</td>
</tr>
<tr>
<td>Early bird</td>
<td>5 March 2027</td>
<td>£179</td>
<td>£298</td>
</tr>
<tr>
<td>General</td>
<td>4 June 2027</td>
<td>£238</td>
<td>£357</td>
</tr>
</tbody>
</table>
<h2 id="last-year-at-ai-in-production-2026">Last year at AI in Production 2026</h2>
<p>The 2027 line-up is still coming together, and there is room on the programme if you would like to speak. In the meantime, last year’s talks are a fair guide to the range: law, water, ecology, online gambling, central government, higher education, clinical research and enterprise software, as well as the software consultancies and tooling vendors you would expect.</p>
<p>Nathan Bilton of the law firm Weightmans covered who carries the liability when a chatbot gives a customer the wrong answer. Mac Misiura of Red Hat went through open source guardrails for securing large language model applications at scale. Grant Beasley of the online gambling operator tombola, with our own Myles Mitchell, showed how deep learning can identify players at risk of gambling harm. Seb Ringrose of Doubleword explained why low latency, low cost and high quality are a pick-two problem, and George Stagg of <a href="https://posit.co/" rel="nofollow" target="_blank">Posit</a> closed the day on what building Posit Assistant taught him about agents. Our <a href="https://www.jumpingrivers.com/blog/2026-ai-in-production-summary/" rel="nofollow" target="_blank">summary of the 2026 conference</a> covers every talk, and you can <a href="https://www.youtube.com/playlist?list=PLGqNj9r9d0Q8" rel="nofollow" target="_blank">watch the full 2026 playlist on YouTube</a>.</p>
<h2 id="getting-here">Getting here</h2>
<p><a href="https://maps.app.goo.gl/sYW4DVtNj1EJAK6K8" rel="nofollow" target="_blank">The Catalyst</a> is at 3 Science Square, Newcastle Helix, Newcastle upon Tyne NE4 5TG, a 10 minute walk from Newcastle Central Station. Trains take about three hours from London and 90 minutes from Edinburgh. <a href="https://www.newcastleairport.com/" rel="nofollow" target="_blank">Newcastle International Airport</a> is about 30 minutes from Central Station on the <a href="https://www.nexus.org.uk/metro" rel="nofollow" target="_blank">Metro</a>. St James’ Park is a short walk away.</p>
<h2 id="make-a-trip-of-it">Make a trip of it</h2>
<p>There is more to the city than the venue, and <a href="https://newcastlegateshead.com/business-directory/things-to-do/attractions" rel="nofollow" target="_blank">NewcastleGateshead</a> has the full list. A few favourites:</p>
<ul>
<li><strong>The Quayside</strong>, where the tilting Millennium Bridge and the Tyne Bridge sit side by side, with a Sunday market under the arches</li>
<li><strong>Grey Street and Grainger Town</strong>, Georgian streets that regularly top “best street in the UK” lists, plus the covered Grainger Market</li>
<li><strong>Newcastle Castle</strong>, the medieval keep that gave the city its name, with rooftop views over the Tyne</li>
<li><strong><a href="https://baltic.art/" rel="nofollow" target="_blank">BALTIC</a> and <a href="https://newcastlegateshead.com/business-directory/things-to-do/the-glasshouse-international-centre-for-music" rel="nofollow" target="_blank">The Glasshouse</a></strong>, contemporary art and music across the river in Gateshead</li>
<li><strong><a href="https://ouseburntrust.org.uk/" rel="nofollow" target="_blank">Ouseburn Valley</a></strong>, the creative quarter: converted warehouses, street art, a city farm and some of the best independent pubs in the North East</li>
<li><strong>St James’ Park</strong>, home of Newcastle United, with <a href="https://www.newcastleunited.com/en/st-james-park/stadium-tours" rel="nofollow" target="_blank">stadium tours</a> most days</li>
</ul>
<h2 id="sponsorship">Sponsorship</h2>
<p>If your organisation would like to support the conference, email <a href="mailto:events@jumpingrivers.com" rel="nofollow" target="_blank">events@jumpingrivers.com</a>.</p>
<p>Super early bird tickets are on sale until 8 January. <a href="https://www.eventbrite.co.uk/e/ai-in-production-2027-tickets-1990567797868" rel="nofollow" target="_blank">Book your place</a>.</p>
<p>
For updates and revisions to this article, see the <a href = "https://www.jumpingrivers.com/blog/ai-in-production-2027/">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/ai-in-production-2027/"> 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/announcing-ai-in-production-2027/">Announcing AI in Production 2027</a>]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">403082</post-id>	</item>
		<item>
		<title>From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse workshop</title>
		<link>https://www.r-bloggers.com/2026/08/from-raw-data-to-regulatory-results-clinical-trial-programming-in-r-using-the-pharmaverse-workshop/</link>
		
		<dc:creator><![CDATA[Dariia Mykhailyshyna]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 15:03:54 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://r-posts.com/?p=19382</guid>

					<description><![CDATA[<p>Join our workshop on From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse,  which is a part of our workshops for Ukraine series!  Here’s some more info:  Title: From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse Date: Thursday, ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/from-raw-data-to-regulatory-results-clinical-trial-programming-in-r-using-the-pharmaverse-workshop/">From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse workshop</a>]]></description>
										<content:encoded><![CDATA[<!-- 
<div style="min-height: 30px;">
[social4i size="small" align="align-left"]
</div>
-->

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;">
[This article was first published on  <strong><a href="http://r-posts.com/from-raw-data-to-regulatory-results-clinical-trial-programming-in-r-using-the-pharmaverse-workshop/"> R-posts.com</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
<p><span style="font-weight: 400">Join our workshop on From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse,</span> <span style="font-weight: 400"> which is a part of our workshops for Ukraine series! </span></p>
<br />
<p><b>Here’s some more info: </b></p>
<br />
<p><b>Title</b><span style="font-weight: 400">: From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse</span></p>
<p><b>Date</b><span style="font-weight: 400">: Thursday, September 17th, 18:00 – 20:00 CEST (Rome, Berlin, Paris timezone) </span></p>
<p><b>Speaker</b><span style="font-weight: 400">: Edoardo Mancini, </span><a href="https://manciniedoardo.github.io/" rel="nofollow" target="_blank"><span style="font-weight: 400">Edoardo</span></a><span style="font-weight: 400"> is a Senior Data Scientist working at Roche in the UK. His work involves leading statistical programming activities in early and late-stage clinical trials across multiple therapeutic areas, including Ophthalmology and Hematology. Edoardo is also a passionate advocate for R and Open Source, pursuing these interests in his current role as maintainer for the open-source </span><a href="https://pharmaverse.github.io/admiral/cran-release/" rel="nofollow" target="_blank"><span style="font-weight: 400">{admiral}</span></a><span style="font-weight: 400"> R package, which is part of the wider </span><a href="https://pharmaverse.org/" rel="nofollow" target="_blank"><span style="font-weight: 400">pharmaverse</span></a><span style="font-weight: 400"> and helps create ADaM datasets.</span><span style="font-weight: 400"> </span></p>
<p><b>Description: </b><span style="font-weight: 400">Have you ever wondered how raw patient data from a clinical trial transforms into the actual evidence used to approve new medicines? This two-hour, hands-on workshop aims to answer this question by giving you a glimpse into the clinical trial data pipeline. We’ll start with a crash course on industry data standards, tracing the journey from raw data to SDTM (Study Data Tabulation Model), into analysis-ready ADaM datasets, and finally into Tables, Listings, and Graphs (TLGs). From there, you will step into the shoes of a clinical statistical programmer: firstly, you will be guided through creating your own simple ADaM dataset(s) using the {admiral} package and test data. Then, you will bring your analyses to life using {ggplot2} and a tabulation package of your choice to build the kinds of polished visualizations and summary tables that drive real-world medical breakthroughs. No prior clinical trial experience is required, just bring your foundational R skills and your curiosity!</span></p>
<p><b>Minimal registration fee:</b><span style="font-weight: 400"> 20 euro (or 20 USD or 800 UAH)</span></p>
<br />
<p><span style="font-weight: 400">Please note that the registration confirmation is sent 1 day before the workshop to all registered participants rather than immediately after registration</span></p>
<br />
<p><b>How can I register?</b></p>
<br />
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">Go to </span><a href="https://bit.ly/3wvwMA6" rel="nofollow" target="_blank"><span style="font-weight: 400">https://bit.ly/3wvwMA6</span></a><span style="font-weight: 400"> or </span><a href="https://bit.ly/4aD5LMC" rel="nofollow" target="_blank"><span style="font-weight: 400">https://bit.ly/4aD5LMC</span></a><span style="font-weight: 400">  or  </span><a href="https://bit.ly/3PFxtNA" rel="nofollow" target="_blank"><span style="font-weight: 400">https://bit.ly/3PFxtNA</span></a><span style="font-weight: 400"> and donate</span><b> at least 20 euro</b><span style="font-weight: 400">. </span><span style="font-weight: 400">Feel free to donate more if you can, all proceeds go directly to support Ukraine.</span></li>
</ul>
<br />
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">Save your donation receipt (after the donation is processed, there is an option to enter your email address on the website to which the donation receipt is sent)</span></li>
</ul>
<br />
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">Fill in the </span><a href="https://forms.gle/1QGiAv1bFJEgbAfy9" rel="nofollow" target="_blank"><span style="font-weight: 400">registration form</span></a><span style="font-weight: 400">, attaching a screenshot of a donation receipt (please attach the screenshot of the donation receipt that was emailed to you rather than the page you see after donation).</span></li>
</ul>
<br />
<p><span style="font-weight: 400">If you are not personally interested in attending, you can also contribute by sponsoring a participation of a student, who will then be able to participate for free. If you choose to sponsor a student, all proceeds will also go directly to organisations working in Ukraine. You can either sponsor a particular student or you can leave it up to us so that we can allocate the sponsored place to students who have signed up for the waiting list.</span></p>
<br />
<p><b>How can I sponsor a student?</b></p>
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">Go to </span><a href="https://bit.ly/3wvwMA6" rel="nofollow" target="_blank"><span style="font-weight: 400">https://bit.ly/3wvwMA6</span></a><span style="font-weight: 400"> or </span><a href="https://bit.ly/4aD5LMC" rel="nofollow" target="_blank"><span style="font-weight: 400">https://bit.ly/4aD5LMC</span></a><span style="font-weight: 400">  or </span><a href="https://bit.ly/3PFxtNA" rel="nofollow" target="_blank"><span style="font-weight: 400">https://bit.ly/3PFxtNA</span></a><span style="font-weight: 400"> and donate </span><b>at least 20 euro </b><span style="font-weight: 400">(or 17 GBP or 20 USD or 800 UAH). </span><span style="font-weight: 400">Feel free to donate more if you can, all proceeds go to support Ukraine!</span></li>
</ul>
<br />
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">Save your donation receipt (after the donation is processed, there is an option to enter your email address on the website to which the donation receipt is sent)</span></li>
</ul>
<br />
<ul>
	<li style="font-weight: 400"><span style="font-weight: 400">Fill in the </span><a href="https://forms.gle/T3Kkh8Yhu8zRYyvx5" rel="nofollow" target="_blank"><span style="font-weight: 400">sponsorship form</span></a><span style="font-weight: 400">, attaching the screenshot of the donation receipt (please attach the screenshot of the donation receipt that was emailed to you rather than the page you see after the donation). You can indicate whether you want to sponsor a particular student or we can allocate this spot ourselves to the students from the waiting list. You can also indicate whether you prefer us to prioritize students from developing countries when assigning place(s) that you sponsored.</span></li>
</ul>
<br />
<br />
<p><span style="font-weight: 400">If you are a university student and cannot afford the registration fee, you can also sign up for the </span><b>waiting list</b> <a href="https://forms.gle/a44918vexV2kG8eaA" rel="nofollow" target="_blank"><span style="font-weight: 400">here</span></a><span style="font-weight: 400">. (Note that you are not guaranteed to participate by signing up for the waiting list).</span></p>
<br />
<br />
<p><span style="font-weight: 400">You can also find more information about this workshop series,  a schedule of our future workshops as well as a list of our past workshops which you can get the recordings &#038; materials </span><a href="http://bit.ly/3wBeY4S" rel="nofollow" target="_blank"><span style="font-weight: 400">here</span></a><span style="font-weight: 400">.</span></p>
<br />
<p><span style="font-weight: 400">Looking forward to seeing you during the workshop!</span></p>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<p><span style="font-weight: 400"> </span></p>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br /><hr style="border-top: black solid 1px" /><a href="http://r-posts.com/from-raw-data-to-regulatory-results-clinical-trial-programming-in-r-using-the-pharmaverse-workshop/" rel="nofollow" target="_blank">From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse workshop</a> was first posted on August 12, 2026 at 3:03 pm.<br />
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="http://r-posts.com/from-raw-data-to-regulatory-results-clinical-trial-programming-in-r-using-the-pharmaverse-workshop/"> R-posts.com</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/from-raw-data-to-regulatory-results-clinical-trial-programming-in-r-using-the-pharmaverse-workshop/">From Raw Data to Regulatory Results: Clinical Trial Programming in R using the pharmaverse workshop</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403088</post-id>	</item>
		<item>
		<title>Why ACF Is More Than Just a Plot</title>
		<link>https://www.r-bloggers.com/2026/08/why-acf-is-more-than-just-a-plot/</link>
		
		<dc:creator><![CDATA[M. Fatih Tüzen]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>1 Introduction<br />
Two observations can have the same units, come from the same source, and still require a different kind of reasoning simply because one was recorded before the other.<br />
That is the defining feature of time series data: order c...</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/why-acf-is-more-than-just-a-plot/">Why ACF Is More Than Just a Plot</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://mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/"> A Statistician&#039;s R Notebook</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-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/timeseries_acf.png?w=578&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:100.0%" data-recalc-dims="1"></p>
</figure>
</div>
<section id="introduction" class="level1" data-number="1">
<h1 data-number="1"><span class="header-section-number">1</span> Introduction</h1>
<p>Two observations can have the same units, come from the same source, and still require a different kind of reasoning simply because one was recorded before the other.</p>
<p>That is the defining feature of time series data: <strong>order carries information</strong>.</p>
<p>In an ordinary dataset, we often study the correlation between two different variables. In a time series, we can ask a different question: is the series related to an earlier version of itself? The <strong>autocorrelation function</strong>, or <strong>ACF</strong>, asks that question repeatedly—one delay at a time.</p>
<p>This article revolves around one central question:</p>
<blockquote class="blockquote">
<p>What does the ACF actually tell us about how a time series remembers its past?</p>
</blockquote>
<p>The most useful first intuition is simple:</p>
<blockquote class="blockquote">
<p><strong>The ACF is a picture of memory.</strong></p>
</blockquote>
<p>But that sentence needs a technical correction. The ACF does not detect every possible kind of memory, and it does not explain why dependence exists. It measures the <strong>linear association between values of the same series separated by different lags</strong>.</p>
<p>That distinction matters. A slowly decaying ACF may reflect genuine short-run persistence, but it can also be produced by trend or other forms of non-stationarity. If we interpret every tall bar as economic memory, the plot can become more misleading than informative.</p>
<p>This is the third article in a connected time series sequence. <a href="https://mfatihtuzen.github.io/posts/2026-04-16_timeseries_stationary/" rel="nofollow" target="_blank">The first article</a> explained why stationarity matters before modeling. <a href="https://mfatihtuzen.github.io/posts/2026-05-07_timeseries_differencing/" rel="nofollow" target="_blank">The second</a> showed that differencing changes both the statistical structure and the meaning of a series. Here, those ideas meet: we will use the ACF to see dependence—and to understand when that dependence should not be taken at face value.</p>
</section>
<section id="dataset-and-setup" class="level1" data-number="2">
<h1 data-number="2"><span class="header-section-number">2</span> Dataset and setup</h1>
<p>Our real-world example is the <strong>Industrial Production: Total Index</strong> (<code>INDPRO</code>) from <a href="https://fred.stlouisfed.org/series/INDPRO" rel="nofollow" target="_blank">FRED</a>, published by the Board of Governors of the Federal Reserve System.</p>
<p>The index measures real output in U.S. manufacturing, mining, and electric and gas utilities. It is monthly, seasonally adjusted, and expressed with 2017 equal to 100. The full FRED series begins in 1919. For the main analysis, we use the postwar sample beginning in January 1947. This keeps a long monthly history while avoiding some of the largest early-period changes in coverage and economic structure.</p>
<p>The local file <code>INDPRO.csv</code> was downloaded from:</p>
<p><a href="https://fred.stlouisfed.org/graph/fredgraph.csv?id=INDPRO" class="uri" rel="nofollow" target="_blank">https://fred.stlouisfed.org/graph/fredgraph.csv?id=INDPRO</a></p>
<p>Keeping the data beside this Quarto document makes the published analysis reproducible even if the online series is later revised.</p>
<div class="cell">
<pre>library(readr)
library(dplyr)
library(tidyr)
library(ggplot2)
library(scales)

theme_set(
  theme_minimal(base_size = 13) +
    theme(
      plot.title.position = &quot;plot&quot;,
      plot.caption.position = &quot;plot&quot;,
      panel.grid.minor = element_blank(),
      legend.position = &quot;bottom&quot;,
      strip.text = element_text(face = &quot;bold&quot;)
    )
)

ink &lt;- &quot;#1f4e5f&quot;
accent &lt;- &quot;#d95f02&quot;
purple &lt;- &quot;#7b3294&quot;
soft_blue &lt;- &quot;#8ecae6&quot;
grey &lt;- &quot;#6b7280&quot;</pre>
</div>
<div class="cell">
<pre>indpro &lt;- read_csv(&quot;INDPRO.csv&quot;, show_col_types = FALSE) |&gt;
  transmute(
    date = as.Date(observation_date),
    production = as.numeric(INDPRO)
  ) |&gt;
  filter(
    date &gt;= as.Date(&quot;1947-01-01&quot;),
    !is.na(production)
  ) |&gt;
  arrange(date) |&gt;
  mutate(
    log_growth = 100 * (log(production) - lag(log(production)))
  )

tidy_acf &lt;- function(x, lag_max = 36, series = &quot;Series&quot;) {
  x &lt;- x[is.finite(x)]
  estimate &lt;- stats::acf(x, lag.max = lag_max, plot = FALSE)$acf[, 1, 1]

  tibble(
    lag = 0:lag_max,
    acf = as.numeric(estimate),
    series = series,
    n = length(x),
    conf = 1.96 / sqrt(length(x))
  ) |&gt;
    filter(lag &gt; 0)
}

plot_acf &lt;- function(data, title, subtitle, x_label = &quot;Lag (months)&quot;) {
  ggplot(data, aes(lag, acf)) +
    geom_hline(yintercept = 0, color = grey, linewidth = 0.4) +
    geom_hline(
      aes(yintercept = conf),
      linetype = &quot;dashed&quot;, color = soft_blue, linewidth = 0.7
    ) +
    geom_hline(
      aes(yintercept = -conf),
      linetype = &quot;dashed&quot;, color = soft_blue, linewidth = 0.7
    ) +
    geom_segment(aes(xend = lag, y = 0, yend = acf), color = ink, linewidth = 0.7) +
    geom_point(color = ink, size = 1.5) +
    scale_x_continuous(breaks = scales::breaks_width(6)) +
    labs(
      title = title,
      subtitle = subtitle,
      x = x_label,
      y = &quot;Autocorrelation&quot;
    )
}</pre>
</div>
<div class="cell">
<pre>ggplot(indpro, aes(date, production)) +
  geom_line(linewidth = 0.7, color = ink) +
  labs(
    title = &quot;Does industrial production return to a stable level?&quot;,
    subtitle = &quot;U.S. Industrial Production Index, monthly and seasonally adjusted, January 1947–June 2026&quot;,
    x = NULL,
    y = &quot;Index (2017 = 100)&quot;,
    caption = &quot;Source: Board of Governors of the Federal Reserve System via FRED (INDPRO).&quot;
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i1.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/industrial-production-level-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The series moves through recessions, recoveries, long expansions, and the exceptional collapse in 2020. More importantly for our purpose, it does not fluctuate around a stable long-run mean. Nearby observations usually occupy similar parts of the long historical path.</p>
<p>That visual persistence is real. The question is what kind of persistence it represents.</p>
</section>
<section id="why-the-past-matters" class="level1" data-number="3">
<h1 data-number="3"><span class="header-section-number">3</span> Why the past matters</h1>
<p>Suppose industrial production is unusually high this month. Would that information change what you expect next month?</p>
<p>For many economic series, the answer is yes. Factories do not rebuild their output level from scratch every month. Production plans, demand, inventories, capacity, and business cycles evolve over time. Adjacent observations therefore tend to be related.</p>
<p>But “the past matters” is too vague for analysis. We need to specify <strong>which past</strong> and <strong>how strongly</strong> it is related to the present. That is where lags enter.</p>
<section id="what-a-lag-means" class="level2" data-number="3.1">
<h2 data-number="3.1" class="anchored" data-anchor-id="what-a-lag-means"><span class="header-section-number">3.1</span> What a lag means</h2>
<p>A lag is a time separation.</p>
<p>For monthly data:</p>
<ul>
<li>lag 1 compares each month with the previous month;</li>
<li>lag 2 compares each month with two months earlier;</li>
<li>lag 12 compares each month with the same calendar distance one year earlier.</li>
</ul>
<p>The unit is not always a month. Lag 1 means one observation step: one day for daily data, one quarter for quarterly data, and one year for annual data. A lag is meaningful only when interpreted in the frequency and context of the series.</p>
<p>To make the idea visible, let us temporarily use a controlled series and align it with a one-period-delayed copy of itself.</p>
<div class="cell">
<pre>set.seed(20260811)
lag_demo &lt;- tibble(
  time = 1:18,
  current = as.numeric(arima.sim(model = list(ar = 0.6), n = 18))
) |&gt;
  mutate(`Lagged by one step` = lag(current, 1)) |&gt;
  pivot_longer(
    cols = c(current, `Lagged by one step`),
    names_to = &quot;version&quot;,
    values_to = &quot;value&quot;
  ) |&gt;
  mutate(
    version = recode(version, current = &quot;Original series&quot;)
  )

ggplot(lag_demo, aes(time, value, color = version)) +
  geom_line(linewidth = 0.9, na.rm = TRUE) +
  geom_point(size = 2, na.rm = TRUE) +
  scale_color_manual(values = c(&quot;Original series&quot; = ink, &quot;Lagged by one step&quot; = accent)) +
  scale_x_continuous(breaks = 1:18) +
  labs(
    title = &quot;What does lag 1 do?&quot;,
    subtitle = &quot;It shifts the series by one observation so that each value can be paired with its immediate predecessor&quot;,
    x = &quot;Observation order&quot;,
    y = &quot;Value&quot;,
    color = NULL
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/lag-as-shift-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The orange line is not a second variable collected from somewhere else. It is the same series moved one step to the right. After the shift, every valid pair contains a current value and the value immediately before it.</p>
<p>Increase the shift to 2, 3, or 12, and we create different pairs. The ACF summarizes the correlation in each set of pairs.</p>
</section>
<section id="autocorrelation-versus-ordinary-correlation" class="level2" data-number="3.2">
<h2 data-number="3.2" class="anchored" data-anchor-id="autocorrelation-versus-ordinary-correlation"><span class="header-section-number">3.2</span> Autocorrelation versus ordinary correlation</h2>
<p>Ordinary correlation and autocorrelation use the same basic language of linear association, but they answer different questions.</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>Question</th>
<th>Ordinary correlation</th>
<th>Autocorrelation</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>What is compared?</td>
<td>Two different variables, such as income and consumption</td>
<td>One variable and a lagged copy of itself</td>
</tr>
<tr class="even">
<td>What creates the pairs?</td>
<td>Matching rows or observational units</td>
<td>Time separation</td>
</tr>
<tr class="odd">
<td>What does one coefficient describe?</td>
<td>Association between the two variables</td>
<td>Association at one specific lag</td>
</tr>
<tr class="even">
<td>Why do we need a function?</td>
<td>Often one coefficient is enough for one pair of variables</td>
<td>We need one coefficient for every lag of interest</td>
</tr>
</tbody>
</table>
<p>The word <strong>function</strong> in autocorrelation function matters. An ACF is not one number. It is the sequence of correlations obtained as the lag changes.</p>
</section>
</section>
<section id="building-an-acf-conceptually" class="level1" data-number="4">
<h1 data-number="4"><span class="header-section-number">4</span> Building an ACF conceptually</h1>
<p>The ACF can feel mysterious when it first appears as a finished forest of vertical bars. Its construction is much less mysterious:</p>
<ol type="1">
<li>choose a lag <img src="https://latex.codecogs.com/png.latex?k">;</li>
<li>shift the series by <img src="https://latex.codecogs.com/png.latex?k"> periods;</li>
<li>keep the overlapping current–past pairs;</li>
<li>calculate their correlation;</li>
<li>repeat for the next lag;</li>
<li>plot correlation against lag.</li>
</ol>
<p>Allison Horst’s illustrated <a href="https://allisonhorst.com/time-series-acf" rel="nofollow" target="_blank">Time Series ACF Series</a> teaches this especially well by treating lags as distances between generations. Her sequence begins with a present-day character and a line of ancestors. It then increases the generational distance one step at a time: parent at lag 1, grandparent at lag 2, great-grandparent at lag 3, and so on. At every step, the observed similarity or difference becomes one new bar in the ACF.</p>
<p>That progression reveals something a finished correlogram can hide: <strong>every bar comes from a new set of time-separated pairs</strong>. The ACF plot is the final summary of those repeated comparisons, not the starting point.</p>
<p>The visual below follows that same pedagogical sequence—change the separation, rebuild the pairs, and observe the correlation—but uses an independently simulated series and original R code rather than reproducing Horst’s artwork.</p>
<div class="cell">
<pre>set.seed(20260811)
pair_series &lt;- as.numeric(arima.sim(model = list(ar = 0.6), n = 180))

lag_pairs &lt;- bind_rows(lapply(c(1, 2, 6), function(k) {
  tibble(
    past = pair_series[1:(length(pair_series) - k)],
    current = pair_series[(k + 1):length(pair_series)],
    lag = paste0(&quot;Lag &quot;, k, &quot;   r = &quot;, round(cor(past, current), 2))
  )
}))

ggplot(lag_pairs, aes(past, current)) +
  geom_point(color = ink, alpha = 0.55, size = 1.7) +
  geom_smooth(method = &quot;lm&quot;, se = FALSE, color = accent, linewidth = 0.8) +
  facet_wrap(~ lag, nrow = 1) +
  coord_equal() +
  labs(
    title = &quot;How does similarity change as observations move farther apart?&quot;,
    subtitle = &quot;Each panel is an ordinary correlation computed from a different lagged pairing of the same AR(1) series&quot;,
    x = &quot;Past value&quot;,
    y = &quot;Current value&quot;
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/lag-pair-scatterplots-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>At lag 1, the point cloud has a clear positive slope: large values tend to follow large values, and small values tend to follow small values. The relationship weakens as the separation grows. Each reported correlation becomes one bar in the ACF.</p>
<p>Lag 0 is usually omitted from discussion because it compares every value with itself. Its autocorrelation is therefore exactly 1 and teaches us nothing new.</p>
<section id="the-formula-after-the-picture" class="level2" data-number="4.1">
<h2 data-number="4.1" class="anchored" data-anchor-id="the-formula-after-the-picture"><span class="header-section-number">4.1</span> The formula, after the picture</h2>
<p>For a series <img src="https://latex.codecogs.com/png.latex?y_1,%5Cldots,y_T">, the sample autocorrelation at lag <img src="https://latex.codecogs.com/png.latex?k"> can be written as</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ar_k%20=%0A%5Cfrac%7B%0A%5Csum_%7Bt=k+1%7D%5E%7BT%7D(y_t-%5Cbar%7By%7D)(y_%7Bt-k%7D-%5Cbar%7By%7D)%0A%7D%7B%0A%5Csum_%7Bt=1%7D%5E%7BT%7D(y_t-%5Cbar%7By%7D)%5E2%0A%7D.%0A"></p>
<p>The numerator asks whether current and lagged values tend to sit on the same side of the overall mean. When both are above the mean—or both below it—their contribution is positive. When they fall on opposite sides, their contribution is negative. The denominator scales the result so the coefficient lies between <img src="https://latex.codecogs.com/png.latex?-1"> and <img src="https://latex.codecogs.com/png.latex?1">.</p>
<p>The formula is useful because it makes the technical meaning precise. The ACF measures <strong>linear dependence at each lag</strong>. It is not a general detector of every form of dependence, and it is not a causal explanation.</p>
</section>
</section>
<section id="a-benchmark-with-no-linear-memory" class="level1" data-number="5">
<h1 data-number="5"><span class="header-section-number">5</span> A benchmark with no linear memory</h1>
<p>Before interpreting structured series, we need a benchmark. <strong>White noise</strong> contains independent shocks with constant mean and variance. Its theoretical autocorrelation is zero at every non-zero lag.</p>
<p>That does not mean a sample ACF will display perfect zeros. A finite random sample produces small positive and negative correlations by chance.</p>
<div class="cell">
<pre>set.seed(20260811)
white_noise &lt;- rnorm(400)

white_series &lt;- tibble(
  index = 1:400,
  value = white_noise
)

white_acf &lt;- tidy_acf(white_noise, lag_max = 30, series = &quot;White noise&quot;)

white_series |&gt;
  filter(index &lt;= 160) |&gt;
  ggplot(aes(index, value)) +
  geom_hline(yintercept = 0, color = grey, linewidth = 0.4) +
  geom_line(color = ink, linewidth = 0.6) +
  labs(
    title = &quot;What does a process without linear memory look like?&quot;,
    subtitle = &quot;The path is unpredictable, although short accidental runs still occur in a finite sample&quot;,
    x = &quot;Observation order (first 160 shown)&quot;,
    y = &quot;Value&quot;
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/white-noise-benchmark-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
<pre>plot_acf(
  white_acf,
  title = &quot;The sample ACF of white noise is close to zero—not exactly zero&quot;,
  subtitle = &quot;Small spikes are expected from sampling variation; dashed lines show approximate pointwise 95% bounds&quot;,
  x_label = &quot;Lag (observation steps)&quot;
)</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/white-noise-benchmark-2.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The time plot still contains clusters that a human eye can mistake for a pattern. The ACF provides a disciplined comparison: no systematic decay and no repeated structure dominate the plot. A few bars may cross a confidence line by chance, especially when many lags are inspected.</p>
</section>
<section id="controlled-memory-ar1-processes" class="level1" data-number="6">
<h1 data-number="6"><span class="header-section-number">6</span> Controlled memory: AR(1) processes</h1>
<p>White noise gives us one extreme. To isolate different degrees of persistence, consider an autoregressive process of order one:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ax_t%20=%20%5Cphi%20x_%7Bt-1%7D%20+%20%5Cvarepsilon_t,%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%5Cvarepsilon_t"> is a new white-noise shock and <img src="https://latex.codecogs.com/png.latex?%5Cphi"> controls how much of the previous value carries into the present.</p>
<p>For a stationary AR(1) process with <img src="https://latex.codecogs.com/png.latex?%7C%5Cphi%7C%3C1">, the theoretical ACF is</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Crho_k%20=%20%5Cphi%5Ek.%0A"></p>
<p>This is not a rule to memorize. It tells a story. A shock is multiplied by <img src="https://latex.codecogs.com/png.latex?%5Cphi"> after one period, by <img src="https://latex.codecogs.com/png.latex?%5Cphi%5E2"> after two periods, and so on. When <img src="https://latex.codecogs.com/png.latex?%5Cphi"> is small, its influence fades quickly. When <img src="https://latex.codecogs.com/png.latex?%5Cphi"> is close to 1, it fades slowly.</p>
<div class="cell">
<pre>set.seed(20260811)

phi_values &lt;- c(0.2, 0.6, 0.9)

ar_series &lt;- bind_rows(lapply(phi_values, function(phi) {
  tibble(
    index = 1:400,
    value = as.numeric(arima.sim(model = list(ar = phi), n = 400)),
    process = paste0(&quot;phi = &quot;, phi)
  )
})) |&gt;
  mutate(process = factor(process, levels = paste0(&quot;phi = &quot;, phi_values)))

ar_series |&gt;
  filter(index &lt;= 160) |&gt;
  ggplot(aes(index, value)) +
  geom_hline(yintercept = 0, color = grey, linewidth = 0.35) +
  geom_line(color = ink, linewidth = 0.55) +
  facet_wrap(~ process, ncol = 1, scales = &quot;free_y&quot;) +
  labs(
    title = &quot;How does persistence change as phi increases?&quot;,
    subtitle = &quot;Larger values of phi create longer runs above or below the mean in otherwise comparable AR(1) processes&quot;,
    x = &quot;Observation order (first 160 shown)&quot;,
    y = NULL
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/simulate-ar1-processes-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>At <img src="https://latex.codecogs.com/png.latex?%5Cphi=0.2">, shocks disappear quickly and the series frequently changes direction. At <img src="https://latex.codecogs.com/png.latex?%5Cphi=0.6">, runs become more visible. At <img src="https://latex.codecogs.com/png.latex?%5Cphi=0.9">, the process can remain high or low for long stretches even though it is still stationary in theory.</p>
<p>The time plots suggest different kinds of memory. Their ACFs make the contrast explicit.</p>
<div class="cell">
<pre>ar_acf &lt;- ar_series |&gt;
  group_by(process) |&gt;
  summarise(acf_data = list(tidy_acf(value, lag_max = 24)), .groups = &quot;drop&quot;) |&gt;
  unnest(acf_data)

ggplot(ar_acf, aes(lag, acf)) +
  geom_hline(yintercept = 0, color = grey, linewidth = 0.4) +
  geom_hline(aes(yintercept = conf), linetype = &quot;dashed&quot;, color = soft_blue) +
  geom_hline(aes(yintercept = -conf), linetype = &quot;dashed&quot;, color = soft_blue) +
  geom_segment(aes(xend = lag, y = 0, yend = acf), color = ink, linewidth = 0.65) +
  geom_point(color = ink, size = 1.3) +
  geom_line(aes(y = as.numeric(sub(&quot;phi = &quot;, &quot;&quot;, process))^lag), color = accent, linewidth = 0.8) +
  facet_wrap(~ process, ncol = 1) +
  scale_x_continuous(breaks = scales::breaks_width(4)) +
  labs(
    title = &quot;What does controlled memory look like in an ACF?&quot;,
    subtitle = &quot;Sample autocorrelations (bars) follow the theoretical AR(1) decay rho[k] = phi^k (orange line)&quot;,
    x = &quot;Lag (observation steps)&quot;,
    y = &quot;Autocorrelation&quot;
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i1.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/ar1-acf-comparison-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The important feature is not one isolated spike. It is the <strong>shape across lags</strong>. Weak persistence disappears quickly; strong persistence produces a long, smooth decay. This is why an ACF should be read as a profile, not as a collection of unrelated significance tests.</p>
</section>
<section id="the-real-data-acf-persistence-everywhere" class="level1" data-number="7">
<h1 data-number="7"><span class="header-section-number">7</span> The real-data ACF: persistence everywhere</h1>
<p>We can now return to industrial production.</p>
<div class="cell">
<pre>level_acf &lt;- tidy_acf(
  indpro$production,
  lag_max = 36,
  series = &quot;Industrial production level&quot;
)

plot_acf(
  level_acf,
  title = &quot;How strongly is the industrial production level related to its past?&quot;,
  subtitle = &quot;The ACF remains above 0.90 even at lag 36, producing an extremely slow decay&quot;,
  x_label = &quot;Lag (months)&quot;
)</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/industrial-production-level-acf-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The raw level has an autocorrelation of approximately 0.998 at lag 1 and 0.906 at lag 36. If the ACF were interpreted mechanically, we might conclude that industrial production has an extraordinarily long economic memory.</p>
<p>That conclusion would be too quick.</p>
</section>
<section id="the-persistence-versus-non-stationarity-trap" class="level1" data-number="8">
<h1 data-number="8"><span class="header-section-number">8</span> The persistence versus non-stationarity trap</h1>
<p>Recall what the level plot showed: industrial production in later decades generally occupies a different range from production in the early postwar years. When a series trends, observations that are close in time also tend to be close in level. The ACF records that similarity, even if it is driven by movement in the mean rather than a stable dependence mechanism.</p>
<p>This is the central trap:</p>
<blockquote class="blockquote">
<p>A slow ACF decay in a non-stationary series is evidence of persistence in the observed levels, but it is not automatically evidence of a stable, long-lived economic memory.</p>
</blockquote>
<p>The distinction is subtle but practical. The sample ACF averages relationships across the whole period as though one mean and one dependence structure were meaningful. A strongly trending series violates that interpretation. Its high correlations partly compare “early with early” and “late with late.” Time location itself is doing much of the work.</p>
<p>This is why the earlier article on <a href="https://mfatihtuzen.github.io/posts/2026-04-16_timeseries_stationary/" rel="nofollow" target="_blank">stationarity</a> comes before ACF interpretation in this series. ACF is most interpretable as a stable memory profile when the underlying process is at least approximately stationary.</p>
</section>
<section id="what-changes-after-transformation" class="level1" data-number="9">
<h1 data-number="9"><span class="header-section-number">9</span> What changes after transformation?</h1>
<p>To shift the question from the <strong>level of production</strong> to its <strong>month-to-month proportional change</strong>, we calculate</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ag_t%20=%20100%5Cleft%5B%5Clog(x_t)-%5Clog(x_%7Bt-1%7D)%5Cright%5D.%0A"></p>
<p>For small changes, <img src="https://latex.codecogs.com/png.latex?g_t"> is approximately the monthly percentage growth rate. This transformation does more than make the plot look stable. It changes the object being studied—from how high production is to how quickly it is changing.</p>
<div class="cell">
<pre>ggplot(indpro, aes(date, log_growth)) +
  geom_hline(yintercept = 0, color = grey, linewidth = 0.4) +
  geom_line(linewidth = 0.6, color = accent, na.rm = TRUE) +
  coord_cartesian(ylim = c(-15, 10)) +
  labs(
    title = &quot;Does monthly industrial production growth behave more stably?&quot;,
    subtitle = &quot;Log growth removes the changing level, but recessions and the exceptional 2020 shock remain visible&quot;,
    x = NULL,
    y = &quot;Monthly log growth (%)&quot;,
    caption = &quot;The vertical scale is limited to preserve readability; the April 2020 value falls below the displayed range.&quot;
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/industrial-production-growth-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The transformed series fluctuates around a much more stable center. It still contains economic structure: the first months of recessions tend to cluster, and volatility changes during major disruptions. Transformation has not turned the data into white noise, nor should that be the goal.</p>
<p>Now compare the ACFs on the two scales.</p>
<div class="cell">
<pre>growth_acf &lt;- tidy_acf(
  indpro$log_growth,
  lag_max = 36,
  series = &quot;Monthly log growth&quot;
)

acf_comparison &lt;- bind_rows(
  level_acf |&gt;
    mutate(series = &quot;Production level&quot;),
  growth_acf |&gt;
    mutate(series = &quot;Monthly log growth&quot;)
) |&gt;
  mutate(series = factor(series, levels = c(&quot;Production level&quot;, &quot;Monthly log growth&quot;)))

ggplot(acf_comparison, aes(lag, acf)) +
  geom_hline(yintercept = 0, color = grey, linewidth = 0.4) +
  geom_hline(aes(yintercept = conf), linetype = &quot;dashed&quot;, color = soft_blue) +
  geom_hline(aes(yintercept = -conf), linetype = &quot;dashed&quot;, color = soft_blue) +
  geom_segment(aes(xend = lag, y = 0, yend = acf), color = ink, linewidth = 0.65) +
  geom_point(color = ink, size = 1.3) +
  facet_wrap(~ series, ncol = 1, scales = &quot;free_y&quot;) +
  scale_x_continuous(breaks = scales::breaks_width(6)) +
  labs(
    title = &quot;What part of the apparent memory survives transformation?&quot;,
    subtitle = &quot;The slow decay in levels largely disappears when the question changes to monthly proportional growth&quot;,
    x = &quot;Lag (months)&quot;,
    y = &quot;Autocorrelation&quot;
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i1.wp.com/mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/index_files/figure-html/level-growth-acf-comparison-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The contrast is the article’s main empirical result. The raw level ACF remains close to 1 across three years of lags. The growth ACF drops from about 0.33 at lag 1 to about 0.1 at lag 2, then stays much closer to zero.</p>
<p>Some short-run dependence remains. That is economically plausible and potentially modelable. But most of the dramatic “memory” in the level ACF was tied to the non-stationary level, not to a stationary mechanism transmitting nearly the same influence for years.</p>
<p>The negative correlations around annual and two-year lags should also be interpreted cautiously. They may reflect business-cycle dynamics, historical episodes, changing volatility, data construction, or sampling variation. An ACF identifies a pattern to investigate; it does not name its cause.</p>
<p>This returns us to the key lesson from <a href="https://mfatihtuzen.github.io/posts/2026-05-07_timeseries_differencing/" rel="nofollow" target="_blank">the differencing article</a>: a transformation does not merely clean the same variable. It changes the question.</p>
</section>
<section id="how-to-read-an-acf" class="level1" data-number="10">
<h1 data-number="10"><span class="header-section-number">10</span> How to read an ACF</h1>
<p>An ACF becomes more useful when read in a consistent order.</p>
<section id="start-with-the-sign" class="level2" data-number="10.1">
<h2 data-number="10.1" class="anchored" data-anchor-id="start-with-the-sign"><span class="header-section-number">10.1</span> Start with the sign</h2>
<ul>
<li><strong>Positive autocorrelation</strong> means high values tend to follow high values, and low values tend to follow low values, at that lag.</li>
<li><strong>Negative autocorrelation</strong> means high values tend to be paired with low values, and vice versa. This can indicate alternation or oscillation.</li>
<li><strong>Near-zero autocorrelation</strong> means little linear association at that lag. It does not prove independence.</li>
</ul>
</section>
<section id="then-consider-magnitude" class="level2" data-number="10.2">
<h2 data-number="10.2" class="anchored" data-anchor-id="then-consider-magnitude"><span class="header-section-number">10.2</span> Then consider magnitude</h2>
<p>An autocorrelation near 1 or <img src="https://latex.codecogs.com/png.latex?-1"> indicates a strong linear relationship. A value near zero indicates a weak linear relationship. Magnitude should be judged together with sample size, context, and the rest of the ACF—not by a universal cutoff.</p>
</section>
<section id="read-the-decay-not-only-the-spikes" class="level2" data-number="10.3">
<h2 data-number="10.3" class="anchored" data-anchor-id="read-the-decay-not-only-the-spikes"><span class="header-section-number">10.3</span> Read the decay, not only the spikes</h2>
<ul>
<li>a fast decay suggests short memory;</li>
<li>a slow, smooth decay can indicate strong persistence, but may also signal trend or non-stationarity;</li>
<li>an alternating decay suggests oscillatory behavior;</li>
<li>repeated peaks at seasonal lags suggest a seasonal pattern.</li>
</ul>
<p>For monthly data, peaks at 12, 24, and 36 have a different interpretation from peaks at 1, 2, and 3. The horizontal axis is elapsed time, not merely a sequence of bar numbers.</p>
</section>
<section id="read-the-plot-beside-the-series" class="level2" data-number="10.4">
<h2 data-number="10.4" class="anchored" data-anchor-id="read-the-plot-beside-the-series"><span class="header-section-number">10.4</span> Read the plot beside the series</h2>
<p>Never interpret the ACF without first looking at the time plot. Trend, seasonality, structural breaks, outliers, and changing variance can all shape autocorrelations. The ACF compresses the series; compression is useful, but it hides time location.</p>
</section>
</section>
<section id="what-do-the-confidence-bands-mean" class="level1" data-number="11">
<h1 data-number="11"><span class="header-section-number">11</span> What do the confidence bands mean?</h1>
<p>The dashed lines in the plots are approximately</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cpm%20%5Cfrac%7B1.96%7D%7B%5Csqrt%7BT%7D%7D,%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?T"> is the number of observations. Under a white-noise benchmark, a sample autocorrelation outside these pointwise bounds would be unusual for one prespecified lag.</p>
<p>Three cautions are essential.</p>
<p>First, the lines are <strong>reference bands, not proof of a model</strong>. A bar inside the band does not establish independence, and a bar outside it does not explain the dependence.</p>
<p>Second, they are pointwise. If we inspect many lags, the chance that at least one bar crosses a 95% line is greater than 5%. One isolated crossing can easily be sampling variation.</p>
<p>Third, the simple bands are derived for a white-noise comparison. They should not be used as a mechanical significance filter for a visibly non-stationary series such as the industrial production level.</p>
<p>The pattern across lags and the behavior of the original series matter more than a binary inside/outside classification.</p>
</section>
<section id="common-mistakes" class="level1" data-number="12">
<h1 data-number="12"><span class="header-section-number">12</span> Common mistakes</h1>
<p>Most ACF mistakes are interpretive rather than computational.</p>
<p><strong>Mistake 1: treating one spike as a complete structure</strong></p>
<p>One large coefficient can be interesting, but dependence is usually understood from the profile across neighboring and contextually meaningful lags.</p>
<p><strong>Mistake 2: reading slow decay as pure economic memory</strong></p>
<p>Trend and other forms of non-stationarity can create large positive autocorrelations over many lags. Inspect and stabilize the series before giving the decay a stationary interpretation.</p>
<p><strong>Mistake 3: assuming zero autocorrelation means randomness</strong></p>
<p>The ACF measures linear dependence. A process can have zero autocorrelation while retaining nonlinear dependence, changing variance, or other non-random structure.</p>
<p><strong>Mistake 4: equating correlation with causation</strong></p>
<p>Autocorrelation says that values separated by a lag move together linearly. It does not show that the earlier observation causes the later one. Both may respond to shared dynamics, trends, seasonality, policy, or measurement procedures.</p>
<p><strong>Mistake 5: ignoring the meaning of a lag</strong></p>
<p>Lag 12 is one year for monthly data but 12 days for daily data. The same numerical lag can represent a completely different mechanism at a different frequency.</p>
<p><strong>Mistake 6: using confidence bands as a model-selection vending machine</strong></p>
<p>Counting significant bars is not a substitute for understanding the series, checking stationarity, comparing plausible models, and diagnosing residuals.</p>
</section>
<section id="a-practical-workflow" class="level1" data-number="13">
<h1 data-number="13"><span class="header-section-number">13</span> A practical workflow</h1>
<p>When using an ACF in real analysis, follow this sequence:</p>
<ol type="1">
<li><strong>Define the series and its frequency.</strong> Know what one lag means in calendar and substantive terms.</li>
<li><strong>Plot the series first.</strong> Look for trend, seasonality, breaks, outliers, and changing variance.</li>
<li><strong>Clarify the analytical question.</strong> Are you studying levels, absolute changes, proportional growth, or residuals?</li>
<li><strong>Assess whether a stationary interpretation is plausible.</strong> Transform or difference only when the transformation matches the question.</li>
<li><strong>Inspect the ACF as a shape.</strong> Read sign, magnitude, decay, oscillation, and seasonal repetition together.</li>
<li><strong>Use confidence bands cautiously.</strong> Treat them as a white-noise reference, not a final decision rule.</li>
<li><strong>Investigate plausible mechanisms.</strong> Ask what economic or operational process could create the observed lags.</li>
<li><strong>Revisit the time plot.</strong> Confirm that the compressed ACF story is consistent with what happened over time.</li>
<li><strong>Use the ACF as one input to modeling.</strong> Model comparison and residual diagnostics must still follow.</li>
</ol>
<p>This workflow prevents two opposite errors: ignoring genuine dependence and inventing a memory story from non-stationary levels.</p>
</section>
<section id="final-thoughts" class="level1" data-number="14">
<h1 data-number="14"><span class="header-section-number">14</span> Final thoughts</h1>
<p>The ACF is more than a plot because it changes a vague statement—“the past matters”—into a profile of linear dependence across time separations.</p>
<p>White noise showed what no systematic linear memory looks like. AR(1) simulations isolated how persistence changes the rate of decay. Industrial production then demonstrated the harder real-world lesson: a striking ACF can be statistically correct and still invite the wrong interpretation.</p>
<p>The raw production level remembers its historical position so strongly that correlations remain near 1 for years. After moving to monthly log growth, most of that slow decay disappears and a shorter dependence pattern remains. The transformation reveals that the original ACF mixed at least two ideas: short-run dynamics and a changing long-run level.</p>
<p>So the memorable idea needs its full version:</p>
<blockquote class="blockquote">
<p><strong>The ACF is a picture of linear memory—but first make sure the series is capable of remembering in a stable way.</strong></p>
</blockquote>
<p>The next article will turn to the <strong>partial autocorrelation function (PACF)</strong>. The purpose will not be to memorize AR and MA identification rules, but to ask a more precise question: what does a lag add after the shorter lags have already had their say?</p>
</section>
<section id="references-and-further-reading" class="level1" data-number="15">
<h1 data-number="15"><span class="header-section-number">15</span> References and further reading</h1>
<p><strong>Data and methodology</strong></p>
<ul>
<li>Board of Governors of the Federal Reserve System. <a href="https://fred.stlouisfed.org/series/INDPRO" rel="nofollow" target="_blank">Industrial Production: Total Index (INDPRO)</a>. FRED, Federal Reserve Bank of St. Louis.</li>
<li>Board of Governors of the Federal Reserve System. <a href="https://www.federalreserve.gov/releases/g17/IpNotes.htm" rel="nofollow" target="_blank">Industrial Production and Capacity Utilization: Explanatory Notes</a>.</li>
</ul>
<p><strong>Autocorrelation and time series foundations</strong></p>
<ul>
<li>Hyndman, R. J., and Athanasopoulos, G. <a href="https://otexts.com/fpp3/acf.html" rel="nofollow" target="_blank"><em>Forecasting: Principles and Practice</em>, 3rd edition—Autocorrelation</a>. OTexts.</li>
<li>NIST/SEMATECH. <a href="https://www.itl.nist.gov/div898/handbook/eda/section3/autocopl.htm" rel="nofollow" target="_blank">e-Handbook of Statistical Methods: Autocorrelation Plot</a>.</li>
<li>R Core Team. <a href="https://stat.ethz.ch/R-manual/R-devel/library/stats/html/acf.html" rel="nofollow" target="_blank"><code>acf</code>: Auto- and Cross-Covariance and Correlation Function Estimation</a>. R documentation.</li>
</ul>
<p><strong>Visual intuition</strong></p>
<ul>
<li>Horst, A. <a href="https://allisonhorst.com/time-series-acf" rel="nofollow" target="_blank">Time Series ACF Series</a>. Artwork by Allison Horst. The illustrated sequence uses generations to show how lag-specific comparisons become the bars of an ACF. The analytical figures in this article are independent recreations made in R and do not reproduce the artwork.</li>
</ul>
<p><strong>Earlier articles in this series</strong></p>
<ul>
<li>Tüzen, M. F. <a href="https://mfatihtuzen.github.io/posts/2026-04-16_timeseries_stationary/" rel="nofollow" target="_blank">Why Most Time Series Models Fail Before They Start</a>.</li>
<li>Tüzen, M. F. <a href="https://mfatihtuzen.github.io/posts/2026-05-07_timeseries_differencing/" rel="nofollow" target="_blank">Differencing: A Transformation or a Trap?</a>.</li>
</ul>
<hr>
<!--
SEO Title: Why ACF Is More Than Just a Plot: A Practical Guide to Time Series Memory
SEO Description: Learn what the autocorrelation function really measures, how to read lags and decay, and why non-stationary data can create misleading persistence.
Medium Topics: Time Series Analysis, Data Science, R Programming, Statistics, Forecasting
Hero image: timeseries_acf.png
-->


<!-- -->

</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://mfatihtuzen.github.io/posts/2026-08-11_timeseries_acf/"> A Statistician&#039;s R Notebook</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/why-acf-is-more-than-just-a-plot/">Why ACF Is More Than Just a Plot</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403100</post-id>	</item>
		<item>
		<title>The Resource Project: Tell R How Much Memory You Need</title>
		<link>https://www.r-bloggers.com/2026/08/the-resource-project-tell-r-how-much-memory-you-need/</link>
		
		<dc:creator><![CDATA[JottR on R]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.jottr.org/2026/08/11/roadmap-resources-memory/</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>Fit a cross-validated elastic net with cv.glmnet(x, y) on a design matrix that comfortably fits in memory, and the call can still fail. The reason is that the function needs several times the size of x while it runs, and nothing anywhere in your sc...</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/the-resource-project-tell-r-how-much-memory-you-need/">The Resource Project: Tell R How Much Memory You Need</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/11/roadmap-resources-memory/"> 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>


<p><img src="https://i2.wp.com/www.jottr.org/post/oom-dialog-macos.png?w=578&#038;ssl=1" alt="A macOS-style alert dialog with an R application icon marked by a red exclamation badge. The heading reads 'Out of memory' and the message below reads 'R cannot allocate a vector of size 7.5 GiB.' A blue OK button sits at the bottom." style="width: 50%; float: right; margin-left: 0.3em;" data-recalc-dims="1"/></p>

<p>Fit a cross-validated elastic net with <code>cv.glmnet(x, y)</code> on a design matrix that comfortably fits in memory, and the call can still fail. The reason is that the function needs several times the size of <code>x</code> while it runs, and nothing anywhere in your script says so. This post is about giving that requirement a home &#8211; a <em>memory specification</em>, written in R next to the code that knows it &#8211; first for a plain sequential call, then for the same call running in parallel.</p>

<div class="alert info ">
  <p>This post describes experimental ideas and future plans for the Futureverse ecosystem. With one exception &#8211; ‘futurize()’, which is on CRAN today &#8211; the features shown here are not yet implemented.</p>
</div>

<h2 id="tl-dr">TL;DR</h2>

<p>Write down what the call needs, as one annotation on otherwise ordinary code;</p>

<pre>fit &lt;- cv.glmnet(x, y) |&gt; resources(memory(4 * object.size(x)))
</pre>

<p>Better still, the memory specification need not stay at the call site. Once <code>cv.glmnet()</code> carries its own declaration, parallelization frameworks can make use of it, e.g.</p>

<pre>fits &lt;- lapply(xs, FUN = cv.glmnet, y = y) |&gt; futurize()
</pre>

<p>Either way, that one declaration is meant to do two jobs;</p>

<ol>
<li>checked <strong>before</strong> the work starts &#8211; parallel or not &#8211; it fails fast instead of minutes or hours later, and</li>
<li>handed to a parallel framework, it also decides how many tasks can run in parallel.</li>
</ol>

<p>The goal of the <em><a href="https://www.futureverse.org/roadmap/resources.html" rel="nofollow" target="_blank">Resource Project</a></em> is to study, design, and implement these features in the <a href="https://www.futureverse.org/" rel="nofollow" target="_blank">Futureverse</a>. Feedback and suggestions are welcome.</p>

<h2 id="problem-we-have-no-way-of-declaring-memory-needs">Problem: We have no way of declaring memory needs</h2>

<p>We check arguments all the time. A <code>stopifnot(is.matrix(x))</code> at the top of a function is second nature and in our muscle memory. It is useful because it helps functions fail fast when the wrong arguments are passed, and because we can give an informative error message. However, <strong>we have nothing for checking whether the machine is actually capable of running the function</strong>.</p>

<p>For example, you might not have enough memory available to perform a calculation. If you run out of memory, you might get:</p>

<pre>&gt; fit &lt;- cv.glmnet(x, y)
Error: cannot allocate vector of size 1.7 Gb
</pre>

<p>Worse is when the operating system steps in first and kills the R process outright. There is no R error to catch, no traceback, and no chance to shut down gracefully:</p>

<pre>$ Rscript fit-model.R
Killed
$
</pre>

<p>Neither one tells you how much was needed, and both arrive after the work is already underway.</p>

<p>After you have identified the problem to be a memory issue, it often becomes a trial-and-error game, and it can take a while to figure out how much memory you need. The finding might then end up as a source-code comment in the script, or as a rarely-read sentence in a package help page. Either way, since the memory needs are not in the code, R cannot check it, cannot size the parallelization from it, and cannot give informative error messages when you run out of memory.</p>

<h2 id="proposal-check-memory-and-fail-fast">Proposal: Check memory and fail fast</h2>

<p>From rudimentary measurements and code inspections of <strong><a href="https://glmnet.stanford.edu/" rel="nofollow" target="_blank">glmnet</a></strong>, I found that <code>cv.glmnet(x, y)</code> needs roughly 3-4 times the memory of <code>object.size(x)</code> in addition to <code>x</code> itself. We can improve on this memory model<sup class="footnote-ref" id="fnref:model"><a href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fn:model" rel="nofollow" target="_blank">1</a></sup>, but for simplicity, let’s assume <code>cv.glmnet(x, y)</code> needs <code>4 * object.size(x)</code> <em>additional</em> memory to be successful.</p>

<p>Let’s take a numeric design matrix of 250,000 rows and 1,000 columns, which clocks in around 1.9 GiB of memory, as an example:</p>

<pre>x &lt;- matrix(rnorm(250e3 * 1e3), nrow = 250e3, ncol = 1e3)
format(object.size(x), units = &quot;GiB&quot;)
#&gt; [1] &quot;1.9 GiB&quot;
</pre>

<p>Imagine a <code>freeMemory()</code> function for querying how much memory the current R process has available. With that, we could gatekeep our <code>fit &lt;- cv.glmnet(x, y)</code> call as:</p>

<pre>stopifnot(freeMemory() &gt;= 4 * object.size(x))
#&gt; Error: freeMemory() &gt;= 4 * object.size(x) is not TRUE
</pre>

<p>That fails <em>instantly</em> and <em>before</em> attempting the model fit, if there is not enough memory available. We could also imagine a richer vocabulary that provides us with more informative error messages, e.g.</p>

<pre>assert_resources(memory(4 * object.size(x)))
#&gt; Error: UnmetResourceError: requires memory 7.5 GiB, available 3.1 GiB
</pre>

<p>In this project, we’re proposing an <code>expr |&gt; resources(...)</code> syntax for declaring and asserting resource needs, including memory requirements, next to the code where it applies. In our example, it would look like:</p>

<pre>fit &lt;- cv.glmnet(x, y) |&gt; resources(memory(4 * object.size(x)))
#&gt; Error: UnmetResourceError: cv.glmnet(x, y) requires memory 7.5 GiB, available 3.1 GiB
</pre>

<p>This syntax preserves the original code and logic as-is, while allowing you to declare resource requirements that R can act on. In its most basic form, it effectively works like:</p>

<pre>fit &lt;- {
  assert_resources(memory(4 * object.size(x)))
  cv.glmnet(x, y)
}
</pre>

<p>If you wonder whether this is worth the extra code, consider who pays when it is missing. Even an experienced user might spend hours locating the reason for a “cannot allocate vector” error or an “OOM-killed” message. A researcher new to high-performance compute (HPC) clusters might lose a day to it, file a support ticket, or quietly conclude that R cannot handle their data.</p>

<p><em>The goal of this project is to not only reduce the amount of wasted compute resources, but also wasted human resources.</em></p>

<h3 id="batch-still-sequential">Batch, still sequential</h3>

<p>Now suppose we are not fitting one model but ten. Consider a study of patients with some clinical endpoint (<code>y</code>) profiled in several ways, including expression, copy number, miRNA, methylation, somatic mutation, chromatin accessibility, proteomics, phosphoproteomics, metabolomics, and lipidomics. Assume we want to know which of these omics datasets predict the outcome the best. We have one <code>y</code>, and ten <code>x</code> design matrices in a list:</p>

<pre>xs &lt;- list(...)  # one design matrix per assay, ~8 GiB of them in total
</pre>

<p>Without memory protection, we would fit these models as:</p>

<pre>fits &lt;- lapply(xs, function(assay) cv.glmnet(assay, y))
</pre>

<p>The resolution of the technologies varies greatly, so the different <code>x</code> matrices vary greatly in size. Methylation is often of the highest resolution. Let’s assume its design matrix is 1.9 GiB in position <code>xs[[4]]</code>, and the smallest is a tenth of that. Similarly to before, we could protect against memory overuse by using:</p>

<pre>fits &lt;- lapply(xs, function(assay) {
  cv.glmnet(assay, y) |&gt; resources(memory(4 * object.size(assay)))
})
#&gt; Error: UnmetResourceError: cv.glmnet(assay, y) requires memory 7.5 GiB, available 3.1 GiB
</pre>

<p>This tells you that one of the model fits would fail due to insufficient memory. Unfortunately, it does not fail instantly - it only fails when it tries to fit the too-large design matrix. Given that the largest matrix in this case happens to be in position four, you have already wasted efforts processing three cross-validation fits before failing. In the worst case, it could have processed nine out of the ten design matrices, before failing.</p>

<p>It would be better if the map-reduce call fails instantly, before attempting any model fits at all. We could make this happen if we move the declaration outside, so that the whole check happens prior to the model fits:</p>

<pre>fits &lt;- lapply(xs, function(assay) cv.glmnet(assay, y)) |&gt;
  resources(function(assay) memory(4 * object.size(assay)))
#&gt; Error: UnmetResourceError: cv.glmnet(assay, y) requires memory 7.5 GiB for xs[[4]], available 3.1 GiB
</pre>

<p>What is new is that the specification is a <em>function</em> rather than a fixed declaration. Somewhat simplified, this effectively checks the memory needs for all cross-validation fits first, before fitting them. Something like:</p>

<pre>fits &lt;- {
  assert_resources(lapply(xs, function(assay) memory(4 * object.size(assay))))
  lapply(xs, function(assay) cv.glmnet(assay, y))
}
</pre>

<p>This is the argument from the top of this post applied to a batch rather than to a call, and it is where failing fast saves the most. You avoid wasting all calls, which means less wasted compute resources, faster troubleshooting, and quicker fixes.</p>

<h3 id="the-declaration-belongs-on-the-function">The declaration belongs on the function</h3>

<p>In the above two examples, the <em>caller</em> had to declare the memory needs. That is a bit backwards. In order to do this, I had to investigate what <code>cv.glmnet()</code> needs, and nobody should have to repeat that exercise for every modeling function they call. It would be better if the author of the function could specify that. One approach would be to attach a resource-specification function<sup class="footnote-ref" id="fnref:attr"><a href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fn:attr" rel="nofollow" target="_blank">2</a></sup> to the function itself;</p>

<pre>resources(cv.glmnet) &lt;- function(x, ...) memory(4 * object.size(x))
</pre>

<p>Technically, that sets <code>attr(cv.glmnet, &quot;resources&quot;)</code> after having validated the function definition.</p>

<p>Next, we can have <code>resources()</code> look for such “resources” attributes and use them as the default, if found. So, if set, the memory-asserting call would become:</p>

<pre>fit &lt;- cv.glmnet(x, y) |&gt; resources()
#&gt; Error: UnmetResourceError: cv.glmnet(x, y) requires memory 7.5 GiB, available 3.1 GiB
</pre>

<p>Note how the burden is no longer on the caller, but on the function maintainer, to declare resources.</p>

<p>The map-reduce call works the same way, once we tell it which function is used:</p>

<pre>fits &lt;- lapply(xs, function(assay) cv.glmnet(assay, y)) |&gt; resources(cv.glmnet)
#&gt; Error: UnmetResourceError: cv.glmnet(assay, y) requires memory 7.5 GiB for xs[[4]], available 3.1 GiB
</pre>

<p>Handed a function that carries a declaration, <code>resources()</code> will use those declarations by default<sup class="footnote-ref" id="fnref:borrow"><a href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fn:borrow" rel="nofollow" target="_blank">3</a></sup>.</p>

<h3 id="functions-guarding-themselves">Functions guarding themselves</h3>

<p>With a resource function attached to the function, the <code>cv.glmnet()</code> function could easily guard the resources upfront by using<sup class="footnote-ref" id="fnref:assert-resources"><a href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fn:assert-resources" rel="nofollow" target="_blank">4</a></sup>:</p>

<pre>cv.glmnet &lt;- function(x, y, ...) {
  assert_resources()
  # ... the real work
}
</pre>

<p>This would remove the need for the caller to use <code>|&gt; resources()</code>;</p>

<pre>fit &lt;- cv.glmnet(x, y)
#&gt; Error: UnmetResourceError: cv.glmnet(x, y) requires memory 7.5 GiB, available 3.1 GiB
</pre>

<h3 id="attaching-it-to-code-you-do-not-maintain">Attaching it to code you do not maintain</h3>

<p>Even if a function does not carry a resource declaration, you can attach one yourself, e.g.</p>

<pre>cv.glmnet &lt;- glmnet::cv.glmnet
resources(cv.glmnet) &lt;- function(x, ...) memory(4 * object.size(x))
</pre>

<p>That might be worth doing even for a single function in a single analysis script, preferably at the top of the script. It helps to gather such declarations in one place and avoids cluttering up the code, especially if the same function is used in multiple places.</p>

<h2 id="parallel-the-same-memory-specification-decides-how-many-tasks-run-at-once">Parallel: the same memory specification decides how many tasks run at once</h2>

<p>So far, we’ve only discussed resource specification in sequential processing. Another goal of this project is to make use of them also in parallel processing. For instance, already today we can use the <strong><a href="https://futurize.futureverse.org/" rel="nofollow" target="_blank">futurize</a></strong> package and its <code>futurize()</code> function to parallelize <code>lapply()</code>, <code>purrr::map()</code>, <code>foreach()</code>, and friends through that single pipe. In our case, we could use:</p>

<pre>fits &lt;- lapply(xs, function(assay) cv.glmnet(assay, y)) |&gt;
  futurize()
</pre>

<p>Without memory protection, there is a great risk that you will run out of memory before you run out of CPU. Because of this, the goal is to support also:</p>

<pre>fits &lt;- lapply(xs, function(assay) cv.glmnet(assay, y)) |&gt;
  resources(function(assay) memory(4 * object.size(assay))) |&gt;
  futurize()
</pre>

<p>Here the resource specifications would not only protect against memory overuse, but also be used for scheduling the parallel tasks, e.g. by limiting the number of memory-hungry parallel tasks running concurrently on the same machine.</p>

<h3 id="on-a-single-machine">On a single machine</h3>

<p>Consider using <code>plan(multisession)</code> where you have access to 32 GiB of memory and 16 CPU cores. That initiates 16 parallel workers. Each worker is a separate R process, and each task exports the matrix it is about to fit, so a task costs that matrix plus what fitting <code>cv.glmnet()</code> needs on top of that.</p>

<p>It is unlikely that you will be able to run 16 parallel model-fitting tasks with only 32 GiB. Even before turning to parallelization, you know that your design matrices in <code>xs</code> occupy 8 GiB of that memory, leaving you with at most 24 GiB for the parallel tasks. To fit the methylation assay, you need to export the 1.9 GiB matrix to the worker and then another 7.5 GiB to fit it, totaling 9.4 GiB. A task on one of the small assays costs a little under 4 GiB.</p>

<p>If you launched 16 parallel fits blindly, it’s quite likely that you would run out of memory and the operating system’s out-of-memory (OOM) killer may terminate your analysis. Given that none of this resource-specification framework exists today, the best you can do is to work the numbers yourself, and spin up only as many parallel workers as you can afford:</p>

<pre>## 32 GiB RAM, ~8 GiB for the R session holding 'xs', 9.4 GiB for the largest task
plan(multisession, workers = parallelly::availableCores(max = (32 - 8) / 9.4))
#&gt; 2 workers
</pre>

<p>Here we’re using <code>availableCores()</code> from the <strong><a href="https://parallelly.futureverse.org/" rel="nofollow" target="_blank">parallelly</a></strong> package, which respects common CPU allocations, while also limiting it manually via a custom memory-limit equation.
Two concurrent parallel tasks is the best guess you have. However, with the above resource specifications, <code>futurize()</code> could probably do better and fit additional cross-validation models concurrently, especially the smaller ones.</p>

<h3 id="on-a-compute-cluster">On a compute cluster</h3>

<p>On a high-performance compute (HPC) cluster, the <em>job scheduler</em> (e.g. Slurm and SGE) decides which execution nodes your job lands on, based on the amount of memory it requests. For example, Slurm declaration <code>#SBATCH --mem=10G</code> tells the scheduler that this job requires 10 GiB of RAM to run.</p>

<p>By declaring such memory needs within R;</p>

<pre>plan(future.batchtools::batchtools_slurm)

fits &lt;- lapply(xs, function(assay) cv.glmnet(assay, y)) |&gt;
  resources(function(assay) memory(4 * object.size(assay))) |&gt;
  futurize()
</pre>

<p>the <strong><a href="https://future.futureverse.org/" rel="nofollow" target="_blank">future</a></strong> framework could work together with <strong><a href="https://future.batchtools.futureverse.org/" rel="nofollow" target="_blank">future.batchtools</a></strong> to translate each of the calculated resource needs into declarations understood by the job scheduler, which then can find appropriately sized slots on the cluster - all while maximizing the memory use but without ever running out of memory.</p>

<h3 id="ideally-everything-is-hidden-away">Ideally, everything is hidden away</h3>

<p>Just as with <code>resources()</code>, if <code>cv.glmnet()</code> declares its own resource needs, <code>futurize()</code> can also take advantage of that. That would close the circle such that code existing already today, e.g.</p>

<pre>fits &lt;- lapply(xs, FUN = cv.glmnet, y = y) |&gt; futurize()
</pre>

<p>would <strong>become resource aware, protect against overuse, and optimize scheduling overnight - all without code changes</strong>.</p>

<h2 id="outro">Outro</h2>

<p>Phew, that was quite long, and yet, I only got to cover a tiny bit of what the <em><a href="https://www.futureverse.org/roadmap/resources.html" rel="nofollow" target="_blank">Resource Project</a></em> aims for. I discussed how we can manage <em>memory</em> from within R, but there are many other compute resources that limit us. For example, we also want to manage walltime, scratch space, GPU cores, and GPU memory.</p>

<p>If you have other thoughts or ideas, we’d love to hear from you. Please reach out on the <a href="https://github.com/orgs/futureverse/discussions" rel="nofollow" target="_blank">Futureverse Discussions</a> forum.</p>

<p><em>May the future be with you!</em></p>

<p>Henrik</p>
<div class="footnotes">

<hr />

<ol>
<li id="fn:model">A better memory model for <code>cv.glmnet()</code> has the form <code>a + b * object.size(x) + c * ncol(x)</code>: a fixed cost <code>a</code> that dominates while <code>x</code> is small, the <code>b * object.size(x)</code> term used above, and a <code>c * ncol(x)</code> term for per-column bookkeeping that only becomes visible on very wide matrices. Working out the coefficients, and how to measure them, is a project in itself. Once <code>x</code> is large, <code>b * object.size(x)</code> becomes the dominant term.
 <a class="footnote-return" href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fnref:model" rel="nofollow" target="_blank"><sup>[return]</sup></a></li>
<li id="fn:attr">The resource function arguments should match that of the function. Because of that, we could generate those automatically and simplify the setter to just be a quoted expression, e.g. <code>quote(resources(memory(4 * object.size(x))))</code>.
 <a class="footnote-return" href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fnref:attr" rel="nofollow" target="_blank"><sup>[return]</sup></a></li>
<li id="fn:borrow">It might be that static-code inspection can be used to avoid having to declare <code>resources(cv.glmnet)</code> and instead just use <code>fits &lt;- lapply(xs, function(assay) cv.glmnet(assay, y)) |&gt; resources()</code>.
 <a class="footnote-return" href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fnref:borrow" rel="nofollow" target="_blank"><sup>[return]</sup></a></li>
<li id="fn:assert-resources">Called without arguments, <code>assert_resources()</code> queries <code>sys.function()</code> for the function currently being evaluated, takes its <code>&quot;resources&quot;</code> attribute, and calls it with the arguments of the call in progress.
 <a class="footnote-return" href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/#fnref:assert-resources" rel="nofollow" target="_blank"><sup>[return]</sup></a></li>
</ol>
</div>

<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://www.jottr.org/2026/08/11/roadmap-resources-memory/"> 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/the-resource-project-tell-r-how-much-memory-you-need/">The Resource Project: Tell R How Much Memory You Need</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403072</post-id>	</item>
		<item>
		<title>R-Community A moderated Google Group to share R knowledge</title>
		<link>https://www.r-bloggers.com/2026/08/r-community-a-moderated-google-group-to-share-r-knowledge/</link>
		
		<dc:creator><![CDATA[https://pacha.dev/blog]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 23:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://pacha.dev/blog/2026/08/11/r-community/index.html</guid>

					<description><![CDATA[<p>For R users that are just starting with R or that have been using it for years</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/r-community-a-moderated-google-group-to-share-r-knowledge/">R-Community A moderated Google Group to share R knowledge</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/11/r-community/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>I created <a href="https://groups.google.com/g/r-community" rel="nofollow" target="_blank">R-Community</a> for R users that are just starting with R or that have been using it for years.</p>
<p>I miss when I used Twitter and people had the “#rstats” hashtag that lead to a nice community. I closed my Twitter account when Mr. Elon Musk bought the platform, and similarly many users left to Mastodon, Bluesky, and other platforms. I thought Google Groups would be a good option as it does not require to configure an extra account as other technologies do (e.g., Slack, Discord, etc.). I also thought about moderation, as I received some trolling/bullying when I was a novice R user on Stackoverflow a few years ago.</p>
<p>This also follows from AI rise, which I find interesting but it replaces a lot of socialization in a world where smartphones already replaced parts of it in daily life. I tested a few AI engines things I have already worked on, and I observe that such engines often over-complicate answers.</p>
<p>If you are based in London, I am interested in reactivating the London R User Group as I think that PhD life sometimes puts you in an echo chamber that tends to over-specialization. I am willing to organize events to talk about R and socialize over pints or pizza.</p>
<p>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>.</p>
<p>Below is the group description.</p>
<p><strong>Goal</strong>: to have a space to ask and answer R questions. No R preference dominates this space. Questions can be about base R, Tidyverse, data.table or anything in the wide R ecosystem.</p>
<p><strong>Code of conduct</strong>: No bullying, hate, or rude acts of any kind. What is hateful to you, do not do to your fellow.</p>
<p><strong>Community over Code</strong>: A strong, diverse community matters more than the software itself, because a healthy group can always improve bad code.</p>
<p><strong>Earned Authority (Meritocracy)</strong>: Influence and decision-making power are earned through active, public contributions.</p>
<p><strong>Community of Peers</strong>: All individual contributors have equal weight in discussions, and hierarchical corporate structures do not apply here.</p>
<p><strong>Openness and Transparency</strong>: Technical discussions happen in public view on this mailing list so everyone can participate conditional on the code of conduct.</p>
<p><strong>Neutrality</strong>: Discussion about problem-solving, need for a new package or others are independent of any single vendor or corporate sponsor, protecting the long-term public good.</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/11/r-community/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/r-community-a-moderated-google-group-to-share-r-knowledge/">R-Community A moderated Google Group to share R knowledge</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403068</post-id>	</item>
		<item>
		<title>Dual scaled y-axis with ggplot()</title>
		<link>https://www.r-bloggers.com/2026/08/dual-scaled-y-axis-with-ggplot/</link>
		
		<dc:creator><![CDATA[Andrea Onofri]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 22:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.statforbiology.com/posts/R_ggplot_dualScaledAxes.html</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; ">
<p>I have often found myself needing to plot a single graph with two y-axes having different scales. For example, this might be useful for representing temperature and rainfall data at a given location. Unfortunately, doing this with ggplot() is no...</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/dual-scaled-y-axis-with-ggplot/">Dual scaled y-axis with ggplot()</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.statforbiology.com/posts/R_ggplot_dualScaledAxes.html"> Statforbiology</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>
 





<p>I have often found myself needing to plot a single graph with two y-axes having different scales. For example, this might be useful for representing temperature and rainfall data at a given location. Unfortunately, doing this with <code>ggplot()</code> is not straightforward.</p>
<p>While searching for possible solutions, I discovered that graphs with dual y-axes are generally not regarded as good tools for data visualisation. Hadley Wickham, the author of <code>ggplot2</code> and several other important R packages, has given some good reasons for this (e.g., at <a href="https://stackoverflow.com/questions/3099219/ggplot-with-2-y-axes-on-each-side-and-different-scales" rel="nofollow" target="_blank">this link</a>). I do not intend to question these general arguments. Nonetheless, the idea that I cannot do with <code>ggplot()</code> something that I could easily do with Excel gets on my nerves. After all, such graphs can be quite useful in some specific circumstances, such as when displaying weather data.</p>
<p>I therefore started looking for a reasonable solution and eventually found my way. I would like to share it in this post.</p>
<p>Let’s consider the dataset <code>DailyMeteoData.csv</code>, which contains daily average temperature and rainfall data for two years at a location in my region (Umbria, central Italy). Let’s open the dataset and use <code>dplyr</code> to make a few useful transformations, such as:</p>
<ol type="1">
<li>converting the date character string into a date object;</li>
<li>adding variables for Year, Month, and Day of the Year (DOY).</li>
</ol>
<div class="cell">
<pre>library(dplyr)
fileName &lt;- &quot;https://www.casaonofri.it/_datasets/DailyMeteoData.csv&quot;
dataMeteo &lt;- read.csv(fileName) |&gt;
  mutate(Date = as.Date(Date, format = &quot;%d/%m/%Y&quot;),
         Year = as.numeric(format(Date, format=&quot;%Y&quot;)),
         Month = as.numeric(format(Date, format=&quot;%m&quot;)),
         DOY = as.numeric(format(Date, format=&quot;%j&quot;)))
head(dataMeteo)</pre>
<div class="cell-output cell-output-stdout">
<pre>        Date Tavg Rain Year Month DOY
1 2011-01-01  5.9  0.0 2011     1   1
2 2011-01-02  6.2  0.6 2011     1   2
3 2011-01-03  4.5  0.0 2011     1   3
4 2011-01-04 -0.3  0.0 2011     1   4
5 2011-01-05  2.3  0.2 2011     1   5
6 2011-01-06  6.9  0.0 2011     1   6</pre>
</div>
</div>
<p>Temperature is a continuous variable and can be easily represented using a line graph, whereas rainfall consists of discrete events and is therefore more usefully accumulated over periods such as ten-days or a month. We can use <code>dplyr</code> once more to create a new rainfall dataset containing the accumulated monthly rainfall, which is more suitable for our purposes. For the sake of simplicity, we assume that the year is divided into 12 months of equal length (approximately 30.4 days), and we calculate the DOY corresponding to the central day of each month, which will be used as the centre of the respective plot bar.</p>
<div class="cell">
<pre>dataMeteo2 &lt;- dataMeteo |&gt;
  group_by(Year, Month) |&gt;
  summarise(Rain = sum(Rain)) |&gt;
  mutate(DOY = seq(15, 365, by = 365/12))
head(dataMeteo2)</pre>
<div class="cell-output cell-output-stdout">
<pre># A tibble: 6 × 4
# Groups:   Year [1]
   Year Month  Rain   DOY
  &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt;
1  2011     1  40.4  15  
2  2011     2  32.8  45.4
3  2011     3 113.   75.8
4  2011     4  16.6 106. 
5  2011     5  27.4 137. 
6  2011     6  61.2 167. </pre>
</div>
</div>
<p>Now we can produce our first graph showing both rainfall and temperature. In the box below, I have played a little with the x-axis ticks and labels, and I have left the y-axis label empty for the moment.</p>
<div class="cell">
<pre>library(ggplot2)
ggplot() +
  geom_bar(dataMeteo2, mapping = aes(x = DOY, y = Rain), fill = &quot;grey&quot;,
           stat = &quot;identity&quot;, width = 28) +
  geom_line(dataMeteo, mapping = aes(x = DOY, y = Tavg), col = &quot;blue&quot;) +
  scale_x_continuous(breaks = c(365/12, 365/12*4, 365/12*8, 365) - 15, 
                     labels = c(&quot;Jan&quot;, &quot;Apr&quot;, &quot;Aug&quot;, &quot;Dec&quot;),
                     name = &quot;&quot;) +
  scale_y_continuous(name = &quot;&quot;) +
  facet_wrap(~Year) +
  theme_bw()</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i0.wp.com/www.statforbiology.com/posts/R_ggplot_dualScaledAxes_files/figure-html/unnamed-chunk-3-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The previous graph does not work very well: the temperature line is hardly visible because its measurement scale is much smaller than that of rainfall. We therefore need to ‘scale’ the temperature variable so that it ranges approximately from 150 to 250. This will make the blue line clearly visible without interfering with the rainfall bars. The minimum and maximum temperature values are -4.2°C and 28.9°C, respectively; we want to map these original values to the new values 150 and 250, respectively, as shown in the figure below.</p>
<div class="cell" data-layout-align="center">
<div class="cell-output-display">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://i2.wp.com/www.statforbiology.com/posts/R_ggplot_dualScaledAxes_files/figure-html/unnamed-chunk-4-1.png?w=450&#038;ssl=1" class="img-fluid quarto-figure quarto-figure-center figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>The previous figure tells us that we could transform the original temperature scale by using the equation of the straight line passing through the two points (-4.2, 150) and (28.9, 250). Thanks to what we remember from geometry courses, we can calculate the slope of such a straight line as:</p>
<p><img src="https://latex.codecogs.com/png.latex?m%20=%20%5Cfrac%7B250%20-%20150%7D%7B28.9%20+%204.2%7D%20=%203.02"></p>
<p>while the intercept is:</p>
<p><img src="https://latex.codecogs.com/png.latex?q%20=%20250%20-%203.02%20%5Ctimes%2028.9%20=%20162.69"></p>
<p>Thus, the scaling equation is:</p>
<p><img src="https://latex.codecogs.com/png.latex?Y_N%20=%20162.69%20+%203.02%20,,%20Y_O"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?Y_N"> is the new temperature scale, while <img src="https://latex.codecogs.com/png.latex?Y_O"> is the original one. The reverse transformation is:</p>
<p><img src="https://latex.codecogs.com/png.latex?Y_O%20=%20%5Cfrac%7BY_N%20-%20162.69%7D%7B3.02%7D"></p>
<p>Now, we are ready to plot the graph. We transform the temperature to the new scale and plot it; furthermore, we include the second axis by using the <code>sec.axis</code> argument and the <code>sec_axis()</code> function, in which we specify the back-transformation function to the original scale.</p>
<div class="cell">
<pre>dataMeteo &lt;- dataMeteo %&gt;% 
  mutate(newTavg = 162.69 + 3.02 * Tavg)

ggplot() +
  geom_bar(dataMeteo2, mapping = aes(x = DOY, y = Rain), fill = &quot;grey&quot;,
           stat = &quot;identity&quot;, width = 28) +
  geom_line(dataMeteo, mapping = aes(x = DOY, y = newTavg), col = &quot;blue&quot;) +
  scale_x_continuous(breaks = c(365/12, 365/12*4, 365/12*8, 365) - 15, 
                     labels = c(&quot;Jan&quot;, &quot;Apr&quot;, &quot;Aug&quot;, &quot;Dec&quot;),
                     name = &quot;&quot;) +
  scale_y_continuous(name = &quot;Rain (mm)&quot;, 
                     sec.axis = sec_axis(~ (. - 162.69)/3.02, 
                                         name = &quot;Daily Temperature (°C)&quot;,
                                         breaks = c(-10, 0, 10, 20, 30))) +
  facet_wrap(~Year) +
  theme_bw()</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i1.wp.com/www.statforbiology.com/posts/R_ggplot_dualScaledAxes_files/figure-html/unnamed-chunk-5-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>And we are done!</p>
<section id="another-possible-approach" class="level1">
<h1>Another possible approach</h1>
<p>Ross Gilmore from Galileo Consulting (Kuala Lumpur) sent me an interesting comment, suggesting the use of the <code>lubridate</code> package for managing dates and the <code>ggh4x</code> package to take advantage of its nested-axis capabilities. Basically, his graph does not use facets; instead, the two years are placed one after the other. Furthermore, he calculated the monthly means for temperature and fitted a cubic spline using <code>geom_smooth()</code> and <code>method = &quot;gam&quot;</code>. He also made use of minor ticks, which might be a good idea.</p>
<p>Ross’ code follows. I’d like to thank him very much!</p>
<div class="cell">
<pre>library(lubridate)
library(ggh4x)
dataMeteo1 &lt;- dataMeteo |&gt;
  mutate(
    Date = as.Date(Date, format = &quot;%d/%m/%Y&quot;),
    Year = lubridate::year(Date),
    Month = lubridate::month(Date))

dataMeteo2 &lt;- dataMeteo1 |&gt;
  group_by(Year, Month) |&gt;
  summarise(
    Rain = sum(Rain),
    Temp = mean(Tavg, rm.na = TRUE)) |&gt;
  mutate(Mth = factor(month.abb[Month], 
                      levels = c(&quot;Jan&quot;, &quot;Feb&quot;, &quot;Mar&quot;, &quot;Apr&quot;,
                                 &quot;May&quot;, &quot;Jun&quot;, &quot;Jul&quot;, &quot;Aug&quot;,
                                 &quot;Sep&quot;, &quot;Oct&quot;, &quot;Nov&quot;, &quot;Dec&quot;)))
dataMeteo3 &lt;- dataMeteo2 |&gt;
  mutate(newTemp = 162.69 + 3.02 * Temp)

ggplot(dataMeteo3, mapping = aes(x = interaction(Mth, Year),
                                 group = 1)) +
  geom_col(aes(y = Rain), fill = &quot;grey&quot;) +
  geom_point(aes(y = newTemp), size = 2, col = &quot;blue&quot;) +
  geom_smooth(
    method = &quot;gam&quot;, formula = y ~ s(x, bs = &quot;cc&quot;),
    aes(x = as.numeric(interaction(Mth, Year)), y = newTemp),
    col = &quot;blue&quot;) +
  geom_point(aes(y = newTemp), size = 3, col = &quot;blue&quot;, 
             fill = &quot;white&quot;, shape = 21, stroke = 1) +
  scale_y_continuous(
    minor_breaks = scales::breaks_width(20),
    name = &quot;Mean Monthly Total Rainfall (mm)&quot;,
    sec.axis = sec_axis(~ (. - 162.69) / 3.02,
      name = &quot;Mean Monthly Daily Temperature (°C)&quot;,
      breaks = c(-10, 0, 10, 20, 30))) +
  guides(x = &quot;axis_nested&quot;,
        y = guide_axis(minor.ticks=TRUE),
        y.sec = guide_axis(minor.ticks=TRUE)) +
  theme_bw(base_size = 18) +
  theme(
     panel.grid.minor = element_blank(),
     axis.title.x = element_blank(),
     axis.text.x = element_text(face = &quot;bold&quot;, size = rel(0.5), angle = 90),
     axis.ticks = element_line(colour = &quot;red&quot;),
     ggh4x.axis.nestline.x = element_line(linewidth = 0.6),
     ggh4x.axis.nesttext.x = element_text(colour = &quot;blue&quot;, 
                                          face = &quot;bold&quot;, 
                                          size = rel(1.0))
  )</pre>
<div class="cell-output-display">
<div>
<figure class="figure">
<p><img src="https://i2.wp.com/www.statforbiology.com/posts/R_ggplot_dualScaledAxes_files/figure-html/unnamed-chunk-6-1.png?w=450&#038;ssl=1" class="img-fluid figure-img"  data-recalc-dims="1"></p>
</figure>
</div>
</div>
</div>
<p>Thanks for reading and happy coding; should you have further comments to improve these graphs, please, drop me a note at the address below.</p>
<p>And … don’t forget to check out my new book!</p>
<p>Prof. Andrea Onofri<br>
Department of Agricultural, Food and Environmental Sciences<br>
University of Perugia (Italy)<br>
Send comments to: <a href="mailto:andrea.onofri@unipg.it" rel="nofollow" target="_blank">andrea.onofri@unipg.it</a></p>
<p><a href="https://www.awin1.com/cread.php?awinmid=26429&#038;awinaffid=2675822&#038;ued=https%3A%2F%2Flink.springer.com%2Fbook%2F10.1007%2F978-3-032-08199-5" rel="nofollow" target="_blank"><img src="https://i0.wp.com/www.statforbiology.com/Figures/Email_Signature_978-3-032-08199-5.png?w=578&#038;ssl=1" alt="Book cover" class="cover" align="center" data-recalc-dims="1"></a></p>
<hr>
<p>This post was originally published on 6-11-2023, and updated on 06-06-2024</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.statforbiology.com/posts/R_ggplot_dualScaledAxes.html"> Statforbiology</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/dual-scaled-y-axis-with-ggplot/">Dual scaled y-axis with ggplot()</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403066</post-id>	</item>
		<item>
		<title>Institutions Are a Package Deal: What a Correlation Can’t Tell You About the Rule of Law</title>
		<link>https://www.r-bloggers.com/2026/08/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/</link>
		
		<dc:creator><![CDATA[Giles Dickenson-Jones]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 05:45:08 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://www.gilesd-j.com/?p=4272</guid>

					<description><![CDATA[<div style = "width:60%; display: inline-block; float:left; "> This post asks how institutions should be conceptualized and analyzed: can the rule of law (and other institutions) sensibly be examined one at a time, or do they need to be treated as parts of an interdependent system?<br />
The post Institutions Are a Package Deal: What a Correlation Can’t ...</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/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/">Institutions Are a Package Deal: What a Correlation Can’t Tell You About the Rule of Law</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/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/"> 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>This is the third post in a series examining relationships between the rule of law and other institutions as measured by the <a href="https://www.worldbank.org/en/publication/worldwide-governance-indicators" rel="nofollow" target="_blank">World Bank’s Worldwide Governance Indicators</a></em> (WGI). <em>Earlier posts demonstrated that the</em> <a href="https://worldjusticeproject.org/rule-of-law-index/" rel="nofollow" target="_blank">World Justice Project’s (WJP) measure</a> <em>can be proxied by the WGI Rule of Law (RoL) index, to take advantage of its wider coverage over a longer time-period.</em></p>



<p class="wp-block-paragraph"><em>This post builds on that analysis by asking how institutions should be conceptualized and analyzed: can the rule of law (and other institutions) sensibly be examined one at a time, or do they need to be treated as parts of an interdependent system? Using the six dimensions of the WGI as a proxy for institutional strength, results suggest dimensions are indeed bound together. Countries that score well on the rule of law also frequently score well on government effectiveness and corruption controls, year-to-year movements are positively linked across most dimensions and a common measure (or cause) accounts for the bulk of the variation between countries.</em></p>



<p class="wp-block-paragraph"><em>None of this will surprise anyone familiar with the literature and the analysis isn’t intended to prove institutions matter or that the WGI measure them. The point is narrower: to show why the rule of law can’t be examined in isolation and the risks of coming to the wrong conclusions when we try. It also sets up later posts in the series which will look at how the rule of law is connected with other national characteristics, such as economic growth.</em></p>



<h2 class="wp-block-heading"><strong>Background</strong></h2>



<p class="wp-block-paragraph">Institutions describe <a href="https://www.forbes.com/sites/artcarden/2015/11/24/he-helped-us-understand-the-process-of-economic-change-douglass-c-north-1920-2015/" rel="nofollow" target="_blank">the constraints that structure political, economic and social interaction</a>, such as how power is gained and exercised, how order is kept, and the way public resources are sourced, divided and used. <a href="https://economics.mit.edu/sites/default/files/inline-files/Acemoglu%20Nobel%20lecture%20v11-1.pdf" rel="nofollow" target="_blank">Institutions shape incentives and opportunities</a>: good institutions lead to more of the things we care about, like economic growth, peace and prosperity; while bad institutions lead to more of the thing we want less of, like poverty, inequality and conflict.</p>



<p class="wp-block-paragraph">A sizable share of my work sits in economic development, so institutions are never far from mind. This series, though, started with a specific job: a client hired me to look at the links between the rule of law and economic growth, and I went looking for an accessible introduction to the topic I could share. What I found was <a href="https://www.atlanticcouncil.org/in-depth-research-reports/issue-brief/why-the-rule-of-law-is-the-key-to-prosperity-lessons-from-thirty-years-of-data/" rel="nofollow" target="_blank">this analysis from the Atlantic Council on why the rule of law is the key to prosperity</a>. </p>



<p class="wp-block-paragraph">Having noted how important institutions are and how difficult they are to define and measure, the authors move briskly to their conclusion that the rule of law is the single most influential factor behind long-term economic growth and societal wellbeing:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="450" loading="lazy" src="https://www.gilesd-j.com/wp-content/uploads/2026/08/atlantic_council_RoL_screenshot-1024x910.webp" alt="" class="wp-image-4267" srcset_temp="https://www.gilesd-j.com/wp-content/uploads/2026/08/atlantic_council_RoL_screenshot-1024x910.webp 1024w, https://www.gilesd-j.com/wp-content/uploads/2026/08/atlantic_council_RoL_screenshot-300x267.webp 300w, https://www.gilesd-j.com/wp-content/uploads/2026/08/atlantic_council_RoL_screenshot-768x682.webp 768w, https://www.gilesd-j.com/wp-content/uploads/2026/08/atlantic_council_RoL_screenshot.webp 1093w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



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



<p class="wp-block-paragraph"><strong>Source:</strong> Annie (Yu-Lin) Lee, Joseph Lemoine, 20 August 2025, Why the rule of law is the key to prosperity: Lessons from thirty years of data, Atlantic Council (<a href="https://www.atlanticcouncil.org/in-depth-research-reports/issue-brief/why-the-rule-of-law-is-the-key-to-prosperity-lessons-from-thirty-years-of-data/" rel="nofollow" target="_blank">link</a>) [Accessed 5 August 2026]</p>



<p class="wp-block-paragraph">That’s quite the claim and one of the inspirations for this post as from the looks of it, much of their evidence seems to come from the strength of a collection of pairwise correlations between the rule of law and their chosen proxies for well being (as measured by the Atlantic Council’s <a href="https://freedom-and-prosperity-indexes.atlanticcouncil.org/" rel="nofollow" target="_blank">Freedom and Prosperity Index</a>).</p>



<p class="wp-block-paragraph">Now, <em>I’m no big city lawyer</em>, but it seems to me that pairwise correlations are a poor piece of evidence to provide for such a strong claim. Particularly when we’re wading into the murky waters of prosperity, institutions and the causal connection between the two. And the characteristics being analyzed are plausibly all part of the same interdependent system.</p>



<p class="wp-block-paragraph">But, I’m not here to judge. Firstly, as the authors <em>do</em> mention that prosperity depends on the interplay of multiple institutional pillars, not just the rule of law. They’ve also tackled a complicated topic in an accessible way, which is an achievement in itself. And <a href="https://www.gilesd-j.com/2023/02/14/everything-is-correlated/" rel="nofollow" target="_blank">I’ve been there before too</a>: as cross-country correlations are fun to explore and can provide a seemingly endless array of plausible policy interventions for making the world a better place. Also, the point of this post isn’t to criticize their analysis, but to help fill the gap I noticed when searching for resources on the topic.</p>



<p class="wp-block-paragraph">Instead, this post attempts to fill a gap by presenting analysis demonstrating why the rule of law, and institutions more generally, are best conceptualized as interdependent pillars within a mutually reinforcing system. That interdependence is what makes the literature so conceptually interesting and so frustrating to analyze, since the little data available on the topic arrives bundled with multicollinearity, endogeneity, collider bias, omitted variables and measurement error.</p>



<h2 class="wp-block-heading">The rule of law and institutions: a primer</h2>



<p class="wp-block-paragraph">To obnoxiously paraphrase research<sup data-fn="7371b0b4-0d25-45e8-98bb-2869772bcebc" class="fn"><a href="https://www.gilesd-j.com/2026/08/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/#7371b0b4-0d25-45e8-98bb-2869772bcebc" id="7371b0b4-0d25-45e8-98bb-2869772bcebc-link" rel="nofollow" target="_blank">1</a></sup>, the rule of law is thought to influence economic development and wider prosperity through a variety of avenues, such as enabling the enforcement of property rights, easing trade between unrelated parties and providing a peaceful means for resolving disputes. However, because the rule of law and its outcomes <em>also</em> depend on a wider set of institutions and a tangled coalition different interests, analyzing it is no simple task.<sup data-fn="2fa2233b-5d92-4076-b096-e615cb6cd194" class="fn"><a href="https://www.gilesd-j.com/2026/08/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/#2fa2233b-5d92-4076-b096-e615cb6cd194" id="2fa2233b-5d92-4076-b096-e615cb6cd194-link" rel="nofollow" target="_blank">2</a></sup></p>



<p class="wp-block-paragraph">On one level this is because institutions and their outcomes are interdependent. For instance, maybe the rule of law does drive prosperity, but perhaps prosperous countries are better able to invest in their legal systems too. Similarly, perhaps how the rule of law drives economic outcomes changes depending on the political system, geography and urbanization.</p>



<p class="wp-block-paragraph">Defining and measuring amorphous concepts like the <em>rule of law</em> also happens to be hard, which means researchers lean heavily on surveys asking respondents for their <em>perception</em> of corruption, law and order and government effectiveness (etc). However, because a person’s opinion of a country’s performance in one area is likely to be influenced by their impression of it in others, these measures are likely to agree with one another for reasons that have little to do with the institutions themselves. A respondent who has watched a corruption scandal unfold is unlikely to rate the courts generously that same year.</p>



<p class="wp-block-paragraph">Finally, it’s generally accepted that institutions are slow moving (or ‘sticky’), which means they don’t change much from one year to the next and will often exert their influence on wider outcomes indirectly. As a result, some of the most influential studies exploring the connection between institutions and prosperity analyse periods of a hundred years or more, such as <a href="https://www.aeaweb.org/articles?id=10.1257%2Faer.91.5.1369" rel="nofollow" target="_blank">Acemoglu, Johnson and Robinson</a> who compared current income with a century old proxy for institutional quality (settler mortality).</p>



<p class="wp-block-paragraph"><strong>Figure: Income vs. Settler mortality</strong></p>



<p class="wp-block-paragraph"><em>Countries with higher GDP per capita now tended to be those with stronger colonial institutions (as proxied by settler mortality)</em></p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="450" loading="lazy" src="https://www.gilesd-j.com/wp-content/uploads/2026/08/acemoglue_figure.webp" alt="" class="wp-image-4269" srcset_temp="https://www.gilesd-j.com/wp-content/uploads/2026/08/acemoglue_figure.webp 510w, https://www.gilesd-j.com/wp-content/uploads/2026/08/acemoglue_figure-300x200.webp 300w" sizes="auto, (max-width: 510px) 100vw, 510px" /></figure>



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



<p class="wp-block-paragraph"><strong>Source:</strong> Acemoglu, Daron, Simon Johnson, and James A. Robinson. 2001. “The Colonial Origins of Comparative Development: An Empirical Investigation.” American Economic Review 91 (5): 1369–1401. DOI: 10.1257/aer.91.5.1369</p>



<h2 class="wp-block-heading">The Worldwide Governance Indicators</h2>



<p class="wp-block-paragraph">Although <a href="https://www.cambridge.org/core/journals/journal-of-institutional-economics/article/how-not-to-measure-institutions/197716CE594C7203BFF7D6CDDB150F3A" rel="nofollow" target="_blank">there are good reasons to question whether the WGI provides a reliable and holistic measure of institutions</a>, (or <a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1233045" rel="nofollow" target="_blank">even that it measure what it claims to</a>) it is arguably one of the more reliable and well-tested attempts at measuring the cross-country quality of institutions. I also have a personal preference for the WGI as somebody who is hired to design, build and evaluate composite indicators, because it gets a core part of index design right: <em>transparently sharing their methodology and data, and <a href="https://www.worldbank.org/content/dam/sites/govindicators/doc/The%20Worldwide%20Governance%20Indicators%202025%20Methodology%20Revision.pdf" rel="nofollow" target="_blank">being open to making revisions to reflect feedback</a>.</em></p>



<p class="wp-block-paragraph">The WGI also has the practical advantage of being intuitive enough for outsiders to understand what it’s trying to measure. With <em>governance</em> reflecting the traditions and institutions by which authority in a country is exercised across six dimensions:</p>



<ul class="wp-block-list">
<li class=""><em><strong>Voice and Accountability:</strong> perceptions of the extent to which citizens can participate in selecting their government including electoral integrity, and of accountability mechanisms for citizens—reflected in the ability to access information, governmental oversight bodies, and a robust traditional/digital media landscape; and</em></li>



<li class=""><em><strong>Political Stability:</strong> perceptions of the extent to which political power and governance are secure from destabilization, and of the likelihood that authority will be challenged or altered through violent, coercive, or unconstitutional means. The second aspect—government capacity to formulate and implement sound policies—includes:</em></li>



<li class=""><em><strong>Government Effectiveness:</strong> perceptions of the quality of public services, the civil service, policy formulation and implementation, and the credibility of a government’s decisions; and</em></li>



<li class=""><em><strong>Regulatory Quality:</strong> perceptions of the government’s ability to design and implement policies and regulations that promote private sector development. The third aspect—respect for institutions that govern economic and social interactions— comprises:</em></li>



<li class=""><em><strong>Rule of Law:</strong> perceptions of the extent to which agents respect and follow the rules of society, including contract enforcement, property rights, the police, courts, and the likelihood of crime and violence; and</em></li>



<li class=""><em><strong>Control of Corruption:</strong> perceptions of the extent to which public power is used for private gain, including both petty and grand corruption, as well as capture of the state by elites and private interests.</em></li>
</ul>



<p class="wp-block-paragraph"><strong>Source:</strong> World Bank, 2025, “The Worldwide Governance Indicators: Revised Methodology for Measuring Governance Using Perception Data December 2025”, <a href="https://www.worldbank.org/content/dam/sites/govindicators/doc/The%20Worldwide%20Governance%20Indicators%202025%20Methodology%20Revision.pdf" rel="nofollow" target="_blank">link</a>.</p>



<p class="wp-block-paragraph"><strong>Note:</strong> For brevity, this post uses institutions, governance and the WGI interchangeably, in full knowledge that they aren’t the same thing. The WGI is the World Bank’s conceptualization of a particular set of institutions it considers useful, interesting and/or relevant. The WGI is not meant to be an all-encompassing measure suited to every use case, it’s just the most suitable measure for this analysis. The scores are also <em>proxies</em> for institutional quality rather than <em>measures</em> of it. Proxies come with the territory when trying to measure hard-to-define things like <em>institutions and/or governance</em>, as direct measurement is unavailable or unsuitable, particularly for cross-country comparisons.</p>



<p class="wp-block-paragraph"><strong>Figure: Worldwide Governance Indicator Dimensions</strong></p>



<p class="wp-block-paragraph"><em>The WGI attempts to measure governance across six dimensions</em></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/governance_figure.png?w=450&#038;ssl=1" alt="" class="wp-image-4274" srcset_temp="https://i0.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/governance_figure.png?w=450&#038;ssl=1 685w, https://www.gilesd-j.com/wp-content/uploads/2026/08/governance_figure-300x157.png 300w" sizes="auto, (max-width: 685px) 100vw, 685px" data-recalc-dims="1" /></figure>



<p class="wp-block-paragraph"><strong>Source:</strong> my exceptional design skills</p>



<p class="wp-block-paragraph"><strong>FYI:</strong> Daniel Kaufmann has also <a href="https://openknowledge.worldbank.org/entities/publication/760809bf-8feb-5fe5-aa75-a8944b0cef78" rel="nofollow" target="_blank">publicly responded to criticism</a> of the WGI, which has resulted in several iterations of the methodology over time.</p>



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



<p class="wp-block-paragraph">Once again, the <a href="https://www.gilesd-j.com/2026/07/21/level-with-me-can-the-wgi-predict-the-wjps-rule-of-law-scores/gilesd-j.com/shared_resources/blogs/260310_RoL/wgidataset_with_sourcedata-2025.xlsx" rel="nofollow" target="_blank">WGI data can be downloaded here</a>. Because the code analyzes <em>all</em> WGI indicators, fnc_read_wgi_sheets() load data from each sheet and combine it into a single dataframe.</p>



<p class="wp-block-paragraph"><strong>Code: Plotting functions</strong></p>



<pre>#load the packages we'll probably need
library(tidyverse)
library(readxl)
library(janitor)
library(qgraph)
library(rstatix)   
library(broom)     
#Note: ppcor is also required, but called directly to avoid conflicts
#Install with install.packages(&quot;ppcor&quot;) if needed.


#define path
ref_wgi_path &lt;- &quot;./Data/wgidataset_with_sourcedata-2025.xlsx&quot;

# sheet code -&gt; output column name
ref_wgi_indices &lt;- c(
  va = &quot;wgi_voice&quot;,     # Voice and Accountability
  pv = &quot;wgi_polstab&quot;,   # Political Stability &#038; Absence of Violence
  ge = &quot;wgi_goveff&quot;,    # Government Effectiveness
  rq = &quot;wgi_regqual&quot;,   # Regulatory Quality
  rl = &quot;wgi_rol&quot;,       # Rule of Law
  cc = &quot;wgi_corrupt&quot;    # Control of Corruption
)

# Read one WGI sheet and return iso3c, year, and the single renamed estimate.
# Renaming per sheet avoids the shared 'governance_estimate...' column colliding
# on join. Fails loudly if a sheet's estimate column is named differently.
fnc_read_wgi_sheet &lt;- function(sheet_code, col_name, path) {
  read_excel(path, sheet = sheet_code) |&gt;
    clean_names() |&gt;
    rename(iso3c = economy_code,
           !!col_name := governance_estimate_approx_2_5_to_2_5) |&gt;
    select(iso3c, year, all_of(col_name))
}

# read all six, then join on the common keys
dta_wgi_2025 &lt;- ref_wgi_indices |&gt;
  imap(\(col_name, sheet_code) fnc_read_wgi_sheet(sheet_code, col_name, ref_wgi_path)) |&gt;
  reduce(full_join, by = c(&quot;iso3c&quot;, &quot;year&quot;))

#Add metadata (country + year-specific income bracket) taken once, from any sheet
tmp_dta_wgi_meta &lt;- read_excel(ref_wgi_path, sheet = &quot;rl&quot;) |&gt;
  clean_names() |&gt;
  rename(iso3c = economy_code, country = economy_name) |&gt;
  select(iso3c, year, country, income_classification)

#add meta data
dta_wgi_2025 &lt;- dta_wgi_2025 |&gt;
  left_join(tmp_dta_wgi_meta, by = c(&quot;iso3c&quot;, &quot;year&quot;)) |&gt;
  relocate(iso3c, country, year, income_classification)

#drop temporary objects (ref_ objects are kept so the chunk can be re-run)
rm(tmp_dta_wgi_meta)

#define index column names and labels:
ref_wgi_cols   &lt;- c(&quot;wgi_voice&quot;, &quot;wgi_polstab&quot;, &quot;wgi_goveff&quot;,
                    &quot;wgi_regqual&quot;, &quot;wgi_rol&quot;, &quot;wgi_corrupt&quot;)

ref_wgi_labels &lt;- c(wgi_voice   = &quot;Voice &#038; accountability&quot;,
                    wgi_polstab = &quot;Political stability&quot;,
                    wgi_goveff  = &quot;Govt effectiveness&quot;,
                    wgi_regqual = &quot;Regulatory quality&quot;,
                    wgi_rol     = &quot;Rule of law&quot;,
                    wgi_corrupt = &quot;Control of corruption&quot;)

#shortened labels  
ref_wgi_short  &lt;- c(wgi_voice   = &quot;Voice &#038; Acc.&quot;,
                    wgi_polstab = &quot;Pol. stability&quot;,
                    wgi_goveff  = &quot;Gov. effect.&quot;,
                    wgi_regqual = &quot;Reg. quality&quot;,
                    wgi_rol     = &quot;Rule of law&quot;,
                    wgi_corrupt = &quot;Corruption&quot;)

# assumptions
ref_min_years &lt;- 20    # minimum years of data before a country average is used
ref_pcor_cut  &lt;- 0.05  # partial correlations below this aren't drawn in the network</pre>



<h2 class="wp-block-heading">The rule of law as part of an institutional portfolio</h2>



<p class="wp-block-paragraph">To demonstrate what a statistical minefield analyzing institutions is, we’ll start by applying the same technique that I criticized earlier: pairwise correlations. With the idea being to demonstrate that <em>different</em> <em>measures of institutional strength tend to agree with one another</em>. In addition to this being what you might expect if institutions were endogenously determined, interdependent and/or WGI dimensions measured something similar. It also illustrates why you can’t rely on associations alone to cleanly establish the causal connection between a particular institution and economic and social outcomes.</p>



<p class="wp-block-paragraph">For comparing the shared strength of institutions we’ll use a country’s average score for each WGI dimension from 1996 to 2024.3 Although this reduces our sample size and ignores a variety of country-specific factors that might influence institutions, it provides a simple way to reduce year-to-year noise and focus on <em>between-country effects</em>. It’s also unlikely we’ll lose too much information, given institutions move slowly from year-to-year. But, averaging cuts both ways too: as stripping out year-to-year noise also strips out measurement error, making correlations mechanically higher within any single year.</p>



<p class="wp-block-paragraph">Reflecting that WGI dimensions might just be <a href="https://www.gilesd-j.com/2026/08/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/#0" rel="nofollow" target="_blank">unrelated series that are trending together</a>, we’ll also examine pairwise correlations for annual changes in WGI scores across dimensions. Aside from checking whether the level associations are just shared trends, dimensions moving together year-to-year would be consistent with institutions being connected to one another. For instance, if the rule of law supports efforts to fight corruption, we might expect the quality of these two institutions to move in the same direction.</p>



<p class="wp-block-paragraph">However, the use of averages for comparing levels and raw values for first-differences also warrants caution when making comparisons: As while the two describe the same countries, the different units of analysis and number of observations aren’t directly comparable.</p>



<p class="wp-block-paragraph"><strong>Code: calculate country averages and first differences</strong></p>



<pre>#Country averages (levels).
#Countries are only kept where every dimension has at least ref_min_years of
#data
dta_wgi_country &lt;- dta_wgi_2025 |&gt;
  group_by(iso3c) |&gt;
  filter(if_all(all_of(ref_wgi_cols), \(x) sum(!is.na(x)) &gt;= ref_min_years)) |&gt;
  summarise(across(all_of(ref_wgi_cols), \(x) mean(x, na.rm = TRUE)),
            nmb_years = n(),
            .groups = &quot;drop&quot;)


#how many countries survive the coverage filter
chk_wgi_country_nmb &lt;- nrow(dta_wgi_country)
chk_wgi_country_nmb

#first difference column names
ref_wgi_diff_cols &lt;- paste0(&quot;d_&quot;, ref_wgi_cols)

#work out which rows sit next to a consecutive year
#(the WGI was published every other year before 2002, so we can't just lag blindly)
dta_wgi_gaps &lt;- dta_wgi_2025 |&gt;
  arrange(iso3c, year) |&gt;
  group_by(iso3c) |&gt;
  mutate(yr_gap = year - lag(year)) |&gt;
  ungroup()

#then take the year-on-year change, but only where the gap is a single year
dta_wgi_diff &lt;- dta_wgi_gaps |&gt;
  group_by(iso3c) |&gt;
  mutate(across(all_of(ref_wgi_cols),
                \(x) if_else(yr_gap == 1, x - lag(x), NA_real_),
                .names = &quot;d_{.col}&quot;)) |&gt;
  ungroup() |&gt;
  select(iso3c, year, all_of(c(ref_wgi_cols, ref_wgi_diff_cols)))

#Complete-case frames used for BOTH the zero-order and partial correlations.
dta_wgi_lvl_cc &lt;- dta_wgi_country |&gt;
  select(all_of(ref_wgi_cols)) |&gt;
  drop_na()

dta_wgi_dif_cc &lt;- dta_wgi_diff |&gt;
  select(iso3c, all_of(ref_wgi_diff_cols)) |&gt;
  drop_na()

#sample sizes 
chk_sample_sizes &lt;- tibble(
  series       = c(&quot;WGI Levels&quot;, &quot;WGI First Differences&quot;),
  nmb_obs      = c(nrow(dta_wgi_lvl_cc), nrow(dta_wgi_dif_cc)),
  nmb_country  = c(nrow(dta_wgi_lvl_cc), n_distinct(dta_wgi_dif_cc$iso3c)),
  unit         = c(&quot;country&quot;, &quot;country-year&quot;)
)</pre>



<p class="wp-block-paragraph"><strong>Note:</strong> The WGI methodology has been updated in 2025 to improve comparability of governance scores over time.</p>



<p class="wp-block-paragraph"><strong>Code: Correlation analysis</strong></p>



<pre>{r}
#create a function for calculating correlations 
fnc_cor_p &lt;- function(dta, cols) {
  dta |&gt;
    select(all_of(cols)) |&gt;
    cor_test(vars = cols) |&gt;
    rename(x = var1, y = var2) |&gt;
    filter(match(x, cols) &lt; match(y, cols)) |&gt;
    mutate(across(c(x, y), \(v) sub(&quot;^d_&quot;, &quot;&quot;, v))) |&gt;
    select(x, y, cor, p)
}

#calculate association(s) on the shared complete-case frames
sum_wgi_cor_level &lt;- fnc_cor_p(dta_wgi_lvl_cc, ref_wgi_cols)
sum_wgi_cor_diff  &lt;- fnc_cor_p(dta_wgi_dif_cc, ref_wgi_diff_cols)

#Holm adjustment for testing 15 pairs at once. Note the p-values for the
#differences assume ~5,000 independent observations when they are really ~200
#countries observed repeatedly - so significance there is close to guaranteed
#and the effect sizes are what matter.
sum_wgi_cor_level &lt;- sum_wgi_cor_level |&gt;
  mutate(sig = p.adjust(p, &quot;holm&quot;) &lt; 0.05,
         r2  = cor^2)

sum_wgi_cor_diff &lt;- sum_wgi_cor_diff |&gt;
  mutate(sig = p.adjust(p, &quot;holm&quot;) &lt; 0.05,
         r2  = cor^2)</pre>



<p class="wp-block-paragraph">This code chunk just creates a basic bubble plot for visualizing the correlation matrix. This is perhaps more verbose than it needs to be, but it felt fair to give Claude the satisfaction of trying to apply my style guidelines.</p>



<p class="wp-block-paragraph"><strong>Code: Plotting functions</strong></p>



<pre>#Bubble plot for a correlation matrix. value_col lets the same function handle
#zero-order (cor) and partial (pcor) tables.
fnc_plot_bubbles &lt;- function(dta, value_col = &quot;cor&quot;, wrap = 12) {
  
  ref_lbl &lt;- setNames(str_wrap(ref_wgi_labels, wrap), ref_wgi_cols)
  
  dta &lt;- dta |&gt;
    rename(value = all_of(value_col)) |&gt;
    mutate(x   = factor(x, ref_wgi_cols),
           y   = factor(y, ref_wgi_cols),
           lbl = ifelse(sig, sprintf(&quot;%.2f&quot;, value), sprintf(&quot;(%.2f)&quot;, value)))
  
  ggplot(dta, aes(x, y)) +
    geom_point(aes(size = abs(value), fill = value), shape = 21, colour = &quot;white&quot;) +
    geom_text(aes(label = lbl, colour = abs(value) &gt; 0.55), size = 3, fontface = &quot;bold&quot;) +
    scale_fill_gradient2(low = &quot;#922C40&quot;, mid = &quot;#FFFFFF&quot;, high = &quot;#16AF8E&quot;,
                         midpoint = 0, limits = c(-1, 1), name = &quot;Correlation&quot;) +
    scale_colour_manual(values = c(`TRUE` = &quot;white&quot;, `FALSE` = &quot;#121212&quot;), guide = &quot;none&quot;) +
    scale_size_area(max_size = 16, limits = c(0, 1), guide = &quot;none&quot;) +
    scale_x_discrete(labels = ref_lbl) +
    scale_y_discrete(labels = ref_lbl, limits = rev) +
    coord_fixed() +
    labs(x = NULL, y = NULL) +
    theme_minimal()
}

# Convert the long pairwise tibble back into a symmetric matrix for qgraph
fnc_pcor_matrix &lt;- function(dta_pcor, col) {
  tmp_mat &lt;- matrix(0, length(ref_wgi_cols), length(ref_wgi_cols),
                    dimnames = list(ref_wgi_cols, ref_wgi_cols))
  tmp_mat[cbind(dta_pcor$x, dta_pcor$y)] &lt;- dta_pcor[[col]]
  tmp_mat[cbind(dta_pcor$y, dta_pcor$x)] &lt;- dta_pcor[[col]]
  tmp_mat
}

#Network of direct links. Edges below `cut` in absolute size are NOT DRAWN, so a
#missing line means &quot;smaller than the threshold&quot;, not &quot;zero&quot;.
fnc_plot_network &lt;- function(dta_pcor, cut = ref_pcor_cut, layout = &quot;spring&quot;, seed = 123) {
  
  set.seed(seed)
  
  mat_pcor &lt;- fnc_pcor_matrix(dta_pcor, &quot;pcor&quot;)
  chk_sig  &lt;- fnc_pcor_matrix(dta_pcor, &quot;sig&quot;) == 1
  
  qgraph(mat_pcor,
         layout = layout, minimum = cut, maximum = 1, esize = 9, fade = TRUE,
         posCol = &quot;#16AF8E&quot;, negCol = &quot;#922C40&quot;,
         color = &quot;white&quot;, border.color = &quot;#E5E7EB&quot;, border.width = 2,
         labels = ref_wgi_short[ref_wgi_cols], label.cex = 0.8,
         label.color = &quot;#121212&quot;, vsize = 11, shape = &quot;circle&quot;,
         lty = ifelse(chk_sig, 1, 2),
         edge.labels = ifelse(chk_sig, sprintf(&quot;%.2f&quot;, mat_pcor),
                              sprintf(&quot;(%.2f)&quot;, mat_pcor)),
         edge.label.cex = 0.75, edge.label.bg = &quot;white&quot;,
         edge.label.color = &quot;#121212&quot;,  mar = rep(5, 4))
}</pre>



<h3 class="wp-block-heading"><em>Strong institutions coincide with one another</em></h3>



<p class="wp-block-paragraph"><em>In this section, pairwise correlations are used to test whether the strength of individual institutions tends to occur together.</em></p>



<p class="wp-block-paragraph">It’ll come as no surprise that all of the WGI’s dimensions are strongly associated with each other, which suggests that on average a country scoring highly on one dimension probably scores highly on the others too. For the WGI’s Rule of Law measure, the pairwise associations are strong across the board, with the implied R squared statistic suggesting it accounts for somewhere between 65 and 90 percent of the variation in the other dimensions. Political Stability has the weakest associations, accounting for somewhere between 50 and 75 percent.</p>



<p class="wp-block-paragraph"><strong>Figure: WGI pairwise correlations (levels)</strong></p>



<p class="wp-block-paragraph"><em>The strength of institutions are strongly associated with one another across WGI dimensions</em></p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" loading="lazy" src="https://i1.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/figure_levels_corr-01.png?w=450&#038;ssl=1" alt="" class="wp-image-4281" srcset_temp="https://i1.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/figure_levels_corr-01.png?w=450&#038;ssl=1 539w, https://www.gilesd-j.com/wp-content/uploads/2026/08/figure_levels_corr-01-300x206.png 300w" sizes="auto, (max-width: 539px) 100vw, 539px" data-recalc-dims="1" /></figure>



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



<p class="wp-block-paragraph">The network plot presents the same pairwise associations once the influence of the other dimensions is accounted for. Where partial correlations fall well below the zero-order correlations shown above, it points to dimensions carrying overlapping information. For the <em>Rule of Law</em>, the network diagram presents much weaker associations with other dimensions to the figure above, with moderate associations remaining for <em>Voice and Accountability, Control of Corruption</em> and <em>Political Stability.</em> Although it’s best not to take the implications of this analysis too far, one interpretation of these results that makes intuitive sense is that different institutions relate to one another differently. And sometimes this relationship might be indirect, such as government effectiveness indirectly influencing the rule of law via corruption controls.</p>



<p class="wp-block-paragraph"><strong>Figure: WGI pairwise partial correlations (levels)</strong></p>



<p class="wp-block-paragraph"><em>Pairwise correlations are lower once the influence of other WGI dimensions are accounted for</em></p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="450" height="368" loading="lazy" src="https://i2.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/pcorr_levels.png?resize=450%2C368&#038;ssl=1" alt="" class="wp-image-4283" srcset_temp="https://i2.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/pcorr_levels.png?resize=450%2C368&#038;ssl=1 450w, https://www.gilesd-j.com/wp-content/uploads/2026/08/pcorr_levels-300x245.png 300w" sizes="auto, (max-width: 450px) 100vw, 450px" data-recalc-dims="1" /></figure>



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



<p class="wp-block-paragraph"><strong>Code:</strong> Plotting pairwise correlations at levels</p>



<pre>#produce and display the correlation matrix
plt_wgi_cor_level &lt;- fnc_plot_bubbles(sum_wgi_cor_level)

plt_wgi_cor_level

#network plot for partial correlations 
plt_wgi_net_level &lt;- fnc_plot_network(sum_wgi_pcor_level)</pre>



<h3 class="wp-block-heading"><em>Institutions are connected to one another over time (albeit, loosely)</em></h3>



<p class="wp-block-paragraph"><em>This section explores pairwise correlations between year-to-year movements of WGI dimension to explore evidence for institutions being interdependent.</em></p>



<p class="wp-block-paragraph">Focusing on year-to-year movements in WGI dimensions rather than levels results in smaller correlation statistics across the board, which is to be expected given taking the first difference naturally inflates the influence of noise and measurement errors. Still, the associations point to a similar picture to associations at the levels: a positive and statistically significant association between all dimensions, with <em>Political Stability</em> being the weakest.</p>



<p class="wp-block-paragraph">Having said this, statistical significance just suggests that shared movement between series isn’t zero, not that it’s particularly interesting. Added to this, given the first differences pool values so they are considered independent (despite being from the same countries), significance is close to guaranteed, which is yet another reason they should be interpreted with caution.</p>



<p class="wp-block-paragraph">The implied explanatory power is also probably what matters more and it’s <em>generally</em> modest. For instance, the R squared statistic for <em>Political stability</em> suggests it can explain between 1 to 4 percent of the year-to-year variation in other dimensions. Whereas the <em>Rule of Law,</em> which holds the strongest pairwise explanatory power across dimensions (setting political stability aside), only explains somewhere between 9 to 17 percent of the variation, which while being nothing to sneeze at, still leaves a lot of unexplained movement.</p>



<p class="wp-block-paragraph"><strong>Figure: WGI pairwise correlations (year-to-year changes)</strong></p>



<p class="wp-block-paragraph"><em>Year-to-year movements in institutional quality are statistically associated with one another</em></p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" loading="lazy" src="https://i2.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/figure_diff_corr-01.png?w=450&#038;ssl=1" alt="" class="wp-image-4287" srcset_temp="https://i2.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/figure_diff_corr-01.png?w=450&#038;ssl=1 523w, https://www.gilesd-j.com/wp-content/uploads/2026/08/figure_diff_corr-01-300x190.png 300w" sizes="auto, (max-width: 523px) 100vw, 523px" data-recalc-dims="1" /></figure>



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



<p class="wp-block-paragraph"><strong>Note:</strong> Associations between first differences are expected to be weaker for several reasons: institutions are slow moving; interdependence between institutions may operate with lags or in a non-linear fashion; and as differencing strips out persistent components of each series while retaining measurement errors intact, the signal to noise ratio is likely to be lower. The authors of the WGI also <a href="https://www.brookings.edu/wp-content/uploads/2016/06/09_wgi_kaufmann.pdf" rel="nofollow" target="_blank">warn against taking too much stock of year-to-year changes in scores</a>.</p>



<p class="wp-block-paragraph">The second plot once again presents how year-to-year movements are associated with each other, but after accounting for influences outside the examined pair. Once again, the <em>Rule of Law</em> holds the strongest association with other dimensions, but its association with <em>Control of Corruption</em> is much lower, suggesting that much of the pairwise association between year-to-year changes above relate to other dimension outside the pair.</p>



<p class="wp-block-paragraph"><strong>Figure: WGI pairwise partial correlations (year-to-year changes)</strong></p>



<p class="wp-block-paragraph"><em>Pairwise correlations are reduced, but in most cases remain statistically significant, once the influence of other dimensions is accounted for</em></p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="450" height="368" loading="lazy" src="https://i1.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/pcorr_diff.png?resize=450%2C368&#038;ssl=1" alt="" class="wp-image-4289" srcset_temp="https://i1.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/pcorr_diff.png?resize=450%2C368&#038;ssl=1 450w, https://www.gilesd-j.com/wp-content/uploads/2026/08/pcorr_diff-300x245.png 300w" sizes="auto, (max-width: 450px) 100vw, 450px" data-recalc-dims="1" /></figure>



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



<p class="wp-block-paragraph"><strong>Code:</strong> plot first difference associations</p>



<pre>#correlaton plot for correlations @ first difference
plt_wgi_cor_diff &lt;- fnc_plot_bubbles(sum_wgi_cor_diff)

plt_wgi_cor_diff

#network plot for partial correlatons @ first difference
plt_wgi_net_diff &lt;- fnc_plot_network(sum_wgi_pcor_diff,
                                     layout = plt_wgi_net_level$layout)</pre>



<h2 class="wp-block-heading">Six dimensions, one signal(?)</h2>



<p class="wp-block-paragraph"><em>This section uses Principal Components Analysis (PCA)</em> <em>to explore whether all six dimensions of the WGI measure, or are caused by, the same thing.</em></p>



<p class="wp-block-paragraph"><a href="https://openknowledge.worldbank.org/entities/publication/b4dd83d8-0d2b-5d36-96d5-cfb3433f7c92" rel="nofollow" target="_blank">A strong critique of the WGI</a> is that dimensions more or less measure the same thing. From a conceptual standpoint this might have some weight, due to the <em>perceived</em> quality of one dimension influencing perceptions of another, the use of overlapping data sources and the fact that the definitions are not precisely defined. But, as somebody that works a lot with composite indices, I’d say this comes with the territory and I’m happy to <a href="https://openknowledge.worldbank.org/entities/publication/760809bf-8feb-5fe5-aa75-a8944b0cef78" rel="nofollow" target="_blank">leave it to the experts to argue among themselves</a>.</p>



<p class="wp-block-paragraph">However, from a statistical standpoint this might matter a lot, as it determines whether a measured link between the <em>Rule of Law</em> and outcomes like prosperity can be meaningfully interpreted as telling us anything about the <em>Rule of Law</em>.</p>



<p class="wp-block-paragraph">This is explored in the code below by applying PCA to the WGI levels and first differences. PCA attempts to collapse collinear variables into a set of principal components (PC) that explain as much variability as possible. If the WGI’s dimensions are measuring <em>or</em> being driven by something common, we might expect a small number of PCs will explain a disproportional share of the variance.</p>



<p class="wp-block-paragraph"><strong>Code:</strong> apply PCA to levels and first differences</p>



<pre>#Fit a scaled PCA and pull out variance shares and PC1 loadings.
#Scaling so each index counts equally regardless of how spread out it is.
#Note the levels PCA runs on country averages (one row per country) while the
#differences PCA runs on pooled country-years - the shares are not measured on
#the same unit of analysis.
fnc_pca_tidy &lt;- function(dta, cols, series) {
  
  dta_input &lt;- dta |&gt;
    select(all_of(cols)) |&gt;
    drop_na()
  
  mod &lt;- prcomp(dta_input, scale. = TRUE)
  
  #share of variance picked up by each component
  sum_var &lt;- mod |&gt;
    tidy(matrix = &quot;eigenvalues&quot;) |&gt;
    transmute(series, pc = paste0(&quot;PC&quot;, PC), var_pct = percent,
              nmb_obs = nrow(dta_input))
  
  #how strongly each index marks the first component
  sum_load &lt;- mod |&gt;
    tidy(matrix = &quot;rotation&quot;) |&gt;
    filter(PC == 1) |&gt;
    transmute(series, index = sub(&quot;^d_&quot;, &quot;&quot;, column), loading = abs(value))
  
  list(var = sum_var, load = sum_load)
}

ref_pca_series &lt;- c(&quot;WGI Levels&quot;, &quot;WGI First Differences&quot;)

tmp_pca &lt;- list(fnc_pca_tidy(dta_wgi_lvl_cc, ref_wgi_cols,      ref_pca_series[1]),
                fnc_pca_tidy(dta_wgi_dif_cc, ref_wgi_diff_cols, ref_pca_series[2]))

sum_wgi_pca_var &lt;- tmp_pca |&gt;
  map(&quot;var&quot;) |&gt;
  list_rbind() |&gt;
  mutate(series = factor(series, ref_pca_series))

sum_wgi_pca_load &lt;- tmp_pca |&gt;
  map(&quot;load&quot;) |&gt;
  list_rbind() |&gt;
  mutate(series = factor(series, ref_pca_series))

#drop temporary objects
rm(tmp_pca)</pre>



<p class="wp-block-paragraph">The first plot presents the share of variance explained by each principal component. In the case of the the WGI’s levels, the PCA indicates that almost 90 percent of the measured variance in governance can be explained by a single <em>principal component</em>. Indicating that the majority of information presented by the six WGI dimensions could be efficiently described by a single measure. A result that supports the idea that either the WGI is measuring a similar thing and/or that a common factor is driving all six dimensions.</p>



<p class="wp-block-paragraph">Both PCAs point in a similar direction, although the first differences are much less dramatic: with the first component picks up around 40 percent of the variance, against nearly 90 percent for the levels. Bear in mind these two numbers aren’t measured on the same unit of analysis, making them not directly comparable (i.e. approximately 200 country averages vs 5,000+ first differences). It’s therefore best not to read too much into comparisons, particularly given <a href="https://www.worldbank.org/en/publication/worldwide-governance-indicators/frequently-asked-questions" rel="nofollow" target="_blank">the authors of the WGI explicitly warn against analyzing year-to-year score movements.</a></p>



<p class="wp-block-paragraph"><strong>Figure: Variance explained by principal component</strong></p>



<p class="wp-block-paragraph"><em>The majority of variance can be explained by a single PC for WGI levels, while a larger number of PCs are required to provide a sufficient explanation of variability for first differences</em></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/scree_variance_explained.png?w=450&#038;ssl=1" alt="" class="wp-image-4291" srcset_temp="https://i0.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/scree_variance_explained.png?w=450&#038;ssl=1 651w, https://www.gilesd-j.com/wp-content/uploads/2026/08/scree_variance_explained-300x181.png 300w" sizes="auto, (max-width: 651px) 100vw, 651px" data-recalc-dims="1" /></figure>



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



<p class="wp-block-paragraph"><strong>Code: how concentrated is the common dimension?</strong></p>



<pre>{r}
plt_wgi_pca_scree &lt;- sum_wgi_pca_var |&gt;
  ggplot(aes(pc, var_pct)) +
  geom_col(fill = &quot;#1E298D&quot;, width = 0.7) +
  geom_text(aes(label = scales::percent(var_pct, accuracy = 1)),
            vjust = -0.4, size = 3, colour = &quot;grey40&quot;) +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  facet_wrap(~ series) +
  labs(x = NULL, y = &quot;Variance explained (%)&quot;) +
  theme_minimal(base_size = 10) +
  theme(panel.grid.major.x = element_blank(),
        panel.grid.minor   = element_blank(),
        strip.text         = element_text(face = &quot;bold&quot;, hjust = 0))

plt_wgi_pca_scree</pre>



<p class="wp-block-paragraph">The plot below presents PCA loadings, which measure how strongly each dimension contributes to PC1. Higher loadings mean a dimension is more closely tied to the component, and so shares more with the others. That the dimensions carry comparable loadings at both the levels and the first differences is again consistent with them measuring, or being influenced by, something common.</p>



<p class="wp-block-paragraph">Political Stability sits lower than the rest, but I’d be reluctant to take my interpretation of that too far given how much variance remains unexplained. It’s also roughly what you might expect of a dimension intended to capture shocks rather than gradual shifts, or one behaving non-linearly (questions better answered in a separate post).</p>



<p class="wp-block-paragraph"><strong>Figure: PCA Loadings</strong></p>



<p class="wp-block-paragraph"><em>PCA loadings for both the levels and first differences are generally evenly spread</em></p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" loading="lazy" src="https://i1.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/pca_loadings.png?w=450&#038;ssl=1" alt="" class="wp-image-4294" srcset_temp="https://i1.wp.com/www.gilesd-j.com/wp-content/uploads/2026/08/pca_loadings.png?w=450&#038;ssl=1 651w, https://www.gilesd-j.com/wp-content/uploads/2026/08/pca_loadings-300x181.png 300w" sizes="auto, (max-width: 651px) 100vw, 651px" data-recalc-dims="1" /></figure>



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



<p class="wp-block-paragraph"><strong>Code: PCA loadings</strong></p>



<pre>plt_wgi_pca_load &lt;- sum_wgi_pca_load |&gt;
  mutate(index = factor(index, ref_wgi_cols, ref_wgi_labels) |&gt; fct_reorder(loading)) |&gt;
  ggplot(aes(loading, index)) +
  geom_col(fill = &quot;#1E298D&quot;, width = 0.65) +
  geom_text(aes(label = sprintf(&quot;%.2f&quot;, loading)),
            hjust = -0.25, size = 3, colour = &quot;grey40&quot;) +
  scale_x_continuous(limits = c(0, 0.7), expand = expansion(c(0, 0.05))) +
  facet_wrap(~ series) +
  labs(x = &quot;Absolute loading on PC1&quot;, y = NULL) +
  theme_minimal(base_size = 10) +
  theme(panel.grid.major.y = element_blank(),
        panel.grid.minor   = element_blank(),
        strip.text         = element_text(face = &quot;bold&quot;, hjust = 0))

plt_wgi_pca_load</pre>



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



<p class="wp-block-paragraph">Across three views of the same data, the six WGI dimensions behave like parts of one system rather than six separable things. Country averages correlate strongly across every pair; most associations remain even when accounting for the influence of other dimensions; and a single principal component accounts for a significant share of variation in governance scores between countries. Dimensions seem to move together too, albeit weakly, which is while not a slam dunk, does point to WGI scores having some interdependence.</p>



<p class="wp-block-paragraph">But, it’s worth being clear about what this analysis doesn’t establish: Collinearity isn’t endogeneity and while a system of interdependent institutions might produce this pattern, so would WGI dimensions measuring the same thing.</p>



<p class="wp-block-paragraph">But, the point of this post isn’t to support the legitimacy of the WGI or any other measure. And the distinction matters little to the point of this post. As whether dimensions move together because they co-determine each other, share a common driver, <em>or</em> share source data, the implications for anyone trying to understand or analyze institutions is the same: they have to be examined as a set as a correlation between one institution and an outcome may strong regardless of whether that pillar is doing the actual work or not.</p>



<p class="wp-block-paragraph"><strong>How AI was used for this post:</strong> Claude was used <em>heavily</em> to refine the code and <em>lightly</em> leaned on to improve the accuracy and readability of the text. The former was mainly in an attempt to address Claude’s almost endless array of suggestions for adding more code, while the latter was mainly a result of having stared too long at my own writing. </p>



<p class="wp-block-paragraph">No doubt errors remain as it’s quite the topic, which means I’ve made quite a number of edits to both the text and code. Feel free to contact me <a href="https://www.gilesd-j.com/contact/" rel="nofollow" target="_blank">here</a>.</p>



<p class="wp-block-paragraph"><strong>Additional Note:</strong></p>



<p class="wp-block-paragraph">The motivation for writing this was the absence of descriptive analysis on the links between law and order and economic growth aimed at a general audience. Keeping the code in the post has probably cost it some readability, but my reasoning for sharing the analysis so openly was to make it easier for others to build on this post to fill the many gaps out there (including me in my future posts)<strong>.</strong></p>



<p class="wp-block-paragraph">The inspiration for this series came from work I completed in 2025 for the Bingham Centre for the Rule of Law and the Law Society of England and Wales. A paper based on the work summarizing research on the topic is available <a href="https://binghamcentre.biicl.org/publications/the-rule-of-law-and-the-institutional-roots-of-economic-performance" rel="nofollow" target="_blank">here</a>.</p>



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


<ol class="wp-block-footnotes"><li id="7371b0b4-0d25-45e8-98bb-2869772bcebc">For a great summary of the research, see Dr Lopez-Gomez, L. (2026, <em>The Rule of Law and the Institutional Roots of Economic Performance.</em> The British Institute of International and Comparative Law, <em>(<a href="https://binghamcentre.biicl.org/documents/172_the_rule_of_law_and_the_institutional_roots_of_economic_performance.pdf" rel="nofollow" target="_blank">link</a>). Also see:</em> Haggard, S., MacIntyre, A. and Tiede, L., 2008. The rule of law and economic development. <em>Annu. Rev. Polit. Sci.</em>, <em>11</em>(1), pp.205-234; and Besley, T., Bogart, D., Chapman, J. and Nuno, P., 2025. Justices of the peace: Legal foundations of the industrial revolution (No. 20214). Centre for Economic Policy Research. <a href="https://www.gilesd-j.com/2026/08/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/#7371b0b4-0d25-45e8-98bb-2869772bcebc-link" aria-label="Jump to footnote reference 1" rel="nofollow" target="_blank"><img src="https://i2.wp.com/s.w.org/images/core/emoji/17.0.2/72x72/21a9.png?w=578&#038;ssl=1" alt="&#x21a9;" class="wp-smiley" style="height: 1em; max-height: 1em;" data-recalc-dims="1" />︎</a></li><li id="2fa2233b-5d92-4076-b096-e615cb6cd194">One of the better attempts I’ve seen to empirically test a causal connection between the two: Besley, T., Bogart, D., Chapman, J. and Nuno, P., 2025. <em>Justices of the peace: Legal foundations of the industrial revolution</em> (No. 20214). Centre for Economic Policy Research. <a href="https://www.gilesd-j.com/2026/08/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/#2fa2233b-5d92-4076-b096-e615cb6cd194-link" aria-label="Jump to footnote reference 2" rel="nofollow" target="_blank"><img src="https://i2.wp.com/s.w.org/images/core/emoji/17.0.2/72x72/21a9.png?w=578&#038;ssl=1" alt="&#x21a9;" class="wp-smiley" style="height: 1em; max-height: 1em;" data-recalc-dims="1" />︎</a></li></ol>


<p class="wp-block-paragraph"></p>
<p>The post <a href="https://www.gilesd-j.com/2026/08/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/" rel="nofollow" target="_blank">Institutions Are a Package Deal: What a Correlation Can’t Tell You About the Rule of Law</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/10/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/"> 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/institutions-are-a-package-deal-what-a-correlation-cant-tell-you-about-the-rule-of-law/">Institutions Are a Package Deal: What a Correlation Can’t Tell You About the Rule of Law</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

		<post-id xmlns="com-wordpress:feed-additions:1">403043</post-id>	</item>
		<item>
		<title>From Census Data to Demographic Analysis with ARcenso: A Reproducible Workflow in R</title>
		<link>https://www.r-bloggers.com/2026/08/from-census-data-to-demographic-analysis-with-arcenso-a-reproducible-workflow-in-r/</link>
		
		<dc:creator><![CDATA[rOpenSci]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[R bloggers]]></category>
		<guid isPermaLink="false">https://ropensci.org/blog/2026/08/10/analisis-demografico-con-arcenso/</guid>

					<description><![CDATA[<p>Read it in: Español. Population censuses are a key source of information for understanding the composition of populations and how they change across regions within a country. They provide essential evidence for research, the design and evaluation of public policies, and informed decision-making.<br />
Working with census data typically involves ...</p>
<strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/from-census-data-to-demographic-analysis-with-arcenso-a-reproducible-workflow-in-r/">From Census Data to Demographic Analysis with ARcenso: A Reproducible Workflow 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://ropensci.org/blog/2026/08/10/analisis-demografico-con-arcenso/"> rOpenSci - open tools for open science</a></strong>, and kindly contributed to <a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers</a>].  (You can report issue about the content on this page <a href="https://www.r-bloggers.com/contact-us/">here</a>)
<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div>

<p><a href='https://ropensci.org/es/blog/2026/08/10/analisis-demografico-con-arcenso/' rel="nofollow" target="_blank">Read it in: Español</a>.</p> <p>Population censuses are a key source of information for understanding the composition of populations and how they change across regions within a country. They provide essential evidence for research, the design and evaluation of public policies, and informed decision-making.</p>
<p>Working with census data typically involves several preliminary steps: identifying what information is available, downloading the data, organizing it, and preparing it for analysis. When this information is scattered across multiple sources and provided in different formats, the process can become complex.</p>
<br>
<figure><img src="https://i2.wp.com/ropensci.org/es/blog/2026/08/10/analisis-demografico-con-arcenso/portada-blog.es.png?w=578&#038;ssl=1"
alt="Illustration of the Argentine Census featuring the ARcenso and rOpenSci logos, a hornero (the national bird), and graphic material from the 1970 and 1980 censuses." data-recalc-dims="1"><figcaption>
<p>ARcenso blog homepage</p>
</figcaption>
</figure>
<p><a href="https://soyandrea.github.io/arcenso/" rel="nofollow" target="_blank">ARcenso</a> is an R package developed as part of <a href="https://ropensci.org/es/champions/" rel="nofollow" target="_blank">the rOpenSci Champions Program</a> to facilitate access to Argentine census data and simplify its analysis.</p>
<p>In this article, the ARcenso development team demonstrates how to work with the historical census data available through the package in a simple, reproducible way.</p>
<p>The goal is to conduct a demographic analysis using official national data from the 1970 and 1980 censuses, combining visualisations and tables to explore the structure of the population—that is, how it is distributed by age and sex. This type of analysis helps characterise demographic changes over time, providing key information for research and the design of public policies.</p>
<h2>
Getting started
</h2><p>To run the code examples from this article on your own computer, make sure you have the following packages installed:</p>
<pre># If you don&#39;t have pak installed
install.packages(&quot;pak&quot;)

# Install ARcenso from GitHub
pak::pkg_install(&quot;soyandrea/arcenso&quot;)

# Install the required packages from CRAN
install.packages(c(&quot;dplyr&quot;, &quot;tidyr&quot;, &quot;ggplot2&quot;, &quot;gt&quot;))
</pre><p>Next, we load the packages needed to work with the census data and build indicators for the analysis:</p>
<pre>library(arcenso) # access census data
library(dplyr) # data manipulation
library(tidyr) # data tidying and transformation
library(ggplot2) # data visualisation
library(gt) # table formatting
</pre><h2>
Accessing census data
</h2><p>Now that the required packages are installed and loaded, we can use ARcenso to access census data from the 1970 and 1980 censuses. The package allows users to retrieve information by census year, topic, and geographic area of interest, using the official geographic codes defined by the
<a href="https://www.indec.gob.ar/indec/web/Nivel3-Tema-2-41" rel="nofollow" target="_blank"> National Institute of Statistics and Censuses (INDEC) of Argentina</a> .</p>
<p>The <code>check_repository()</code> function helps identify the datasets available in the package. The <code>topic</code> argument specifies the subject of interest (for example, population structure), while <code>geo_code</code> identifies the geographic area.</p>
<p>If you are unsure which values are available for these arguments, you can explore the package metadata. The <code>geo_metadata</code> object contains the available geographic areas and their corresponding codes, while <code>census_metadata</code> provides information about the topics and tables included in the package.</p>
<p>In this example, we use population structure data (<code>topic = &quot;estructura&quot;</code>) for the entire country (<code>geo_code = &quot;00&quot;</code>). As a first step, we use <code>check_repository()</code> to see which datasets are available.</p>
<pre>check_repository(topic = &quot;estructura&quot;, geo_code = &quot;00&quot;)

# A tibble: 4 × 3
id_cuadro anio titulo
&lt;chr&gt; &lt;dbl&gt; &lt;chr&gt;
1 1970_00_estructura_01 1970 Cuadro 1. Total del país. Población total, por gr…
2 1980_00_estructura_01 1980 Cuadro G3. Centros urbanos según tamaño y poblaci…
3 1980_00_estructura_02 1980 Cuadro G1. Total del país. Población total según …
4 1980_00_estructura_03 1980 Cuadro G2. Total del país. Población según sexo y…
</pre><p>The output of <code>check_repository()</code> lists the available years for the selected combination of topic and geographic area, together with the corresponding table identifiers (IDs) and their titles. Based on this information, we select the tables <code>1970_00_estructura_01</code> and <code>1980_00_estructura_03</code>, which contain tabulations of the country’s total population by sex and age group and will be used throughout this analysis.</p>
<blockquote>
<p><strong>Tip</strong>: You can also explore the available data interactively using <code>arcenso_app()</code>. Once you find the table you need, simply copy its ID and use it in your analysis.</p>
</blockquote>
<figure><img src="https://i0.wp.com/ropensci.org/es/blog/2026/08/10/analisis-demografico-con-arcenso/shiny_arcenso.es.png?w=578&#038;ssl=1"
alt="Shiny ARcenso Application: View census data filtered by year, geographic scope, and topic, displaying the 1970 literacy table." data-recalc-dims="1"><figcaption>
<p>Interactive census data explorer with <code>arcenso_app()</code></p>
</figcaption>
</figure>
<h2>
Data preparation
</h2><p>Once the tables of interest have been identified, we use <code>get_census()</code> to import them directly into the R session using their table identifiers.</p>
<p>As the structures of the two tables are not identical, we perform a series of transformations to harmonise the variables and create a consistent basis for comparison across censuses. In particular, we recode the age categories into five-year groups to ensure a common structure and add a column identifying the census year.</p>
<h3>
1970 Census
</h3><p>For the 1970 census, the data are already grouped into five-year age groups, so we only need to adjust the format of some labels and keep the variables relevant to the analysis.</p>
<pre># Country Total
poblacion_1970 &lt;- get_census(id = &quot;1970_00_estructura_01&quot;)

pob_1970 &lt;- poblacion_1970 |&gt;
 filter(sexo != &quot;Total&quot;) |&gt;
 mutate(
 censo = 1970,
 grupo_de_edad = case_when(
 grupo_de_edad == &quot;0-4&quot; ~ &quot;00-04&quot;,
 grupo_de_edad == &quot;5-9&quot; ~ &quot;05-09&quot;,
 TRUE ~ grupo_de_edad
 )
 ) |&gt;
 rename(grupo_edad = grupo_de_edad) |&gt;
 select(censo, sexo, grupo_edad, poblacion)
</pre><h3>
1980 Census
</h3><p>In contrast, the 1980 census table reports age as single years and includes additional information that is not relevant to this analysis. We therefore select the categories of interest and aggregate the data into five-year age groups to make them comparable with the 1970 census.</p>
<pre>poblacion_1980 &lt;- get_census(id = &quot;1980_00_estructura_03&quot;)

pob_1980 &lt;- poblacion_1980 |&gt;
 filter(urbano_rural == &quot;Total&quot;, sexo != &quot;Total&quot;, edad != &quot;Total&quot;) |&gt;
 mutate(
 censo = 1980,
 edad_num = ifelse(edad == &quot;85 y más&quot;, 85, as.numeric(edad)),
 grupo_edad = case_when(
 edad_num %in% c(0:4) ~ &quot;00-04&quot;,
 edad_num %in% c(5:9) ~ &quot;05-09&quot;,
 edad_num %in% c(10:14) ~ &quot;10-14&quot;,
 edad_num %in% c(15:19) ~ &quot;15-19&quot;,
 edad_num %in% c(20:24) ~ &quot;20-24&quot;,
 edad_num %in% c(25:29) ~ &quot;25-29&quot;,
 edad_num %in% c(30:34) ~ &quot;30-34&quot;,
 edad_num %in% c(35:39) ~ &quot;35-39&quot;,
 edad_num %in% c(40:44) ~ &quot;40-44&quot;,
 edad_num %in% c(45:49) ~ &quot;45-49&quot;,
 edad_num %in% c(50:54) ~ &quot;50-54&quot;,
 edad_num %in% c(55:59) ~ &quot;55-59&quot;,
 edad_num %in% c(60:64) ~ &quot;60-64&quot;,
 edad_num %in% c(65:69) ~ &quot;65-69&quot;,
 edad_num %in% c(70:74) ~ &quot;70-74&quot;,
 edad_num %in% c(75:79) ~ &quot;75-79&quot;,
 edad_num %in% c(80:84) ~ &quot;80-84&quot;,
 TRUE ~ &quot;85 y más&quot;
 )
 ) |&gt;
 select(censo, sexo, grupo_edad, poblacion)
</pre><h3>
Building the integrated database
</h3><p>Once both tables have been processed, we combine them into a single dataset and define the final structure of the variables, preparing the data for visualisation and the analysis of demographic indicators.</p>
<pre># We combine both geographic areas and prepare the variables for use
poblacion &lt;-
 bind_rows(
 pob_1970,
 pob_1980
 ) |&gt;
 mutate(
 poblacion = as.numeric(poblacion),
 sexo = factor(sexo, levels = c(&quot;Varones&quot;, &quot;Mujeres&quot;)),
 grupo_edad = factor(
 grupo_edad,
 levels = c(
 &quot;00-04&quot;,
 &quot;05-09&quot;,
 &quot;10-14&quot;,
 &quot;15-19&quot;,
 &quot;20-24&quot;,
 &quot;25-29&quot;,
 &quot;30-34&quot;,
 &quot;35-39&quot;,
 &quot;40-44&quot;,
 &quot;45-49&quot;,
 &quot;50-54&quot;,
 &quot;55-59&quot;,
 &quot;60-64&quot;,
 &quot;65-69&quot;,
 &quot;70-74&quot;,
 &quot;75-79&quot;,
 &quot;80-84&quot;,
 &quot;85 y más&quot;
 )
 )
 )
</pre><h2>
Population structure
</h2><p>Now that we have a dataset combining both censuses, with population totals organised by census year, sex, and age group, we can consistently compare the population composition across the two census years.</p>
<pre>head(poblacion)


 # A tibble: 6 × 4
 censo sexo grupo_edad poblacion
 &lt;dbl&gt; &lt;fct&gt; &lt;fct&gt; &lt;dbl&gt;
 1 1970 Varones 00-04 1196950
 2 1970 Mujeres 00-04 1158350
 3 1970 Varones 05-09 1163050
 4 1970 Mujeres 05-09 1133950
 5 1970 Varones 10-14 1114300
 6 1970 Mujeres 10-14 1086850
</pre><p>Organising the population by sex and five-year age groups allows us to analyse its structure using a variety of visualisations. In this case, we use a population pyramid, which makes it easier to interpret both dimensions together.</p>
<h3>
Population pyramid
</h3><p>A population pyramid displays the distribution of the population by age and sex simultaneously, typically showing males on the left and females on the right. By representing the proportion of people in each five-year age group, it provides a clear visual summary of the population structure.</p>
<p>In this case, we calculated the relative distribution of the population within each census, allowing us to compare the population structure between 1970 and 1980 regardless of total population size. The shape of the pyramid also allows us to identify broad demographic patterns, such as a higher concentration of younger age groups or a relatively older population profile.</p>
<p>We then calculated the relative distribution of the population in each census and constructed a population pyramid to compare the population structure of the two censuses.</p>
<pre># Dataset for the Population Pyramid
piramide &lt;- poblacion |&gt;
 group_by(censo, sexo) |&gt;
 mutate(
 poblacion_rel = if_else(
 sexo == &quot;Varones&quot;,
 -poblacion / sum(poblacion),
 poblacion / sum(poblacion)
 )
 ) |&gt;
 ungroup()

# Comparison Pyramid
piramide |&gt;
 ggplot(aes(x = poblacion_rel, y = grupo_edad, fill = sexo)) +
 geom_col() +
 facet_wrap(~censo, ncol = 2) +
 scale_fill_manual(values = c(&quot;#00f59b&quot;, &quot;#7014f2&quot;)) +
 scale_x_continuous(
 labels = function(x) paste0(abs(round(x * 100, 1)), &quot;%&quot;),
 limits = c(-0.15, 0.15),
 breaks = seq(-0.15, 0.15, by = 0.05)
 ) +
 labs(
 title = &quot;Gráfico 1. Estructura de la población por sexo y grupo quinquenal de edad.&quot;,
 subtitle = &quot;Argentina. Años 1970 y 1980&quot;,
 x = &quot;Porcentaje&quot;,
 y = &quot;Grupo quinquenal de edad&quot;,
 caption = &quot;Fuente: INDEC, Censo Nacional de Población 1970 y 1980. Procesado con ARcenso.&quot;,
 fill = &quot;Sexo&quot;
 ) +
 theme_bw() +
 theme(
 legend.position = &quot;bottom&quot;,
 strip.text = element_text(face = &quot;bold&quot;, size = 12)
 )
</pre><figure><img src="https://i1.wp.com/ropensci.org/es/blog/2026/08/10/analisis-demografico-con-arcenso/piramide_poblacional_1.es.png?w=578&#038;ssl=1"
alt="Population pyramids comparing the distribution by age and sex in Argentina between 1970 and 1980. A narrower base is observed in 1980, along with a slight relative increase in the adult and older adult populations, with differences between men and women." data-recalc-dims="1"><figcaption>
<p>Figure 1. Population structure by sex and five-year age group. Argentina. Year 1970 and 1980. Source: INDEC, Censo Nacional de Población 1970 y 1980. Processed with ARcenso.</p>
</figcaption>
</figure>
<p>Both censuses show a young population structure, with a high concentration of people in the younger age groups. However, by 1980, a slight shift towards the adult age groups is already apparent, suggesting the early stages of population aging.</p>
<h2>
Construction of demographic indicators
</h2><p>While the population pyramid provides an overall view of the population structure, demographic indicators offer summary measures that allow these patterns to be quantified and compared more precisely. In this section, we calculate two commonly used indicators to complement the visual analysis.</p>
<h3>
Aging index
</h3><p>The aging index compares the number of older adults (aged 65 years and over) with the number of children (aged 0–14 years). It provides a simple way to see whether the population is weighted more toward younger age groups or older age groups.</p>
<pre>envejecimiento &lt;- poblacion |&gt;
 group_by(censo) |&gt;
 summarise(
 poblacion_0a14 = sum(poblacion[
 grupo_edad %in% c(&quot;00-04&quot;, &quot;05-09&quot;, &quot;10-14&quot;)
 ]),
 poblacion_65ymas = sum(poblacion[
 grupo_edad %in% c(&quot;65-69&quot;, &quot;70-74&quot;, &quot;75-79&quot;, &quot;80-84&quot;, &quot;85 y más&quot;)
 ]),
 indice = round(poblacion_65ymas / poblacion_0a14 * 100, 0)
 )


gt(envejecimiento) |&gt;
 tab_header(
 title = &quot;Comparación del índice de envejecimiento&quot;,
 subtitle = &quot;Argentina. Años 1970 y 1980&quot;
 ) |&gt;
 tab_spanner(
 label = &quot;Población&quot;,
 columns = c(poblacion_0a14, poblacion_65ymas)
 ) |&gt;
 fmt_number(
 columns = c(poblacion_0a14, poblacion_65ymas),
 decimals = 0,
 sep_mark = &quot;.&quot;
 ) |&gt;
 cols_label(
 poblacion_0a14 = &quot;0 a 14 años&quot;,
 poblacion_65ymas = &quot;65 años y más&quot;,
 indice = &quot;Indice&quot;
 ) |&gt;
 tab_source_note(
 source_note = md(
 &quot;**Fuente:** elaboración propia en base a datos de INDEC (Censos Nacionales de Población 1970 y 1980).&quot;
 )
 )
</pre><div id="acttjajtlr" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
<style>#acttjajtlr table {
font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

#acttjajtlr thead, #acttjajtlr tbody, #acttjajtlr tfoot, #acttjajtlr tr, #acttjajtlr td, #acttjajtlr th {
border-style: none;
}

#acttjajtlr p {
margin: 0;
padding: 0;
}

#acttjajtlr .gt_table {
display: table;
border-collapse: collapse;
line-height: normal;
margin-left: auto;
margin-right: auto;
color: #333333;
font-size: 16px;
font-weight: normal;
font-style: normal;
background-color: #FFFFFF;
width: auto;
border-top-style: solid;
border-top-width: 2px;
border-top-color: #A8A8A8;
border-right-style: none;
border-right-width: 2px;
border-right-color: #D3D3D3;
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #A8A8A8;
border-left-style: none;
border-left-width: 2px;
border-left-color: #D3D3D3;
}

#acttjajtlr .gt_caption {
padding-top: 4px;
padding-bottom: 4px;
}

#acttjajtlr .gt_title {
color: #333333;
font-size: 125%;
font-weight: initial;
padding-top: 4px;
padding-bottom: 4px;
padding-left: 5px;
padding-right: 5px;
border-bottom-color: #FFFFFF;
border-bottom-width: 0;
}

#acttjajtlr .gt_subtitle {
color: #333333;
font-size: 85%;
font-weight: initial;
padding-top: 3px;
padding-bottom: 5px;
padding-left: 5px;
padding-right: 5px;
border-top-color: #FFFFFF;
border-top-width: 0;
}

#acttjajtlr .gt_heading {
background-color: #FFFFFF;
text-align: center;
border-bottom-color: #FFFFFF;
border-left-style: none;
border-left-width: 1px;
border-left-color: #D3D3D3;
border-right-style: none;
border-right-width: 1px;
border-right-color: #D3D3D3;
}

#acttjajtlr .gt_bottom_border {
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
}

#acttjajtlr .gt_col_headings {
border-top-style: solid;
border-top-width: 2px;
border-top-color: #D3D3D3;
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
border-left-style: none;
border-left-width: 1px;
border-left-color: #D3D3D3;
border-right-style: none;
border-right-width: 1px;
border-right-color: #D3D3D3;
}

#acttjajtlr .gt_col_heading {
color: #333333;
background-color: #FFFFFF;
font-size: 100%;
font-weight: normal;
text-transform: inherit;
border-left-style: none;
border-left-width: 1px;
border-left-color: #D3D3D3;
border-right-style: none;
border-right-width: 1px;
border-right-color: #D3D3D3;
vertical-align: bottom;
padding-top: 5px;
padding-bottom: 6px;
padding-left: 5px;
padding-right: 5px;
overflow-x: hidden;
}

#acttjajtlr .gt_column_spanner_outer {
color: #333333;
background-color: #FFFFFF;
font-size: 100%;
font-weight: normal;
text-transform: inherit;
padding-top: 0;
padding-bottom: 0;
padding-left: 4px;
padding-right: 4px;
}

#acttjajtlr .gt_column_spanner_outer:first-child {
padding-left: 0;
}

#acttjajtlr .gt_column_spanner_outer:last-child {
padding-right: 0;
}

#acttjajtlr .gt_column_spanner {
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
vertical-align: bottom;
padding-top: 5px;
padding-bottom: 5px;
overflow-x: hidden;
display: inline-block;
width: 100%;
}

#acttjajtlr .gt_spanner_row {
border-bottom-style: hidden;
}

#acttjajtlr .gt_group_heading {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px;
color: #333333;
background-color: #FFFFFF;
font-size: 100%;
font-weight: initial;
text-transform: inherit;
border-top-style: solid;
border-top-width: 2px;
border-top-color: #D3D3D3;
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
border-left-style: none;
border-left-width: 1px;
border-left-color: #D3D3D3;
border-right-style: none;
border-right-width: 1px;
border-right-color: #D3D3D3;
vertical-align: middle;
text-align: left;
}

#acttjajtlr .gt_empty_group_heading {
padding: 0.5px;
color: #333333;
background-color: #FFFFFF;
font-size: 100%;
font-weight: initial;
border-top-style: solid;
border-top-width: 2px;
border-top-color: #D3D3D3;
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
vertical-align: middle;
}

#acttjajtlr .gt_from_md > :first-child {
margin-top: 0;
}

#acttjajtlr .gt_from_md > :last-child {
margin-bottom: 0;
}

#acttjajtlr .gt_row {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px;
margin: 10px;
border-top-style: solid;
border-top-width: 1px;
border-top-color: #D3D3D3;
border-left-style: none;
border-left-width: 1px;
border-left-color: #D3D3D3;
border-right-style: none;
border-right-width: 1px;
border-right-color: #D3D3D3;
vertical-align: middle;
overflow-x: hidden;
}

#acttjajtlr .gt_stub {
color: #333333;
background-color: #FFFFFF;
font-size: 100%;
font-weight: initial;
text-transform: inherit;
border-right-style: solid;
border-right-width: 2px;
border-right-color: #D3D3D3;
padding-left: 5px;
padding-right: 5px;
}

#acttjajtlr .gt_stub_row_group {
color: #333333;
background-color: #FFFFFF;
font-size: 100%;
font-weight: initial;
text-transform: inherit;
border-right-style: solid;
border-right-width: 2px;
border-right-color: #D3D3D3;
padding-left: 5px;
padding-right: 5px;
vertical-align: top;
}

#acttjajtlr .gt_row_group_first td {
border-top-width: 2px;
}

#acttjajtlr .gt_row_group_first th {
border-top-width: 2px;
}

#acttjajtlr .gt_summary_row {
color: #333333;
background-color: #FFFFFF;
text-transform: inherit;
padding-top: 8px;
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px;
}

#acttjajtlr .gt_first_summary_row {
border-top-style: solid;
border-top-color: #D3D3D3;
}

#acttjajtlr .gt_first_summary_row.thick {
border-top-width: 2px;
}

#acttjajtlr .gt_last_summary_row {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px;
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
}

#acttjajtlr .gt_grand_summary_row {
color: #333333;
background-color: #FFFFFF;
text-transform: inherit;
padding-top: 8px;
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px;
}

#acttjajtlr .gt_first_grand_summary_row {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px;
border-top-style: double;
border-top-width: 6px;
border-top-color: #D3D3D3;
}

#acttjajtlr .gt_last_grand_summary_row_top {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px;
border-bottom-style: double;
border-bottom-width: 6px;
border-bottom-color: #D3D3D3;
}

#acttjajtlr .gt_striped {
background-color: rgba(128, 128, 128, 0.05);
}

#acttjajtlr .gt_table_body {
border-top-style: solid;
border-top-width: 2px;
border-top-color: #D3D3D3;
border-bottom-style: solid;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
}

#acttjajtlr .gt_footnotes {
color: #333333;
background-color: #FFFFFF;
border-bottom-style: none;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
border-left-style: none;
border-left-width: 2px;
border-left-color: #D3D3D3;
border-right-style: none;
border-right-width: 2px;
border-right-color: #D3D3D3;
}

#acttjajtlr .gt_footnote {
margin: 0px;
font-size: 90%;
padding-top: 4px;
padding-bottom: 4px;
padding-left: 5px;
padding-right: 5px;
}

#acttjajtlr .gt_sourcenotes {
color: #333333;
background-color: #FFFFFF;
border-bottom-style: none;
border-bottom-width: 2px;
border-bottom-color: #D3D3D3;
border-left-style: none;
border-left-width: 2px;
border-left-color: #D3D3D3;
border-right-style: none;
border-right-width: 2px;
border-right-color: #D3D3D3;
}

#acttjajtlr .gt_sourcenote {
font-size: 90%;
padding-top: 4px;
padding-bottom: 4px;
padding-left: 5px;
padding-right: 5px;
}

#acttjajtlr .gt_left {
text-align: left;
}

#acttjajtlr .gt_center {
text-align: center;
}

#acttjajtlr .gt_right {
text-align: right;
font-variant-numeric: tabular-nums;
}

#acttjajtlr .gt_font_normal {
font-weight: normal;
}

#acttjajtlr .gt_font_bold {
font-weight: bold;
}

#acttjajtlr .gt_font_italic {
font-style: italic;
}

#acttjajtlr .gt_super {
font-size: 65%;
}

#acttjajtlr .gt_footnote_marks {
font-size: 75%;
vertical-align: 0.4em;
position: initial;
}

#acttjajtlr .gt_asterisk {
font-size: 100%;
vertical-align: 0;
}

#acttjajtlr .gt_indent_1 {
text-indent: 5px;
}

#acttjajtlr .gt_indent_2 {
text-indent: 10px;
}

#acttjajtlr .gt_indent_3 {
text-indent: 15px;
}

#acttjajtlr .gt_indent_4 {
text-indent: 20px;
}

#acttjajtlr .gt_indent_5 {
text-indent: 25px;
}

#acttjajtlr .katex-display {
display: inline-flex !important;
margin-bottom: 0.75em !important;
}

#acttjajtlr div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {
height: 0px !important;
}
</style>
<table class="gt_table" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
<thead>
<tr class="gt_heading">
<td colspan="4" class="gt_heading gt_title gt_font_normal" style>Aging Index Comparison</td>
</tr>
<tr class="gt_heading">
<td colspan="4" class="gt_heading gt_subtitle gt_font_normal gt_bottom_border" style>Argentina. 1970s and 1980s</td>
</tr>
<tr class="gt_col_headings gt_spanner_row">
<th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="censo">Census</th>
<th class="gt_center gt_columns_top_border gt_column_spanner_outer" rowspan="1" colspan="2" scope="colgroup" id="Population">
<div class="gt_column_spanner">Population</div>
</th>
<th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="index">Index</th>
</tr>
<tr class="gt_col_headings">
<th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="population_0to14">Ages 0 to 14</th>
<th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="population_65andover">65 years and older</th>
</tr>
</thead>
<tbody class="gt_table_body">
<tr><td headers="census" class="gt_row gt_right">1970</td>
<td headers="population_0to14" class="gt_row gt_right">6,853,450</td>
<td headers="population_65andover" class="gt_row gt_right">1,631,400</td>
<td headers="index" class="gt_row gt_right">24</td></tr>
<tr><td headers="census" class="gt_row gt_right">1980</td>
<td headers="population_0to14" class="gt_row gt_right">8,480,768</td>
<td headers="population_65_and_over" class="gt_row gt_right">2,290,564</td>
<td headers="index" class="gt_row gt_right">27</td></tr>
</tbody>
<tfoot>
<tr class="gt_sourcenotes">
<td class="gt_sourcenote" colspan="4"><span class='gt_from_md'><strong>Source:</strong> Author’s own calculations based on INDEC data (1970 and 1980 National Population Censuses).</span></td>
</tr>
</tfoot>
</table>
</div>
<p>The increase in the aging index reflects a change in the population age structure between 1970 and 1980, indicating a larger relative share of people aged 65 years and over compared with the younger population. This suggests a gradual aging of the population.</p>
<h3>
Female-to-Male ratio
</h3><p>This indicator shows how many women there are for every 100 men in a specific population group. Here, we calculate it for people aged 60 years and over, where differences between women and men tend to become more pronounced.</p>
<pre>feminidad &lt;- poblacion |&gt;
 filter(
 grupo_edad %in% c(&quot;60-64&quot;, &quot;65-69&quot;, &quot;70-74&quot;, &quot;75-79&quot;, &quot;80-84&quot;, &quot;85 y más&quot;)
 ) |&gt;
 group_by(censo, grupo_edad, sexo) |&gt;
 summarise(poblacion = sum(poblacion), .groups = &quot;drop&quot;) |&gt;
 pivot_wider(names_from = sexo, values_from = poblacion) |&gt;
 mutate(
 indice_feminidad = round(Mujeres / Varones * 100, 0)
 ) |&gt;
 select(-Varones, -Mujeres)


feminidad_plot &lt;- feminidad |&gt;
 pivot_wider(
 names_from = censo,
 values_from = indice_feminidad,
 names_prefix = &quot;censo_&quot;
 ) |&gt;
 ggplot(aes(y = grupo_edad)) +
 geom_segment(
 aes(x = censo_1970, xend = censo_1980, yend = grupo_edad),
 color = &quot;grey85&quot;,
 linewidth = 1
 ) +
 geom_point(
 aes(x = censo_1970),
 color = &quot;#ff0f7b&quot;,
 size = 3
 ) +
 geom_point(
 aes(x = censo_1980),
 color = &quot;#f89b29&quot;,
 size = 3
 ) +
 geom_text(
 aes(x = censo_1970, label = censo_1970),
 hjust = 1.4,
 size = 3
 ) +
 geom_text(
 aes(x = censo_1980, label = censo_1980),
 hjust = -0.4,
 size = 3
 ) +
 labs(
 x = &quot;Mujeres por cada 100 varones&quot;,
 y = &quot;Grupo de edad&quot;,
 title = &quot;Cambio en el índice de feminidad de la población de 60 años y más&quot;,
 subtitle = &quot;Argentina, Años 1970 y 1980&quot;,
 caption = &quot;Fuente: INDEC, Censo Nacional de Población 1970 y 1980. Procesado con ARcenso.&quot;
 ) +
 theme_minimal()

feminidad_plot
</pre><figure><img src="https://i0.wp.com/ropensci.org/es/blog/2026/08/10/analisis-demografico-con-arcenso/indice_feminidad.es.png?w=578&#038;ssl=1"
alt="Graph comparing the sex ratio (women per 100 men) in the population aged 60 and older between 1970 and 1980, by five-year age groups. An increase in the ratio is observed across all age groups, with higher values among older age groups, indicating a relatively higher proportion of women." data-recalc-dims="1"><figcaption>
<p>Change in the female-to-male ratio among the population aged 60 and older. Argentina. Year 1970 and 1980. Source: INDEC, Censo Nacional de Población 1970 y 1980. Processed with ARcenso.</p>
</figcaption>
</figure>
<p>The female-to-male ratio shows a higher proportion of women in the older age groups. This difference becomes more pronounced between 1970 and 1980 across all the age groups analysed, reflecting patterns of higher female life expectancy.</p>
<h2>
What does this analysis tell us?
</h2><p>In demographic analysis, the most time-consuming part is often not calculating indicators or creating visualisations, but the work that comes beforehand: identifying the available data for each census, understanding how it is organised, and building a consistent framework that enables comparisons across census years.</p>
<p>In this example, that process involved locating the relevant tables, understanding the structural differences between the 1970 and 1980 censuses, and harmonising the variables based on the available information to create a comparable dataset. This is precisely what ARcenso is designed to simplify. The <code>check_repository()</code> function helps identify the available tables, <code>get_census()</code> imports the data into R in a structured format, and <code>arcenso_app()</code> provides an interactive way to explore the repository.</p>
<p>Once the data have been organised, the analysis becomes more accessible, reproducible, and easier to extend to new questions.</p>
<p>ARcenso is still a work in progress. Future developments will incorporate additional census years and continue expanding the package’s analytical capabilities. The package was created to make Argentine census data easier to discover, access, and analyse, while building on the collaborative efforts of the open-source community.</p>
<p>If you would like to explore further,check out the <a href="https://soyandrea.github.io/arcenso/articles/indicadores_demograficos.html" rel="nofollow" target="_blank"><em>vignettes</em></a> in the package, which include additional examples to work with census data.</p>
<div style="border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;">
<div style="text-align: center;">To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://ropensci.org/blog/2026/08/10/analisis-demografico-con-arcenso/"> rOpenSci - open tools for open science</a></strong>.</div>
<hr />
<a href="https://www.r-bloggers.com/" rel="nofollow">R-bloggers.com</a> offers <strong><a href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers" rel="nofollow">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" href="https://www.r-project.org/" rel="nofollow">R</a> news and tutorials about <a title="R tutorials" href="https://www.r-bloggers.com/how-to-learn-r-2/" rel="nofollow">learning R</a> and many other topics. <a title="Data science jobs" href="https://www.r-users.com/" rel="nofollow">Click here if you're looking to post or find an R/data-science job</a>.

<hr>Want to share your content on R-bloggers?<a href="https://www.r-bloggers.com/add-your-blog/" rel="nofollow"> click here</a> if you have a blog, or <a href="http://r-posts.com/" rel="nofollow"> here</a> if you don't.
</div><strong>Continue reading</strong>: <a href="https://www.r-bloggers.com/2026/08/from-census-data-to-demographic-analysis-with-arcenso-a-reproducible-workflow-in-r/">From Census Data to Demographic Analysis with ARcenso: A Reproducible Workflow in R</a>]]></content:encoded>
					
		
		<enclosure url="" length="0" type="" />

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