Skip to Content
Course content

170: Reading SPSS/SAS/Stata Files with haven

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I recently had a collaborator send me a dataset from a longitudinal study on urban transit. They're SPSS power users, so they sent me a .sav file. Now, if you've only worked with CSVs or Parquet files, you might be tempted to just ask them to "export it to Excel," but that's a rookie move. You lose the metadata—the variable labels and value labels—that are baked into the SPSS format. We want that data.

The "Why isn't this working?" moment

My first instinct was to see if base R could handle it. I tried a few things, but it quickly became apparent that R doesn't natively understand the binary structure of a .sav file. I needed the haven package. It's the industry standard for this kind of thing because it's designed specifically to bridge the gap between R and the "big three" of social science software: SPSS, SAS, and Stata.

install.packages("haven")
library(haven)

# Let's try to bring in the transit survey data
transit_data <- read_sav("city_transit_survey.sav")
head(transit_data)

At first glance, the data looks fine. I see my columns: user_id, commute_time, and satisfaction_level. But when I look closer at satisfaction_level, something is off. The values are numbers—1, 2, 3—but I know that in the original survey, 1 meant "Very Dissatisfied" and 5 meant "Very Satisfied."

The mystery of the labelled class

I ran class(transit_data$satisfaction_level) and got something I've never seen in a standard data frame: "haven_labelled". This is where haven does something clever (and slightly annoying if you aren't expecting it). It doesn't just give you the numbers, and it doesn't just give you the text labels; it gives you both, wrapped in a special class.

If I try to run a simple summary, it behaves like a numeric vector. But if I want to use these as categories for a plot or a regression, I need them to be actual R factors. I tried to just wrap it in as.factor(), but that only turned the numbers (1, 2, 3) into factors. The descriptive labels—the "Very Dissatisfied" part—were still hidden in the metadata.

# This doesn't do what I want; it just factors the numbers
as.factor(transit_data$satisfaction_level) 
# Output: [1] "1" "2" "1" "3" ...

Bridging the gap to R factors

This is where as_factor() from the haven package comes in. Notice the underscore—it's different from the base R as.factor(). This specific function is designed to look at those haven_labelled attributes and actually swap the numbers for the labels stored in the file.

# Let's convert the labelled columns to actual R factors
transit_clean <- as_factor(transit_data)

head(transit_clean$satisfaction_level)
# Output: [1] "Very Dissatisfied" "Neutral" "Very Satisfied" ...

That's much better. Now the data is actually readable. If I have a massive dataset and I only want to convert specific columns, I can do that within a mutate call using dplyr, but applying as_factor() to the whole data frame is usually the quickest way to "R-ify" a social science dataset.

What about Stata and SAS?

I haven't had to deal with Stata or SAS files as often, but the logic in haven is identical. You just swap the reading function. If a colleague hands you a .dta file from Stata or a .sas7bdat from SAS, you don't need to learn a new workflow.

# For Stata
stata_data <- read_dta("health_metrics.dta")

# For SAS
sas_data <- read_sas("clinical_trial.sas7bdat")

# And then, just like before:
stata_clean <- as_factor(stata_data)

The consistency here is great. Whether it's SPSS, SAS, or Stata, the pattern is always: read_[format] → as_factor() → analysis.




📋 Practical Task

Converting Legacy Health Records from Stata

You have been provided with a Stata file named patient_outcomes.dta. This file contains a column called treatment_group and another called recovery_status. Both are stored as labelled vectors (e.g., 1 = "Control", 2 = "Experimental").

Write a script that:

  • Loads the haven library.
  • Reads the patient_outcomes.dta file into a data frame.
  • Converts all labelled columns into R factors so that the text labels (like "Control") are visible instead of the numeric codes.
  • Displays the first 6 rows of the resulting cleaned data frame to verify the labels have been applied.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.