diff --git a/R/fixest.R b/R/fixest.R index 9795ea8a2..6986787ca 100644 --- a/R/fixest.R +++ b/R/fixest.R @@ -3,10 +3,14 @@ #' #' @param x A `fixest` object returned from any of the `fixest` estimators #' @template param_confint -#' @param ... Additional arguments passed to `summary` and `confint`. Important -#' arguments are `se` and `cluster`. Other arguments are `dof`, `exact_dof`, -#' `forceCovariance`, and `keepBounded`. -#' See [`summary.fixest`][fixest::summary.fixest()]. +#' @param ... Additional arguments passed to [`summary.fixest`][fixest::summary.fixest()] +#' and [`confint.fixest`][fixest::confint.fixest()]. Important arguments are +#' `vcov` (a VCOV type string like `"hetero"`, or a one-sided formula like +#' `~Product + Year` for clustering), `cluster`, and `ssc` (small sample +#' correction). The `se` argument is accepted but deprecated in recent +#' `fixest` versions; use `vcov` instead. Other arguments include +#' `forceCovariance` and `keepBounded`. +#' See [`summary.fixest`][fixest::summary.fixest()] for details. #' @evalRd return_tidy(regression = TRUE) #' #' @details The `fixest` package provides a family of functions for estimating @@ -45,7 +49,9 @@ #' #' tidy(gravity, conf.int = TRUE, cluster = c("Product", "Year")) #' -#' tidy(gravity, conf.int = TRUE, se = "threeway") +#' tidy(gravity, conf.int = TRUE, vcov = ~Product + Year) +#' +#' tidy(gravity, conf.int = TRUE, vcov = "hetero") #' #' # 2) or, feed tidy() a summary.fixest object that has already accepted #' # these arguments diff --git a/man/augment.fixest.Rd b/man/augment.fixest.Rd index 900a47345..10c07f7b9 100644 --- a/man/augment.fixest.Rd +++ b/man/augment.fixest.Rd @@ -32,14 +32,18 @@ the \code{data} argument will be ignored.} \item{type.predict}{Passed to \code{\link[fixest:predict.fixest]{predict.fixest}} \code{type} argument. Defaults to \code{"link"} (like \code{predict.glm}).} -\item{type.residuals}{Passed to \code{\link[fixest:resid.fixest]{predict.fixest}} +\item{type.residuals}{Passed to \code{\link[fixest:residuals.fixest]{predict.fixest}} \code{type} argument. Defaults to \code{"response"} (like \code{residuals.lm}, but unlike \code{residuals.glm}).} -\item{...}{Additional arguments passed to \code{summary} and \code{confint}. Important -arguments are \code{se} and \code{cluster}. Other arguments are \code{dof}, \code{exact_dof}, -\code{forceCovariance}, and \code{keepBounded}. -See \code{\link[fixest:summary.fixest]{summary.fixest}}.} +\item{...}{Additional arguments passed to \code{\link[fixest:summary.fixest]{summary.fixest}} +and \code{\link[fixest:confint.fixest]{confint.fixest}}. Important arguments are +\code{vcov} (a VCOV type string like \code{"hetero"}, or a one-sided formula like +\code{~Product + Year} for clustering), \code{cluster}, and \code{ssc} (small sample +correction). The \code{se} argument is accepted but deprecated in recent +\code{fixest} versions; use \code{vcov} instead. Other arguments include +\code{forceCovariance} and \code{keepBounded}. +See \code{\link[fixest:summary.fixest]{summary.fixest}} for details.} } \description{ Augment accepts a model object and a dataset and adds @@ -85,7 +89,7 @@ data, so you must provide it manually. augment.fixest only works for \code{\link[fixest:feols]{fixest::feols()}}, \code{\link[fixest:feglm]{fixest::feglm()}}, and \code{\link[fixest:femlm]{fixest::femlm()}} models. It does not work with results from -\code{\link[fixest:femlm]{fixest::fenegbin()}}, \code{\link[fixest:feNmlm]{fixest::feNmlm()}}, or \code{\link[fixest:feglm]{fixest::fepois()}}. +\code{\link[fixest:fenegbin]{fixest::fenegbin()}}, \code{\link[fixest:feNmlm]{fixest::feNmlm()}}, or \code{\link[fixest:fepois]{fixest::fepois()}}. } \examples{ \dontshow{if (rlang::is_installed("fixest") & !broom:::is_cran_check()) withAutoprint(\{ # examplesIf} @@ -108,7 +112,9 @@ augment(gravity, trade) tidy(gravity, conf.int = TRUE, cluster = c("Product", "Year")) -tidy(gravity, conf.int = TRUE, se = "threeway") +tidy(gravity, conf.int = TRUE, vcov = ~Product + Year) + +tidy(gravity, conf.int = TRUE, vcov = "hetero") # 2) or, feed tidy() a summary.fixest object that has already accepted # these arguments @@ -123,8 +129,8 @@ tidy(gravity_summ, conf.int = TRUE) \seealso{ \code{\link[=augment]{augment()}}, \code{\link[fixest:feglm]{fixest::feglm()}}, \code{\link[fixest:femlm]{fixest::femlm()}}, \code{\link[fixest:feols]{fixest::feols()}} -Other fixest tidiers: -\code{\link{tidy.fixest}()} +Other fixest tidiers: +\code{\link[=tidy.fixest]{tidy.fixest()}} } \concept{fixest tidiers} \value{ diff --git a/man/glance.fixest.Rd b/man/glance.fixest.Rd index 4c1251e3d..64d676816 100644 --- a/man/glance.fixest.Rd +++ b/man/glance.fixest.Rd @@ -9,10 +9,14 @@ \arguments{ \item{x}{A \code{fixest} object returned from any of the \code{fixest} estimators} -\item{...}{Additional arguments passed to \code{summary} and \code{confint}. Important -arguments are \code{se} and \code{cluster}. Other arguments are \code{dof}, \code{exact_dof}, -\code{forceCovariance}, and \code{keepBounded}. -See \code{\link[fixest:summary.fixest]{summary.fixest}}.} +\item{...}{Additional arguments passed to \code{\link[fixest:summary.fixest]{summary.fixest}} +and \code{\link[fixest:confint.fixest]{confint.fixest}}. Important arguments are +\code{vcov} (a VCOV type string like \code{"hetero"}, or a one-sided formula like +\code{~Product + Year} for clustering), \code{cluster}, and \code{ssc} (small sample +correction). The \code{se} argument is accepted but deprecated in recent +\code{fixest} versions; use \code{vcov} instead. Other arguments include +\code{forceCovariance} and \code{keepBounded}. +See \code{\link[fixest:summary.fixest]{summary.fixest}} for details.} } \description{ Glance accepts a model object and returns a \code{\link[tibble:tibble]{tibble::tibble()}} @@ -61,7 +65,9 @@ augment(gravity, trade) tidy(gravity, conf.int = TRUE, cluster = c("Product", "Year")) -tidy(gravity, conf.int = TRUE, se = "threeway") +tidy(gravity, conf.int = TRUE, vcov = ~Product + Year) + +tidy(gravity, conf.int = TRUE, vcov = "hetero") # 2) or, feed tidy() a summary.fixest object that has already accepted # these arguments diff --git a/man/tidy.fixest.Rd b/man/tidy.fixest.Rd index 4e2099e01..9fc4ad8fe 100644 --- a/man/tidy.fixest.Rd +++ b/man/tidy.fixest.Rd @@ -16,10 +16,14 @@ interval in the tidied output. Defaults to \code{FALSE}.} if \code{conf.int = TRUE}. Must be strictly greater than 0 and less than 1. Defaults to 0.95, which corresponds to a 95 percent confidence interval.} -\item{...}{Additional arguments passed to \code{summary} and \code{confint}. Important -arguments are \code{se} and \code{cluster}. Other arguments are \code{dof}, \code{exact_dof}, -\code{forceCovariance}, and \code{keepBounded}. -See \code{\link[fixest:summary.fixest]{summary.fixest}}.} +\item{...}{Additional arguments passed to \code{\link[fixest:summary.fixest]{summary.fixest}} +and \code{\link[fixest:confint.fixest]{confint.fixest}}. Important arguments are +\code{vcov} (a VCOV type string like \code{"hetero"}, or a one-sided formula like +\code{~Product + Year} for clustering), \code{cluster}, and \code{ssc} (small sample +correction). The \code{se} argument is accepted but deprecated in recent +\code{fixest} versions; use \code{vcov} instead. Other arguments include +\code{forceCovariance} and \code{keepBounded}. +See \code{\link[fixest:summary.fixest]{summary.fixest}} for details.} } \description{ Tidy summarizes information about the components of a model. @@ -67,7 +71,9 @@ augment(gravity, trade) tidy(gravity, conf.int = TRUE, cluster = c("Product", "Year")) -tidy(gravity, conf.int = TRUE, se = "threeway") +tidy(gravity, conf.int = TRUE, vcov = ~Product + Year) + +tidy(gravity, conf.int = TRUE, vcov = "hetero") # 2) or, feed tidy() a summary.fixest object that has already accepted # these arguments @@ -80,11 +86,11 @@ tidy(gravity_summ, conf.int = TRUE) \dontshow{\}) # examplesIf} } \seealso{ -\code{\link[=tidy]{tidy()}}, \code{\link[fixest:feglm]{fixest::feglm()}}, \code{\link[fixest:femlm]{fixest::fenegbin()}}, -\code{\link[fixest:feNmlm]{fixest::feNmlm()}}, \code{\link[fixest:femlm]{fixest::femlm()}}, \code{\link[fixest:feols]{fixest::feols()}}, \code{\link[fixest:feglm]{fixest::fepois()}} +\code{\link[=tidy]{tidy()}}, \code{\link[fixest:feglm]{fixest::feglm()}}, \code{\link[fixest:fenegbin]{fixest::fenegbin()}}, +\code{\link[fixest:feNmlm]{fixest::feNmlm()}}, \code{\link[fixest:femlm]{fixest::femlm()}}, \code{\link[fixest:feols]{fixest::feols()}}, \code{\link[fixest:fepois]{fixest::fepois()}} -Other fixest tidiers: -\code{\link{augment.fixest}()} +Other fixest tidiers: +\code{\link[=augment.fixest]{augment.fixest()}} } \concept{fixest tidiers} \value{ diff --git a/tests/testthat/test-fixest.R b/tests/testthat/test-fixest.R index 554770167..555134e39 100644 --- a/tests/testthat/test-fixest.R +++ b/tests/testthat/test-fixest.R @@ -48,6 +48,63 @@ test_that("tidy.fixest", { check_dims(td, 2, 5) }) +test_that("tidy.fixest with cluster argument", { + # Explicit multi-way clustering changes SEs vs default + td_default <- tidy(fit2) + td_cluster <- tidy(fit2, cluster = c("id", "v1")) + + expect_false(identical(td_default$std.error, td_cluster$std.error)) + expect_false(identical(td_default$p.value, td_cluster$p.value)) + check_tidy_output(td_cluster) + + # cluster + conf.int works + td_ci <- tidy(fit2, cluster = c("id", "v1"), conf.int = TRUE) + check_tidy_output(td_ci) + check_dims(td_ci, 1, 7) + expect_true(all(c("conf.low", "conf.high") %in% names(td_ci))) + + # Single cluster variable works + td_one <- tidy(fit2, cluster = "id") + check_tidy_output(td_one) + expect_false(identical(td_default$std.error, td_one$std.error)) + + # se = "threeway" documented in examples + td_threeway <- tidy(fit2, se = "threeway") + check_tidy_output(td_threeway) +}) + +test_that("tidy.fixest with vcov argument", { + # vcov = character scalar produces same result as deprecated se + td_vcov_hetero <- tidy(fit2, vcov = "hetero") + td_se_hetero <- tidy(fit2, se = "hetero") + check_tidy_output(td_vcov_hetero) + expect_identical(td_vcov_hetero$std.error, td_se_hetero$std.error) + + # vcov = "threeway" matches se = "threeway" + td_vcov_threeway <- tidy(fit2, vcov = "threeway") + td_se_threeway <- tidy(fit2, se = "threeway") + check_tidy_output(td_vcov_threeway) + expect_identical(td_vcov_threeway$std.error, td_se_threeway$std.error) + + # vcov formula syntax matches cluster argument + td_vcov_form <- tidy(fit2, vcov = ~id + v1) + td_cluster <- tidy(fit2, cluster = c("id", "v1")) + check_tidy_output(td_vcov_form) + expect_identical(td_vcov_form$std.error, td_cluster$std.error) + + # vcov formula with conf.int + td_vcov_ci <- tidy(fit2, vcov = ~id + v1, conf.int = TRUE) + check_tidy_output(td_vcov_ci) + check_dims(td_vcov_ci, 1, 7) + expect_true(all(c("conf.low", "conf.high") %in% names(td_vcov_ci))) + + # single-variable vcov formula + td_vcov_one <- tidy(fit2, vcov = ~id) + check_tidy_output(td_vcov_one) + td_default <- tidy(fit2) + expect_false(identical(td_default$std.error, td_vcov_one$std.error)) +}) + test_that("glance.fixest", { gl <- glance(fit) gl2 <- glance(fit2) @@ -165,6 +222,14 @@ test_that("tidiers work with model results or summary of model results", { expect_equal(glance(res_glm, se = "hetero"), glance(res_glm_summ)) expect_equal(augment(res_glm, df, se = "hetero"), augment(res_glm_summ, df)) + # Same equivalence holds for cluster argument + fit2_clust <- summary(fit2, cluster = c("id", "v1")) + expect_equal(tidy(fit2, cluster = c("id", "v1")), tidy(fit2_clust)) + expect_equal( + tidy(fit2, cluster = c("id", "v1"), conf.int = TRUE), + tidy(fit2_clust, conf.int = TRUE) + ) + # We rely on behavior from fixest where summary.fixest() doesn't change the # `se` or `dof` arguments if they've already been set. # Test that claim here. (Calling summary again does change other things, like