Import & Export: Reading SAS, CSV and Excel with haven & readr
Bring your existing .sas7bdat and .xpt files into R — and write them back out — using haven, readr and readxl.
Learning objectives
- 1Read SAS7BDAT and XPORT (.xpt) files into R with the haven package.
- 2Import and export CSV and Excel files with readr and readxl/writexl.
- 3Preserve variable labels and formats when moving between SAS and R.
- 4Understand where PROC IMPORT/EXPORT and LIBNAME map in an R workflow.
Key SAS® → R comparison
| Task | SAS® | R |
|---|---|---|
| Read .sas7bdat | LIBNAME + set | haven::read_sas('dm.sas7bdat') |
| Read transport .xpt | PROC CIMPORT / xport engine | haven::read_xpt('dm.xpt') |
| Write transport .xpt | PROC COPY / xport engine | haven::write_xpt(dm, 'dm.xpt') |
| Read CSV | PROC IMPORT dbms=csv | readr::read_csv('dm.csv') |
| Write CSV | PROC EXPORT dbms=csv | readr::write_csv(dm, 'dm.csv') |
| Read Excel | PROC IMPORT dbms=xlsx | readxl::read_excel('dm.xlsx') |
| Write Excel | PROC EXPORT dbms=xlsx | writexl::write_xlsx(dm, 'dm.xlsx') |
| Variable label | LABEL statement | attr(df$var, 'label') / labelled |
Code examples
libname adam "/data/adam";
data adsl;
set adam.adsl;
run;library(haven)
adsl <- read_sas("/data/adam/adsl.sas7bdat")
# Labels survive the trip:
attr(adsl$AGE, "label") # "Age"
# View all labels at once:
library(labelled)
var_label(adsl)Note: haven imports SAS labels as a 'label' attribute on each column and SAS formats as labelled values — so your metadata travels with the data.
proc import datafile="raw/lb.csv"
out=lb dbms=csv replace; getnames=yes;
run;
proc export data=lb outfile="out/lb.xlsx"
dbms=xlsx replace;
run;library(readr); library(writexl)
lb <- read_csv("raw/lb.csv") # types guessed per column
write_xlsx(lb, "out/lb.xlsx")
# Control column types explicitly when needed:
lb <- read_csv("raw/lb.csv",
col_types = cols(USUBJID = col_character(),
AVAL = col_double()))Note: read_csv guesses types from the data; for regulatory work, specify col_types so an all-numeric-looking ID column is never silently turned into a number.
libname out xport "out/dm.xpt";
proc copy in=work out=out;
select dm;
run;library(haven)
# Basic transport write:
write_xpt(dm, "out/dm.xpt", version = 5)
# For SDTM/ADaM submissions, xportr adds
# compliant labels, lengths, types & ordering
# (covered in the SDTM module).Note: haven::write_xpt produces a v5 transport file; the xportr package layers on the CDISC compliance checks you need for an actual submission.
Explanation
Import and export is the bridge that lets you adopt R incrementally without abandoning your existing SAS data. The haven package reads and writes .sas7bdat and .xpt transport files directly, and critically it preserves the metadata you care about: variable labels become column attributes and SAS formats become 'labelled' vectors that remember both code and decode. This means you can pull an existing ADSL into R, work on it, and retain its labels throughout.
For flat files, readr's read_csv/write_csv are faster and more predictable than base R's read.csv, and readxl/writexl handle Excel without any Java dependency. The professional habit to build is being explicit about column types on import. read_csv guesses types from the first chunk of rows, which is convenient for exploration but risky for clinical data — a USUBJID that looks numeric can be coerced incorrectly. Supplying col_types removes that ambiguity.
When it's time to deliver, write_xpt creates a version-5 transport file, the format regulators expect. On its own it does not enforce the full set of CDISC conformance rules — variable lengths, label lengths, ordering — which is exactly what the xportr package adds. We build a complete, compliant export in the SDTM Implementation module.