The Problem

Importing SAS data into R is straightforward: the haven package provides read_sas(), which reads *.sas7bdat files into an R data frame. Going the other direction is harder. In my experience, *.sas7bdat files created by haven::write_sas() were not reliably readable in SAS.

At first, it seems that a shared data format should solve the problem. R can export CSV files, and SAS can import them. Wouldn’t that be enough?

Usually, no. Accurate CSV import in SAS is not always simple. SAS has to infer the type of each column, and unusual values such as quotes, commas, and special characters can break the import process.

Excel can be even less predictable than CSV.

A database such as MySQL or SQL Server would work, but setting up a database just to transfer a data frame is too much overhead.

If SAS supported CSV with YAML front matter, that would help. But it does not.

The Solution

The most reliable solution I found is to run R code from SAS. Let R read the R data file, then use ImportDataSetFromR() to transfer the R data frame into a SAS dataset. The SAS code looks like this:

proc iml;
  submit / R;
    df = readRDS("path\\to\\example.rds")
  endsubmit;
  run ImportDataSetFromR( "sas_data", "df" );
run;

If you use the CSVY format in R, the third line would look like this:

df = data.table::fread("path\\to\\example.csv", yaml = TRUE)

This approach is simple, and it has been much more reliable for me than haven::write_sas().

Yes, it requires SAS to be installed on the machine. But if you are preparing data for SAS users, that is usually a reasonable requirement.

The R Workflow Solution

The one thing I dislike about the solution above is that it asks me to leave my R workflow and run a SAS script.

When I need to export R data to SAS, I am usually working in an R project and preparing final data products for colleagues who use SAS.

I could open SAS and run the script manually, but it is better to keep the workflow inside R. That is possible because R can launch a SAS script from the command line.

First, create a SAS script named “ImportFromR.sas” that contains the SAS code above. Then call that script from R:

sas_exe = '"C:\\Program Files\\SASHome\\SASFoundation\\9.4\\sas.exe"' 
sas_cfg = '"C:\\Program Files\\SASHome\\SASFoundation\\9.4\\sasv9.cfg"' 
sas_autoexec = '"my\\autoexec.sas"'
sas_script = '"path\\to\\ImportFromR.sas"'
sas_log = '"path\\to\\sas.log"'
sas_command = paste0(sas_exe, 
                     " -CONFIG ", sas_cfg, 
                     " -AUTOEXEC ", sas_autoexec, 
                     " -RLANG",
                     " -NOSPLASH",
                     " -SYSIN ", sas_script,
                     " -LOG ", sas_log)
system(sas_command, intern = TRUE, invisible = FALSE)

Two notes:

  • -RLANG is included in the SAS command because I do not enable it in the SAS configuration file.
  • R_HOME must be assigned in the SAS configuration file, the SAS autoexec file, or “ImportFromR.sas”. See Run R Code in SAS.

To make the process reusable, write an R function that creates “ImportFromR.sas” with the correct input and output paths, then runs the script. The following pseudocode shows the idea:

ExportToSAS = function(r_data_fn, sas_data_fn)
{
  // build the SAS code into a string, which contains r_data_fn and sas_data_fn

  // use writeLines() to save the SAS code string into "ImportFromR.sas"

  // use system() to run the SAS script "ImportFromR.sas" from command line
}