πŸ“Š

SAS β†’ R Transition

Hands-on Seminar Β· Statistics Denmark Β· 2026

Tools, patterns, and institutional knowledge for a durable open-source migration

Agenda

What We Will Cover

πŸ—ΊοΈ
Migration Philosophy
Replace the pipeline, not just the syntax
πŸ”§
Conversion Tools
AI translators, open-source accelerators
πŸ’Š
Pharmaverse Lessons
Pipeline packages & AGENTS.md pattern
⚠️
Watch-Outs
Silent bugs, numerical discrepancies

Foundation

Two Approaches to Migration

❌ Syntax Translation
Line-by-line conversion
  • β€’ Converts SAS DATA steps β†’ R code mechanically
  • β€’ Produces R that "thinks in SAS"
  • β€’ Fragile β€” SAS idioms don't map cleanly
  • β€’ Macros encoding business rules are lost
βœ… Pipeline Replacement
Rebuild each stage in idiomatic R
  • β€’ Map SAS roles to purpose-built R packages
  • β€’ Tidyverse + domain packages (survey, gt, haven)
  • β€’ Maintainable, reviewable, submission-ready
  • β€’ Pharmaverse proved this at scale

Layer 1

AI-Powered Conversion Tools

Tool Type Best For License
CSO Ireland Transcompiler Shiny app + OpenAI API NSO context β€” built by stats office for stats office MIT / FOSS
codeconvert.ai Browser-based, no signup Live seminar demos β€” paste SAS, get R instantly Free tier
Claude 3.7 Sonnet / GPT-4o Direct API / chat Module-by-module macro decomposition Commercial API
Brillio / AWS Bedrock Human-in-the-loop pipeline Large-scale batch migration with audit trail Commercial
HMS Code2X Agentic migration tool Claims ~75% automation; regulated environments Commercial

⚠️ All tools handle DATA steps and PROC SQL well β€” all fail silently on complex SAS macros

Layer 2

SAS β†’ R Package Crosswalk

SAS ConceptR Equivalent
DATA stepdplyr verbs β€” mutate, filter, group_by, summarise
PROC SQLdbplyr, dplyr, sqldf
PROC MEANS / SUMMARYsummarise(), janitor, gtsummary
PROC FREQtable(), janitor::tabyl()
PROC SGPLOTggplot2
ODS / RTF outputgt, flextable, Quarto
SAS macrosR functions + purrr
.sas7bdat fileshaven::read_sas()
SAS formats / labelsforcats (factor labels)
πŸ’Š

Lessons from Pharmaverse

The pharmaceutical industry has run the largest regulated SAS→R migrations in the world.
Their patterns are directly transferable to national statistics.

Pharmaverse

The Pharmaverse Package Stack

SAS RolePharmaverse R PackageEquivalent
PROC IMPORT / .sas7bdat{haven}Read SAS datasets; handles date epoch shift
SDTM derivation macros{sdtm.oak}Map raw data to SDTM domains (DM, AE …)
ADaM derivation macros{admiral}Build ADSL, ADAE, ADTTE with auditable derivations
PROC EXPORT / XPT files{xportr}Submission-compliant XPT with define.xml metadata
RTF/PDF output macros{r2rtf}, {rtables}Clinical-quality table output
SAS log files{logrx}R execution logs mirroring SAS .log format
Interactive data review{teal}Shiny ADaM explorer

Each package replaces a process role β€” not individual lines of code

Merck / PharmaSUG 2025

GenAI-Assisted Conversion Workflow

1
Import first β€” haven::read_sas() to ingest .sas7bdat without rewriting ETL
2
Decompose macros β€” split into modules (data prep β†’ model β†’ output) and convert each separately; feeding the full macro to an LLM fails
3
Manual parameter verification β€” replace PROC MIXED β†’ lmer() + emmeans(); check degrees-of-freedom method explicitly
4
Numerical equivalence check β€” run SAS and R outputs side-by-side; Merck confirmed identical PK summary results
5
Output: use templates β€” GPT-4o is weak at RTF macros; use existing {r2rtf} templates and adapt

Key Pattern

AGENTS.md β€” Encoding Institutional Knowledge

The {admiral} package publishes a 1,300-line auto-generated AGENTS.md β€” a README for AI agents, not humans

What admiral's AGENTS.md contains
ADaM conventions LLMs can't infer β€” e.g. flag vars take "Y" or NA, never "N"
Full ecosystem pipeline context: sdtm.oak β†’ admiral β†’ xportr β†’ XPT
Unit testing rules, documentation workflow, compliance check comment
DST equivalent would encode
StatBank table format conventions and px-file structure
Eurostat reporting requirements and metadata standards
Survey weighting conventions that an LLM cannot guess

rOpenSci now requires AGENTS.md as a peer-review artifact β€” this norm is arriving regardless

⚠️ Watch Out

Silent Failure Modes

SAS Macros encode business rules, not syntax
LLM translators handle DATA steps and PROC SQL well but fail silently on macros β€” macro logic must be interpreted by a human, not translated character by character
Missing values behave differently
In SAS, IF x > 5 where x = . evaluates to FALSE. In R, x > 5 where x = NA returns NA β€” propagating silently through pipelines. CAMIS working group documents all such discrepancies.
PROC MIXED β†’ lmer() is the hardest step
Kenward-Roger vs Satterthwaite degrees-of-freedom methods differ by default between SAS and R β€” producing different p-values for the same model. Always verify manually.
haven::read_sas() drops some format labels silently
Always run str() on imported data and cross-check variable types before any analysis step

Resources

Key References for the Seminar

admiral AGENTS.md (live)
github.com/pharmaverse/admiral
1,300-line production AGENTS.md β€” best real-world example of encoding domain knowledge for AI
CSO Ireland Transcompiler
github.com/CSOIreland/Code-Transcompiler-using-LLMs-public
MIT-licensed Shiny app built by a national statistics office for SAS→R; deployable on Posit Connect
pharmaverse/examples
pharmaverse.github.io/examples
End-to-end SDTM→ADaM→TLG pipelines in R; runnable on Posit Cloud
CAMIS Working Group
IAIMS / R Consortium
Documents numerical discrepancies between SAS, R, and Python for standard statistical procedures
EpiR Handbook β€” Transition to R
epirhandbook.com
Public health / statistics-office flavored crosswalk guide; free and openly licensed
PharmaSUG 2025 AI-136
pharmasug.org/proceedings/2025
Merck's validated GenAI-assisted PK table conversion β€” numerical equivalence confirmed

Hands-On Seminar

Suggested Seminar Structure

🎬 Demo
Live translation β€” paste a PROC MEANS + DATA step into Claude 3.7; inspect R output; fix one edge case manually to teach critical evaluation, not blind trust
πŸ“₯ Import
haven::read_sas() β€” import an existing .sas7bdat file; run str(); identify any label-drop issues before touching analysis
πŸ”¬ CAMIS
Equivalence check β€” run one real procedure in both SAS and R; dramatise the "bitwise reproducibility" problem for official statistics
πŸ€– AGENTS
Write a DST AGENTS.md stub β€” encode one DST output convention (e.g. StatBank px-format rule) as a reusable AI instruction file
πŸš€ Deploy
CSO Ireland transcompiler β€” explore self-hosting the MIT-licensed app on Posit Connect as a DST internal tool
πŸ‡©πŸ‡°

The Key Insight

Automated tools handle syntax.
AGENTS.md handles institutional knowledge.
The latter determines whether migrated code is actually correct.

FOSS-first
haven + tidyverse + pharmaverse patterns
Validate always
CAMIS equivalence checks for official statistics
Encode conventions
AGENTS.md before AI touches DST pipelines