anotherSAS7bdat reads SAS .sas7bdat files into R in
configurable chunks using the ReadStat C library by
Evan Miller. Each file is
opened once, and reading continues between chunks without restarting the
parser. Each chunk is an R data frame.
It also converts SAS files directly to one Parquet file with
sas7bdat_to_parquet(). Decoded values stay in native
buffers and pass through the Arrow C Data Interface to the optional
arrow package, without constructing intermediate R data
frames or collecting the whole dataset.
Install the development version from GitHub:
# install.packages("remotes")
remotes::install_github("Fred-Wu/anotherSAS7bdat")Source installation requires Rtools on Windows, or C++ build tools on macOS and Linux.
For SAS-to-Parquet conversion, also install arrow (version 14.0.0 or later):
install.packages("arrow")The package includes three example files generated with SAS 9.4:
example_uncompressed.sas7bdat: uncompressed.example_rle.sas7bdat: RLE compression
(COMPRESS=CHAR).example_rdc.sas7bdat: RDC compression
(COMPRESS=BINARY).Each contains the same 120 rows with numeric values, Unicode text,
dates, times, datetimes, column labels, and ordinary and special missing
values. Use any of these filenames in system.file() below.
SAS is not needed to read the bundled files.
Both examples below read the entire file into sas_data,
an R data frame. The complete dataset and temporary chunks must fit in
memory.
For faster binding of many chunks, use data.table::rbindlist()
from the optional data.table package in place of
do.call(rbind, chunks). It returns a
data.table, which also inherits from
data.frame.
Using sas7bdat_read():
library(anotherSAS7bdat)
path <- system.file("examples", "example_uncompressed.sas7bdat",
package = "anotherSAS7bdat")
chunks <- list()
sas7bdat_read(
path,
chunk_rows = 25,
callback = function(data) {
chunks[[length(chunks) + 1L]] <<- data
}
)
sas_data <- do.call(rbind, chunks)
# Faster alternative with data.table installed:
# sas_data <- data.table::rbindlist(chunks, use.names = TRUE)
rm(chunks)The callback stores each data frame in chunks;
rbind combines them into sas_data. The file
closes automatically.
Using sas7bdat_open() and
sas7bdat_read_chunk():
library(anotherSAS7bdat)
path <- system.file("examples", "example_rle.sas7bdat",
package = "anotherSAS7bdat")
reader <- sas7bdat_open(path, chunk_rows = 25)
chunks <- list()
repeat {
data <- sas7bdat_read_chunk(reader)
if (is.null(data)) break
chunks[[length(chunks) + 1L]] <- data
}
sas7bdat_close(reader)
sas_data <- do.call(rbind, chunks)
# Faster alternative with data.table installed:
# sas_data <- data.table::rbindlist(chunks, use.names = TRUE)
rm(chunks)The loop reads until NULL signals the end of the file,
then closes the reader. Use sas7bdat_info(reader) to
inspect its current status and reading information.
See ?sas7bdat_read for the reader functions and their
settings.
sas7bdat_to_parquet() streams batches into one Parquet
file. chunk_rows limits rows per batch; an optional
chunk_bytes sets an approximate decoded byte target. The
output directory must exist, and existing files are never overwritten.
The completed file is published only after conversion succeeds.
library(anotherSAS7bdat)
path <- system.file("examples", "example_rle.sas7bdat",
package = "anotherSAS7bdat")
output <- tempfile(fileext = ".parquet")
result <- sas7bdat_to_parquet(
path,
output,
chunk_rows = 100000L,
compression = "snappy",
progress = FALSE
)
result$rows # 120
result$completed # TRUE
# Read back the small example file.
sas_data <- arrow::read_parquet(output)
head(sas_data)
unlink(output)Reading back with arrow::read_parquet() collects the
output into R. For large output files, use
arrow::open_dataset(output) to query the file without
collecting the entire dataset.
Recognized SAS dates and datetimes are converted by default; SAS
times retain double seconds and are restored as hms when
read into R with arrow. Column labels and SAS formats are retained in
metadata. Set dates = FALSE to keep the original SAS
numeric units. Ordinary and special numeric missing values become
Parquet nulls; set missing_tags = TRUE to retain special
codes in additional <name>__sas_missing columns.
Set progress = TRUE to show a progress bar; it is
enabled by default in interactive R sessions. See
?sas7bdat_to_parquet for all conversion settings and
metadata details.
This package was developed with assistance from GPT models using OpenAI’s Codex. The overall feature design and logic decisions are mine; GPT models were used to generate and iterate on the implementation.