| Title: | Analyzing the Survey of Consumer Finances |
|---|---|
| Description: | 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. |
| Authors: | Joseph Cohen [aut, cre] |
| Maintainer: | Joseph Cohen <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.2.0 |
| Built: | 2026-10-19 23:59:40 UTC |
| Source: | https://github.com/jncohen/scf |
Functions to analyze the Federal Reserve's Survey of Consumer Finances
(SCF) public-use microdata. The package stores the SCF's five implicates and
replicate weights in one object (scf_mi_survey) and provides means,
medians, percentiles, frequencies, cross-tabs, ratios, tests, regression
models, and plots.
It is written for analysts who know standard statistics but not the details of multiply imputed or survey-weighted data.
The SCF is a detailed source of data on U.S. household finances. It is nationally representative, oversamples wealthy households, and fills in missing answers by multiple imputation. Valid estimates need three things:
Sampling weights. The SCF uses a dual-frame probability sample, so estimates must use the weights.
Replicate weights. Each household has 999 bootstrap replicate weights, built by the Fed, for estimating sampling variance.
Multiple imputation. Each household appears in five implicates. Estimates are pooled across them to get point estimates and standard errors.
The package handles all three. See scf_variance for how standard errors are computed.
For the methods behind these functions, see Cohen (2026).
Download the data with scf_download().
Load it with scf_load(), which returns a scf_mi_survey object.
Change variables with scf_update() or scf_update_by_implicate(), and
keep a subset of households with scf_subset().
Describe the data with scf_mean(), scf_median(), scf_percentile(),
scf_freq(), scf_xtab(), scf_ratio(), scf_corr(), and
scf_pctile_sum().
Test hypotheses with scf_ttest() and scf_prop_test().
Fit models with scf_ols(), scf_logit(), scf_glm(), and
scf_quantreg(), and tabulate them with scf_regtable().
Plot with scf_plot_dist(), scf_plot_hist(), scf_plot_dbar(),
scf_plot_cbar(), scf_plot_bbar(), scf_plot_smooth(), and
scf_plot_hex(). Change the look of the plots with scf_theme().
Convert raw x dollar variables to real dollars with scf_real(),
and dollar results to nominal survey-year dollars with scf_nominal().
See the estimate from each implicate with scf_implicates(), and pool
your own estimates with scf_MIcombine().
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.
Summary variables made by the Fed, such as income, networth, and
asset, are in the dollars of one year in every survey year, so they can
be compared across years as they are. That year is the Fed's latest
survey: 2025 for files downloaded since October 2026, 2022 before that.
scf_load() records it as dollar_year.
Raw survey variables, named x followed by a number, are in nominal
dollars: dollars of the year the survey asked about. They are not adjusted
for inflation. Convert them with scf_real() before comparing years or
combining them with summary variables. To report a summary variable in
nominal dollars, use scf_nominal() on the result.
Functions work on a scf_mi_survey object, made by scf_design() via
scf_load(). Use scf_design() directly only for designs built outside
scf_load(). It is a list with:
mi_design: five survey::svrepdesign() objects, one per implicate
year: the survey year
n_households: the weighted number of households
dollar_year: the year whose dollars the summary variables are in
The SCF fills in missing answers by multiple imputation (Kennickell 1998).
A model of the observed data is used to draw five plausible values for each
missing answer, giving five complete data sets, called implicates. The
spread across implicates shows how uncertain the filled-in values are. See
scf_variance and scf_MIcombine().
CRAN limits a package to 5 MB, and one wave of the SCF is over 100 MB, so
the package cannot include the data. Examples and tests use a small mock
data set (scf2022_mock_raw.rds in inst/extdata/): the first 200
households of each 2022 implicate, with the variables the examples use.
It is not suitable for analysis or inference.
All plots use scf_theme(). See its help page to change the theme.
The package can be used to teach survey analysis and missing data, because each step can be inspected:
Each implicate's design is in x$mi_design[[i]].
Each implicate's data is in x$mi_design[[i]]$variables.
Results keep implicate-level estimates (see scf_implicates()).
Standard errors come from replicate weights and the spread across implicates, so students can follow how each part adds to the total.
Joseph N. Cohen, CUNY Queens College
Cohen JN. Analyzing the Survey of Consumer Finances with scf: Methodology, User Guide, and Package Validation. Working paper, CUNY Queens College; 2026. https://academicworks.cuny.edu/qc_pubs/695/
Barnard J, Rubin DB. Small-sample degrees of freedom with multiple imputation. Biometrika. 1999;86(4):948–955. doi:10.1093/biomet/86.4.948
Board of Governors of the Federal Reserve System. Codebook for the 2025 Survey of Consumer Finances. https://www.federalreserve.gov/econres/scfindex.htm
Bricker J, Henriques AM, Moore KB. Updates to the sampling of wealthy families in the Survey of Consumer Finances. Finance and Economics Discussion Series 2017-114. Board of Governors of the Federal Reserve System; 2017.
Kennickell AB. Multiple imputation in the Survey of Consumer Finances. Board of Governors of the Federal Reserve System; 1998.
Kennickell AB. Multiple imputation in the Survey of Consumer Finances. Statistical Journal of the IAOS. 2017;33(1):143–151. doi:10.3233/SJI-160278
Kennickell AB, McManus DA, Woodburn RL. Weighting design for the 1992 Survey of Consumer Finances. Board of Governors of the Federal Reserve System; 1996. https://www.federalreserve.gov/Pubs/OSS/oss2/papers/weight92.pdf
Little RJA, Rubin DB. Statistical Analysis with Missing Data. 3rd ed. Wiley; 2019. ISBN: 9780470526798.
Lumley T. Analysis of complex survey samples. Journal of Statistical Software. 2004;9(8):1–19. doi:10.18637/jss.v009.i08
Lumley T. Complex Surveys: A Guide to Analysis Using R. Wiley; 2010. ISBN: 9781118210932.
Lumley T. survey: Analysis of complex survey samples. R package. https://CRAN.R-project.org/package=survey
Rubin DB. Multiple Imputation for Nonresponse in Surveys. Wiley; 1987.
Useful links:
Estimates the weighted Pearson correlation between two variables, with a t-test of zero correlation.
scf_corr(scf, var1, var2, variance = "fed") ## S3 method for class 'scf_corr' summary(object, ...)scf_corr(scf, var1, var2, variance = "fed") ## S3 method for class 'scf_corr' summary(object, ...)
scf |
A |
var1 |
One-sided formula specifying the first variable |
var2 |
One-sided formula specifying the second variable |
variance |
|
object |
A |
... |
Currently unused; included for S3 generic compatibility. |
Use it to measure how closely two variables move together, such as income
and net worth. runs from -1 to 1: the sign gives the direction,
values near 0 mean little linear association. A small p-value means the
population correlation is unlikely to be zero. measures linear
association only, is sensitive to outliers, and does not adjust for other
variables; for that, use scf_ols().
In each implicate, the weighted correlation is
where is the household weight and ,
are weighted means. The sampling variance comes from the 999 replicate
weights (see scf_variance). The five estimates are pooled (see
scf_MIcombine). The t-statistic is divided by its standard error.
An object of class scf_corr, containing:
Data frame with pooled correlation estimate, standard error, t-statistic, degrees of freedom, p-value, and minimum/maximum values across implicates.
Named vector of implicate-level correlations.
Variable names used in the estimation.
# Correlate income and net worth corr <- scf_corr(scf2022, ~income, ~networth) print(corr) summary(corr)# Correlate income and net worth corr <- scf_corr(scf2022, ~income, ~networth) print(corr) summary(corr)
Wraps a list of implicate designs into an scf_mi_survey object.
scf_design(design, year, n_households, dollar_year = NULL)scf_design(design, year, n_households, dollar_year = NULL)
design |
A list of five |
year |
Numeric SCF survey year (e.g., 2022). |
n_households |
Numeric total U.S. households represented in |
dollar_year |
Year whose dollars the Fed's summary variables, such as
|
scf_load() calls this function. Call it directly only when building
implicate designs outside the download-and-load workflow. Each element of
design must be a survey::svrepdesign() object.
An object of class "scf_mi_survey" with:
List of replicate-weighted designs (one per implicate).
SCF survey year.
Estimated number of U.S. households.
Year whose dollars the summary variables are in, or
NA if unknown.
# Construct scf_mi_survey object obj <- scf_design( design = scf2022$mi_design, year = 2022, n_households = scf2022$n_households ) class(obj) length(obj$mi_design)# Construct scf_mi_survey object obj <- scf_design( design = scf2022$mi_design, year = 2022, n_households = scf2022$n_households ) class(obj) length(obj$mi_design)
Downloads SCF public-use data from the Federal Reserve and saves one .rds
file per year for scf_load().
scf_download(years = seq(1989, 2025, 3), overwrite = FALSE, verbose = TRUE)scf_download(years = seq(1989, 2025, 3), overwrite = FALSE, verbose = TRUE)
years |
Integer vector of SCF years to download (e.g., |
overwrite |
Logical. If |
verbose |
Logical. If |
Run it once per survey year, before scf_load(). Requires an internet
connection. Files go to the working directory; years already on disk are
skipped unless overwrite = TRUE.
For each year, the function downloads the full public data, the summary extract, and the replicate weights; merges them by household; multiplies each replicate weight by its multiplicity factor; and saves the five implicates as a list.
The Fed puts the summary variables of every year, such as income and
networth, in dollars of its latest survey: 2025 for files downloaded
since October 2026, 2022 before that. Each file records this as its
dollar year, which scf_load() reads.
A character vector of paths to the .rds files written to disk (one per year).
Each file contains a list of five implicate data frames ready for use with scf_load().
Board of Governors of the Federal Reserve System. Codebook for the 2025 Survey of Consumer Finances. https://www.federalreserve.gov/econres/files/codebk2025.txt
scf_load(), scf_design(), scf_update()
if (interactive()) { # Download the 2025 survey into the working directory, then load it scf_download(2025) scf2025 <- scf_load(2025) }if (interactive()) { # Download the 2025 survey into the working directory, then load it scf_download(2025) scf2025 <- scf_load(2025) }
Estimates the share of households in each category of a discrete variable, overall or by group.
scf_freq(scf, var, by = NULL, percent = TRUE, variance = "fed")scf_freq(scf, var, by = NULL, percent = TRUE, variance = "fed")
scf |
A |
var |
A one-sided formula specifying a categorical variable (e.g., |
by |
Optional one-sided formula specifying a discrete grouping variable (e.g., |
percent |
Logical. If |
variance |
|
Use it to describe how households divide across categories, such as
education levels. Each estimate is the share of households in a category;
with by, shares sum to 1 (or 100) within each group. A share plus or
minus two standard errors gives a rough 95 percent range. No tests are
reported. For two-way tables, use scf_xtab().
Proportions are estimated in each implicate with survey::svymean() and
pooled across implicates (see scf_variance). Numeric variables with more
than 25 distinct values are treated as continuous and give an error.
A list of class "scf_freq" with:
Pooled category proportions and standard errors, with
columns group, category, proportion, se_proportion, and n. Without
by, group is "All". With percent = TRUE (the default),
proportion and se_proportion are percentages.
n is the number of sample households (unweighted) in each row,
averaged over implicates.
Groups of 10 or fewer households give a warning.
A named list of implicate-level proportion estimates.
Metadata about the variable and grouping structure.
# Proportions of homeownership scf_freq(scf2022, ~own) # Homeownership proportions by education scf_freq(scf2022, ~own, by = ~edcl)# Proportions of homeownership scf_freq(scf2022, ~own) # Homeownership proportions by education scf_freq(scf2022, ~own, by = ~edcl)
Fits a generalized linear model to each implicate with survey::svyglm()
and pools the results with Rubin's rules.
scf_glm(object, formula, family)scf_glm(object, formula, family)
object |
A |
formula |
A valid model formula, e.g., |
family |
A GLM family object such as |
Use it for models that scf_ols() and scf_logit() do not cover: counts
with poisson(), shares between 0 and 1 with quasibinomial(), or
positive, skewed amounts with Gamma(link = "log"). For linear
regression use scf_ols(); for logistic regression with odds ratios use
scf_logit(). family sets the link and error distribution and must be
given.
Coefficients are on the link scale (log-odds for binomial(), log counts
for poisson()). A positive coefficient means the outcome rises with the
predictor, holding the others constant. The p-value tests whether a
coefficient is zero, using a t distribution with Rubin's degrees of
freedom. AIC is averaged across implicates; compare it only across models
of the same outcome, where lower is better. Pooling is described in
scf_MIcombine().
Each distinct warning from svyglm() is reported once, with the implicates
it came from. "non-integer #successes in a binomial glm!", which survey
weights always produce, is dropped. The function stops if the model fails
in any implicate.
An object of class "scf_glm" and "scf_model_result" with:
A data frame of pooled coefficients, standard errors, t-values, p-values, and significance stars. P-values use a t distribution with Rubin's degrees of freedom (see scf_MIcombine).
Fit statistics: mean and SD of AIC (AIC, AIC.sd) across implicates, pseudo-R-squared (pseudo_r2, pseudo_r2.sd; binomial models only), observations per implicate (nobs), and their mean (nobs_mean).
A list of implicate-level svyglm model objects.
The matched function call.
scf_ols(), scf_logit(), scf_regtable()
# Run logistic regression model <- scf_glm(scf2022, own ~ hhsex, family = binomial()) summary(model)# Run logistic regression model <- scf_glm(scf2022, own ~ hhsex, family = binomial()) summary(model)
Returns the five implicate-level estimates behind an scf result.
scf_implicates(x, long = FALSE)scf_implicates(x, long = FALSE)
x |
A result object containing implicate-level estimates. Three types are supported:
|
long |
Logical. If TRUE, returns stacked data frame. If FALSE, returns list. |
Use it to check how much imputation affects a result. Implicate estimates close together mean imputation adds little uncertainty; estimates far apart mean the result depends on imputed values, and its standard error is larger. Works on results from the descriptive functions and the models.
A list of implicate-level data frames, or a single stacked data frame if long = TRUE.
# Extract implicate-level results out <- scf_freq(scf2022, ~own) scf_implicates(out, long = TRUE)# Extract implicate-level results out <- scf_freq(scf2022, ~own) scf_implicates(out, long = TRUE)
Loads .rds files made by scf_download() as scf_mi_survey objects.
scf_load(min_year, max_year = min_year, data_directory = ".")scf_load(min_year, max_year = min_year, data_directory = ".")
min_year |
Integer. First SCF year to load (1989 to 2025). |
max_year |
Integer. Last SCF year to load. Defaults to |
data_directory |
Character. Directory containing the |
Start every analysis here and assign the result, for example
scf2022 <- scf_load(2022). Printing the object shows the year, the number
of households represented, and the number of implicates.
Each file holds five implicate data frames with the weight wgt and
replicate weights wt1b1 to wt1b999. Each implicate's design uses
wgt * 5, because the Fed divides wgt by 5 so the five implicates sum to
the population. The wgt column is not changed.
The object records the dollar year of the summary variables, which
scf_real() and scf_nominal() use. Files made by older versions of the
package do not store it, so it is found from the data. Loading years in
different dollar years gives a warning.
Invisibly, a scf_mi_survey object (or a named list of them if several years
are loaded), with elements mi_design, year, n_households, and
dollar_year, and a
logical mock attribute.
scf_download(), scf_design(), scf_update(), survey::svrepdesign()
scf2022 <- scf_load(2022) scf2022scf2022 <- scf_load(2022) scf2022
Fits a logistic regression to each implicate and pools the results with Rubin's rules. Returns odds ratios by default.
scf_logit(object, formula, odds = TRUE)scf_logit(object, formula, odds = TRUE)
object |
A |
formula |
A model formula specifying a binary outcome and predictors, e.g., |
odds |
Logical. If |
Use it for binary outcomes, such as homeownership. An odds ratio above 1 means higher odds of the outcome per one-unit increase in the predictor; below 1, lower odds. An odds ratio of 1.5 means 50 percent higher odds. The p-value tests an odds ratio of 1 (no association).
Calls scf_glm() with family = binomial(). With odds = TRUE,
estimates are exponentiated and standard errors converted to the
odds-ratio scale. vcov() stays on the log-odds scale; confint() computes
intervals on that scale and exponentiates them.
An object of class "scf_logit" and "scf_model_result" with:
A data frame of pooled estimates (log-odds or odds ratios), standard errors, and test statistics.
Fit statistics: mean and SD of AIC (AIC, AIC.sd) and pseudo-R-squared (pseudo_r2, pseudo_r2.sd) across implicates, observations per implicate (nobs), and their mean (nobs_mean).
List of implicate-level svyglm model objects.
The matched function call.
scf_glm(), scf_ols(), scf_MIcombine()
# Run logistic regression model <- scf_logit(scf2022, own ~ hhsex) summary(model)# Run logistic regression model <- scf_logit(scf2022, own ~ hhsex) summary(model)
Estimates the weighted mean of a continuous variable, overall or by group.
scf_mean(scf, var, by = NULL, verbose = FALSE, variance = "fed")scf_mean(scf, var, by = NULL, verbose = FALSE, variance = "fed")
scf |
A |
var |
A one-sided formula identifying the continuous variable to summarize (e.g., ~networth). |
by |
Optional one-sided formula specifying a discrete grouping variable for stratified means. |
verbose |
Logical. If TRUE, include implicate-level results in print output. Default is FALSE. |
variance |
|
Use it for the average value of a variable in the population or a group.
The estimate is in the variable's units. Summary dollar variables, such as
networth, are in the data's dollar year (see scf_load()); raw x
variables are in nominal dollars (see scf_real()).
min and max give the range of the implicate estimates.
Wealth and income are right-skewed, so a few very wealthy households pull
their means well above their medians; report scf_median() as well.
The mean is estimated in each implicate with survey::svymean() and pooled
across implicates (see scf_variance).
A list of class "scf_mean" with:
Pooled estimates with standard errors and range across implicates. One row per group, or one row total.
n is the number of sample households (unweighted) in each row,
averaged over implicates.
Groups of 10 or fewer households give a warning.
A named list of implicate-level estimates.
Variable and group metadata.
scf_median(), scf_percentile(), scf_xtab(), scf_plot_dist()
# Estimate means scf_mean(scf2022, ~networth) scf_mean(scf2022, ~networth, by = ~edcl)# Estimate means scf_mean(scf2022, ~networth) scf_mean(scf2022, ~networth, by = ~edcl)
Estimates the weighted median of a continuous variable, overall or by group.
scf_median( scf, var, by = NULL, verbose = FALSE, method = c("implicate", "stack"), variance = "fed" )scf_median( scf, var, by = NULL, verbose = FALSE, method = c("implicate", "stack"), variance = "fed" )
scf |
A |
var |
A one-sided formula specifying the continuous variable of interest (e.g., |
by |
Optional one-sided formula for a categorical grouping variable. |
verbose |
Logical; if TRUE, show implicate-level results. |
method |
|
variance |
|
Use it for skewed variables such as wealth and income, where it describes
the typical household better than the mean. Half of households are below
the estimate and half above. Summary dollar variables, such as
networth, are in the data's dollar year (see scf_load()); raw x
variables are in nominal dollars (see scf_real()).
Calls scf_percentile() with q = 0.5. With method = "implicate", the
estimate is the mean of the five implicate medians; the standard error
combines sampling and imputation variance (see scf_variance). With
method = "stack", the five implicates are stacked and one weighted median
is taken, as in the Fed's published tables; no standard error is given.
The two can differ in small groups.
A list of class c("scf_median", "scf_percentile") with:
A data frame with pooled medians (quantile is 0.5),
standard errors, and range across implicates. With method = "stack",
se, min, and max are NA and imps is NULL.
n is the number of sample households (unweighted) in each row,
averaged over implicates.
Groups of 10 or fewer households give a warning.
A list of implicate-level results.
Variable and grouping metadata.
# Estimate medians scf_median(scf2022, ~networth) scf_median(scf2022, ~networth, by = ~edcl) scf_median(scf2022, ~networth, method = "stack")# Estimate medians scf_median(scf2022, ~networth) scf_median(scf2022, ~networth, by = ~edcl) scf_median(scf2022, ~networth, method = "stack")
The SCF comes as five implicates: five complete copies of the data that
differ only in the values the Fed filled in for missing answers. Every
scf estimate is computed on each implicate and the five results are
combined into a consolidated, final result. This page explains how these
results are produced. To see how variance is computed, see scf_variance.
scf_MIcombine() does the combining for the regression models. It can
also be called directly on any list of implicate results. For univariate
and bivariate inferential statistics, the results are combined within the
functions that compute them, such as scf_mean(), scf_ratio(), as these
operations can differ depending on the statistic. Call it yourself when you
need a statistic or model that scf does not provide: compute it on each
implicate and pass the list of results.
scf_MIcombine(results, variances, call = sys.call(), df.complete = Inf) ## S3 method for class 'scf_MIresult' SE(object, ...)scf_MIcombine(results, variances, call = sys.call(), df.complete = Inf) ## S3 method for class 'scf_MIresult' SE(object, ...)
results |
A list of implicate-level results. Each element is an object with |
variances |
Optional list of variance-covariance matrices. If omitted, extracted using |
call |
Optional. The originating function call. Defaults to |
df.complete |
Optional degrees of freedom for the complete-data model. Used for small-sample
corrections. Defaults to |
object |
A pooled result object of class |
... |
Not used. |
An object of class "scf_MIresult" with components:
Pooled point estimates across implicates.
Pooled variance-covariance matrix.
Degrees of freedom for each parameter.
Estimated fraction of missing information for each parameter.
Number of implicates used in pooling.
The function call.
Supports coef(), vcov(), confint() (t distribution with Rubin's
degrees of freedom), and SE().
The final estimate is the average of the implicate estimates:
where is the estimate from implicate and is the
number of implicates (5).
The total variance adds two parts:
is the sampling variance, from the replicate weights (see
scf_variance). is the variance of the implicate
estimates around , which measures how much the filled-in
values move the answer. The factor allows for having only
five implicates. The standard error is . The Fed's codebook
uses the same formula, written as .
Tests and intervals use a t distribution. Its degrees of freedom are:
It depends on the ratio of sampling variance () to imputation
variance (). When imputation adds little variance, is
large and the t is close to normal. When it adds a lot, can be
small. If df.complete is given, the Barnard-Rubin small-sample version
is used instead.
The fraction of missing information (missinfo) is the share of the
total variance that comes from the missing data. Values near 0 mean the
imputation adds little uncertainty. Large values mean the result leans
heavily on imputed answers.
scf_ols(), scf_glm(), scf_logit(), and scf_quantreg() fit the
model separately on each implicate, then combine the results the same way,
with vectors and matrices in place of single numbers:
The coefficients are the average of the implicate coefficients.
is the average of the implicate variance-covariance matrices.
is the covariance matrix of the coefficients across implicates.
, and each coefficient gets its own .
This is what the Fed's codebook does for regressions (the MISECOMP SAS
macro). Models always average over all implicates.
Fit statistics, such as R-squared, are summarized across implicates rather than combined with Rubin's rules. Each model's help page says how.
In the descriptive functions, such as scf_mean(), a group can be empty
in some implicates, for example when a subset uses an imputed variable.
Its estimate then combines only the implicates that have it, and
is the number of those implicates. scf_MIcombine() itself stops if any
implicate result is NA.
Barnard J, Rubin DB. Small-sample degrees of freedom with multiple imputation. Biometrika. 1999;86(4):948–955. doi:10.1093/biomet/86.4.948
Board of Governors of the Federal Reserve System. Codebook for the 2025 Survey of Consumer Finances. https://www.federalreserve.gov/econres/scfindex.htm
Little RJA, Rubin DB. Statistical Analysis with Missing Data. 3rd ed. Wiley; 2019. ISBN: 9780470526798.
Rubin DB. Multiple Imputation for Nonresponse in Surveys. Wiley; 1987.
# Pool simple survey mean for mock data outlist <- lapply(scf2022$mi_design, function(d) survey::svymean(~I(age >= 65), d)) pooled <- scf_MIcombine(outlist) # vcov/coef extracted automatically SE(pooled); coef(pooled)# Pool simple survey mean for mock data outlist <- lapply(scf2022$mi_design, function(d) survey::svymean(~I(age >= 65), d)) pooled <- scf_MIcombine(outlist) # vcov/coef extracted automatically SE(pooled); coef(pooled)
Generic S3 methods dispatched on objects of class "scf_model_result",
as returned by scf_ols(), scf_glm(),
scf_logit(), and scf_quantreg(). Use them as you
would on any fitted model in R.
coef()Pooled coefficient estimates (Rubin's Rules).
vcov()Pooled variance-covariance matrix. For
scf_logit it is on the log-odds scale, even when the estimates
are odds ratios.
confint()Confidence intervals from the t distribution with Rubin's degrees of freedom. For odds ratios, the interval is computed on the log-odds scale and then exponentiated.
AIC()Mean AIC across implicates. Compare it only across models fit to the same outcome and data; lower is better.
residuals()Residuals from the first implicate model (diagnostic use only).
predict()Mean predictions pooled across all five implicate models.
formula()The model formula.
SE()Pooled standard errors, the square roots of the
diagonal of vcov(). For scf_logit they are on the log-odds scale,
unlike std.error in results, which is on the odds-ratio scale.
## S3 method for class 'scf_model_result' formula(x, ...) ## S3 method for class 'scf_model_result' residuals(object, ...) ## S3 method for class 'scf_model_result' coef(object, ...) ## S3 method for class 'scf_model_result' vcov(object, ...) ## S3 method for class 'scf_model_result' AIC(object, k = 2, ...) ## S3 method for class 'scf_model_result' predict(object, newdata, type = "link", ...) ## S3 method for class 'scf_model_result' confint(object, parm, level = 0.95, ...)## S3 method for class 'scf_model_result' formula(x, ...) ## S3 method for class 'scf_model_result' residuals(object, ...) ## S3 method for class 'scf_model_result' coef(object, ...) ## S3 method for class 'scf_model_result' vcov(object, ...) ## S3 method for class 'scf_model_result' AIC(object, k = 2, ...) ## S3 method for class 'scf_model_result' predict(object, newdata, type = "link", ...) ## S3 method for class 'scf_model_result' confint(object, parm, level = 0.95, ...)
x |
An object of class |
... |
Additional arguments (not used by most methods). |
object |
An object of class |
k |
Not used. The AIC is the mean of the implicate AICs, with the usual penalty of 2. |
newdata |
Optional data frame of new observations for |
type |
Prediction scale for |
parm |
Coefficients to include, by name or position. Defaults to all. |
level |
Confidence level for |
Converts dollar estimates from real dollars to dollars of the survey year.
scf_nominal(x, from_year = NULL, force = FALSE)scf_nominal(x, from_year = NULL, force = FALSE)
x |
An object of class |
from_year |
Integer. Year whose real-dollar units are assumed for the
input estimate. Defaults to the dollar year of the data, as recorded by
|
force |
Logical. If |
Estimates, standard errors, and confidence limits are multiplied by the
CPI-U-RS for the survey year divided by that for from_year. Works on
results from scf_mean(), scf_median(), scf_percentile(), and
scf_ttest().
The Fed's summary variables, such as income and networth, are in the
dollars of the Fed's latest survey: 2025 for files downloaded since
October 2026, 2022 before that (see scf_download()). Raw x variables
are already nominal. Income in the summary
extract is first moved to survey-year dollars, so converted income is in
survey-year dollars, as in the Fed's nominal tables. Medians in Tables 1
and 4 match the Fed within 0.05 percent for 1989 to 2025, except a few
medians near zero, where the Fed's rounding is larger than that.
The function stops, unless force = TRUE, if the result is for a raw x
variable or for a variable not known to be in real dollars (the Fed's
dollar summary variables and names ending in "_real"). Converting twice
gives a warning.
The input object with dollar estimates, standard errors, and
confidence intervals rescaled to nominal survey-year dollars. Attributes
"nominal" and "from_year" are set on the returned object.
Board of Governors of the Federal Reserve System. SAS macro for SCF Bulletin variable definitions (bulletin.macro.txt). https://www.federalreserve.gov/econres/files/bulletin.macro.txt
U.S. Bureau of Labor Statistics. Consumer Price Index research series using current methods (CPI-U-RS). https://www.bls.gov/cpi/research-series/r-cpi-u-rs-home.htm
scf_real(), scf_mean(), scf_median(), scf_percentile(),
scf_ttest()
# Convert a mean estimate to nominal survey-year dollars result <- scf_mean(scf2022, ~networth) result_nominal <- scf_nominal(result) # Works with median and percentile results med <- scf_median(scf2022, ~networth) med_nominal <- scf_nominal(med)# Convert a mean estimate to nominal survey-year dollars result <- scf_mean(scf2022, ~networth) result_nominal <- scf_nominal(result) # Works with median and percentile results med <- scf_median(scf2022, ~networth) med_nominal <- scf_nominal(med)
Fits a linear regression to each implicate with survey::svyglm() and
pools the results with Rubin's rules.
scf_ols(object, formula)scf_ols(object, formula)
object |
A |
formula |
A model formula specifying a continuous outcome and predictor variables (e.g., |
Use it for continuous outcomes. For binary outcomes use scf_logit(); for
skewed outcomes such as wealth, consider scf_quantreg() or a log
transform.
Each coefficient is the expected change in the outcome for a one-unit
increase in the predictor, holding the others constant. The p-value tests
whether a coefficient is zero, using a t distribution with Rubin's degrees
of freedom. R-squared is the share of variance explained and AIC compares
models of the same outcome (lower is better); both are averaged across
implicates. Pooling is described in scf_MIcombine().
An object of class "scf_ols" and "scf_model_result" with:
A data frame of pooled coefficients, standard errors, t-values, p-values, and significance stars.
Fit statistics: mean and SD of AIC (AIC, AIC.sd) and R-squared (r.squared, r.squared.sd) across implicates, observations per implicate (nobs), and their mean (nobs_mean).
A list of implicate-level svyglm model objects.
The matched call used to produce the model.
scf_glm(), scf_logit(), scf_MIcombine()
# Run OLS model model <- scf_ols(scf2022, networth ~ income + age) summary(model)# Run OLS model model <- scf_ols(scf2022, networth ~ income + age) summary(model)
Groups households by percentiles of a variable and summarizes a variable within each group.
scf_pctile_sum( scf, var, probs = seq(0, 1, by = 0.1), labels = NULL, varname = NULL, method = c("implicate", "stack"), stat = c("mean", "median", "none"), stat_var = NULL )scf_pctile_sum( scf, var, probs = seq(0, 1, by = 0.1), labels = NULL, varname = NULL, method = c("implicate", "stack"), stat = c("mean", "median", "none"), stat_var = NULL )
scf |
A |
var |
A one-sided formula naming the continuous variable to cut
(e.g., |
probs |
Numeric vector of values between 0 and 1 defining group boundaries,
including 0 and 1 as endpoints. Defaults to deciles
( |
labels |
Optional character vector of group labels, length equal to
|
varname |
Optional name for the new grouping variable. Defaults to
|
method |
Character. One of |
stat |
|
stat_var |
Optional one-sided formula naming the variable to summarize
within each group. Defaults to |
Use it to compare households at different points of a distribution, such
as mean net worth of the top 10 percent against the bottom 50 percent.
Each row is one group; the estimate is the mean or median of stat_var in
that group. For the Fed's own groups, such as nwcat and inccat, use
by in scf_mean() or scf_median() instead.
Households are sorted by the variable and household identifier, and the cumulative weighted population share sets each household's group, as in the Fed's SAS macro. Ties at a boundary are split by identifier, so each group holds its share of the population.
method = "implicate" assigns groups within each implicate and returns
estimates with standard errors. method = "stack" stacks the implicates
with weights divided by five, assigns groups once, and computes a weighted
statistic without a standard error; it reproduces the Fed's published
figures. With stat = "none", the data are returned with the group
variable added.
With stat = "none", the scf_mi_survey object with the group
variable (named by varname) added; pass it to by in other functions.
With method = "implicate", the result of scf_mean() or
scf_median(). With method = "stack", a data frame with columns
group, variable, and estimate.
Board of Governors of the Federal Reserve System. SAS macro for SCF Bulletin variable definitions (bulletin.macro.txt). https://www.federalreserve.gov/econres/files/bulletin.macro.txt
scf_mean(), scf_median(),
scf_percentile(), scf_update_by_implicate()
# Mean net worth, top 10 vs bottom 90 percent, stack method (fast) scf_pctile_sum(scf2022, ~networth, probs = c(0, 0.9, 1), labels = c("bottom90", "top10"), method = "stack") # Implicate method (default) scf_pctile_sum(scf2022, ~networth, probs = c(0, 0.9, 1), labels = c("bottom90", "top10")) # Return grouping variable only, no summary statistic (implicate method) scf2022 <- scf_pctile_sum(scf2022, ~networth, probs = c(0, 0.9, 1), labels = c("bottom90", "top10"), stat = "none")# Mean net worth, top 10 vs bottom 90 percent, stack method (fast) scf_pctile_sum(scf2022, ~networth, probs = c(0, 0.9, 1), labels = c("bottom90", "top10"), method = "stack") # Implicate method (default) scf_pctile_sum(scf2022, ~networth, probs = c(0, 0.9, 1), labels = c("bottom90", "top10")) # Return grouping variable only, no summary statistic (implicate method) scf2022 <- scf_pctile_sum(scf2022, ~networth, probs = c(0, 0.9, 1), labels = c("bottom90", "top10"), stat = "none")
Estimates a weighted percentile of a continuous variable, overall or by group.
scf_percentile( scf, var, q = 0.5, by = NULL, verbose = FALSE, method = c("implicate", "stack"), variance = "fed" )scf_percentile( scf, var, q = 0.5, by = NULL, verbose = FALSE, method = c("implicate", "stack"), variance = "fed" )
scf |
A |
var |
A one-sided formula naming the continuous variable to
summarize (for example |
q |
Percentile, between 0 and 1. Default 0.5 (median). |
by |
Optional one-sided formula naming a categorical grouping variable. If supplied, the percentile is estimated separately within each group. |
verbose |
Logical. If TRUE, print implicate-level estimates. Default FALSE. |
method |
|
variance |
|
Use it for points of a distribution other than the middle, such as the
90th percentile of net worth. With q = 0.9, 90 percent of households are
below the estimate. Summary dollar variables, such as
networth, are in the data's dollar year (see scf_load()); raw x
variables are in nominal dollars (see scf_real()).
method = "implicate" estimates the percentile in each implicate with
survey::svyquantile() and pools the estimates (see scf_variance).
method = "stack" stacks the implicates with weights divided by five and
computes one weighted percentile without a standard error; it reproduces
the Fed's published figures.
Warnings from survey::svyquantile() are passed on with the implicate
number. One notice, "Not all replicate weight designs give valid standard
errors for quantiles", is dropped; it concerns jackknife weights, and the
SCF's are bootstrap weights.
An object of class "scf_percentile" containing:
A data frame containing pooled percentile estimates, pooled
standard errors, and implicate min/max values. One row per group (if
by is supplied) or one row otherwise. With method = "stack",
se, min, and max are NA and imps is NULL.
n is the number of sample households (unweighted) in each row,
averaged over implicates.
Groups of 10 or fewer households give a warning.
A list of implicate-level percentile estimates and standard errors.
A list containing the variable name, optional group variable name, the quantile requested, the survey year, and the dollar year.
Logical flag indicating whether implicate-level estimates
should be printed by print() or summary().
Board of Governors of the Federal Reserve System. SAS macro for SCF Bulletin variable definitions (bulletin.macro.txt). https://www.federalreserve.gov/econres/files/bulletin.macro.txt
# Estimate the 75th percentile of net worth scf_percentile(scf2022, ~networth, q = 0.75) # Estimate the median net worth by ownership group scf_percentile(scf2022, ~networth, q = 0.5, by = ~own)# Estimate the 75th percentile of net worth scf_percentile(scf2022, ~networth, q = 0.75) # Estimate the median net worth by ownership group scf_percentile(scf2022, ~networth, q = 0.5, by = ~own)
Plots a pooled two-way table as stacked bars.
scf_plot_bbar( design, rowvar, colvar, scale = c("percent", "count"), percent_by = c("total", "row", "col"), title = NULL, xlab = NULL, ylab = NULL, fill_colors = NULL, row_labels = NULL, col_labels = NULL )scf_plot_bbar( design, rowvar, colvar, scale = c("percent", "count"), percent_by = c("total", "row", "col"), title = NULL, xlab = NULL, ylab = NULL, fill_colors = NULL, row_labels = NULL, col_labels = NULL )
design |
A |
rowvar |
A one-sided formula for the x-axis grouping variable (e.g., |
colvar |
A one-sided formula for the stacked fill variable (e.g., |
scale |
Character. One of |
percent_by |
Character. One of |
title |
Optional character string for the plot title. |
xlab |
Optional character string for the x-axis label. |
ylab |
Optional character string for the y-axis label. |
fill_colors |
Optional vector of fill colors to pass to |
row_labels |
Optional named vector to relabel |
col_labels |
Optional named vector to relabel |
Use it to show how two discrete variables relate, such as homeownership by
education. Each bar is a category of rowvar, split by colvar. Heights
are percentages of all households, or of each row or column
(percent_by), or estimated counts (scale = "count"). Estimates come
from scf_xtab().
A ggplot2 object.
# Stacked bar chart: education by ownership scf_plot_bbar(scf2022, ~own, ~edcl) # Column percentages instead of total percent scf_plot_bbar(scf2022, ~own, ~edcl, percent_by = "col") # Raw counts (estimated number of households) scf_plot_bbar(scf2022, ~own, ~edcl, scale = "count")# Stacked bar chart: education by ownership scf_plot_bbar(scf2022, ~own, ~edcl) # Column percentages instead of total percent scf_plot_bbar(scf2022, ~own, ~edcl, percent_by = "col") # Raw counts (estimated number of households) scf_plot_bbar(scf2022, ~own, ~edcl, scale = "count")
Plots a mean, median, or percentile of a continuous variable for each group.
scf_plot_cbar( design, yvar, xvar, stat = "mean", method = c("implicate", "stack"), title = NULL, xlab = NULL, ylab = NULL, fill = "#0072B2", angle = 30, label_map = NULL )scf_plot_cbar( design, yvar, xvar, stat = "mean", method = c("implicate", "stack"), title = NULL, xlab = NULL, ylab = NULL, fill = "#0072B2", angle = 30, label_map = NULL )
design |
A |
yvar |
One-sided formula for the continuous variable (e.g., |
xvar |
One-sided formula for the grouping variable (e.g., |
stat |
|
method |
For medians and percentiles, |
title |
Plot title (optional). |
xlab |
X-axis label (optional). |
ylab |
Y-axis label (optional). |
fill |
Bar fill color. Default is |
angle |
Angle of x-axis labels. Default is 30. |
label_map |
Optional named vector to relabel x-axis category labels. |
Use it to show how a variable differs across groups, such as median net
worth by education. Bar heights are point estimates from scf_mean(),
scf_median(), or scf_percentile() with by; run those functions for
standard errors. Use method = "stack" for medians and percentiles that
match the Fed's published tables.
A ggplot2 object.
scf_mean(), scf_median(), scf_percentile(), scf_theme()
# Visualize 90th percentile of income by education scf_plot_cbar(scf2022, ~income, ~edcl, stat = 0.9, fill = "#D55E00")# Visualize 90th percentile of income by education scf_plot_cbar(scf2022, ~income, ~edcl, stat = 0.9, fill = "#D55E00")
Plots the percent of households in each category of a discrete variable.
scf_plot_dbar( design, variable, title = NULL, xlab = NULL, ylab = "Percent", angle = 30, fill = "#0072B2", label_map = NULL )scf_plot_dbar( design, variable, title = NULL, xlab = NULL, ylab = "Percent", angle = 30, fill = "#0072B2", label_map = NULL )
design |
A |
variable |
A one-sided formula specifying a categorical variable (e.g., |
title |
Optional character string for the plot title. Default: |
xlab |
Optional x-axis label. Default: variable name. |
ylab |
Optional y-axis label. Default: |
angle |
Integer. Rotation angle for x-axis labels. Default is |
fill |
Fill color for bars. Default is |
label_map |
Optional named vector to relabel x-axis category labels. |
Use it to show how households divide across categories, such as education
levels. Estimates come from scf_freq().
A ggplot2 object representing the pooled bar chart.
scf_freq(), scf_plot_bbar(), scf_xtab()
# Bar chart of education categories scf_plot_dbar(scf2022, ~edcl)# Bar chart of education categories scf_plot_dbar(scf2022, ~edcl)
Plots the percent of households in each category or bin of a variable.
scf_plot_dist( design, variable, bins = 30, title = NULL, xlab = NULL, ylab = "Percent", angle = 30, fill = "#0072B2", labels = NULL )scf_plot_dist( design, variable, bins = 30, title = NULL, xlab = NULL, ylab = "Percent", angle = 30, fill = "#0072B2", labels = NULL )
design |
A |
variable |
A one-sided formula specifying the variable to plot. |
bins |
Approximate number of bins for continuous variables; the
breaks are set by |
title |
Optional plot title. |
xlab |
Optional x-axis label. |
ylab |
Optional y-axis label. Default is "Percent". |
angle |
Angle for x-axis tick labels. Default is 30. |
fill |
Fill color for bars. Default is |
labels |
Optional named vector of custom axis labels (for discrete variables only). |
Use it for a quick look at any variable. Factors and numeric variables
with 25 or fewer values are treated as discrete and plotted with
scf_freq(). Continuous variables are binned with the same breaks in every
implicate and pooled.
A ggplot2 object.
# Distribution of homeownership scf_plot_dist(scf2022, ~own) # Distribution of age scf_plot_dist(scf2022, ~age, bins = 10)# Distribution of homeownership scf_plot_dist(scf2022, ~own) # Distribution of age scf_plot_dist(scf2022, ~age, bins = 10)
Plots the joint distribution of two continuous variables with hexagonal bins.
scf_plot_hex(design, x, y, bins = 50, title = NULL, xlab = NULL, ylab = NULL)scf_plot_hex(design, x, y, bins = 50, title = NULL, xlab = NULL, ylab = NULL)
design |
A |
x |
A one-sided formula for the x-axis variable (e.g., |
y |
A one-sided formula for the y-axis variable (e.g., |
bins |
Integer. Number of hexagonal bins in each direction. Default is |
title |
Optional character string for the plot title. |
xlab |
Optional x-axis label. Defaults to the variable name. |
ylab |
Optional y-axis label. Defaults to the variable name. |
Use it in place of a scatterplot when there are too many points to see. Brighter cells hold more households; the color scale is logarithmic. The implicates are stacked with weights divided by five. Requires the hexbin package.
A ggplot2 object.
scf_corr(), scf_plot_smooth(), scf_theme()
# Plot hexbin of income vs. net worth # Note: mock data has 200 rows per implicate; hexbin output will be sparse. # Results on full SCF data will show a meaningful joint density. if (requireNamespace("hexbin", quietly = TRUE)) { scf_plot_hex(scf2022, ~income, ~networth) }# Plot hexbin of income vs. net worth # Note: mock data has 200 rows per implicate; hexbin output will be sparse. # Results on full SCF data will show a meaningful joint density. if (requireNamespace("hexbin", quietly = TRUE)) { scf_plot_hex(scf2022, ~income, ~networth) }
Plots the pooled share of households in each bin of a continuous variable.
scf_plot_hist( design, variable, bins = 30, xlim = NULL, title = NULL, xlab = NULL, ylab = "Proportion of Households", fill = "#0072B2" )scf_plot_hist( design, variable, bins = 30, xlim = NULL, title = NULL, xlab = NULL, ylab = "Proportion of Households", fill = "#0072B2" )
design |
A |
variable |
A one-sided formula indicating the numeric variable to plot. |
bins |
Number of bins (default: 30). |
xlim |
Optional numeric range. Values outside will be included in edge bins. |
title |
Optional plot title. |
xlab |
Optional x-axis label. Defaults to the variable name. |
ylab |
Optional y-axis label. Defaults to "Proportion of Households". |
fill |
Fill color for bars (default: |
Use it to see the shape of a variable such as age. For very skewed
variables, such as net worth, set xlim; values outside it are moved to
the nearest endpoint. Empty bins stay on the axis. Bins use the same breaks
in every implicate and are pooled with scf_freq().
A ggplot2 object.
scf_freq(), scf_plot_dbar(), scf_plot_smooth(), scf_update_by_implicate()
# Plot histogram of age scf_plot_hist(scf2022, ~age, bins = 10)# Plot histogram of age scf_plot_hist(scf2022, ~age, bins = 10)
Plots a smoothed curve of the share of households near each value.
scf_plot_smooth( design, variable, binwidth = NULL, xlim = NULL, method = "loess", span = 0.2, color = "blue", xlab = NULL, ylab = "Percent of Households", title = NULL )scf_plot_smooth( design, variable, binwidth = NULL, xlim = NULL, method = "loess", span = 0.2, color = "blue", xlab = NULL, ylab = "Percent of Households", title = NULL )
design |
A |
variable |
A one-sided formula specifying a continuous variable (e.g., |
binwidth |
Optional bin width. Default uses Freedman-Diaconis rule. |
xlim |
Optional numeric vector of length 2. Limits the plotted range; shares are still computed over all households. |
method |
Character. Smoothing method: |
span |
Numeric LOESS span. Default is |
color |
Line color. Default is |
xlab |
Optional label for x-axis. Defaults to the variable name. |
ylab |
Optional label for y-axis. Defaults to |
title |
Optional plot title. |
Use it to see the shape, skew, or peaks of a variable such as age or income. The curve shows shape, not exact shares. The implicates are stacked with weights divided by five and binned (by default with the Freedman-Diaconis width), and a smoothing curve is fit to the bin shares.
A ggplot2 object.
scf_plot_hist(), scf_plot_dist(), scf_theme()
# Plot smoothed distribution scf_plot_smooth( scf2022, ~networth, xlim = c(0, 2e6), method = "loess", span = 0.25 )# Plot smoothed distribution scf_plot_smooth( scf2022, ~networth, xlim = c(0, 2e6), method = "loess", span = 0.25 )
Tests a proportion against a null value, or the difference between two groups.
scf_prop_test( design, var, group = NULL, p = NULL, alternative = c("two.sided", "less", "greater"), conf.level = 0.95, variance = "fed" )scf_prop_test( design, var, group = NULL, p = NULL, alternative = c("two.sided", "less", "greater"), conf.level = 0.95, variance = "fed" )
design |
A |
var |
A one-sided formula indicating a binary variable (e.g., |
group |
Optional one-sided formula indicating a binary grouping variable (e.g., |
p |
Null hypothesis value. Defaults to |
alternative |
Character. One of |
conf.level |
Confidence level for the confidence interval. Default is |
variance |
|
Use it for yes/no variables, such as whether a household owns its home, and to compare two groups, such as homeownership among men and women. The one-sample estimate is the share with the trait; the two-sample estimate is the first group's share minus the second's. A small p-value means a difference this large is unlikely under the null hypothesis; the confidence interval gives the plausible range.
The variable must be logical or coded 0/1. Proportions are estimated in each implicate with the replicate weights and pooled (see scf_variance). The two-sample test accounts for the covariance between groups.
An object of class "scf_prop_test" with:
Estimate, standard error, t-statistic, degrees of freedom, p-value, confidence interval, and stars. For two-sample tests, the estimate is the first group minus the second.
(Only in two-sample tests) A data frame of pooled proportions by group.
A list describing the method, null value, alternative hypothesis, and confidence level.
scf_ttest(), scf_mean(), scf_MIcombine()
# Wrangle data for example scf2022 <- scf_update(scf2022, rich = networth > 1e6, female = factor(hhsex, levels = 1:2, labels = c("Male","Female")), over50 = age > 50 ) # One-sample test scf_prop_test(scf2022, ~rich, p = 0.10) # Two-sample test scf_prop_test(scf2022, ~rich, ~female, alternative = "less")# Wrangle data for example scf2022 <- scf_update(scf2022, rich = networth > 1e6, female = factor(hhsex, levels = 1:2, labels = c("Male","Female")), over50 = age > 50 ) # One-sample test scf_prop_test(scf2022, ~rich, p = 0.10) # Two-sample test scf_prop_test(scf2022, ~rich, ~female, alternative = "less")
Fits a weighted quantile regression to each implicate with
quantreg::rq() and pools the results with Rubin's rules.
scf_quantreg( object, formula, tau = 0.5, se = c("replicate", "nid", "iid", "ker", "boot"), ... )scf_quantreg( object, formula, tau = 0.5, se = c("replicate", "nid", "iid", "ker", "boot"), ... )
object |
A |
formula |
A model formula specifying the outcome and predictors,
e.g., |
tau |
Quantile to estimate, between 0 and 1. Default 0.5 (median). |
se |
Within-implicate covariance method: |
... |
Additional arguments passed to |
Use it for skewed outcomes such as wealth and income, where the mean is
pulled by a few very wealthy households. Each coefficient is the change in
the tau quantile of the outcome per one-unit increase in the predictor;
with tau = 0.5, the change in the median. Comparing several values of
tau shows whether a predictor matters more at the top or bottom of the
distribution. Estimate each quantile in a separate call. P-values use a t
distribution with Rubin's degrees of freedom. The function stops if the
predictors are collinear.
se sets the within-implicate covariance:
"replicate" (default)Refits the model with each of the 999 replicate weights (see scf_variance). The only method that uses the SCF's design, and consistent for quantiles (Rust and Rao 1996). About 5,000 fits; several minutes on the full data.
"nid", "iid", "ker", "boot"
From
quantreg::summary.rq(). These treat the weighted sample as a simple
random sample. "nid" allows the error density to vary; "iid"
assumes it is constant (Koenker and Bassett 1978); "ker" uses a kernel
estimate; "boot" is a pairs bootstrap. Analytic methods are unreliable
at quantiles with mass points, such as low quantiles of net worth.
Fit statistics (Koenker and Machado 1999) compare the model with an
intercept-only model at the same tau:
rho, rho_null
Weighted check-loss of the model and the null model, averaged across implicates. Weights are scaled to mean 1.
r1. A local measure of fit at
tau, from 0 to 1.
r1_adj. A descriptive
adjustment, not derived from Koenker and Machado.
nobs, nobs_mean
Observations per implicate and their mean.
An object of class "scf_quantreg" and "scf_model_result" with:
resultsA data frame of pooled coefficients, standard errors, t-values, p-values, and significance stars.
tauThe quantile estimated.
se_methodThe SE method used.
fitFit statistics: rho, rho_null, r1, r1_adj,
nobs, nobs_mean. See Details.
modelsA list of implicate-level rq model objects for
direct inspection.
imp_vcovA list of implicate-level variance-covariance matrices.
callThe matched call.
formulaThe model formula.
Koenker R, Bassett G. Regression quantiles. Econometrica. 1978;46(1):33–50. doi:10.2307/1913643
Koenker R, Machado JAF. Goodness of fit and related inference processes for quantile regression. Journal of the American Statistical Association. 1999;94(448):1296–1310. doi:10.2307/2669943
Rust KF, Rao JNK. Variance estimation for complex surveys using replication techniques. Statistical Methods in Medical Research. 1996;5(3):283–310. doi:10.1177/096228029600500305
scf_ols(), scf_glm(), scf_MIcombine(), quantreg::rq()
# Median regression of net worth on age and education, with fast standard # errors; the default, se = "replicate", uses the SCF's design but is slower m_med <- scf_quantreg(scf2022, networth ~ age + factor(edcl), tau = 0.5, se = "nid") summary(m_med) # Access goodness-of-fit statistics m_med$fit$r1 m_med$fit$r1_adj# Median regression of net worth on age and education, with fast standard # errors; the default, se = "replicate", uses the SCF's design but is slower m_med <- scf_quantreg(scf2022, networth ~ age + factor(edcl), tau = 0.5, se = "nid") summary(m_med) # Access goodness-of-fit statistics m_med$fit$r1 m_med$fit$r1_adj
Estimates the ratio of two population totals, overall or by group.
scf_ratio(scf, num, den, by = NULL, variance = "fed")scf_ratio(scf, num, den, by = NULL, variance = "fed")
scf |
A |
num |
A one-sided formula for the numerator (e.g., |
den |
A one-sided formula for the denominator (e.g., |
by |
Optional one-sided formula for a grouping variable. |
variance |
|
Use it for aggregate shares like those in the Fed's Bulletin, such as financial assets as a share of all assets. An estimate of 0.25 means the numerator total is a quarter of the denominator total. This is not the mean of household ratios.
The ratio is sum(w * num) / sum(w * den), estimated in each implicate
with survey::svyratio() and pooled (see scf_variance).
An object of class "scf_ratio" with:
Pooled ratio, standard error, range across implicates, and
group ("All" without by), one row per group.
Implicate-level estimates.
Variable names and survey year.
# Share of all income received by homeowners scf2022 <- scf_update(scf2022, owner_income = income * (own == 1)) scf_ratio(scf2022, ~owner_income, ~income)# Share of all income received by homeowners scf2022 <- scf_update(scf2022, owner_income = income * (own == 1)) scf_ratio(scf2022, ~owner_income, ~income)
Adds versions of raw x dollar variables in the dollars of the summary
variables to every implicate.
scf_real(scf, vars, income = NULL, suffix = "_real", force = FALSE)scf_real(scf, vars, income = NULL, suffix = "_real", force = FALSE)
scf |
A |
vars |
A one-sided formula naming raw dollar variables, such as
|
income |
Optional one-sided formula naming the variables in |
suffix |
Text added to each converted variable's name. Defaults to
|
force |
Logical. If |
Follows the Fed's SCF Bulletin SAS macro. Amounts are multiplied by the
September CPI-U-RS for the data's dollar year (2025 for files downloaded
since October 2026; see scf_download()) divided by that for the survey
year. Income
items, reported for the prior year, are first multiplied by the ratio of
annual CPI-U-RS in the survey year to the prior year; name them in
income. In other amounts, the codes -1 and -2 are left unchanged. A
converted variable equals the matching summary variable; for example,
x5702 with income = ~x5702 equals wageinc.
The function stops, unless force = TRUE, if a variable is not a raw x
variable, has fewer than 25 distinct values, or the new variable already
exists. It cannot tell which variables are income items; check the SCF
codebook.
The scf_mi_survey object with the converted variables added.
Assign it back to keep them.
Board of Governors of the Federal Reserve System. SAS macro for SCF Bulletin variable definitions (bulletin.macro.txt). https://www.federalreserve.gov/econres/files/bulletin.macro.txt
U.S. Bureau of Labor Statistics. Consumer Price Index research series using current methods (CPI-U-RS). https://www.bls.gov/cpi/research-series/r-cpi-u-rs-home.htm
# Wages (x5702, an income item) and the market # value of directly held stock (x3915) in the data's dollars scf2022 <- scf_real(scf2022, ~x5702 + x3915, income = ~x5702) scf_median(scf2022, ~x5702_real)# Wages (x5702, an income item) and the market # value of directly held stock (x3915) in the data's dollars scf2022 <- scf_real(scf2022, ~x5702 + x3915, income = ~x5702) scf_median(scf2022, ~x5702_real)
Formats one or more scf model results as a side-by-side table.
scf_regtable( ..., model.names = NULL, digits = NULL, auto_digits = is.null(digits), labels = NULL, output = c("console", "markdown", "latex", "csv"), file = NULL )scf_regtable( ..., model.names = NULL, digits = NULL, auto_digits = is.null(digits), labels = NULL, output = c("console", "markdown", "latex", "csv"), file = NULL )
... |
One or more SCF regression model objects, or a single list of such models. |
model.names |
Optional character vector naming the models. Defaults to
|
digits |
Integer. Decimal places for every estimate. If NULL
(default), decimal places are chosen by size (see |
auto_digits |
Logical; if |
labels |
Optional named character vector or labeling function to replace term names with descriptive labels. |
output |
Output format: one of |
file |
File path for CSV output; required if |
Use it to report several models together. Each cell shows the estimate, significance stars, and standard error in parentheses. Terms appear in the order first seen; terms absent from a model show "–". Fit statistics follow the coefficients: N for all models, R-squared and AIC for OLS, AIC and pseudo-R-squared for binomial models, and tau, R1, and adjusted R1 for quantile regression. Compare AIC only across models of the same type on the same data.
Invisibly returns a data frame with formatted regression results and fit statistics.
# Perform OLS regression m1 <- scf_ols(scf2022, income ~ age) # Print regression results as a console table scf_regtable(m1, digits = 2)# Perform OLS regression m1 <- scf_ols(scf2022, income ~ age) # Print regression results as a console table scf_regtable(m1, digits = 2)
Returns the pooled standard errors of a result.
## S3 method for class 'scf_mean' SE(object, ...) ## S3 method for class 'scf_percentile' SE(object, ...) ## S3 method for class 'scf_ratio' SE(object, ...) ## S3 method for class 'scf_model_result' SE(object, ...)## S3 method for class 'scf_mean' SE(object, ...) ## S3 method for class 'scf_percentile' SE(object, ...) ## S3 method for class 'scf_ratio' SE(object, ...) ## S3 method for class 'scf_model_result' SE(object, ...)
object |
A result from one of the functions above. |
... |
Not used. |
Works on results from scf_mean(), scf_median(), scf_percentile(),
scf_ratio(), the models (scf_ols(), scf_logit(), scf_glm(),
scf_quantreg()), and scf_MIcombine(). The values are the se column of
the results, named by group, or by variable when there is no by.
Stack-method percentiles have no standard error and return NA. For
scf_logit(), standard errors are on the log-odds scale, unlike
std.error in results, which is on the odds-ratio scale.
A named numeric vector of standard errors.
SE(scf_mean(scf2022, ~networth, by = ~edcl))SE(scf_mean(scf2022, ~networth, by = ~edcl))
scf_mi_survey ObjectKeeps households that meet a condition, in every implicate.
scf_subset(scf, expr)scf_subset(scf, expr)
scf |
A |
expr |
A logical expression used to filter rows, evaluated separately in each implicate's variable frame (e.g., |
Use it to study one part of the population, such as renters or an age
range. Estimates from a subset describe that group only: a mean from
scf_subset(scf2022, age >= 65) is the mean among senior-headed
households. To compare groups side by side, use by in the estimating
functions instead.
The condition is applied in each implicate separately, so a household with
imputed values can be in the subset in some implicates and not others.
Rows where the condition is NA are dropped. The function stops if no rows
meet the condition in an implicate. n_households is the weighted count,
averaged across implicates.
A new scf_mi_survey object (see scf_design())
# Filter for working-age households with positive net worth scf_sub <- scf_subset(scf2022, age < 65 & networth > 0) scf_mean(scf_sub, ~income)# Filter for working-age households with positive net worth scf_sub <- scf_subset(scf2022, age < 65 & networth > 0) scf_mean(scf_sub, ~income)
The ggplot2 theme used by all scf_plot_*() functions.
scf_theme(base_size = 13, base_family = "sans", grid = TRUE, axis = TRUE, ...) scf_activate_theme()scf_theme(base_size = 13, base_family = "sans", grid = TRUE, axis = TRUE, ...) scf_activate_theme()
base_size |
Base font size. Defaults to 13. |
base_family |
Font family. Defaults to "sans". |
grid |
Logical. Show gridlines? Defaults to TRUE. |
axis |
Logical. Include axis ticks and lines? Defaults to TRUE. |
... |
Additional arguments passed to |
Based on theme_minimal() and sized for 5.5 by 5.5 inch figures at 300
dpi. Add a ggplot2::theme() call to change settings.
scf_activate_theme() sets it as the session default.
scf_theme() returns a ggplot2 theme object.
scf_activate_theme() returns NULL invisibly; called for
its side effect of setting the session-wide ggplot2 theme.
ggplot2::theme(), scf_plot_dist()
library(ggplot2) ggplot(mtcars, aes(factor(cyl))) + geom_bar(fill = "#0072B2") + scf_theme() # Activate globally for the session, then restore the previous theme: old <- ggplot2::theme_get() scf_activate_theme() ggplot2::theme_set(old)library(ggplot2) ggplot(mtcars, aes(factor(cyl))) + geom_bar(fill = "#0072B2") + scf_theme() # Activate globally for the session, then restore the previous theme: old <- ggplot2::theme_get() scf_activate_theme() ggplot2::theme_set(old)
Tests a mean against a null value, or the difference between two group means.
scf_ttest( design, var, group = NULL, mu = 0, alternative = c("two.sided", "less", "greater"), conf.level = 0.95, variance = "fed" )scf_ttest( design, var, group = NULL, mu = 0, alternative = c("two.sided", "less", "greater"), conf.level = 0.95, variance = "fed" )
design |
A |
var |
A one-sided formula specifying a numeric variable (e.g., |
group |
Optional one-sided formula specifying a binary grouping variable (e.g., |
mu |
Numeric. Null hypothesis value. Default is |
alternative |
Character. One of |
conf.level |
Confidence level for the confidence interval. Default is |
variance |
|
Use it to test a claim about a mean, such as whether mean income differs between two groups. The one-sample estimate is the mean; the two-sample estimate is the first group's mean minus the second's. A small p-value means a difference this large is unlikely under the null hypothesis; the confidence interval gives the plausible range. Means of wealth and income are dominated by the top of the distribution and may not reflect the typical household.
Means are estimated in each implicate with survey::svymean() and pooled
(see scf_variance). The two-sample test accounts for the covariance
between groups.
An object of class scf_ttest with:
A data frame with pooled estimate, standard error, t-statistic, degrees of freedom, p-value, significance stars, and confidence interval. For two-sample tests, the estimate is the first group minus the second.
Group-specific means (for two-sample tests only).
List describing the test type, null hypothesis, confidence level, and alternative.
Survey year, variable name, and dollar year, used by scf_nominal().
scf_prop_test(), scf_mean(), scf_MIcombine()
# Derive analysis vars scf2022 <- scf_update(scf2022, female = factor(hhsex, levels = 1:2, labels = c("Male", "Female")), over50 = age > 50 ) # One-sample t-test scf_ttest(scf2022, ~income, mu = 75000) # Two-sample t-test scf_ttest(scf2022, ~income, group = ~female)# Derive analysis vars scf2022 <- scf_update(scf2022, female = factor(hhsex, levels = 1:2, labels = c("Male", "Female")), over50 = age > 50 ) # One-sample t-test scf_ttest(scf2022, ~income, mu = 75000) # Two-sample t-test scf_ttest(scf2022, ~income, group = ~female)
Creates or changes variables in every implicate.
scf_update(.object, ...)scf_update(.object, ...)
.object |
A |
... |
Named expressions assigning new or modified variables using |
Use it before analysis to recode values, convert types, or create ratios,
differences, and indicators. Expressions are evaluated in order in each
implicate's data. Set a variable to NULL to remove it. The result is a
new object; assign it back to keep the changes. For variables that depend
on the distribution within each implicate, such as ranks or percentile
cutoffs, use scf_update_by_implicate().
The input scf_mi_survey object with mi_design updated to reflect
the new or modified variables. Other elements (year, n_households,
dollar_year) and the mock attribute are kept. n_households is not
recomputed.
scf_load(), scf_update_by_implicate(), survey::svrepdesign()
# Create a binary indicator for being over age 50 scf2022 <- scf_update(scf2022, over50 = age > 50 ) # Example: Create a log-transformed income variable scf2022 <- scf_update(scf2022, log_income = log(income + 1) )# Create a binary indicator for being over age 50 scf2022 <- scf_update(scf2022, over50 = age > 50 ) # Example: Create a log-transformed income variable scf2022 <- scf_update(scf2022, log_income = log(income + 1) )
Applies a function to each implicate's data frame.
scf_update_by_implicate(object, f)scf_update_by_implicate(object, f)
object |
A |
f |
A function that takes one implicate's data frame as its sole argument
and returns a modified data frame with the same number of rows. The function
signature should be |
Use for variables that depend on the distribution within each implicate, such as ranks, implicate-specific quantiles, or group z-scores. The function must return a data frame with the same number of rows. The result is a new object; assign it back to keep the changes.
A modified scf_mi_survey object with updated implicate-level designs.
# Flag households in the top 10% of net worth, # using each implicate's weighted 90th percentile as the threshold. scf2022 <- scf_update_by_implicate(scf2022, function(df) { o <- order(df$networth) share <- cumsum(df$wgt[o]) / sum(df$wgt) threshold <- df$networth[o][which(share >= 0.90)[1]] df$top10nw <- df$networth >= threshold df }) # Compute implicate-specific z-scores of income, # using the weighted mean and standard deviation scf2022 <- scf_update_by_implicate(scf2022, function(df) { mu <- stats::weighted.mean(df$income, df$wgt) sigma <- sqrt(stats::weighted.mean((df$income - mu)^2, df$wgt)) df$z_income <- (df$income - mu) / sigma df }) # Verify new variable exists head(scf2022$mi_design[[1]]$variables$z_income)# Flag households in the top 10% of net worth, # using each implicate's weighted 90th percentile as the threshold. scf2022 <- scf_update_by_implicate(scf2022, function(df) { o <- order(df$networth) share <- cumsum(df$wgt[o]) / sum(df$wgt) threshold <- df$networth[o][which(share >= 0.90)[1]] df$top10nw <- df$networth >= threshold df }) # Compute implicate-specific z-scores of income, # using the weighted mean and standard deviation scf2022 <- scf_update_by_implicate(scf2022, function(df) { mu <- stats::weighted.mean(df$income, df$wgt) sigma <- sqrt(stats::weighted.mean((df$income - mu)^2, df$wgt)) df$z_income <- (df$income - mu) / sigma df }) # Verify new variable exists head(scf2022$mi_design[[1]]$variables$z_income)
Every SCF estimate has two sources of error. scf adds them to get the
standard error, using Rubin's rules (see scf_MIcombine). This page
explains the first source, sampling error, and the two ways scf can
measure it.
Sampling error. The SCF interviews a sample of households, not all of
them. A different sample would give a slightly different answer. The Fed
measures this with 999 replicate weights: each one re-weights the sample as
if it had been drawn again. scf re-computes the estimate with each set of
replicate weights. The sampling variance is the spread of those 999
answers: the sum of their squared distances from their mean, divided by
998.
Imputation error. Some answers are missing, and the Fed fills them in
five times, giving five implicates. The spread of the five implicate
estimates is the imputation variance . See scf_MIcombine.
Each implicate has its own sampling variance. The two methods differ only
in which ones are used for .
"fed" (default) is the sampling variance from the first
implicate. This is what the Fed's own SAS code (the MEANIT macro in the
SCF codebook) does for means and medians. The Fed built the replicate
weights for the first implicate, so this method uses them on the data
they were made for.
"rubin" is the average sampling variance over all five
implicates. This is standard multiple-imputation practice (Rubin 1987)
and what most software does, including mitools and Stata's mi. It
uses more information, so it is slightly more stable.
For estimates on the whole population, the two methods give nearly the same answer. They can differ for groups whose membership depends on imputed values, such as households grouped by net worth or income. A household can fall in the group in one implicate and out of it in another, so the first implicate's group may not represent the others. The Fed's documentation warns about this case.
Regression models (scf_ols(), scf_glm(), scf_logit(),
scf_quantreg()) always use "rubin", as the Fed's codebook does for
regressions (the MISECOMP macro).
If a group is missing from some implicates, "fed" uses the first
implicate that has it.
Take care when scf_subset() or by = uses a variable that the Fed
imputes, such as income, net worth, assets, or debts. Each implicate can
then hold a different set of households. With "fed", the sampling
variance comes from the first implicate's set alone, which may not
represent the other four. With "rubin", it is averaged over all five
sets, so every version of the group counts.
For example, scf_subset(scf2022, networth > 1e6) keeps a different
set of households in each implicate. The same is true of groups made from
imputed variables, such as net worth categories. Use "rubin" for analyses
like these:
rich <- scf_subset(scf2022, networth > 1e6) scf_mean(rich, ~income, variance = "rubin") scf_mean(scf2022, ~income, by = ~nwcat, variance = "rubin") scf_freq(scf2022, ~own, by = ~nwcat, variance = "rubin")
If the subset or group uses a variable that is rarely or never imputed, such as age or household sex, the households are nearly the same in every implicate and the two methods agree.
Use the default, "fed", to match the Fed's published methods and
figures.
Use "rubin" when groups or subsets depend on imputed values (for
example by = ~nwcat, or scf_subset() on income or net worth), or when
results must match other multiple-imputation software.
Report which method you used. For whole-population estimates the choice rarely matters.
Set it with the variance argument:
scf_mean(scf2022, ~networth, variance = "rubin")
The variance argument is available in scf_mean(), scf_median(),
scf_percentile(), scf_freq(), scf_xtab(), scf_corr(), scf_ttest(),
scf_prop_test(), and scf_ratio(). Functions that call these, such as
scf_pctile_sum() and the plot functions, use "fed".
Rubin DB. Multiple Imputation for Nonresponse in Surveys. Wiley, 1987.
Board of Governors of the Federal Reserve System. Codebook for the Survey of Consumer Finances (see the sections on sampling error and the MEANIT SAS macro). https://www.federalreserve.gov/econres/scfindex.htm
Estimates a two-way table of household shares, with standard errors.
scf_xtab(scf, rowvar, colvar, scale = "cell", variance = "fed")scf_xtab(scf, rowvar, colvar, scale = "cell", variance = "fed")
scf |
A |
rowvar |
A one-sided formula specifying the row variable (e.g., |
colvar |
A one-sided formula specifying the column variable (e.g., |
scale |
Character. Proportion basis: "cell" (default), "row", or "col". |
variance |
|
Use it to see how two categorical variables relate, such as homeownership
by education. scale sets which shares print: of all households
("cell"), of each row ("row", rows sum to 100 percent and show how each
row divides across columns), or of each column ("col").
Shares are estimated in each implicate with the replicate weights and
pooled (see scf_variance). Counts come from survey::svytable().
A list of class "scf_xtab" with:
One row per cell: row, col, rowvar, colvar, prop and se (share of all households), row_share, col_share, row_share_se, and col_share_se.
Matrices of cell, row, and col shares, and their standard errors se, se_row, and se_col.
Weighted cell counts for each implicate.
List with rowvar and colvar, their labels, and the scale shown by print().
# Cross-tabulate ownership by sex scf_xtab(scf2022, ~own, ~hhsex, scale = "row")# Cross-tabulate ownership by sex scf_xtab(scf2022, ~own, ~hhsex, scale = "row")