Last active
December 24, 2021 08:12
-
-
Save grantmcdermott/db8501b0281813792bf78393fef98481 to your computer and use it in GitHub Desktop.
A ggplot2 implementation fixest::iplot()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Examples, borrowing from the intro vignette | |
library(fixest) | |
library(ggplot2) | |
## Source the function | |
source('https://gist.githubusercontent.com/grantmcdermott/db8501b0281813792bf78393fef98481/raw/ggiplot.R') | |
## Vanilla TWFE | |
est_did = feols(y ~ x1 + i(period, treat, 5) | id + period, base_did) | |
ggiplot(est_did) | |
ggiplot(est_did, geom_style = 'errorbar', pt.join = TRUE) ## Another option | |
ggiplot(est_did, geom_style = 'ribbon') ## And another | |
## Staggered rollout | |
res_twfe = feols(y ~ x1 + i(time_to_treatment, treated, ref = c(-1, -1000)) | id + year, base_stagg) | |
res_sa20 = feols(y ~ x1 + sunab(year_treated, year) | id + year, base_stagg) | |
ggiplot(list('TWFE' = res_twfe, 'Sun & Abraham (2020)' = res_sa20), ref.line = -1) | |
ggiplot(list('TWFE' = res_twfe, 'Sun & Abraham (2020)' = res_sa20), ref.line = -1, multi_style = 'facet') | |
## A fancier example showing off some of ggiplot's extra features (grouped facetting, etc.) | |
base_stagg_grp = base_stagg | |
base_stagg_grp$grp = ifelse(base_stagg_grp$id %% 2 == 0, 'Evens', 'Odds') | |
res_twfe_grp = feols(y ~ x1 + i(time_to_treatment, treated, ref = c(-1, -1000)) | id + year, base_stagg_grp, split = ~ grp) | |
res_sa20_grp = feols(y ~ x1 + sunab(year_treated, year) | id + year, base_stagg_grp, split = ~ grp) | |
ggiplot(list('TWFE' = res_twfe_grp, 'Sun & Abraham (2020)' = res_sa20_grp), | |
ref.line = -1, | |
main = 'Super duper plot', | |
xlab = 'Time to treatment', | |
multi_style = 'facet', | |
geom_style = 'ribbon', | |
theme = theme_minimal() + | |
theme(text = element_text(family = 'HersheySans'), | |
plot.title = element_text(hjust = 0.5), | |
legend.position = 'none')) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Note: requires that both fixest and ggplot2 are installed and loaded | |
# library(fixest) | |
# library(ggplot2 | |
ggiplot = | |
function(models, | |
geom_style = c('pointrange', 'errorbar', 'ribbon'), | |
multi_style = c('dodge', 'facet'), | |
dict = getFixest_dict(), | |
pt.col = NULL, | |
pt.join = FALSE, | |
facet_args = NULL, | |
zero = TRUE, | |
zero.par = list(col = 'black', lty = 1, lwd = 0.3), | |
ref.line = 'auto', | |
ref.line.par = list(col = 'black', lty = 2, lwd = 0.3), | |
ci_level = 0.95, | |
xlab = NULL, | |
ylab = NULL, | |
main = NULL, | |
theme = NULL) { | |
geom_style = match.arg(geom_style) | |
multi_style = match.arg(multi_style) | |
iplot_data = function(model) { | |
p = iplot(model, only.params = TRUE, ci_level = ci_level, dict = dict) | |
d = p$prms | |
if (class(model)=='fixest_multi') { | |
d$x = rep(p$labels, each = length(model)) | |
d$id = factor(d$id, labels = names(model)) | |
d$dep_var = unique(as.character(lapply(model, function(m) paste(m$call$fml[[2]])))) | |
} else { | |
if (class(p$labels)=='integer') p$labels = as.numeric(p$labels) ## catch | |
if (!identical(d$x, p$labels)) d$x = factor(d$x, labels = p$labels) | |
d$dep_var = paste(model$call$fml[[2]]) | |
} | |
return(d) | |
} | |
if (class(models) %in% c('fixest', 'fixest_multi')) { | |
data = iplot_data(models) | |
data$group = data$id | |
if (class(models)=='fixest_multi') { | |
fct_vars = ~ id | |
} else { | |
multi_style = 'none' | |
} | |
} | |
if (class(models)=='list') { | |
data = lapply(models, iplot_data) | |
nms = names(models) | |
if (is.null(nms)) { | |
if ('fixest' %in% unlist(lapply(models, class))) { | |
nms = paste('Model', seq_along(models)) | |
} else { | |
nms = paste('Group', seq_along(models)) | |
} | |
} | |
nms = as.character(mapply(rep, nms, sapply(data, nrow))) | |
data = do.call('rbind', data) | |
data$group = nms | |
rownames(data) = NULL | |
if (length(unique(data$id))==1) { | |
fct_vars = ~ group | |
} else { | |
fct_vars = ~ id + group | |
} | |
if (is.null(facet_args$ncol)) facet_args$ncol = length(unique(data$group)) | |
} | |
if (is.null(xlab)) xlab = sub('::.*', '', data$estimate_names_raw[1]) | |
if (!is.null(ref.line)) { | |
if (ref.line=='auto') ref.line = data$x[which(data$is_ref)[1]] | |
} | |
if (is.null(ylab)) ylab = paste0('Estimate and ', ci_level*100, '% Conf. Int.') | |
if (is.null(main)) main = paste0('Effect on ', unique(data$dep_var)) | |
if (multi_style=='facet') { | |
facet_defaults = formals(facet_wrap) | |
facet_defaults$facets = fct_vars | |
if (!is.null(facet_args)) { | |
facet_rep_ind = match(names(facet_args), names(facet_defaults)) | |
facet_defaults = replace(facet_defaults, facet_rep_ind, facet_args) | |
} | |
facet_args = facet_defaults | |
} | |
if (multi_style=='none') { | |
if (is.null(pt.col)) { | |
gg = ggplot(data, aes(x, estimate, ymin=ci_low, ymax=ci_high)) | |
} else { | |
gg = ggplot(data, aes(x, estimate, ymin=ci_low, ymax=ci_high, | |
col=pt.col, fill=pt.col)) | |
} | |
} else { | |
gg = ggplot(data, aes(x, estimate, ymin=ci_low, ymax=ci_high, | |
fill=group, col=group, shape = group)) | |
} | |
gg = | |
gg + | |
geom_vline(xintercept=ref.line, col=ref.line.par$col, lwd=ref.line.par$lwd, lty=ref.line.par$lty) + | |
{ | |
if (zero) { | |
geom_hline(yintercept=0, col=zero.par$col, lwd=zero.par$lwd, lty=zero.par$lty) | |
} | |
} + | |
{ | |
if (geom_style %in% c('pointrange', 'errorbar') & multi_style %in% c('none', 'facet')) { | |
if (geom_style=='pointrange') { | |
geom_pointrange() | |
} else { | |
list(geom_point(size = 2.5), | |
geom_errorbar(width = 0.1)) | |
} | |
} | |
} + | |
{ | |
if (geom_style %in% c('pointrange', 'errorbar') & multi_style=='dodge') { | |
if (geom_style=='pointrange') { | |
geom_pointrange(position = position_dodge2(width = 0.5, padding = 0.5)) | |
} else { | |
list(geom_point(size = 2.5, position = position_dodge2(width = 0.5, padding = 0.5)), | |
geom_errorbar(width = 0.2, position = position_dodge(width = 0.5))) | |
} | |
} | |
} + | |
{ | |
if (geom_style=='ribbon') { | |
list(geom_ribbon(alpha = 0.5, col = NA), geom_point()) | |
} | |
} + | |
{ | |
if (geom_style=='ribbon' || (pt.join && multi_style!='dodge')) { | |
geom_line() | |
} | |
} + | |
{ | |
if (geom_style=='ribbon' || (pt.join && multi_style=='dodge')) { | |
geom_line(position = position_dodge2(width = 0.5, padding = 0.5)) | |
} | |
} + | |
scale_color_brewer(palette = 'Set2', aesthetics = c('colour', 'fill')) + | |
{ | |
if (!is.null(pt.col)) { | |
if (multi_style=='none') { | |
list(scale_colour_manual(values = pt.col, aesthetics = c('colour', 'fill')), | |
guides(col = 'none', fill = 'none')) | |
} else { | |
scale_colour_manual(values = pt.col, aesthetics = c('colour', 'fill')) | |
} | |
} | |
} + | |
labs(x = xlab, y = ylab, title = main) + | |
{ | |
if (multi_style=='facet') facet_wrap(facets = facet_args$facets, | |
nrow = facet_args$nrow, | |
ncol = facet_args$ncol, | |
scales = facet_args$scales, | |
shrink = facet_args$shrink, | |
labeller = facet_args$labeller, | |
as.table = facet_args$as.table, | |
switch = facet_args$switch, | |
drop = facet_args$drop, | |
dir = facet_args$dir, | |
strip.position = facet_args$strip.position) | |
} | |
if (!is.null(theme)) { | |
gg = | |
gg + | |
theme | |
} else { | |
gg = | |
gg + | |
theme_linedraw() + | |
theme(#panel.grid.minor = element_blank(), | |
plot.title = element_text(hjust = 0.5), | |
legend.position = 'bottom', legend.title = element_blank()) | |
} | |
return(gg) | |
} |
Glad you like it. Fwiw I've now bundled it into an actual package. http://grantmcdermott.com/ggiplot/
I haven't added coefplot functionality yet, though. In part because I haven't had time. And in other part because I think modelsummary::modelplot has this pretty well covered already. Check it out: https://vincentarelbundock.github.io/modelsummary/articles/modelplot.html
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just stumbled upon this page. This is great! Do you know if anyone has created a ggplot2 version of
fixest::coefplot()
?