SCF: An R Package for Analyzing the Survey of Consumer Finances

License: MIT R version Lifecycle: stable CRAN status

Overview

The scf R package analyzes the U.S. Federal Reserve’s Survey of Consumer Finances (SCF), a detailed source of data on U.S. households’ wealth, debt, and income. It covers the thirteen public-use waves from 1989 to 2025.

It stores the SCF’s five implicates and 999 replicate weights in one object (scf_mi_survey) and provides functions for descriptive statistics, tests, regression models, and plots. Standard errors follow the Fed’s method by default (see ?scf_variance).

The SCF’s unit is the primary economic unit: the person or couple at the economic center of the household and everyone financially dependent on them. The Fed calls this unit a “family”; this package calls it a household.

Table of Contents

Features

Data Preparation

Descriptive Statistics

Statistical Inference

Regression Modeling

All model functions return objects of class scf_model_result, with methods for coef(), vcov(), confint(), predict(), AIC() (not for quantile models), residuals(), and summary(). Fit statistics include observation counts (nobs, nobs_mean).

Visualization

Diagnostics and Output

Installation

Install the released version from CRAN:

install.packages("scf")

This README describes version 1.2.0. Until it reaches CRAN, install it from GitHub:

# install.packages("remotes")
remotes::install_github("jncohen/scf")

The package requires R 3.6 or later and these packages, which install with it:

Getting Started

Download and Load Data

# Download SCF data for 2022:
scf_download(2022)

# Load the data into a survey design object:
scf2022 <- scf_load(2022)

Explore and Summarize

Univariate Distributions

# Frequency of education categories
scf_freq(scf2022, ~edcl)

# Median household net worth
scf_median(scf2022, ~networth)

# 90th percentile of income
scf_percentile(scf2022, ~income, q = 0.9)

# Histogram of net worth distribution
scf_plot_hist(scf2022, ~networth)

# Smoothed density plot of income
scf_plot_smooth(scf2022, ~income)

Bivariate Relationships

# Cross-tabulation of education and homeownership
scf_xtab(scf2022, ~edcl, ~own)

# Stacked bar chart: homeownership by education
scf_plot_bbar(scf2022, ~edcl, ~own)

# Weighted bar chart: mean net worth by education
scf_plot_cbar(scf2022, ~networth, ~edcl, stat = "mean")

# Grouped median income by race
scf_median(scf2022, ~income, by = ~racecl)

# Correlation between income and net worth
scf_corr(scf2022, ~income, ~networth)

# Hexbin plot: income vs. net worth
scf_plot_hex(scf2022, ~income, ~networth)

Statistical Testing

# One-sample proportion test: Is more than 10% of households rich?
scf_prop_test(scf2022, ~I(networth > 1e6), p = 0.10, alternative = "greater")

# Two-sample proportion test: Are men more likely than women to be rich?
# The estimate is the first group (Male) minus the second (Female).
scf_prop_test(scf2022, ~I(networth > 1e6), ~factor(hhsex, labels = c("Male", "Female")), alternative = "greater")

# One-sample t-test: Is mean income different from $75,000?
scf_ttest(scf2022, ~income, mu = 75000)

# Two-sample t-test: Are households with heads over 50 wealthier?
# The estimate is the first group (FALSE, 50 or under) minus the second (TRUE).
scf_ttest(scf2022, ~networth, ~I(age > 50), alternative = "less")

Regression Modeling

# Linear regression: Predict net worth from income and education
scf_ols(scf2022, networth ~ income + factor(edcl))

# Generalized linear model: owning stocks, logistic regression
scf_glm(scf2022, hstocks ~ income + age + factor(edcl), family = binomial())

# Logit wrapper: owning stocks, reported as odds ratios
scf_logit(scf2022, hstocks ~ age + income + factor(edcl))

Plotting and Visualization

# Bar chart of a single categorical variable
scf_plot_dbar(scf2022, ~edcl)

# Stacked bar chart comparing education by race
scf_plot_bbar(scf2022, ~edcl, ~racecl, scale = "percent", percent_by = "row")

# Smoothed line plot of net worth distribution
scf_plot_smooth(scf2022, ~networth, xlim = c(0, 2e6), method = "loess")

# Histogram of income distribution
scf_plot_hist(scf2022, ~income, bins = 40, xlim = c(0, 300000))

# Bar chart of mean net worth by education level
scf_plot_cbar(scf2022, ~networth, ~edcl, stat = "mean")

# Hexbin plot: net worth vs. income
scf_plot_hex(scf2022, ~income, ~networth, bins = 60)

Wrangling and Transformation

# Create new variables across all implicates
scf2022 <- scf_update(scf2022,
  rich = networth > 1e6,
  senior = age >= 65,
  log_income = log(income + 1)
)

# Apply implicate-specific transformations (e.g., implicate-specific ranks)
scf2022 <- scf_update_by_implicate(scf2022, function(df) {
  threshold <- quantile(df$networth, probs = 0.90, na.rm = TRUE)
  df$top10nw <- df$networth >= threshold
  df
})

# Subset to working-age households with positive net worth
scf_sub <- scf_subset(scf2022, age >= 25 & age < 65 & networth > 0)

# Extract implicate-level estimates from a frequency table
freq <- scf_freq(scf_sub, ~own)
scf_implicates(freq, long = TRUE)

Percentile Grouping

# Mean net worth by decile (implicate method)
scf_pctile_sum(scf2022, ~networth)

# Top 10% vs. bottom 90%, stack method (fast; replicates Fed convention)
scf_pctile_sum(scf2022, ~networth,
               probs  = c(0, 0.9, 1),
               labels = c("bottom90", "top10"),
               method = "stack")

# Add a percentile grouping variable to the object for use in other functions
scf2022 <- scf_pctile_sum(scf2022, ~networth,
                           probs = c(0, 0.5, 0.9, 1),
                           labels = c("bottom50", "next40", "top10"),
                           stat = "none")
scf_median(scf2022, ~income, by = ~networth_pctile)

Real and Nominal Dollars

The SCF data mix two kinds of dollars. Summary variables made by the Fed, such as income, networth, and asset, are in the dollars of one year in every survey year: 2025 for files downloaded since October 2026, 2022 before that. scf_load() records this as dollar_year. Raw survey variables, named x followed by a number, are in dollars of the survey year. Do not compare raw variables across years, or combine them with summary variables, until they are converted.

scf2016 <- scf_load(2016)

# Raw variables to 2025 dollars. Name income items, which are reported
# for the year before the survey, in `income`:
scf2016 <- scf_real(scf2016, ~x5702 + x3915, income = ~x5702)
scf_median(scf2016, ~x5702_real)

# Results from 2025 dollars to 2016 dollars:
m2016 <- scf_mean(scf2016, ~income)
scf_nominal(m2016)

Both functions stop with an error if given the wrong kind of variable, unless force = TRUE.

Quantile Regression

# Median regression: net worth on income and education
scf_quantreg(scf2022, networth ~ income + factor(edcl), tau = 0.5)

# Several quantiles: one call each
lapply(c(0.25, 0.5, 0.75, 0.9), function(t) {
  scf_quantreg(scf2022, networth ~ income + factor(edcl), tau = t)
})

Regression Tables

# Compare OLS and logit models in a single formatted table
m_ols   <- scf_ols(scf2022, networth ~ income + factor(edcl))
m_logit <- scf_logit(scf2022, I(networth > 1e6) ~ income + factor(edcl))

scf_regtable(m_ols, m_logit)                          # console output
scf_regtable(m_ols, m_logit, output = "markdown")     # Markdown
scf_regtable(m_ols, m_logit, output = "latex")        # LaTeX
scf_regtable(m_ols, m_logit, output = "csv",
             file = "results/table1.csv")             # CSV file

Documentation

Note on Mock Data

This package includes a small mock data set (scf2022_mock_raw.rds) for examples and tests. It keeps 200 rows per implicate and a few variables. It has the same structure as the real data but is not suitable for analysis. scf_load() prints a message when it loads it.

Citation

If you use scf in published work, please cite it as:

Joseph N. Cohen (2026). scf: Analyzing the Survey of Consumer Finances. R package version 1.2.0. https://github.com/jncohen/scf

For the methods and validation, cite:

Joseph N. Cohen (2026). Analyzing the Survey of Consumer Finances with scf: Methodology, User Guide, and Package Validation. Working Paper, CUNY Queens College. https://academicworks.cuny.edu/qc_pubs/695/

Use citation("scf") in R for formatted references.

Use of AI Tools

Development of versions 1.1.0 to 1.2.0 used Claude (Anthropic), an AI assistant, to help write and revise code, tests, and documentation; to audit functions for errors; and to run validation checks. The author directed this work, made all methodological decisions, and reviewed the changes. The package’s results were checked against the Federal Reserve’s published SCF Bulletin tables (Tables 1-5, 1989-2022, for 1.1.0; Tables 1-4 of the 2025 historical workbooks for 1.2.0) and against independent calculations with the survey and mitools packages.

Author

Joseph N. Cohen
Department of Sociology & Program in Data Analytics
Queens College, City University of New York
joseph.cohen@qc.cuny.edu