haven write_sav() in R: Export Data Frames to SPSS Files
The haven write_sav() function exports an R data frame to an SPSS .sav file in one line of code, carrying variable labels and value labels across with it. Because the SPSS format is openly understood, files written this way open reliably in SPSS itself.
library(haven) # load the package write_sav(df, "data.sav") # write a .sav file write_sav(df, "data.sav", compress = "zsav") # smaller ZSAV file write_sav(df, "data.sav", compress = "none") # uncompressed file read_sav("data.sav") # read the file back in write_dta(df, "data.dta") # Stata instead of SPSS write_sas(df, "data.sas7bdat") # SAS instead of SPSS
Need explanation? Read on for examples and pitfalls.
What write_sav() does
write_sav() writes an R data frame to an SPSS data file. It belongs to the haven package, which bridges R and the binary formats used by SPSS, SAS, and Stata. You pass a data frame and a destination path ending in .sav, and haven serializes the data, the column types, and any variable or value labels into one SPSS-native file. Unlike the proprietary SAS format, the SPSS .sav layout is well understood, so files that write_sav() produces open dependably in SPSS, PSPP, and other SPSS-compatible tools.
write_sav() syntax
The function takes three arguments, two of them required. The signature is write_sav(data, path, compress = c("byte", "none", "zsav")). The data argument is the data frame or tibble you want to export, path is the file path where the .sav file is written, and compress controls how the bytes are packed on disk. write_sav() returns the input data invisibly, so it slots into a pipe without breaking the chain.
Column types map to SPSS predictably. Numeric and integer columns become SPSS numeric variables, character columns become SPSS string variables, and labelled columns carry both their variable label and value labels across. Factors are converted to labelled numeric vectors, so the level text survives as value labels rather than as an R factor.
Export a data frame to an SPSS file
A single call writes the file to disk. Start by loading haven and building a small data frame to export.
Pass that data frame and a path to write_sav(). Writing to tempdir() keeps the example self-contained and avoids cluttering your working directory.
Read it back with read_sav() to confirm the round trip preserved every column.
The real strength of .sav is metadata. Attach a variable label with attr() and a value-label map with labelled(), and write_sav() stores both inside the file.
Choosing a compression mode
The compress argument trades file size against compatibility. It accepts three values. "byte" is the default and applies the standard SAV byte compression that every SPSS version reads. "none" writes an uncompressed file, the largest option, useful only when a downstream tool struggles with compressed input. "zsav" writes a ZSAV file that is far smaller, but it requires SPSS 21 or newer to open.
The table below summarizes when to reach for each mode.
| compress value | File written | SPSS support | Use when |
|---|---|---|---|
"byte" |
Compressed .sav |
All versions | The default, safe for any recipient |
"none" |
Uncompressed .sav |
All versions | A tool rejects compressed SAV input |
"zsav" |
Compressed .zsav |
SPSS 21 and newer | File size matters and the recipient is current |
"byte" mode is the only choice that never fails.Common pitfalls
Three mistakes account for most surprising write_sav() results.
- Factors do not return as factors. A factor column is exported as a labelled numeric variable, so
read_sav()reads it back ashaven_labelled, notfactor. Useas_factor()after reading if you need an R factor. - ZSAV files fail on old SPSS. A
compress = "zsav"file silently excludes anyone running SPSS 20 or earlier. Default to"byte"unless every recipient is current. - Invalid variable names get rewritten. SPSS names cannot contain spaces and must start with a letter. haven sanitizes offending names on export, so clean them with
make.names()first if you want predictable results.
The factor pitfall is the one that catches most users.
is.factor() to be TRUE will break. Convert with as_factor() immediately after reading.Try it yourself
Try it: Export the built-in mtcars data frame to a .sav file inside tempdir(). Save the file path to ex_path first, then write the file.
Click to reveal solution
Explanation: file.path(tempdir(), ...) builds a writable path inside the session's temporary directory. write_sav() serializes mtcars to that path, and file.exists() confirms the write succeeded.
Related haven functions
haven exports to every major statistical format. Once write_sav() is familiar, these siblings cover the rest of the workflow.
read_sav()reads.savfiles back into R as a tibble.write_dta()exports a data frame to a Stata.dtafile.write_sas()exports a data frame to a SAS.sas7bdatfile.write_xpt()writes a SAS transport file that SAS reads reliably.as_factor()converts labelled columns from a read-back file into R factors.
For the full argument reference, see the official write_sav() documentation.
FAQ
How do I export an R data frame to SPSS?
Load haven, then call write_sav(df, "path.sav"). The function takes a data frame and a destination path ending in .sav, and writes one SPSS-native file. For a smaller file targeting current SPSS, add compress = "zsav". write_sav() creates the file on disk and returns the data frame invisibly, so it can sit inside a pipe without interrupting the chain.
Can SPSS open files created by write_sav()?
Yes. Unlike the SAS .sas7bdat format, the SPSS .sav specification is well understood, so haven writes files that SPSS, PSPP, and other compatible tools open reliably. The one exception is compress = "zsav": that ZSAV variant needs SPSS 21 or newer. The default "byte" mode opens in every SPSS release.
What is the difference between .sav and .zsav?
Both are SPSS data files written by write_sav(). A .sav file uses standard byte compression and opens in any SPSS version. A .zsav file, produced with compress = "zsav", uses stronger ZLIB compression and is much smaller, but it requires SPSS 21 or later. Choose .zsav only when file size matters and you know the recipient's version.
Does write_sav() preserve variable and value labels?
Yes. Columns with a label attribute keep their variable label, and columns built with haven's labelled() keep their value labels. Both are written into the .sav file and reappear when you call read_sav(). This is the main reason to choose .sav over a plain CSV: the metadata travels with the data instead of being lost.
How is write_sav() different from write_dta()?
Both come from haven and both export a data frame to a binary statistical format. write_sav() writes an SPSS .sav file, while write_dta() writes a Stata .dta file. They share label-preservation behavior, but Stata enforces stricter variable-name and string-length rules, so a data frame that exports cleanly with write_sav() may need cleanup before write_dta() accepts it.