Analyze public-use micro data from the Survey of Consumer Finances. Provides tools to download prepared data files, construct replicate-weighted multiply imputed survey designs, compute descriptive statistics and model estimates, and produce plots and tables. Methods follow design-based inference for complex surveys and pooling across multiple imputations. See the package website and the code book for background.
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.
scf_download(): Downloads and preprocesses SCF microdata, including
all five implicates and 999 replicate weights.scf_load(): Loads .rds files into structured scf_mi_survey
objects ready for analysis.scf_update(): Adds or transforms variables uniformly across all implicates.scf_update_by_implicate(): Applies a user-defined transformation to
each implicate's data frame separately. Use when a computation depends
on the within-implicate distribution (e.g., implicate-specific ranks
or percentile thresholds).scf_subset(): Subsets the data consistently across all implicates.scf_design(): Builds an scf_mi_survey object from your own list of
replicate-weight designs, one per implicate.scf_real(): Converts raw x dollar variables to the dollars of the
summary variables, the way the Fed converts them.scf_freq(): Weighted frequency tables for categorical variables.scf_xtab(): Cross-tabulations by row, column, or cell percentages.scf_mean(), scf_median(), scf_percentile(): Means, medians, and
percentiles, overall or by group.scf_ratio(): Ratios of totals, such as wages as a share of income.scf_pctile_sum(): Creates percentile-based grouping variables for a
continuous variable and optionally computes a summary statistic within
each group. Supports an implicate-specific survey-weighted method
(default) and the Federal Reserve's published stacking convention.scf_corr(): Weighted Pearson correlations.scf_ttest(): One-sample and two-sample t-tests for continuous
variables.scf_prop_test(): One-sample and two-sample proportion tests for
binary variables.scf_MIcombine(): Combines estimates across implicates.scf_variance: Not a function but a help page (?scf_variance) on the
variance option, which chooses the Fed's method (default) or Rubin's
rules for standard errors.scf_ols(): Linear regression with pooled estimates and implicate
diagnostics.scf_glm(): Generalized linear models (e.g., logistic, Poisson).scf_logit(): Wrapper for logistic regression with optional odds
ratio output.scf_quantreg(): Weighted quantile regression with pooled coefficients
across implicates, one quantile per call.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).
scf_plot_dist(): Bar charts of a variable's distribution.scf_plot_dbar(): Bar plots of categorical variable distributions.scf_plot_bbar(): Stacked bar plots for two categorical variables.scf_plot_cbar(): Bar plots for continuous variable summaries by
group.scf_plot_smooth(): Smoothed line plots for continuous distributions.scf_plot_hist(): Weighted histograms of continuous variables.scf_plot_hex(): Weighted hexbin plots for bivariate continuous data.scf_theme(), scf_activate_theme(): The package's ggplot2 theme, to
add to a plot or set as the session default.scf_nominal(): Converts results from scf_mean(), scf_median(),
scf_percentile(), and scf_ttest() from the data's real dollars to
nominal dollars of the survey year, using the CPI-U-RS factors from the Fed's SCF Bulletin
SAS macro. Formerly scf_deflate().scf_regtable(): Regression tables from one or more model results, as
console text, Markdown, LaTeX, or CSV.scf_implicates(): Implicate-level estimates from descriptive results and models.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:
survey (replicate-weighted designs)quantreg (quantile regression)ggplot2 (plots)httr, haven (downloading and reading SCF data)stats, utils# Download SCF data for 2022:
scf_download(2022)
# Load the data into a survey design object:
scf2022 <- scf_load(2022)
# 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)
# 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)
# 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")
# 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))
# 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)
# 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)
# 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)
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.
# 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)
})
# 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
vignette("SCF-guide", package = "scf")?scf_varianceThis 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.
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.
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.
Joseph N. Cohen
Department of Sociology & Program in Data Analytics
Queens College, City University of New York
[email protected]