├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ └── pkgdown.yaml ├── .gitignore ├── CRAN-SUBMISSION ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── aggr_es.R ├── ggcoefplot.R ├── ggiplot.R ├── iplot_data.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── ggfixest.Rproj ├── inst └── tinytest │ ├── _tinysnapshot │ ├── ggcoefplot_did.svg │ ├── ggcoefplot_did_iid.svg │ ├── ggcoefplot_group_names_prefix.svg │ ├── ggcoefplot_group_nonames.svg │ ├── ggcoefplot_group_none.svg │ ├── ggcoefplot_groupnames.svg │ ├── ggcoefplot_interactions.svg │ ├── ggcoefplot_interactions_multici.svg │ ├── ggcoefplot_multi.svg │ ├── ggcoefplot_multi_facet.svg │ ├── ggcoefplot_simple.svg │ ├── ggiplot_list.svg │ ├── ggiplot_multi_complex.svg │ ├── ggiplot_multi_complex_kitchen.svg │ ├── ggiplot_multi_complex_kitchen_iid.svg │ ├── ggiplot_multi_complex_mci.svg │ ├── ggiplot_multi_csw.svg │ ├── ggiplot_multi_csw_facet.svg │ ├── ggiplot_multi_facet.svg │ ├── ggiplot_multi_facet_ribbon.svg │ ├── ggiplot_multi_lhs.svg │ ├── ggiplot_multi_lhs_csw.svg │ ├── ggiplot_multi_lhs_csw_facet.svg │ ├── ggiplot_multi_lhs_facet.svg │ ├── ggiplot_multi_lhs_facet_ribbon.svg │ ├── ggiplot_multi_single.svg │ ├── ggiplot_multi_single_kitchen.svg │ ├── ggiplot_multi_single_kitchen_ribbon.svg │ ├── ggiplot_multi_single_ribbon.svg │ ├── ggiplot_multi_single_unnamed.svg │ ├── ggiplot_multi_sw_pt_join1.svg │ ├── ggiplot_multi_sw_pt_join2.svg │ ├── ggiplot_simple.svg │ ├── ggiplot_simple_errorbar.svg │ ├── ggiplot_simple_mci.svg │ ├── ggiplot_simple_mci_ribbon.svg │ ├── ggiplot_simple_ribbon.svg │ ├── ggiplot_stagg_mci.svg │ └── ggiplot_stagg_mci_ribbon.svg │ ├── test_aggr_es.R │ ├── test_fixest_multi.R │ ├── test_ggcoefplot.R │ ├── test_ggiplot.R │ ├── test_iplot_data.R │ ├── test_nthreads.R │ └── tinysnapshot_helpers.R ├── man ├── aggr_es.Rd ├── figures │ ├── README-coefplot1-1.png │ ├── README-coefplot2-1.png │ ├── README-es1-1.png │ ├── README-es2-1.png │ └── README-es3-1.png ├── ggcoefplot.Rd └── iplot_data.Rd ├── tests └── tinytest.R └── vignettes ├── .gitignore └── ggiplot.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^ggfixest\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^README\.html$ 5 | ^LICENSE\.md$ 6 | ^_pkgdown\.yml$ 7 | ^docs$ 8 | ^pkgdown$ 9 | ^\.github$ 10 | ^TESTING$ 11 | ^.vscode$ 12 | ^cran-comments.md 13 | ^CRAN-SUBMISSION$ 14 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: ci 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | # - {os: macos-latest, r: 'release'} 22 | # - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | # - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | with: 49 | upload-snapshots: true 50 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | steps: 23 | - uses: actions/checkout@v3 24 | 25 | - uses: r-lib/actions/setup-pandoc@v2 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | pak-version: devel 34 | extra-packages: any::pkgdown, local::. 35 | needs: website 36 | 37 | - name: Build site 38 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 39 | shell: Rscript {0} 40 | 41 | - name: Deploy to GitHub pages 🚀 42 | if: github.event_name != 'pull_request' 43 | uses: JamesIves/github-pages-deploy-action@v4.4.1 44 | with: 45 | clean: false 46 | branch: gh-pages 47 | folder: docs 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | .Renviron 7 | .vscode 8 | docs 9 | inst/doc 10 | inst/tinytest/_tinysnapshot_review 11 | inst/tinytest/_tinysnapshot_MacOS 12 | README.html 13 | TESTING 14 | -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 0.3.0 2 | Date: 2025-05-14 02:08:14 UTC 3 | SHA: 1806f1d281d6898a1e552a9a33ce5947ec919bba 4 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ggfixest 2 | Title: Dedicated 'ggplot2' Methods for 'fixest' Objects 3 | Version: 0.3.0 4 | Date: 2025-05-13 5 | Authors@R: c( 6 | person( 7 | given = "Grant", 8 | family = "McDermott", 9 | role = c("aut", "cre"), 10 | email = "gmcd@amazon.com", 11 | comment = c(ORCID = "0000-0001-7883-8573") 12 | ), 13 | person( 14 | given = "Laurent", 15 | family = "Berge", 16 | role = "ctb", 17 | email = "laurent.berge@u-bordeaux.fr" 18 | ), 19 | person( 20 | given = "Teun", 21 | family = "van den Brand", 22 | role = "ctb", 23 | comment = c(ORCID = "0000-0002-9335-7468") 24 | ) 25 | ) 26 | Description: Provides 'ggplot2' equivalents of fixest::coefplot() and fixest::iplot(), 27 | for producing nice coefficient plots and interaction plots. Enables some 28 | additional functionality and convenience features, including grouped 29 | multi-'fixest' object faceting and programmatic updates to existing plots 30 | (e.g., themes and aesthetics). 31 | License: GPL-3 32 | Encoding: UTF-8 33 | LazyData: true 34 | Roxygen: list(markdown = TRUE) 35 | RoxygenNote: 7.3.2 36 | URL: https://grantmcdermott.com/ggfixest/ 37 | BugReports: https://github.com/grantmcdermott/ggfixest/issues 38 | Depends: 39 | ggplot2 (>= 2.2.0), 40 | fixest (>= 0.11.2) 41 | Imports: 42 | dreamerr, 43 | scales, 44 | marginaleffects (>= 0.10.0), 45 | stats, 46 | utils, 47 | legendry (>= 0.2) 48 | Suggests: 49 | knitr, 50 | rmarkdown, 51 | tinytest (>= 1.4.1), 52 | tinysnapshot (>= 0.0.3), 53 | magick, 54 | rsvg, 55 | svglite (>= 2.2.0), 56 | fontquiver, 57 | data.table 58 | VignetteBuilder: knitr 59 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(aggr_es) 4 | export(ggcoefplot) 5 | export(ggiplot) 6 | export(iplot_data) 7 | import(fixest) 8 | import(ggplot2) 9 | importFrom(fixest,coefplot) 10 | importFrom(fixest,iplot) 11 | importFrom(marginaleffects,hypotheses) 12 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # ggfixest 0.3.0 2 | 3 | ### New features 4 | 5 | - The new `aggr_es(..., period = "diff")` convenience keyword argument allows 6 | users to estimate the difference between the (mean) post- and pre-treatment 7 | periods. Thanks to @FBrunamonti for the suggestion. (#52). 8 | 9 | ### Internals 10 | 11 | - Bump `svglite` dependency version and update test snapshots. (#51) 12 | 13 | # ggfixest 0.2.0 14 | 15 | ### Bug fixes 16 | 17 | - Fix missing "hypothesis" attribute for `aggr_es` objects. (#43) 18 | - Fix dodge misalignment between points and lines with multi fixest objects. (#44) 19 | 20 | ### Internals 21 | 22 | - Replace `ggh4x` dependency with `legendry`. (#41 @teunbrand) 23 | 24 | ### Misc 25 | 26 | - Minor website and documentation improvements. 27 | 28 | # ggfixest 0.1.0 29 | 30 | First CRAN release! 31 | 32 | ### New features 33 | 34 | - The `aggr_es` function now supports numeric sequences for aggregating a 35 | specific subset of periods, in addition to the existing keyword strings like 36 | "pre" or "post". This functionality also passes through to the higher order 37 | plotting functions that call `aggr_es` under the hood. For example, 38 | `ggiplot(est, aggr_eff = 6:8)`. (#33) 39 | - Users can now adjust standard errors for model objects on-the-fly at plot 40 | time, by passing an appropriate argument, e.g. `ggcoefplot(est, vcov = "hc1")`. 41 | These on-the-fly adjustments are done via `summary.fixest`, and so the effect is 42 | just the same as passing an adjusted object directly, e.g. 43 | `ggcoefplot(summary(est, vcov = "hc1"))`. However, it may prove more convenient 44 | for simultaneously adjusting a list of multiple models, e.g. 45 | `ggcoefplot(list(est1, est2, est3), vcov = "hc1")`. (#35) 46 | 47 | # ggfixest 0.0.3 48 | 49 | ### Breaking change 50 | 51 | - The package name has been changed to **ggfixest** (#29). 52 | 53 | # ggiplot 0.0.2 54 | 55 | ### New features 56 | 57 | - Support for `ggcoefplot`, a ggplot equivalent of `coefplot` (#28). 58 | - Support `pt.size` argument for controlling the size of point markers (#27). 59 | Thanks @jcvdav. 60 | - Support `keep` and `drop` arguments for subsetting coefficients (#22). 61 | 62 | ### Bug fixes and breaking changes 63 | 64 | - Fix naming mismatch in multiple estimation with different time periods (#10). 65 | Thanks @brockmwilson. 66 | - Slight tweak to default theme, which now uses dotted grid lines to more 67 | closely match `iplot()` (#e5cf0b0). 68 | - Correctly parse formula-transformed dependent variable names, e.g. `log(y` 69 | (#20). 70 | - The confidence intervals for some figures may be slightly wider due to 71 | upstream changes in fixest (#25; see also 72 | https://github.com/lrberge/fixest/pull/408). 73 | 74 | ### Internals 75 | 76 | - Add a (visual) test suite (#12 with several increments thereafter). Thanks to 77 | @vincentarelbundock for ~~tinyviztest~~ 78 | [tinysnapshot](https://github.com/vincentarelbundock/tinysnapshot)! 79 | - Switch to `marginaleffects::hypotheses()` internally for `aggr_es()` to match 80 | the upstream changes in **marginaleffects**. 81 | - Simplify multi_fixest object parsing (#19). 82 | - Minor documentation improvements. 83 | 84 | # ggiplot 0.0.1 85 | 86 | * Tweaks to plot output, including integer breaks on x-axis (where appropriate) 87 | and allow additional user-level control (e.g. CI alpha or width levels) 88 | * Support multiple confidence levels (#2, #5) 89 | * Support multiple LHS variables (#1) 90 | * Added a `NEWS.md` file to track changes to the package. 91 | -------------------------------------------------------------------------------- /R/aggr_es.R: -------------------------------------------------------------------------------- 1 | #' @title Aggregates event-study treatment effects. 2 | #' 3 | #' @md 4 | #' @description Aggregates post- (and/or pre-) treatment effects of an 5 | #' "event-study" estimation, also known as a dynamic difference-in-differences 6 | #' (DDiD) model. The event-study should have been estimated using the `fixest` 7 | #' package, which provides a specialised `i()` operator for this class 8 | #' of models. By default, the function will return the average post-treatment 9 | #' effect (i.e., across multiple periods). However, it can also return the 10 | #' cumulative post-treatment effect and can be used to aggregate pre-treatment 11 | #' effects too. 12 | #' @param object A model object of class `fixest`, where the `i()` operator has 13 | #' been used to facilitate an "event-study" DiD design. See Examples. 14 | #' @param period Keyword string or numeric sequence. Which group of periods 15 | #' are we aggregating? Accepts the following convenience strings: `"post"` (the 16 | #' default), `"pre"`, `"both"`, or `"diff` (for the difference between the post 17 | #' and pre periods). Alternatively, can also be a numeric sequence that 18 | #' designates an explicit subset of periods in the data (e.g. `6:8`). 19 | #' @param rhs Numeric. The null hypothesis value. Defaults to 0. 20 | #' @param aggregation Character string. The aggregation type. Either `"mean"` 21 | #' (the default) or `"cumulative"`. 22 | #' @param abbr_term Logical. Should the leading "term" column of the return 23 | #' data frame be abbreviated? The default is `TRUE`. If `FALSE`, then the term 24 | #' column will retain the full hypothesis test string as per usual with 25 | #' [`marginaleffects::hypotheses()`]. Note that this information is retained as 26 | #' an attribute of the return object, regardless. 27 | #' @param ... Additional arguments passed to [`marginaleffects::hypotheses()`]. 28 | #' @seealso [marginaleffects::hypotheses()], which this function is ultimately a 29 | #' convenience wrapper around. 30 | #' @return A "tidy" data frame of aggregated (pre and/or post) treatment 31 | #' effects, plus inferential information about standard errors, confidence 32 | #' intervals, etc. Potentially useful information about the underlying 33 | #' hypothesis test is also provided as an attribute. See Examples. 34 | #' @importFrom marginaleffects hypotheses 35 | #' @export 36 | #' @examples 37 | #' library(ggfixest) ## Will load fixest too 38 | #' 39 | #' est = feols(y ~ x1 + i(period, treat, 5) | id + period, base_did) 40 | #' 41 | #' # Default hypothesis test is a null mean post-treatment effect 42 | #' (post_mean = aggr_es(est)) 43 | #' 44 | #' # The underlying hypothesis is saved as an attribute 45 | #' attr(post_mean, "hypothesis") 46 | #' 47 | #' # Other hypothesis and aggregation options 48 | #' aggr_es(est, period = "pre") # pre period instead of post 49 | #' aggr_es(est, period = "both") # pre & post periods separately 50 | #' aggr_es(est, period = "diff") # post vs pre difference 51 | #' aggr_es(est, period = 6:8) # specific subset of periods 52 | #' aggr_es(est, period = "pre", rhs = -1) # pre period with H0 value of 1 53 | #' aggr_es(est, aggregation = "cumulative") # cumulative instead of mean effects 54 | #' # Etc. 55 | #' 56 | aggr_es = function(object, 57 | period = c("post", "pre", "both", "diff"), 58 | rhs = 0, 59 | aggregation = c("mean", "cumulative"), 60 | abbr_term = TRUE, 61 | ...) { 62 | aggregation = match.arg(aggregation) 63 | # if (!(any(period %in% c("post", "pre", "both")) || is.numeric(period))) { 64 | # stop('The `period` argument must be one of c("post", "pre", "both"), or a numeric sequence.') 65 | # } 66 | if (!is.numeric(period)) { 67 | period = match.arg(period) 68 | } 69 | 70 | fixest_obj = inherits(object, "fixest") 71 | if (!fixest_obj) stop("Please provide a valid fixest object.") 72 | mm = object$model_matrix_info[[1]] 73 | if (is.null(mm)) { 74 | stop( 75 | "This function only works with fixest objects that were ", 76 | "created with the i() operator. See `?fixest::i()`." 77 | ) 78 | } 79 | coefs = mm$coef_names_full 80 | ref_id = mm$ref_id[1] 81 | ## Store our periods in a list to make the below lapply call easier 82 | if (is.numeric(period)) { 83 | if (!all(period %in% mm$items)) { 84 | stop( 85 | '\nSupplied period sequence does not match periods in model object.', 86 | '\nUser-supplied periods: ', period, 87 | 'Model periods: ', mm$items 88 | ) 89 | } 90 | if (any(period %in% mm$ref)) { 91 | warning("\nThe reference period, ", mm$ref, ", cannot be included in the aggregation and will be dropped.") 92 | period = setdiff(period, mm$ref) 93 | } 94 | idx = list(match(period, mm$items)) 95 | names(idx) = paste("periods", paste(range(period), collapse=":")) 96 | } else if (period == "post") { 97 | idx = list("post" = (ref_id + 1):length(coefs)) 98 | } else if (period == "pre") { 99 | idx = list("pre" = 1:(ref_id - 1)) # ref period is dropped from the model 100 | } else if (period %in% c("both", "diff")) { 101 | idx = list("pre" = 1:(ref_id - 1), "post" = (ref_id + 1):length(coefs)) 102 | if (period == "diff") { 103 | idx = rev(idx) # we want post - pre 104 | rhs = NULL 105 | } 106 | } 107 | 108 | hstring = sapply( 109 | idx, 110 | function(i) gen_hstring( 111 | coefs, 112 | periods = i, 113 | aggregation = aggregation, 114 | rhs = rhs 115 | ) 116 | ) 117 | if (!is.numeric(period) && period == "diff") hstring = paste(hstring, collapse = " = ") 118 | 119 | res = hypotheses(object, hypothesis = hstring, ...) 120 | if (abbr_term) { 121 | if (is.numeric(period)) { 122 | res$term = paste0(names(idx), " (", aggregation, ")") 123 | } else if (period == "diff") { 124 | res$term = "difference (post vs pre mean)" 125 | } else { 126 | res$term = paste0(names(idx), "-treatment (", aggregation, ")") 127 | } 128 | } 129 | 130 | ## Remove "hypothesis" column and rather save as an attribute 131 | res$hypothesis = NULL 132 | attr(res, "hypothesis") = hstring 133 | 134 | return(res) 135 | } 136 | 137 | gen_hstring = function(coefs, periods = NULL, aggregation = "mean", rhs = 0) { 138 | if (is.null(periods)) periods = 1:length(coefs) 139 | coefs = coefs[periods] 140 | hstring = paste(paste0("`", coefs, "`"), collapse = " + ") 141 | if (aggregation == "mean") hstring = paste0("(", hstring, ")/", length(coefs)) 142 | if (!is.null(rhs)) hstring = paste(hstring, "=", rhs) 143 | return(hstring) 144 | } 145 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | oxford = function(some_vec) { 3 | if (length(some_vec)>2) { 4 | paste0("[", paste(utils::head(some_vec, -1), collapse = ", "), ", & ", utils::tail(some_vec, 1), "]") 5 | } else if (length(some_vec)==2) { 6 | paste0("[", some_vec[1], " & ", some_vec[2], "]") 7 | } else { 8 | paste(some_vec) 9 | } 10 | } 11 | 12 | ## 13 | ## Unexported functions borrowed from fixest 14 | 15 | #' @keywords internal 16 | escape_regex = function(x){ 17 | # escape special characters in regular expressions => to make it as "fixed" 18 | 19 | res = gsub("((?<=[^\\\\])|(?<=^))(\\$|\\.|\\+|\\(|\\)|\\[|\\]|\\?|\\^)", "\\\\\\2", x, perl = TRUE) 20 | res 21 | } 22 | 23 | 24 | #' @keywords internal 25 | dict_apply = function(x, dict = NULL){ 26 | 27 | dreamerr::check_arg(dict, "NULL named character vector no na", .message = "The argument `dict` must be a dictionnary, ie a named vector (eg dict=c(\"old_name\"=\"new_name\")") 28 | 29 | if(missing(dict) || length(dict) == 0){ 30 | return(x) 31 | } 32 | 33 | # We make the dictionary names space agnostic, adds a handful of us only 34 | if(any(grepl(" ", x, fixed = TRUE))){ 35 | x_tmp = gsub(" ", "", x, fixed = TRUE) 36 | } else { 37 | x_tmp = x 38 | } 39 | 40 | if(any(grepl(" ", names(dict), fixed = TRUE))){ 41 | names(dict) = gsub(" ", "", names(dict), fixed = TRUE) 42 | if(anyDuplicated(names(dict))){ 43 | dup = duplicated(names(dict)) 44 | stop("The dictionary `dict` contains several items with the same names, it concerns ", 45 | dreamerr::enumerate_items(names(dict)[dup]), " (note that spaces are ignored).") 46 | } 47 | } 48 | 49 | who = x_tmp %in% names(dict) 50 | x[who] = dict[as.character(x_tmp[who])] 51 | x 52 | } 53 | 54 | 55 | #' @keywords internal 56 | replace_and_make_callable = function(text, varlist, text_as_expr = FALSE){ 57 | # used to make a character string callable and substitutes variables inside text with the variables 58 | # in varlist 59 | # ex: "Interacted with __var__" becomes "Interacted with x_beta" 60 | # or: "&paste(\"Interacted with \", x[beta])" 61 | 62 | if(length(text) > 1) stop("Internal problem: length of text should not be greater than 1.") 63 | 64 | text_split = strsplit(paste0(text, " "), "__")[[1]] 65 | 66 | if(length(text_split) < 3){ 67 | # Nothing to be done! 68 | return(text) 69 | } else { 70 | # We need to replace the variables 71 | is_var = seq_along(text_split) %% 2 == 0 72 | my_variables = text_split[is_var] 73 | 74 | if(length(varlist) == 0 || any(!my_variables %in% names(varlist))){ 75 | info = "No special variable is available for this estimation." 76 | if(length(varlist) > 0){ 77 | info = paste0("In this estimation, the only special variable", dreamerr::enumerate_items(paste0("__", names(varlist), "__"), "s.is.start"), ". ") 78 | } 79 | 80 | # warning(info, enumerate_items(paste0("__", setdiff(my_variables, names(varlist)), "__"), "is"), " not valid, thus ignored.", call. = FALSE) 81 | 82 | return("") 83 | 84 | not_var = !my_variables %in% names(varlist) 85 | is_var[is_var][not_var] = FALSE 86 | my_variables = intersect(my_variables, names(varlist)) 87 | if(length(my_variables) == 0){ 88 | return(text) 89 | } 90 | } 91 | 92 | my_variables_values = varlist[my_variables] 93 | 94 | if(any(lengths(varlist[my_variables]) > 1)){ 95 | qui = which(lengths(varlist[my_variables]) > 1)[1] 96 | n_val = lengths(varlist[my_variables])[qui] 97 | warning("The special variable __", my_variables[qui], "__ takes ", n_val, " values, only the first is used.", call. = FALSE) 98 | my_variables_values = sapply(my_variables_values, function(x) x[1]) 99 | } else { 100 | my_variables_values = unlist(my_variables_values) 101 | } 102 | 103 | # we prepare the result (we drop the last space) 104 | n = length(text_split) 105 | text_new = text_split 106 | text_new[n] = gsub(" $", "", text_new[n]) 107 | if(nchar(text_new[n]) == 0){ 108 | text_new = text_new[-n] 109 | is_var = is_var[-n] 110 | } 111 | 112 | 113 | # Do the variables contain expressions? 114 | is_expr = grepl("^&", my_variables_values) 115 | if(any(is_expr)){ 116 | 117 | expr_drop = function(x){ 118 | if(grepl("^&", x)){ 119 | res = gsub("^&", "", x) 120 | if(grepl("^expression\\(", res)){ 121 | res = gsub("(^expression\\()|(\\)$)", "", res) 122 | } else if(grepl("^substitute\\(", res)){ 123 | res = deparse(eval(str2lang(res))) 124 | } 125 | } else { 126 | res = x 127 | } 128 | res 129 | } 130 | 131 | my_vars = sapply(my_variables_values, expr_drop) 132 | 133 | if(text_as_expr){ 134 | text_new[is_var][is_expr] = my_vars[is_expr] 135 | 136 | if(all(is_expr)){ 137 | text_new = paste0("&expression(", paste(text_new, collapse = " "), ")") 138 | } else { 139 | my_var_no_expr = paste0('"', my_vars, '"')[!is_expr] 140 | new_names = paste0("x___", seq_along(my_var_no_expr)) 141 | text_new[is_var][!is_expr] = new_names 142 | text_new = paste0("&substitute(", paste(text_new, collapse = " "), ", list(", paste0(new_names, "=", my_var_no_expr, collapse = ", "), "))") 143 | } 144 | } else { 145 | text_new = paste0('"', text_new, '"') 146 | text_new[is_var][is_expr] = my_vars[is_expr] 147 | 148 | if(all(is_expr)){ 149 | text_new = paste0("&expression(paste(", paste(text_new, collapse = ", "), "))") 150 | } else { 151 | my_var_no_expr = paste0('"', my_vars, '"')[!is_expr] 152 | new_names = paste0("x___", seq_along(my_var_no_expr)) 153 | text_new[is_var][!is_expr] = new_names 154 | text_new = paste0("&substitute(paste(", paste(text_new, collapse = ", "), "), list(", paste0(new_names, "=", my_var_no_expr, collapse = ", "), "))") 155 | } 156 | 157 | } 158 | 159 | return(text_new) 160 | } else { 161 | # They don't contain expressions => fine, we just replace with the variables 162 | if(text_as_expr){ 163 | my_vars = paste0('"', my_variables_values, '"') 164 | new_names = paste0("x___", seq_along(my_vars)) 165 | text_new[is_var] = new_names 166 | text_new = paste0("&substitute(", paste(text_new, collapse = ""), ", list(", paste0(new_names, "=", my_vars, collapse = ", "), "))") 167 | } else { 168 | text_new[is_var] = my_variables_values 169 | text_new = paste(text_new, collapse = "") 170 | } 171 | 172 | return(text_new) 173 | } 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | dpi = 300, 13 | out.width = "100%" 14 | ) 15 | ``` 16 | 17 | # ggfixest 18 | 19 | 20 | [![CRAN status](https://www.r-pkg.org/badges/version/ggfixest)](https://CRAN.R-project.org/package=ggfixest) 21 | [![R-universe status badge](https://grantmcdermott.r-universe.dev/badges/ggfixest)](https://grantmcdermott.r-universe.dev) 22 | [![CRAN checks](https://badges.cranchecks.info/worst/ggfixest.svg)](https://cran.r-project.org/web/checks/check_results_ggfixest.html) 23 | [![R-CMD-check](https://github.com/grantmcdermott/ggfixest/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/grantmcdermott/ggfixest/actions/workflows/R-CMD-check.yaml) 24 | [![Docs](https://img.shields.io/badge/docs-stable-blue.svg)](https://grantmcdermott.com/ggfixest/index.html) 25 | [![Docs](https://img.shields.io/badge/docs-dev-blue.svg)](https://grantmcdermott.com/ggfixest/dev/index.html) 26 | 27 | 28 | The **ggfixest** package provides dedicated **ggplot2** plotting methods for 29 | **fixest** objects. Specifically, it provides drop-in "gg" equivalents of the 30 | latter's [`coefplot`](https://lrberge.github.io/fixest/reference/coefplot.html) 31 | and [`iplot`](https://lrberge.github.io/fixest/reference/coefplot.html) 32 | base plotting functions. 33 | 34 | The goal of **ggfixest** is to produce nice looking coefficient plots and 35 | interaction plots---including 36 | [event study](https://theeffectbook.net/ch-EventStudies.html) 37 | plots---with minimal effort and scope for further customization. 38 | 39 | ## Installation 40 | 41 | The stable version of **ggfixest** is available on CRAN. 42 | 43 | ``` r 44 | install.packages("ggfixest") 45 | ``` 46 | 47 | Or, you can grab the latest development version from R-universe. 48 | 49 | ``` r 50 | install.packages("ggfixest", repos = "https://grantmcdermott.r-universe.dev") 51 | ``` 52 | 53 | ## Quickstart 54 | 55 | The [package website](https://grantmcdermott.com/ggfixest/) 56 | provides a number of examples in the help documentation. (Also available by 57 | typing `?ggcoefplot` or `?ggiplot` in your R console.) But here are a few 58 | quickstart examples to whet your appetite. 59 | 60 | Start by loading the **ggfixest** package. 61 | 62 | ```{r pkgload} 63 | library(ggfixest) 64 | ``` 65 | 66 | Note that this automatically loads **ggplot2** and **fixest** as required 67 | dependencies too. As the package name suggests, **ggfixest** _only_ supports 68 | **fixest** model objects.^[For other model classes, a more generic visualization 69 | package/tool like 70 | [**see**](https://easystats.github.io/see/articles/parameters.html) or 71 | [**modelsummary**](https://modelsummary.com/vignettes/modelplot.html) 72 | would be more appropriate.] 73 | 74 | ### Coefficient plots 75 | 76 | Use `ggcoefplot` to draw basic coefficient plots. 77 | 78 | ```{r coefplot1, message=FALSE} 79 | est = feols( 80 | Petal.Length ~ Petal.Width + Sepal.Length + Sepal.Width + Species, 81 | data = iris 82 | ) 83 | 84 | # coefplot(est) ## base version 85 | ggcoefplot(est) ## this package 86 | ``` 87 | 88 | The above plot call and output should look very familiar to regular **fixest** 89 | users. Like its base equivalent, `ggcoefplot` can be heavily customized and 90 | contains various shortcuts for common operations. For example, we can use regex 91 | to control the coefficient grouping logic. 92 | 93 | ```{r coefplot2, message=FALSE} 94 | ggcoefplot(est, group = list(Sepal = "^^Sepal.", Species = "^^Species")) 95 | ``` 96 | 97 | ### Event study plots 98 | 99 | The `ggiplot` function is a special case of `ggocoefplot` that only plots 100 | coefficients with factor levels or interactions (specifically, those created 101 | with the [`i`](https://lrberge.github.io/fixest/reference/i.html) 102 | operator). This is especially useful for producing event study plots in a 103 | difference-in-differences (DiD) setup. 104 | 105 | ```{r es1, message=FALSE} 106 | est_did = feols(y ~ x1 + i(period, treat, 5) | id+period, base_did) 107 | 108 | # iplot(est_did) ## base version 109 | ggiplot(est_did) ## this package 110 | ``` 111 | 112 | Again, the above plot call and output should look very familiar to regular 113 | **fixest** users. But note that `ggiplot` supports several features that are 114 | not available in the base `iplot` version. For example, plotting multiple 115 | confidence intervals and aggregate treatments effects. 116 | 117 | ```{r es2} 118 | ggiplot( 119 | est_did, 120 | ci_level = c(.8, .95), 121 | aggr_eff = "post", aggr_eff.par = list(col = "orange") 122 | ) 123 | 124 | ``` 125 | 126 | And you can get quite fancy, combining lists of complex multiple estimation 127 | objects with custom themes, and so on. 128 | 129 | ```{r es3} 130 | base_stagg_grp = base_stagg 131 | base_stagg_grp$grp = ifelse(base_stagg_grp$id %% 2 == 0, 'Evens', 'Odds') 132 | 133 | est_twfe_grp = feols( 134 | y ~ x1 + i(time_to_treatment, treated, ref = c(-1, -1000)) | id + year, 135 | data = base_stagg_grp, split = ~grp 136 | ) 137 | 138 | est_sa20_grp = feols( 139 | y ~ x1 + sunab(year_treated, year) | id + year, 140 | data = base_stagg_grp, split = ~grp 141 | ) 142 | 143 | ggiplot( 144 | list("TWFE" = est_twfe_grp, "Sun & Abraham (2020)" = est_sa20_grp), 145 | ref.line = -1, 146 | main = "Staggered treatment: Split mutli-sample", 147 | xlab = "Time to treatment", 148 | multi_style = "facet", 149 | geom_style = "ribbon", 150 | facet_args = list(labeller = labeller(id = \(x) gsub(".*: ", "", x))), 151 | theme = theme_minimal() + 152 | theme( 153 | text = element_text(family = "HersheySans"), 154 | plot.title = element_text(hjust = 0.5), 155 | legend.position = "none" 156 | ) 157 | ) 158 | ``` 159 | 160 | For more `ggiplot` examples and comparisons with its base counterpart, see the 161 | detailed [vignette](http://grantmcdermott.com/ggfixest/articles/ggiplot.html) on 162 | the package homepage (or, by typing `vignette("ggiplot")` in your R console). 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # ggfixest 5 | 6 | 7 | 8 | [![CRAN 9 | status](https://www.r-pkg.org/badges/version/ggfixest)](https://CRAN.R-project.org/package=ggfixest) 10 | [![R-universe status 11 | badge](https://grantmcdermott.r-universe.dev/badges/ggfixest)](https://grantmcdermott.r-universe.dev) 12 | [![CRAN 13 | checks](https://badges.cranchecks.info/worst/ggfixest.svg)](https://cran.r-project.org/web/checks/check_results_ggfixest.html) 14 | [![R-CMD-check](https://github.com/grantmcdermott/ggfixest/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/grantmcdermott/ggfixest/actions/workflows/R-CMD-check.yaml) 15 | [![Docs](https://img.shields.io/badge/docs-stable-blue.svg)](https://grantmcdermott.com/ggfixest/index.html) 16 | [![Docs](https://img.shields.io/badge/docs-dev-blue.svg)](https://grantmcdermott.com/ggfixest/dev/index.html) 17 | 18 | 19 | The **ggfixest** package provides dedicated **ggplot2** plotting methods 20 | for **fixest** objects. Specifically, it provides drop-in “gg” 21 | equivalents of the latter’s 22 | [`coefplot`](https://lrberge.github.io/fixest/reference/coefplot.html) 23 | and [`iplot`](https://lrberge.github.io/fixest/reference/coefplot.html) 24 | base plotting functions. 25 | 26 | The goal of **ggfixest** is to produce nice looking coefficient plots 27 | and interaction plots—including [event 28 | study](https://theeffectbook.net/ch-EventStudies.html) plots—with 29 | minimal effort and scope for further customization. 30 | 31 | ## Installation 32 | 33 | The stable version of **ggfixest** is available on CRAN. 34 | 35 | ``` r 36 | install.packages("ggfixest") 37 | ``` 38 | 39 | Or, you can grab the latest development version from R-universe. 40 | 41 | ``` r 42 | install.packages("ggfixest", repos = "https://grantmcdermott.r-universe.dev") 43 | ``` 44 | 45 | ## Quickstart 46 | 47 | The [package website](https://grantmcdermott.com/ggfixest/) provides a 48 | number of examples in the help documentation. (Also available by typing 49 | `?ggcoefplot` or `?ggiplot` in your R console.) But here are a few 50 | quickstart examples to whet your appetite. 51 | 52 | Start by loading the **ggfixest** package. 53 | 54 | ``` r 55 | library(ggfixest) 56 | #> Loading required package: ggplot2 57 | #> Loading required package: fixest 58 | ``` 59 | 60 | Note that this automatically loads **ggplot2** and **fixest** as 61 | required dependencies too. As the package name suggests, **ggfixest** 62 | *only* supports **fixest** model objects.[^1] 63 | 64 | ### Coefficient plots 65 | 66 | Use `ggcoefplot` to draw basic coefficient plots. 67 | 68 | ``` r 69 | est = feols( 70 | Petal.Length ~ Petal.Width + Sepal.Length + Sepal.Width + Species, 71 | data = iris 72 | ) 73 | 74 | # coefplot(est) ## base version 75 | ggcoefplot(est) ## this package 76 | ``` 77 | 78 | 79 | 80 | The above plot call and output should look very familiar to regular 81 | **fixest** users. Like its base equivalent, `ggcoefplot` can be heavily 82 | customized and contains various shortcuts for common operations. For 83 | example, we can use regex to control the coefficient grouping logic. 84 | 85 | ``` r 86 | ggcoefplot(est, group = list(Sepal = "^^Sepal.", Species = "^^Species")) 87 | ``` 88 | 89 | 90 | 91 | ### Event study plots 92 | 93 | The `ggiplot` function is a special case of `ggocoefplot` that only 94 | plots coefficients with factor levels or interactions (specifically, 95 | those created with the 96 | [`i`](https://lrberge.github.io/fixest/reference/i.html) operator). This 97 | is especially useful for producing event study plots in a 98 | difference-in-differences (DiD) setup. 99 | 100 | ``` r 101 | est_did = feols(y ~ x1 + i(period, treat, 5) | id+period, base_did) 102 | 103 | # iplot(est_did) ## base version 104 | ggiplot(est_did) ## this package 105 | ``` 106 | 107 | 108 | 109 | Again, the above plot call and output should look very familiar to 110 | regular **fixest** users. But note that `ggiplot` supports several 111 | features that are not available in the base `iplot` version. For 112 | example, plotting multiple confidence intervals and aggregate treatments 113 | effects. 114 | 115 | ``` r 116 | ggiplot( 117 | est_did, 118 | ci_level = c(.8, .95), 119 | aggr_eff = "post", aggr_eff.par = list(col = "orange") 120 | ) 121 | ``` 122 | 123 | 124 | 125 | And you can get quite fancy, combining lists of complex multiple 126 | estimation objects with custom themes, and so on. 127 | 128 | ``` r 129 | base_stagg_grp = base_stagg 130 | base_stagg_grp$grp = ifelse(base_stagg_grp$id %% 2 == 0, 'Evens', 'Odds') 131 | 132 | est_twfe_grp = feols( 133 | y ~ x1 + i(time_to_treatment, treated, ref = c(-1, -1000)) | id + year, 134 | data = base_stagg_grp, split = ~grp 135 | ) 136 | 137 | est_sa20_grp = feols( 138 | y ~ x1 + sunab(year_treated, year) | id + year, 139 | data = base_stagg_grp, split = ~grp 140 | ) 141 | 142 | ggiplot( 143 | list("TWFE" = est_twfe_grp, "Sun & Abraham (2020)" = est_sa20_grp), 144 | ref.line = -1, 145 | main = "Staggered treatment: Split mutli-sample", 146 | xlab = "Time to treatment", 147 | multi_style = "facet", 148 | geom_style = "ribbon", 149 | facet_args = list(labeller = labeller(id = \(x) gsub(".*: ", "", x))), 150 | theme = theme_minimal() + 151 | theme( 152 | text = element_text(family = "HersheySans"), 153 | plot.title = element_text(hjust = 0.5), 154 | legend.position = "none" 155 | ) 156 | ) 157 | ``` 158 | 159 | 160 | 161 | For more `ggiplot` examples and comparisons with its base counterpart, 162 | see the detailed 163 | [vignette](http://grantmcdermott.com/ggfixest/articles/ggiplot.html) on 164 | the package homepage (or, by typing `vignette("ggiplot")` in your R 165 | console). 166 | 167 | [^1]: For other model classes, a more generic visualization package/tool 168 | like 169 | [**see**](https://easystats.github.io/see/articles/parameters.html) 170 | or 171 | [**modelsummary**](https://modelsummary.com/vignettes/modelplot.html) 172 | would be more appropriate. 173 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://grantmcdermott.com/ggiplot/ 2 | 3 | home: 4 | title: Dedicated 'ggplot2' Methods for 'fixest' Objects 5 | 6 | template: 7 | bootstrap: 5 8 | 9 | development: 10 | mode: auto 11 | 12 | navbar: 13 | title: ~ 14 | type: default 15 | left: 16 | - text: Tutorials 17 | href: articles/index.html 18 | menu: 19 | - text: Comparing ggiplot to iplot 20 | href: articles/ggiplot.html 21 | - text: Functions 22 | href: reference/index.html 23 | - text: News 24 | href: news/index.html 25 | 26 | reference: 27 | - subtitle: Plotting 28 | desc: > 29 | The main user-facing functions of the package. 30 | contents: 31 | - ggcoefplot 32 | - subtitle: Utilities 33 | desc: > 34 | Helper functions that are used as inputs to the main plotting functions. 35 | contents: 36 | - aggr_es 37 | - iplot_data 38 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | `ggfixest` 0.3.0 is a minor update that address some CRAN errors due to a 4 | dependency change. 5 | 6 | ## Test environments 7 | 8 | * Local: Arch Linux 9 | * GitHub Actions (ubuntu-22.04): oldrel-1, release, devel 10 | * Win Builder 11 | 12 | ## R CMD check results 13 | 14 | 0 errors | 0 warnings | 0 notes 15 | -------------------------------------------------------------------------------- /ggfixest.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: cdb8efca-dc0f-40de-876c-0a55b5e57744 3 | 4 | RestoreWorkspace: No 5 | SaveWorkspace: No 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: No 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: XeLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | LineEndingConversion: Posix 19 | 20 | BuildType: Package 21 | PackageUseDevtools: Yes 22 | PackageInstallArgs: --no-multiarch --with-keep.source 23 | PackageRoxygenize: rd,collate,namespace 24 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_did.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 0 63 | 5 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | x1 77 | 1 78 | 2 79 | 3 80 | 4 81 | 6 82 | 7 83 | 8 84 | 9 85 | 10 86 | 87 | treat × (period = ...) 88 | Estimate and 95% Conf. Int. 89 | Effect on y 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_did_iid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 0 63 | 5 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | x1 77 | 1 78 | 2 79 | 3 80 | 4 81 | 6 82 | 7 83 | 8 84 | 9 85 | 10 86 | 87 | treat × (period = ...) 88 | Estimate and 95% Conf. Int. 89 | Effect on y 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_group_names_prefix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -1 55 | 0 56 | 1 57 | 2 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Constant 69 | Petal.Width 70 | Length 71 | Width 72 | versicolor 73 | virginica 74 | 75 | 76 | Sepal 77 | Species 78 | Estimate and 95% Conf. Int. 79 | Effect on Petal.Length 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_group_nonames.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -1 55 | 0 56 | 1 57 | 2 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Constant 69 | Petal.Width 70 | Sepal.Length 71 | Sepal.Width 72 | Speciesversicolor 73 | Speciesvirginica 74 | 75 | 76 | 77 | 78 | Estimate and 95% Conf. Int. 79 | Effect on Petal.Length 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_group_none.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -1 55 | 0 56 | 1 57 | 2 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Constant 69 | Petal.Width 70 | Sepal.Length 71 | Sepal.Width 72 | Speciesversicolor 73 | Speciesvirginica 74 | Estimate and 95% Conf. Int. 75 | Effect on Petal.Length 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_groupnames.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -1 55 | 0 56 | 1 57 | 2 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Constant 69 | Petal.Width 70 | Sepal.Length 71 | Sepal.Width 72 | Speciesversicolor 73 | Speciesvirginica 74 | 75 | 76 | Sepal 77 | Species 78 | Estimate and 95% Conf. Int. 79 | Effect on Petal.Length 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_interactions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 0.00 53 | 0.05 54 | 0.10 55 | 0.15 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 0 66 | 1 67 | 4:wt 68 | 6:wt 69 | 8:wt 70 | 71 | 72 | hp × (am = ...) 73 | disp × (cyl = ...) 74 | Estimate and 95% Conf. Int. 75 | Effect on mpg 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_interactions_multici.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 0.00 73 | 0.05 74 | 0.10 75 | 0.15 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 0 86 | 1 87 | 4:wt 88 | 6:wt 89 | 8:wt 90 | 91 | 92 | hp × (am = ...) 93 | disp × (cyl = ...) 94 | Estimate and [80% & 95%] Conf. Int. 95 | Effect on mpg 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_multi.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 0 59 | 100 60 | 200 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Constant 69 | 4 70 | 6 71 | 8 72 | 73 | wt × (cyl = ...) 74 | Estimate and 95% Conf. Int. 75 | 76 | group 77 | 78 | 79 | 80 | 81 | 82 | 83 | lhs: mpg 84 | lhs: hp 85 | Effect on [mpg & hp] 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggcoefplot_simple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 1.0 50 | 1.5 51 | 2.0 52 | 2.5 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Constant 62 | Petal.Width 63 | versicolor 64 | virginica 65 | 66 | Species 67 | Estimate and 95% Conf. Int. 68 | Effect on Petal.Length 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 0 64 | 5 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 2 73 | 4 74 | 6 75 | 8 76 | 10 77 | period 78 | Estimate and 95% Conf. Int. 79 | 80 | group 81 | 82 | 83 | 84 | Model 1 85 | Effect on y 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_multi_csw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -2 52 | 0 53 | 2 54 | 4 55 | 6 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 0 64 | 1 65 | vs 66 | Estimate and 95% Conf. Int. 67 | 68 | group 69 | 70 | 71 | 72 | 73 | 74 | 75 | rhs: disp + i(vs, drat) 76 | rhs: disp + qsec + i(vs, drat) 77 | Effect on mpg 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_multi_csw_facet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | disp + i(vs, drat) 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | disp + qsec + i(vs, drat) 84 | 85 | 86 | 87 | 88 | 0 89 | 1 90 | 91 | 92 | 0 93 | 1 94 | -2 95 | 0 96 | 2 97 | 4 98 | 6 99 | 100 | 101 | 102 | 103 | 104 | vs 105 | Estimate and 95% Conf. Int. 106 | 107 | group 108 | 109 | 110 | 111 | 112 | 113 | 114 | rhs: disp + i(vs, drat) 115 | rhs: disp + qsec + i(vs, drat) 116 | Effect on mpg 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_multi_lhs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 0.0 52 | 2.5 53 | 5.0 54 | 7.5 55 | 56 | 57 | 58 | 59 | 60 | 61 | 0 62 | 1 63 | vs 64 | Estimate and 95% Conf. Int. 65 | 66 | group 67 | 68 | 69 | 70 | 71 | 72 | 73 | lhs: mpg 74 | lhs: wt 75 | Effect on [mpg & wt] 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_multi_lhs_csw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -2 62 | 0 63 | 2 64 | 4 65 | 6 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 0 74 | 1 75 | vs 76 | Estimate and 95% Conf. Int. 77 | 78 | group 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | lhs: mpg; rhs: disp + i(vs, drat) 93 | lhs: mpg; rhs: disp + qsec + i(vs, drat) 94 | lhs: wt; rhs: disp + i(vs, drat) 95 | lhs: wt; rhs: disp + qsec + i(vs, drat) 96 | Effect on [mpg & wt] 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_multi_lhs_facet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | mpg 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | wt 84 | 85 | 86 | 87 | 88 | 0 89 | 1 90 | 91 | 92 | 0 93 | 1 94 | 0.0 95 | 2.5 96 | 5.0 97 | 7.5 98 | 99 | 100 | 101 | 102 | vs 103 | Estimate and 95% Conf. Int. 104 | 105 | group 106 | 107 | 108 | 109 | 110 | 111 | 112 | lhs: mpg 113 | lhs: wt 114 | Effect on [mpg & wt] 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_simple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 0 64 | 5 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 2 73 | 4 74 | 6 75 | 8 76 | 10 77 | period 78 | Estimate and 95% Conf. Int. 79 | Effect on y 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_simple_errorbar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 0 84 | 5 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 2 93 | 4 94 | 6 95 | 8 96 | 10 97 | period 98 | Estimate and 95% Conf. Int. 99 | Effect on y 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_simple_mci_ribbon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 0 52 | 5 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 2 61 | 4 62 | 6 63 | 8 64 | 10 65 | period 66 | Estimate and [80% & 95%] Conf. Int. 67 | Effect on y 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_simple_ribbon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 0 48 | 5 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 2 57 | 4 58 | 6 59 | 8 60 | 10 61 | period 62 | Estimate and 95% Conf. Int. 63 | Effect on y 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /inst/tinytest/_tinysnapshot/ggiplot_stagg_mci_ribbon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -6 51 | -3 52 | 0 53 | 3 54 | 6 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -5 64 | 0 65 | 5 66 | time_to_treatment 67 | Estimate and [80% & 95%] Conf. Int. 68 | 69 | group 70 | 71 | 72 | 73 | 74 | TWFE 75 | Staggered treatment 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /inst/tinytest/test_aggr_es.R: -------------------------------------------------------------------------------- 1 | library(ggfixest) 2 | library(tinytest) 3 | 4 | # 5 | # Datasets and models ---- 6 | 7 | data("base_did", package = "fixest") 8 | 9 | est = fixest::feols( 10 | fml = y ~ x1 + i(period, treat, 5) | id + period, 11 | data = base_did 12 | ) 13 | 14 | aggr_post = aggr_es(est) # default post 15 | aggr_cum = aggr_es(est, aggregation = "cumulative") # cumulative instead of mean effects 16 | aggr_pre = aggr_es(est, period = "pre") # pre period instead of post 17 | aggr_both = aggr_es(est, period = "both") # pre & post periods separately 18 | aggr_diff = aggr_es(est, period = "diff") # difference (post vs pre) 19 | aggr_rhs1 = aggr_es(est, period = "pre", rhs = -1) # pre period with H0 value of 1 20 | 21 | # 22 | # Known output ---- 23 | 24 | aggr_post_known = data.frame( 25 | term = "post-treatment (mean)", 26 | estimate = 3.906554122950695, 27 | std.error = 0.8598575665281263, 28 | statistic = 4.543257249830702, 29 | p.value = 5.5391585244779775e-06, 30 | s.value = 17.461901741925015, 31 | conf.low = 2.2212642607213144, 32 | conf.high = 5.591843985180075 33 | ) 34 | 35 | 36 | aggr_cum_known = data.frame( 37 | term = "post-treatment (cumulative)", 38 | estimate = 19.532770614753474, 39 | std.error = 4.299287821377354, 40 | statistic = 4.543257261733131, 41 | p.value = 5.539158211582727e-06, 42 | s.value = 17.461901823419783, 43 | conf.low = 11.106321325682188, 44 | conf.high = 27.95921990382476 45 | ) 46 | 47 | aggr_pre_known = data.frame( 48 | term = "pre-treatment (mean)", 49 | estimate = -1.1798706992411545, 50 | std.error = 0.8561963882056884, 51 | statistic = -1.3780374637106132, 52 | p.value = 0.16819172163573184, 53 | s.value = 2.5718213967199377, 54 | conf.low = -2.857984783817578, 55 | conf.high = 0.49824338533526924 56 | ) 57 | 58 | aggr_both_known = data.frame( 59 | term = c("pre-treatment (mean)", "post-treatment (mean)"), 60 | estimate = c(-1.1798706992411545, 3.906554122950695), 61 | std.error = c(0.8561963882056884, 0.8598575665281263), 62 | statistic = c(-1.3780374637106132, 4.543257249830702), 63 | p.value = c(0.16819172163573184, 5.5391585244779775e-06), 64 | s.value = c(2.5718213967199377, 17.461901741925015), 65 | conf.low = c(-2.857984783817578, 2.2212642607213144), 66 | conf.high = c(0.49824338533526924, 5.591843985180075) 67 | ) 68 | 69 | aggr_diff_known = data.frame( 70 | term = "difference (post vs pre mean)", 71 | estimate = 5.08642482219185, 72 | std.error = 0.47207477585529, 73 | statistic = 10.7746168241598, 74 | p.value = 4.53667090889737e-27, 75 | s.value = 87.5104245518892, 76 | conf.low = 4.16117526350566, 77 | conf.high = 6.01167438087804 78 | ) 79 | 80 | aggr_rhs1_known = data.frame( 81 | term = "pre-treatment (mean)", 82 | estimate = -0.1798706992411545, 83 | std.error = 0.8561963882056884, 84 | statistic = -0.21008112358206216, 85 | p.value = 0.8336043579365446, 86 | s.value = 0.26256527509602834, 87 | conf.low = -1.8579847838175783, 88 | conf.high = 1.4982433853352692 89 | ) 90 | 91 | 92 | # 93 | # tests ---- 94 | tol = 1e-4 95 | 96 | for (col in c("term", "estimate", "std.error", "statistic", "p.value", "s.value", 97 | "conf.low", "conf.high")) { 98 | expect_equivalent(aggr_post[[col]], aggr_post_known[[col]], tolerance = tol) 99 | expect_equivalent(aggr_cum[[col]], aggr_cum_known[[col]], tolerance = tol) 100 | expect_equivalent(aggr_pre[[col]], aggr_pre_known[[col]], tolerance = tol) 101 | expect_equivalent(aggr_both[[col]], aggr_both_known[[col]], tolerance = tol) 102 | expect_equivalent(aggr_diff[[col]], aggr_diff_known[[col]], tolerance = tol) 103 | expect_equivalent(aggr_rhs1[[col]], aggr_rhs1_known[[col]], tolerance = tol) 104 | } 105 | -------------------------------------------------------------------------------- /inst/tinytest/test_fixest_multi.R: -------------------------------------------------------------------------------- 1 | source("tinysnapshot_helpers.R") 2 | using("tinysnapshot") 3 | if (Sys.info()["sysname"] != "Linux") exit_file("Linux snapshots") 4 | 5 | library(ggfixest) 6 | 7 | multi_lhs = feols(c(mpg, wt) ~ i(vs, drat), mtcars) 8 | 9 | p1a = ggiplot(multi_lhs, ref.line = 0.5) 10 | p1b = ggiplot(multi_lhs, multi_style = "facet", ref.line = 0.5) 11 | p1c = ggiplot(multi_lhs, multi_style = "facet", geom_style = "ribbon", ref.line = 0.5) 12 | expect_snapshot_plot(p1a, label = "ggiplot_multi_lhs") 13 | expect_snapshot_plot(p1b, label = "ggiplot_multi_lhs_facet") 14 | expect_snapshot_plot(p1c, label = "ggiplot_multi_lhs_facet_ribbon") 15 | 16 | multi_csw = feols(mpg ~ csw(disp, qsec) + i(vs, drat), mtcars) 17 | 18 | p2a = ggiplot(multi_csw, ref.line = 0.5) 19 | p2b = ggiplot(multi_csw, multi_style = "facet", ref.line = 0.5) 20 | expect_snapshot_plot(p2a, label = "ggiplot_multi_csw") 21 | expect_snapshot_plot(p2b, label = "ggiplot_multi_csw_facet") 22 | 23 | multi_lhs_csw = feols(c(mpg, wt) ~ csw(disp, qsec) + i(vs, drat), mtcars) 24 | 25 | p3a = ggiplot(multi_lhs_csw, ref.line = 0.5) 26 | p3b = ggiplot(multi_lhs_csw, multi_style = "facet", ref.line = 0.5) 27 | expect_snapshot_plot(p3a, label = "ggiplot_multi_lhs_csw") 28 | expect_snapshot_plot(p3b, label = "ggiplot_multi_lhs_csw_facet") 29 | -------------------------------------------------------------------------------- /inst/tinytest/test_ggcoefplot.R: -------------------------------------------------------------------------------- 1 | source("tinysnapshot_helpers.R") 2 | using("tinysnapshot") 3 | if (Sys.info()["sysname"] != "Linux") exit_file("Linux snapshots") 4 | 5 | library(ggfixest) 6 | library(tinytest) 7 | 8 | 9 | # NB: 1st test runs will fail, but write the targets to file. 2nd run(s) should 10 | # pass. 11 | 12 | # 13 | ## Simple ggcoeflot ---- 14 | 15 | est = feols(Petal.Length ~ Petal.Width + i(Species), iris) 16 | p = ggcoefplot(est) 17 | expect_snapshot_plot(p, label = "ggcoefplot_simple") 18 | 19 | # 20 | ## i-based Interactions ---- 21 | 22 | est_i = feols(mpg ~ 0 + i(cyl, wt):disp + i(am, hp), mtcars) 23 | p_i1 = ggcoefplot(est_i) 24 | expect_snapshot_plot(p_i1, label = "ggcoefplot_interactions") 25 | p_i2 = ggcoefplot(est_i, ci_level = c(.8, .95)) 26 | expect_snapshot_plot(p_i2, label = "ggcoefplot_interactions_multici") 27 | 28 | # 29 | ## Multi estimation (with interactions) ---- 30 | 31 | est_multi = feols(c(mpg, hp) ~ i(cyl, wt), mtcars) 32 | p_multi = ggcoefplot(est_multi) 33 | expect_snapshot_plot(p_multi, label = "ggcoefplot_multi") 34 | p_multi_facet = ggcoefplot(est_multi, multi_style = "facet") 35 | expect_snapshot_plot(p_multi_facet, label = "ggcoefplot_multi_facet") 36 | 37 | 38 | # 39 | ## Grouping ---- 40 | 41 | est_grp = feols(Petal.Length ~ Petal.Width + Sepal.Length + Sepal.Width + Species, iris) 42 | 43 | p_grp1 = ggcoefplot(est_grp) # no groups 44 | expect_snapshot_plot(p_grp1, label = "ggcoefplot_group_none") 45 | p_grp2 = ggcoefplot(est_grp, group = list("Sepal", "Species")) # group, no names 46 | expect_snapshot_plot(p_grp2, label = "ggcoefplot_group_nonames") 47 | p_grp3 = ggcoefplot(est_grp, group = list(Sepal = "Sepal", Species = "Species")) # group + names 48 | expect_snapshot_plot(p_grp3, label = "ggcoefplot_groupnames") 49 | p_grp4 = ggcoefplot(est_grp, group = list(Sepal = "^^Sepal.", Species = "^^Species")) # group + ^^names 50 | expect_snapshot_plot(p_grp4, label = "ggcoefplot_group_names_prefix") 51 | 52 | # 53 | ## DiD (mostly to check auto grouping) ---- 54 | 55 | data("base_did", package = "fixest") 56 | est_did = feols(y ~ x1 + i(period, treat, 5) | id + period, base_did) 57 | p_did = ggcoefplot(est_did) 58 | expect_snapshot_plot(p_did, label = "ggcoefplot_did") 59 | 60 | 61 | # 62 | ## vcov adjustment (passed through ...) ---- 63 | 64 | p_did_iid_summ = ggcoefplot(summary(est_did, vcov = "iid")) # manual approach 65 | p_did_iid = ggcoefplot(est_did, vcov = "iid") # passed through "..." 66 | expect_snapshot_plot(p_did_iid_summ, label = "ggcoefplot_did_iid") 67 | expect_snapshot_plot(p_did_iid, label = "ggcoefplot_did_iid") # should be identical 68 | 69 | -------------------------------------------------------------------------------- /inst/tinytest/test_ggiplot.R: -------------------------------------------------------------------------------- 1 | source("tinysnapshot_helpers.R") 2 | using("tinysnapshot") 3 | if (Sys.info()["sysname"] != "Linux") exit_file("Linux snapshots") 4 | 5 | library(ggfixest) 6 | library(tinytest) 7 | 8 | # 9 | # Datasets and models ---- 10 | 11 | data("base_did", package = "fixest") 12 | data("base_stagg", package = "fixest") 13 | base_stagg_grp = base_stagg 14 | base_stagg_grp$grp = ifelse(base_stagg_grp$id %% 2 == 0, 'Evens', 'Odds') 15 | 16 | est = fixest::feols( 17 | fml = y ~ x1 + i(period, treat, 5) | id + period, 18 | data = base_did 19 | ) 20 | 21 | est_twfe = fixest::feols( 22 | y ~ x1 + i(time_to_treatment, treated, ref = c(-1, -1000)) | id + year, 23 | base_stagg 24 | ) 25 | 26 | est_twfe_grp = fixest::feols( 27 | y ~ x1 + i(time_to_treatment, treated, ref = c(-1, -1000)) | id + year, 28 | base_stagg_grp, 29 | split = ~ grp 30 | ) 31 | 32 | est_sa20_grp = fixest::feols( 33 | y ~ x1 + sunab(year_treated, year) | id + year, 34 | base_stagg_grp, 35 | split = ~ grp 36 | ) 37 | 38 | # NB: 1st test runs will fail, but write the targets to file. 2nd run(s) should 39 | # pass. 40 | 41 | # 42 | ## Simple ggiplot ---- 43 | 44 | p1 = ggiplot(est) 45 | p2 = ggiplot(est, geom_style = "errorbar") 46 | p3 = ggiplot(est, geom_style = "ribbon", pt.pch = NA) 47 | p4 = ggiplot(est, ci_level = c(.8,.95)) 48 | p5 = ggiplot(est, ci_level = c(.8,.95), geom_style = 'ribbon', pt.pch = NA) 49 | p6 = ggiplot(list(est)) 50 | expect_snapshot_plot(p1, label = "ggiplot_simple") 51 | expect_snapshot_plot(p2, label = "ggiplot_simple_errorbar") 52 | expect_snapshot_plot(p3, label = "ggiplot_simple_ribbon") 53 | expect_snapshot_plot(p4, label = "ggiplot_simple_mci") 54 | expect_snapshot_plot(p5, label = "ggiplot_simple_mci_ribbon") 55 | expect_snapshot_plot(p6, label = "ggiplot_list") 56 | 57 | # 58 | ## Staggered treatment DiD (common use-case) ---- 59 | 60 | p7 = ggiplot( 61 | est_twfe, 62 | main = 'Staggered treatment', ref.line = -1, pt.join = TRUE, 63 | ci_level = c(.8,.95) 64 | ) 65 | p8 = ggiplot( 66 | list('TWFE' = est_twfe), 67 | main = 'Staggered treatment', ref.line = -1, pt.join = TRUE, 68 | geom_style = "ribbon", pt.pch = NA, 69 | ci_level = c(.8,.95) 70 | ) 71 | expect_snapshot_plot(p7, label = "ggiplot_stagg_mci") 72 | expect_snapshot_plot(p8, label = "ggiplot_stagg_mci_ribbon") 73 | 74 | # 75 | # Multi plots (single panel) ---- 76 | 77 | est_sa20 = fixest::feols( 78 | y ~ x1 + sunab(year_treated, year) | id + year, 79 | base_stagg 80 | ) 81 | 82 | p9 = ggiplot( 83 | list('TWFE' = est_twfe, 'Sun & Abraham (2020)' = est_sa20) 84 | ) 85 | p10 = ggiplot( 86 | list(est_twfe, est_sa20) 87 | ) 88 | p11 = ggiplot( 89 | list('TWFE' = est_twfe, 'Sun & Abraham (2020)' = est_sa20), 90 | geom_style = "ribbon" 91 | ) 92 | p12 = ggiplot( 93 | list('TWFE' = est_twfe, 'Sun & Abraham (2020)' = est_sa20), 94 | main = 'Staggered treatment', ref.line = -1, pt.join = TRUE, 95 | ci_level = c(.8, .95) 96 | ) 97 | p13 = ggiplot( 98 | list('TWFE' = est_twfe, 'Sun & Abraham (2020)' = est_sa20), 99 | main = 'Staggered treatment', ref.line = -1, pt.join = TRUE, 100 | ci_level = c(.8, .95), geom_style = 'ribbon' 101 | ) 102 | expect_snapshot_plot(p9, label = "ggiplot_multi_single") 103 | expect_snapshot_plot(p10, label = "ggiplot_multi_single_unnamed") 104 | expect_snapshot_plot(p11, label = "ggiplot_multi_single_ribbon") 105 | expect_snapshot_plot(p12, label = "ggiplot_multi_single_kitchen") 106 | expect_snapshot_plot(p13, label = "ggiplot_multi_single_kitchen_ribbon") 107 | 108 | # 109 | # Multi plots (facetted) ---- 110 | 111 | p14 = ggiplot( 112 | list('TWFE' = est_twfe, 'Sun & Abraham (2020)' = est_sa20), 113 | main = 'Staggered treatment', ref.line = -1, pt.join = TRUE, 114 | ci_level = c(.8, .95), multi_style = 'facet' 115 | ) 116 | p15 = ggiplot( 117 | list('TWFE' = est_twfe, 'Sun & Abraham (2020)' = est_sa20), 118 | main = 'Staggered treatment', ref.line = -1, pt.join = TRUE, 119 | ci_level = c(.8, .95), multi_style = 'facet', 120 | geom_style = 'ribbon' 121 | ) 122 | expect_snapshot_plot(p14, label = "ggiplot_multi_facet") 123 | expect_snapshot_plot(p15, label = "ggiplot_multi_facet_ribbon") 124 | 125 | # 126 | # Multi plots and split samples (complex) ---- 127 | 128 | p16 = ggiplot( 129 | list('TWFE' = est_twfe_grp, 'Sun & Abraham (2020)' = est_sa20_grp), 130 | main = 'Staggered treatment: Split mutli-sample', 131 | ref.line = -1, 132 | pt.join = TRUE) 133 | p17 = ggiplot( 134 | list('TWFE' = est_twfe_grp, 'Sun & Abraham (2020)' = est_sa20_grp), 135 | main = 'Staggered treatment: Split mutli-sample', 136 | ref.line = -1, pt.join = TRUE, 137 | ci_level = c(.8, .95) 138 | ) 139 | p18 = ggiplot( 140 | list('TWFE' = est_twfe_grp, 'Sun & Abraham (2020)' = est_sa20_grp), 141 | main = 'Staggered treatment: Split mutli-sample', 142 | ref.line = -1, 143 | xlab = 'Time to treatment', 144 | multi_style = 'facet', 145 | geom_style = 'ribbon', 146 | ci_level = c(.8, .95), 147 | theme = theme_minimal() + theme( 148 | text = element_text(family = 'HersheySans'), 149 | plot.title = element_text(hjust = 0.5), 150 | legend.position = 'none' 151 | ) 152 | ) 153 | expect_snapshot_plot(p16, label = "ggiplot_multi_complex") 154 | expect_snapshot_plot(p17, label = "ggiplot_multi_complex_mci") 155 | expect_snapshot_plot(p18, label = "ggiplot_multi_complex_kitchen") 156 | 157 | # Last one(s): Check vcov adjustment of previous plot 158 | # Manual version, passing via summary first... 159 | p19a = ggiplot( 160 | list( 161 | 'TWFE' = summary(est_twfe_grp, vcov = "iid"), 162 | 'Sun & Abraham (2020)' = summary(est_sa20_grp, vcov = "iid") 163 | ), 164 | main = 'Staggered treatment: Split mutli-sample', 165 | ref.line = -1, 166 | xlab = 'Time to treatment', 167 | multi_style = 'facet', 168 | geom_style = 'ribbon', 169 | ci_level = c(.8, .95), 170 | theme = theme_minimal() + theme( 171 | text = element_text(family = 'HersheySans'), 172 | plot.title = element_text(hjust = 0.5), 173 | legend.position = 'none' 174 | ) 175 | ) 176 | # Next, passing as a convenience string (via ...) 177 | p19b = ggiplot( 178 | list('TWFE' = est_twfe_grp, 'Sun & Abraham (2020)' = est_sa20_grp), 179 | vcov = "iid", 180 | main = 'Staggered treatment: Split mutli-sample', 181 | ref.line = -1, 182 | xlab = 'Time to treatment', 183 | multi_style = 'facet', 184 | geom_style = 'ribbon', 185 | ci_level = c(.8, .95), 186 | theme = theme_minimal() + theme( 187 | text = element_text(family = 'HersheySans'), 188 | plot.title = element_text(hjust = 0.5), 189 | legend.position = 'none' 190 | ) 191 | ) 192 | expect_snapshot_plot(p19a, label = "ggiplot_multi_complex_kitchen_iid") 193 | expect_snapshot_plot(p19b, label = "ggiplot_multi_complex_kitchen_iid") 194 | 195 | # 196 | # Making sure pt.join works with sw() and is not sensitive to ordering 197 | 198 | # https://github.com/grantmcdermott/ggfixest/issues/40#issuecomment-2444436162 199 | base_did$y2 = base_did$y*1.5 200 | base_did$y3 = base_did$y*2 201 | 202 | m20a = fixest::feols(y ~ x1 + i(period, treat, 5) | sw0(period, id), base_did) 203 | m20b = fixest::feols(y ~ x1 + i(period, treat, 5) | sw0(id, period), base_did) 204 | p20a = ggiplot(m20a, pt.join = TRUE) 205 | p20b = ggiplot(m20b, pt.join = TRUE) 206 | expect_snapshot_plot(p20a, label = "ggiplot_multi_sw_pt_join1") 207 | expect_snapshot_plot(p20b, label = "ggiplot_multi_sw_pt_join2") 208 | -------------------------------------------------------------------------------- /inst/tinytest/test_iplot_data.R: -------------------------------------------------------------------------------- 1 | library(ggfixest) 2 | library(tinytest) 3 | 4 | # 5 | # Datasets and models ---- 6 | 7 | data("base_did", package = "fixest") 8 | 9 | est = fixest::feols( 10 | fml = y ~ x1 + i(period, treat, 5) | id + period, 11 | data = base_did 12 | ) 13 | 14 | est_log = fixest::feols( 15 | fml = log(y) ~ x1 + i(period, treat, 5) | id + period, 16 | data = base_did, 17 | subset = ~ y >= 0 18 | ) 19 | 20 | 21 | iplot_data_est = iplot_data(est) 22 | iplot_data_est_log = iplot_data(est_log) 23 | 24 | # 25 | # Known output ---- 26 | 27 | # est |> iplot_data() |> dput() 28 | iplot_data_est_known = structure( 29 | list( 30 | estimate = c(-1.40304548628387, -1.24751078444385, -0.273205962244887, -1.79572056399201, 0, 0.784452028314083, 3.59889733159794, 3.81176621201594, 4.73142620090947, 6.60622884191604 ), 31 | ci_low = c(-3.60401957599237, -3.41454238816103, -2.46757561261468, -3.95250166364274, 0, -1.25420738946099, 1.41517633761945, 1.33873906080257, 2.55652832963455, 4.38497957631853), 32 | ci_high = c(0.79792860342463, 0.919520819273338, 1.92116368812491, 0.361060535658718, 0, 2.82311144608915, 5.78261832557643, 6.28479336322932, 6.9063240721844, 8.82747810751355), 33 | estimate_names = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 34 | estimate_names_raw = c("period::1:treat", "period::2:treat", "period::3:treat", "period::4:treat", "period::5:treat", "period::6:treat", "period::7:treat", "period::8:treat", "period::9:treat", "period::10:treat"), 35 | is_ref = c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE), 36 | x = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 37 | id = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), 38 | y = c(-1.40304548628387, -1.24751078444385, -0.273205962244887, -1.79572056399201, 0, 0.784452028314083, 3.59889733159794, 3.81176621201594, 4.73142620090947, 6.60622884191604), 39 | lhs = c("y", "y", "y", "y", "y", "y", "y", "y", "y", "y"), 40 | ci_level = c(0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95) 41 | ), 42 | row.names = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"), 43 | class = "data.frame" 44 | ) 45 | 46 | 47 | # est_log |> iplot_data() |> dput() 48 | iplot_data_est_log_known = structure( 49 | list( 50 | estimate = c(0.141945718569522, -0.417790820616548, 0.429214017066127, -0.224501302353741, 0, 0.521077110055194, 0.929997803535568, 0.605730868274549, 1.00494204591818, 1.17999113089822), 51 | ci_low = c(-0.753702664910143, -1.45184513904486, -0.51466249593292, -1.14227560283322, 0, -0.324231108616199, 0.166487349184158, -0.184924971478124, 0.250280645886709, 0.34075563112618), 52 | ci_high = c(1.03759410204919, 0.616263497811766, 1.37309053006517, 0.693272998125739, 0, 1.36638532872659, 1.69350825788698, 1.39638670802722, 1.75960344594965, 2.01922663067026), 53 | estimate_names = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 54 | estimate_names_raw = c("period::1:treat", "period::2:treat", "period::3:treat", "period::4:treat", "period::5:treat", "period::6:treat", "period::7:treat", "period::8:treat", "period::9:treat", "period::10:treat"), 55 | is_ref = c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE), 56 | x = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 57 | id = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), y = c(0.141945718569522, -0.417790820616548, 0.429214017066127, -0.224501302353741, 0, 0.521077110055194, 0.929997803535568, 0.605730868274549, 1.00494204591818, 1.17999113089822), 58 | lhs = c("log(y)", "log(y)", "log(y)", "log(y)", "log(y)", "log(y)", "log(y)", "log(y)", "log(y)", "log(y)"), 59 | ci_level = c(0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95, 0.95) 60 | ), 61 | row.names = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"), 62 | class = "data.frame" 63 | ) 64 | 65 | # 66 | # tests ---- 67 | tol = 1e-6 68 | 69 | for (col in c("estimate", "ci_low", "ci_high", "estimate_names", 70 | "estimate_names_raw", "is_ref", "x", "id", "y", "lhs", "ci_level")) { 71 | expect_equivalent(iplot_data_est[[col]], iplot_data_est_known[[col]], tolerance = tol) 72 | expect_equivalent(iplot_data_est_log[[col]], iplot_data_est_log_known[[col]], tolerance = tol) 73 | } 74 | -------------------------------------------------------------------------------- /inst/tinytest/test_nthreads.R: -------------------------------------------------------------------------------- 1 | library(tinytest) 2 | 3 | exit_if_not(any(grepl("_R_CHECK", names(Sys.getenv()), fixed = TRUE))) 4 | 5 | # fixest 6 | if ( requireNamespace("fixest", quietly=TRUE) ){ 7 | library(fixest) 8 | nfx = getFixest_nthreads() 9 | expect_equal(nfx, 1, info = "Check fixest threads") 10 | } 11 | 12 | # data.table 13 | if (requireNamespace("data.table", quietly = TRUE)) { 14 | library(data.table) 15 | nDT = getDTthreads() 16 | expect_equal(nDT, 1, info = "Check data.table threads") 17 | } 18 | 19 | # magick 20 | if (requireNamespace("magick", quietly = TRUE)) { 21 | library(magick) 22 | nmg = magick:::magick_threads(1) 23 | expect_equal(nmg, 1, info = "Check magick threads") 24 | } 25 | -------------------------------------------------------------------------------- /inst/tinytest/tinysnapshot_helpers.R: -------------------------------------------------------------------------------- 1 | library(tinytest) 2 | library(tinysnapshot) 3 | options("tinysnapshot_os" = "Linux") 4 | options("tinysnapshot_device" = "svglite") 5 | options("tinysnapshot_device_args" = list(user_fonts = fontquiver::font_families("Liberation"))) 6 | -------------------------------------------------------------------------------- /man/aggr_es.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aggr_es.R 3 | \name{aggr_es} 4 | \alias{aggr_es} 5 | \title{Aggregates event-study treatment effects.} 6 | \usage{ 7 | aggr_es( 8 | object, 9 | period = c("post", "pre", "both", "diff"), 10 | rhs = 0, 11 | aggregation = c("mean", "cumulative"), 12 | abbr_term = TRUE, 13 | ... 14 | ) 15 | } 16 | \arguments{ 17 | \item{object}{A model object of class \code{fixest}, where the \code{i()} operator has 18 | been used to facilitate an "event-study" DiD design. See Examples.} 19 | 20 | \item{period}{Keyword string or numeric sequence. Which group of periods 21 | are we aggregating? Accepts the following convenience strings: \code{"post"} (the 22 | default), \code{"pre"}, \code{"both"}, or \verb{"diff} (for the difference between the post 23 | and pre periods). Alternatively, can also be a numeric sequence that 24 | designates an explicit subset of periods in the data (e.g. \code{6:8}).} 25 | 26 | \item{rhs}{Numeric. The null hypothesis value. Defaults to 0.} 27 | 28 | \item{aggregation}{Character string. The aggregation type. Either \code{"mean"} 29 | (the default) or \code{"cumulative"}.} 30 | 31 | \item{abbr_term}{Logical. Should the leading "term" column of the return 32 | data frame be abbreviated? The default is \code{TRUE}. If \code{FALSE}, then the term 33 | column will retain the full hypothesis test string as per usual with 34 | \code{\link[marginaleffects:hypotheses]{marginaleffects::hypotheses()}}. Note that this information is retained as 35 | an attribute of the return object, regardless.} 36 | 37 | \item{...}{Additional arguments passed to \code{\link[marginaleffects:hypotheses]{marginaleffects::hypotheses()}}.} 38 | } 39 | \value{ 40 | A "tidy" data frame of aggregated (pre and/or post) treatment 41 | effects, plus inferential information about standard errors, confidence 42 | intervals, etc. Potentially useful information about the underlying 43 | hypothesis test is also provided as an attribute. See Examples. 44 | } 45 | \description{ 46 | Aggregates post- (and/or pre-) treatment effects of an 47 | "event-study" estimation, also known as a dynamic difference-in-differences 48 | (DDiD) model. The event-study should have been estimated using the \code{fixest} 49 | package, which provides a specialised \code{i()} operator for this class 50 | of models. By default, the function will return the average post-treatment 51 | effect (i.e., across multiple periods). However, it can also return the 52 | cumulative post-treatment effect and can be used to aggregate pre-treatment 53 | effects too. 54 | } 55 | \examples{ 56 | library(ggfixest) ## Will load fixest too 57 | 58 | est = feols(y ~ x1 + i(period, treat, 5) | id + period, base_did) 59 | 60 | # Default hypothesis test is a null mean post-treatment effect 61 | (post_mean = aggr_es(est)) 62 | 63 | # The underlying hypothesis is saved as an attribute 64 | attr(post_mean, "hypothesis") 65 | 66 | # Other hypothesis and aggregation options 67 | aggr_es(est, period = "pre") # pre period instead of post 68 | aggr_es(est, period = "both") # pre & post periods separately 69 | aggr_es(est, period = "diff") # post vs pre difference 70 | aggr_es(est, period = 6:8) # specific subset of periods 71 | aggr_es(est, period = "pre", rhs = -1) # pre period with H0 value of 1 72 | aggr_es(est, aggregation = "cumulative") # cumulative instead of mean effects 73 | # Etc. 74 | 75 | } 76 | \seealso{ 77 | \code{\link[marginaleffects:hypotheses]{marginaleffects::hypotheses()}}, which this function is ultimately a 78 | convenience wrapper around. 79 | } 80 | -------------------------------------------------------------------------------- /man/figures/README-coefplot1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantmcdermott/ggfixest/3c8d6df3672e6f09af746c826ce49f7d4f2f3031/man/figures/README-coefplot1-1.png -------------------------------------------------------------------------------- /man/figures/README-coefplot2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantmcdermott/ggfixest/3c8d6df3672e6f09af746c826ce49f7d4f2f3031/man/figures/README-coefplot2-1.png -------------------------------------------------------------------------------- /man/figures/README-es1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantmcdermott/ggfixest/3c8d6df3672e6f09af746c826ce49f7d4f2f3031/man/figures/README-es1-1.png -------------------------------------------------------------------------------- /man/figures/README-es2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantmcdermott/ggfixest/3c8d6df3672e6f09af746c826ce49f7d4f2f3031/man/figures/README-es2-1.png -------------------------------------------------------------------------------- /man/figures/README-es3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantmcdermott/ggfixest/3c8d6df3672e6f09af746c826ce49f7d4f2f3031/man/figures/README-es3-1.png -------------------------------------------------------------------------------- /man/iplot_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iplot_data.R 3 | \name{iplot_data} 4 | \alias{iplot_data} 5 | \alias{coefplot_data} 6 | \title{Internal function for grabbing and preparing iplot data.} 7 | \usage{ 8 | iplot_data( 9 | object, 10 | .ci_level = 0.95, 11 | .keep = NULL, 12 | .drop = NULL, 13 | .dict = fixest::getFixest_dict(), 14 | .internal.only.i = TRUE, 15 | .i.select = 1, 16 | .aggr_es = NULL, 17 | .group = "auto", 18 | .vcov = NULL, 19 | .cluster = NULL, 20 | .se = NULL 21 | ) 22 | 23 | coefplot_data( 24 | object, 25 | .ci_level = 0.95, 26 | .keep = NULL, 27 | .drop = NULL, 28 | .group = "auto", 29 | .dict = fixest::getFixest_dict(), 30 | .internal.only.i = FALSE, 31 | .i.select = 1, 32 | .aggr_es = "none", 33 | .vcov = NULL, 34 | .cluster = NULL, 35 | .se = NULL 36 | ) 37 | } 38 | \arguments{ 39 | \item{object}{A model object of class \code{fixest} or \code{fixest_multi}, where 40 | the \code{i()} operator has been used to construct an interaction, or set of 41 | interactions.} 42 | 43 | \item{.ci_level}{A number between 0 and 1 indicating the desired confidence 44 | level, Defaults to 0.95.} 45 | 46 | \item{.keep}{Character vector used to subset the coefficients of interest. 47 | Passed down to \code{fixest::iplot(..., keep = .keep)} and should take the form of 48 | an acceptable regular expression.} 49 | 50 | \item{.drop}{Character vector used to subset the coefficients of interest 51 | (complement of \code{.keep}). Passed down to \code{fixest::iplot(..., drop = .drop)} 52 | and should take the form of an acceptable regular expression.} 53 | 54 | \item{.dict}{A dictionary (i.e. named character vector or a logical scalar). 55 | Used for changing coefficient names. Defaults to the values in 56 | \code{getFixest_dict()}. See the \code{?fixest::coefplot} documentation for more 57 | information. Note: This argument applies dictionary changes directly to the 58 | return object for \code{coefplot_data}. However, it is ignored for \code{iplot_data}, 59 | since we want to preserve the numeric ordering for potential event study 60 | plots. (And imposing an ordered factor would create its own downstream 61 | problems in the case of continuous plot features like ribbons.) Instead, any 62 | dictionary replacement for \code{ggiplot} is deferred to the actual plot call and 63 | applied directly to the labels.} 64 | 65 | \item{.internal.only.i}{Logical variable used for some internal function 66 | handling when passing on to coefplot/iplot.} 67 | 68 | \item{.i.select}{Integer scalar, default is 1. In (gg)iplot, used to select 69 | which variable created with i() to select. Only used when there are several 70 | variables created with i. This is an index, just try increasing numbers to 71 | hopefully obtain what you want. Passed down to 72 | \code{fixest::iplot(..., i.select = .i.select)}} 73 | 74 | \item{.aggr_es}{A keyword string or numeric sequence indicating whether the 75 | aggregated mean treatment effects for some subset of the model should be 76 | added as a column to the returned data frame. Passed to 77 | \code{aggr_es(..., aggregation = "mean")}.} 78 | 79 | \item{.group}{A list, default is missing. Each element of the list reports 80 | the coefficients to be grouped while the name of the element is the group 81 | name. Passed down to \code{fixest::coefplot(..., group = .group)}. Example of 82 | valid uses: 83 | \itemize{ 84 | \item group=list(group_name="pattern") 85 | \item group=list(group_name=c("var_start", "var_end")) 86 | \item group=list(group_name=1:2) 87 | \item See the Details section of \code{?fixest::coefplot} for more. 88 | }} 89 | 90 | \item{.vcov, .cluster, .se}{Alternative options for adjusting the standard 91 | errors of the model object on the fly. See \code{summary.fixest} for details 92 | (although note that the "." period prefix should be ignored in the latter's 93 | argument documentation). Written here in superseding order; \code{.cluster} will 94 | only be considered if \code{.vcov} is not null, etc.} 95 | } 96 | \value{ 97 | A data frame consisting of estimate values, confidence intervals, 98 | relative x-axis positions, and other aesthetic information needed to draw 99 | a ggplot2 object. 100 | } 101 | \description{ 102 | Grabs the underlying data used to construct \code{fixest::iplot}, 103 | with some added functionality and tweaks for the \code{ggiplot} equivalents. 104 | } 105 | \details{ 106 | This function is a wrapper around 107 | \code{fixest::iplot(..., only.params = TRUE)}, but with various checks and tweaks 108 | to better facilitate plotting with \code{ggplot2} and handling of complex object 109 | types (e.g. lists of fixest_multi models) 110 | } 111 | \section{Functions}{ 112 | \itemize{ 113 | \item \code{coefplot_data()}: Internal function for grabbing and preparing coefplot data 114 | 115 | }} 116 | \examples{ 117 | library(fixest) 118 | 119 | est_did = feols(y ~ x1 + i(period, treat, 5) | id+period, 120 | data = base_did) 121 | iplot(est_did, only.params = TRUE) # The "base" version 122 | iplot_data(est_did) # The wrapper provided by this package 123 | 124 | # Illustrative fixest_multi case, where the sample has been split by odd and 125 | # even ID numbers. 126 | est_split = feols(y ~ x1 + i(period, treat, 5) | id+period, 127 | data = base_did, split = ~id\%\%2) 128 | iplot(est_split, only.params = TRUE) # The "base" version 129 | iplot_data(est_split) # The wrapper provided by this package 130 | 131 | } 132 | \seealso{ 133 | \code{\link[fixest:coefplot]{fixest::iplot()}}, \code{\link[=aggr_es]{aggr_es()}}. 134 | } 135 | -------------------------------------------------------------------------------- /tests/tinytest.R: -------------------------------------------------------------------------------- 1 | ## Throttle CPU threads if R CMD check (for CRAN) 2 | 3 | if (any(grepl("_R_CHECK", names(Sys.getenv()), fixed = TRUE))) { 4 | # fixest 5 | if (requireNamespace("fixest", quietly = TRUE)) { 6 | library(fixest) 7 | setFixest_nthreads(1) 8 | } 9 | 10 | # data.table 11 | if (requireNamespace("data.table", quietly = TRUE)) { 12 | library(data.table) 13 | setDTthreads(1) 14 | } 15 | 16 | # magick 17 | if (requireNamespace("magick", quietly = TRUE)) { 18 | library(magick) 19 | magick:::magick_threads(1) 20 | } 21 | } 22 | 23 | 24 | # Run tinytest suite 25 | 26 | if ( requireNamespace("tinytest", quietly=TRUE) ){ 27 | 28 | tinytest::test_package("ggfixest") 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | --------------------------------------------------------------------------------