diff --git a/DESCRIPTION b/DESCRIPTION
index 8188bcd..c727bc2 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: ggfixest
Title: Dedicated ggplot2 methods for fixest objects
-Version: 0.0.3.9000
+Version: 0.0.3.9001
Authors@R:
c(person(given = "Grant",
family = "McDermott",
diff --git a/NAMESPACE b/NAMESPACE
index 4d73143..d52fb34 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -6,3 +6,5 @@ export(ggiplot)
export(iplot_data)
import(fixest)
import(ggplot2)
+importFrom(fixest,coefplot)
+importFrom(fixest,iplot)
diff --git a/NEWS.md b/NEWS.md
index 1adbcbd..23f211e 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,12 +1,19 @@
-# ggfixest 0.0.3.9000 (development version)
+# ggfixest 0.0.3.9001 (development version)
## New features
-- The `aggr_es` function now supports numeric sequences for aggregating
-specific period subsets, in addition to the existing keyword strings like "pre"
-or "post". This functionality passes through to the higher order functions that
-call `aggr_es` under the hood. (#33)
-
+- The `aggr_es` function now supports numeric sequences for aggregating a
+specific subset of periods, in addition to the existing keyword strings like
+"pre" or "post". This functionality also passes through to the higher order
+plotting functions that call `aggr_es` under the hood. For example,
+`ggiplot(est, aggr_eff = 6:8)`. (#33)
+- Users can now adjust standard errors for model objects on-the-fly at plot
+time, by passing an appropriate argument, e.g. `ggcoefplot(est, vcov = "hc1")`.
+These on-the-fly adjustments are done via `summary.fixest`, and so the effect is
+just the same as passing an adjusted object directly, e.g.
+`ggcoefplot(summary(est, vcov = "hc1"))`. However, it may prove more convenient
+for simultaneously adjusting a list of multiple models, e.g.
+`ggcoefplot(list(est1, est2, est3), vcov = "hc1")`. (#35)
# ggfixest 0.0.3
diff --git a/R/ggcoefplot.R b/R/ggcoefplot.R
index aab9a7a..b256a7d 100644
--- a/R/ggcoefplot.R
+++ b/R/ggcoefplot.R
@@ -55,6 +55,10 @@
#' channel. For example, we can make the CI band lighter with
#' `ci.fill.par = list(alpha = 0.2)` (the default alpha is 0.3).
#' * `dict` a dictionary for overriding coefficient names.
+#' * `vcov`, `cluster` or `se` as alternative options for adjusting the
+#' standard errors of the model object(s) on the fly. See `summary.fixest` for
+#' details. Written here in superseding order; `cluster` will only be
+#' considered if `vcov` is not null, etc.
#' @details These functions generally try to mimic the functionality and (where
#' appropriate) arguments of `fixest::coefplot` and `fixest::iplot` as
#' closely as possible. However, by leveraging the ggplot2 API and
diff --git a/R/ggiplot.R b/R/ggiplot.R
index 1e9dc38..73f2216 100644
--- a/R/ggiplot.R
+++ b/R/ggiplot.R
@@ -49,6 +49,10 @@ ggiplot = function(
ref.line.par = list(col = "black", lty = 2, lwd = 0.3)
if (!is.null(dots[["ref.line.par"]])) ref.line.par = utils::modifyList(ref.line.par, dots[["ref.line.par"]])
+ # VCOV adjustments (if any)
+ vcov = if (!is.null(dots[['vcov']])) dots[['vcov']] else NULL
+ cluster = if (!is.null(dots[['cluster']])) dots[['cluster']] else NULL
+ se = if (!is.null(dots[['se']])) dots[['se']] else NULL
# The next few blocks grab the underlying iplot/coefplot data, contingent on the
# object that was passed into the function (i.e. fixest, fixest_multi, or
@@ -58,11 +62,21 @@ ggiplot = function(
if (inherits(object, c("fixest", "fixest_multi"))) {
if (length(ci_level) == 1) {
- data = iplot_data_func(object, .ci_level = ci_level, .dict = dict, .aggr_es = aggr_eff, .keep = keep, .drop = drop, .group = group, .i.select = i.select)
+ data = iplot_data_func(
+ object,
+ .ci_level = ci_level, .dict = dict, .aggr_es = aggr_eff,
+ .keep = keep, .drop = drop, .group = group, .i.select = i.select,
+ .vcov = vcov, .cluster = cluster, .se = se
+ )
} else {
data = lapply(
ci_level,
- function(ci_l) iplot_data_func(object, .ci_level = ci_l, .dict = dict, .aggr_es = aggr_eff, .keep = keep, .drop = drop, .group = group, .i.select = i.select)
+ function(ci_l) iplot_data_func(
+ object,
+ .ci_level = ci_l, .dict = dict, .aggr_es = aggr_eff,
+ .keep = keep, .drop = drop, .group = group, .i.select = i.select,
+ .vcov = vcov, .cluster = cluster, .se = se
+ )
)
data = do.call("rbind", data)
}
@@ -82,13 +96,18 @@ ggiplot = function(
if (length(ci_level) == 1) {
data = lapply(
object, iplot_data_func,
- .ci_level = ci_level, .dict = dict, .aggr_es = aggr_eff, .group = group, .i.select = i.select
+ .ci_level = ci_level, .dict = dict, .aggr_es = aggr_eff,
+ .group = group, .i.select = i.select,
+ .vcov = vcov, .cluster = cluster, .se = se
)
} else {
data = lapply(ci_level, function(ci_l) {
- lapply(object, iplot_data_func,
- .ci_level = ci_l,
- .dict = dict, .aggr_es = aggr_eff, .group = group, .i.select = i.select
+ lapply(
+ object, iplot_data_func,
+ .ci_level = ci_l,
+ .dict = dict, .aggr_es = aggr_eff,
+ .group = group, .i.select = i.select,
+ .vcov = vcov, .cluster = cluster, .se = se
)
})
data = do.call(function(...) Map("rbind", ...), data)
diff --git a/R/iplot_data.R b/R/iplot_data.R
index d7117a0..a04d28a 100644
--- a/R/iplot_data.R
+++ b/R/iplot_data.R
@@ -42,6 +42,11 @@
#' aggregated mean treatment effects for some subset of the model should be
#' added as a column to the returned data frame. Passed to
#' `aggr_es(..., aggregation = "mean")`.
+#' @param .vcov,.cluster,.se Alternative options for adjusting the standard
+#' errors of the model object on the fly. See `summary.fixest` for details
+#' (although note that the "." period prefix should be ignored in the latter's
+#' argument documentation). Written here in superseding order; `.cluster` will
+#' only be considered if `.vcov` is not null, etc.
#' @details This function is a wrapper around
#' `fixest::iplot(..., only.params = TRUE)`, but with various checks and tweaks
#' to better facilitate plotting with `ggplot2` and handling of complex object
@@ -51,6 +56,7 @@
#' relative x-axis positions, and other aesthetic information needed to draw
#' a ggplot2 object.
#' @import ggplot2
+#' @importFrom fixest coefplot iplot
#' @export
#' @examples
#' library(fixest)
@@ -77,7 +83,10 @@ iplot_data = function(
.i.select = 1,
# .aggr_es = c("none", "post", "pre", "both"),
.aggr_es = NULL,
- .group = "auto"
+ .group = "auto",
+ .vcov = NULL,
+ .cluster = NULL,
+ .se = NULL
) {
# .aggr_es = match.arg(.aggr_es)
@@ -94,7 +103,16 @@ iplot_data = function(
.group = NULL
}
- p = fixest::coefplot(object, only.params = TRUE, ci_level = .ci_level, dict = .dict, keep = .keep, drop = .drop, internal.only.i = .internal.only.i, i.select = .i.select)
+ # Catch VCOV adjustments (if any)
+ if (!is.null(.vcov)) {
+ object = summary(object, vcov = .vcov)
+ } else if (!is.null(.cluster)) {
+ object = summary(object, cluster = .cluster)
+ } else if (!is.null(.se)) {
+ object = summary(object, se = .se)
+ }
+
+ p = coefplot(object, only.params = TRUE, ci_level = .ci_level, dict = .dict, keep = .keep, drop = .drop, internal.only.i = .internal.only.i, i.select = .i.select)
d = p$prms
if (inherits(object, "fixest_multi")) {
@@ -423,9 +441,18 @@ coefplot_data = function(
.dict = fixest::getFixest_dict(),
.internal.only.i = FALSE,
.i.select = 1,
- .aggr_es = "none"
+ .aggr_es = "none",
+ .vcov = NULL,
+ .cluster = NULL,
+ .se = NULL
) {
- iplot_data(object, .ci_level = .ci_level, .dict = .dict, .keep = .keep, .drop = .drop, .internal.only.i = .internal.only.i, .group = .group)
+ iplot_data(
+ object,
+ .ci_level = .ci_level, .dict = .dict,
+ .keep = .keep, .drop = .drop,
+ .internal.only.i = .internal.only.i, .group = .group,
+ .vcov = .vcov, .cluster = .cluster, .se = .se
+ )
}
diff --git a/README.Rmd b/README.Rmd
index d05114c..3bbfd8e 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -43,7 +43,7 @@ install.packages("ggfixest", repos = "https://grantmcdermott.r-universe.dev")
## Quickstart
-The [package website](http://grantmcdermott.com/ggfixest)
+The [package website](https://grantmcdermott.com/ggfixest/)
provides a number of examples in the help documentation. (Also available by
typing `?ggcoefplot` or `?ggiplot` in your R console.) But here are a few
quickstart examples to whet your appetite.
diff --git a/README.md b/README.md
index d1fea20..e060a65 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ install.packages("ggfixest", repos = "https://grantmcdermott.r-universe.dev")
## Quickstart
-The [package website](http://grantmcdermott.com/ggfixest) provides a
+The [package website](https://grantmcdermott.com/ggfixest/) provides a
number of examples in the help documentation. (Also available by typing
`?ggcoefplot` or `?ggiplot` in your R console.) But here are a few
quickstart examples to whet your appetite.
diff --git a/inst/tinytest/_tinysnapshot/ggcoefplot_did_iid.svg b/inst/tinytest/_tinysnapshot/ggcoefplot_did_iid.svg
new file mode 100644
index 0000000..6caf367
--- /dev/null
+++ b/inst/tinytest/_tinysnapshot/ggcoefplot_did_iid.svg
@@ -0,0 +1,86 @@
+
+
diff --git a/inst/tinytest/_tinysnapshot/ggiplot_multi_complex_kitchen_iid.svg b/inst/tinytest/_tinysnapshot/ggiplot_multi_complex_kitchen_iid.svg
new file mode 100644
index 0000000..b1a685e
--- /dev/null
+++ b/inst/tinytest/_tinysnapshot/ggiplot_multi_complex_kitchen_iid.svg
@@ -0,0 +1,349 @@
+
+
diff --git a/inst/tinytest/test_aggr_es.R b/inst/tinytest/test_aggr_es.R
index 0c7ab98..ffdeac0 100644
--- a/inst/tinytest/test_aggr_es.R
+++ b/inst/tinytest/test_aggr_es.R
@@ -80,7 +80,7 @@ aggr_rhs1_known = data.frame(
#
# tests ----
-tol = 1e-6
+tol = 1e-4
for (col in c("term", "estimate", "std.error", "statistic", "p.value", "s.value",
"conf.low", "conf.high")) {
diff --git a/inst/tinytest/test_ggcoefplot.R b/inst/tinytest/test_ggcoefplot.R
index 5a44068..b7a5583 100644
--- a/inst/tinytest/test_ggcoefplot.R
+++ b/inst/tinytest/test_ggcoefplot.R
@@ -56,3 +56,13 @@ data("base_did", package = "fixest")
est_did = feols(y ~ x1 + i(period, treat, 5) | id + period, base_did)
p_did = ggcoefplot(est_did)
expect_snapshot_plot(p_did, label = "ggcoefplot_did")
+
+
+#
+## vcov adjustment (passed through ...) ----
+
+p_did_iid_summ = ggcoefplot(summary(est_did, vcov = "iid")) # manual approach
+p_did_iid = ggcoefplot(est_did, vcov = "iid") # passed through "..."
+expect_snapshot_plot(p_did_iid_summ, label = "ggcoefplot_did_iid")
+expect_snapshot_plot(p_did_iid, label = "ggcoefplot_did_iid") # should be identical
+
diff --git a/inst/tinytest/test_ggiplot.R b/inst/tinytest/test_ggiplot.R
index 7d8cbfe..c0cd13f 100644
--- a/inst/tinytest/test_ggiplot.R
+++ b/inst/tinytest/test_ggiplot.R
@@ -153,3 +153,41 @@ p18 = ggiplot(
expect_snapshot_plot(p16, label = "ggiplot_multi_complex")
expect_snapshot_plot(p17, label = "ggiplot_multi_complex_mci")
expect_snapshot_plot(p18, label = "ggiplot_multi_complex_kitchen")
+
+# Last one(s): Check vcov adjustment of previous plot
+# Manual version, passing via summary first...
+p19a = ggiplot(
+ list(
+ 'TWFE' = summary(est_twfe_grp, vcov = "iid"),
+ 'Sun & Abraham (2020)' = summary(est_sa20_grp, vcov = "iid")
+ ),
+ main = 'Staggered treatment: Split mutli-sample',
+ ref.line = -1,
+ xlab = 'Time to treatment',
+ multi_style = 'facet',
+ geom_style = 'ribbon',
+ ci_level = c(.8, .95),
+ theme = theme_minimal() + theme(
+ text = element_text(family = 'HersheySans'),
+ plot.title = element_text(hjust = 0.5),
+ legend.position = 'none'
+ )
+)
+# Next, passing as a convenience string (via ...)
+p19b = ggiplot(
+ list('TWFE' = est_twfe_grp, 'Sun & Abraham (2020)' = est_sa20_grp),
+ vcov = "iid",
+ main = 'Staggered treatment: Split mutli-sample',
+ ref.line = -1,
+ xlab = 'Time to treatment',
+ multi_style = 'facet',
+ geom_style = 'ribbon',
+ ci_level = c(.8, .95),
+ theme = theme_minimal() + theme(
+ text = element_text(family = 'HersheySans'),
+ plot.title = element_text(hjust = 0.5),
+ legend.position = 'none'
+ )
+)
+expect_snapshot_plot(p19a, label = "ggiplot_multi_complex_kitchen_iid")
+expect_snapshot_plot(p19b, label = "ggiplot_multi_complex_kitchen_iid")
diff --git a/man/ggcoefplot.Rd b/man/ggcoefplot.Rd
index 23a513b..5009eb7 100644
--- a/man/ggcoefplot.Rd
+++ b/man/ggcoefplot.Rd
@@ -70,6 +70,10 @@ are requested for the default pointrange style). The default value is 0.2.
channel. For example, we can make the CI band lighter with
\code{ci.fill.par = list(alpha = 0.2)} (the default alpha is 0.3).
\item \code{dict} a dictionary for overriding coefficient names.
+\item \code{vcov}, \code{cluster} or \code{se} as alternative options for adjusting the
+standard errors of the model object(s) on the fly. See \code{summary.fixest} for
+details. Written here in superseding order; \code{cluster} will only be
+considered if \code{vcov} is not null, etc.
}}
\item{aggr_eff}{A keyword string or numeric sequence, indicating whether
diff --git a/man/iplot_data.Rd b/man/iplot_data.Rd
index e967196..3e2a6e4 100644
--- a/man/iplot_data.Rd
+++ b/man/iplot_data.Rd
@@ -14,7 +14,10 @@ iplot_data(
.internal.only.i = TRUE,
.i.select = 1,
.aggr_es = NULL,
- .group = "auto"
+ .group = "auto",
+ .vcov = NULL,
+ .cluster = NULL,
+ .se = NULL
)
coefplot_data(
@@ -26,7 +29,10 @@ coefplot_data(
.dict = fixest::getFixest_dict(),
.internal.only.i = FALSE,
.i.select = 1,
- .aggr_es = "none"
+ .aggr_es = "none",
+ .vcov = NULL,
+ .cluster = NULL,
+ .se = NULL
)
}
\arguments{
@@ -78,6 +84,12 @@ valid uses:
group=list(group_name=c(\"var_start\", \"var_end\")),
group=list(group_name=1:2))
See the Details section of \code{?fixest::coefplot} for more.}
+
+\item{.vcov, .cluster, .se}{Alternative options for adjusting the standard
+errors of the model object on the fly. See \code{summary.fixest} for details
+(although note that the "." period prefix should be ignored in the latter's
+argument documentation). Written here in superseding order; \code{.cluster} will
+only be considered if \code{.vcov} is not null, etc.}
}
\value{
A data frame consisting of estimate values, confidence intervals,