vincentarelbundock / modelsummary

Beautiful and customizable model summaries in R.
http://modelsummary.com
Other
911 stars 75 forks source link

`kableExtra` LaTeX with post-processing functions #760

Closed hhadah closed 5 months ago

hhadah commented 5 months ago

I am using the following code to create a latex table using modelsummary and kableExtra. The code used to work but it seems that either modelsummary or kableExtra made some changes that made it harder to use both. Is there a way that editing the following table would result in a latex table being saved? I already tried change output = "kableExtra" and the latex table was empty.

I used the modelsummary codes many times before and the product was an amazing latex table that could use in latex documents. I tried the code above that I ran many times before and was expecting the table to be saved in a script called tab01-etwfe.tex. Is there a way to fix this?

Here is the code I am using

mod_es = list(
    "Poor Health"       = emfx(models$`Poor Health`, type = "event", collapse = FALSE),
    "School Lunch"      = emfx(models$`School Lunch`, type = "event", collapse = FALSE),
    "Log School Lunch"  = emfx(models$`Log School Lunch`, type = "event", collapse = FALSE),
    "SNAP"              = emfx(models$`SNAP`, type = "event", collapse = FALSE),
    "Log SNAP"          = emfx(models$`Log SNAP`, type = "event", collapse = FALSE)
    )

# Quick renaming function to replace ".Dtreat" with something more meaningful
rename_fn = function(old_names) {
  new_names = gsub(".Dtreat", "Years post treatment =", old_names)
  setNames(new_names, old_names)
}

f1 <- function(x) format(round(x, 3), big.mark=".")
f2 <- function(x) format(round(x, 0), big.mark=",")

gm <- list(
  list(raw = "nobs", clean = "Observations", fmt = f2),
  list(raw = "FE..first_treat", clean = "Cohort FE", fmt = 0),
  list(raw = "FE..year", clean = "Year FE", fmt = 0),
  list(raw = "std.error.type", clean = "Standard Errors", fmt = 0)
)
options("modelsummary_format_numeric_latex" = "plain")

regression_tab  <- modelsummary(
  mod_es, 
  fmt         = f1, 
  shape       = term:event:statistic ~ model,
  coef_rename = rename_fn,
  stars       = c('***' = 0.01, '**' = 0.05, '*' = 0.1),
  gof_map     = gm,
  escape      = F,
  output = "latex",
  title       = "Extended Two Way Fixed Effects \\label{tab:etwfe}") |>
  kable_styling(latex_options = c("scale_down", "HOLD_position")) |> 
  footnote(number = c("\\\\footnotesize{Each column is the results of the extended two-way fixed effects estimation. 
                      Standard errors are clustered on the county level.}",
                      "\\\\footnotesize{Data source is the 1994-2019 Current Population Survey.}"),
           footnote_as_chunk = F, title_format = c("italic"),
           escape = F, threeparttable = T, fixed_small_size = T)

regression_tab %>%
  save_kable(file.path(tables_wd,"tab01-etwfe.tex"))

Doing the following edit produces a mutant latex document with html table

regression_tab  <- modelsummary(
  mod_es, 
  fmt         = f1, 
  shape       = term:event:statistic ~ model,
  coef_rename = rename_fn,
  stars       = c('***' = 0.01, '**' = 0.05, '*' = 0.1),
  gof_map     = gm,
  escape      = F,
  title       = "Extended Two Way Fixed Effects \\label{tab:etwfe}") |>
  kable(format = "latex", booktabs = T) |> 
  kable_styling(latex_options = c("scale_down", "HOLD_position")) |> 
  footnote(number = c("\\\\footnotesize{Each column is the results of the extended two-way fixed effects estimation. 
                      Standard errors are clustered on the county level.}",
                      "\\\\footnotesize{The samples include first, second, third, and fourth+ generation Hispanic children ages 17 and below who live in intact families. 
                      A first-generation Hispanic child is one that is born in a Spanish-speaking country. 
                      A second-generation Hispanic child is one that is born in the United States with at least one parent born in a Spanish-speaking country.
                      Third-generation Hispanic immigrant children are native-born with native-born parents and at least one grandparent is born in a Spanish-speaking country.
                      country.
                      Fourth-generation+ are native born with native-born parents, all grandparents are born in the United States, and one parent self-reported Hispanic identity.}",
                      "\\\\footnotesize{Data source is the 1994-2019 Current Population Survey.}"),
           footnote_as_chunk = F, title_format = c("italic"),
           escape = F, threeparttable = T, fixed_small_size = T)

Output Table:

\begin{table}[H]
\centering
\resizebox{\ifdim\width>\linewidth\linewidth\else\width\fi}{!}{
\begin{threeparttable}
\begin{tabular}{l}
\toprule
x\\
\midrule
<table style="NAborder-bottom: 0; width: auto !important; margin-left: auto; margin-right: auto;" class="table">
<caption>Extended Two Way Fixed Effects \textbackslash{}label\{tab:etwfe\}</caption>
 <thead>
  <tr>
   <th style="text-align:left;">   </th>
   <th style="text-align:center;"> Poor Health </th>
   <th style="text-align:center;"> School Lunch </th>
   <th style="text-align:center;"> Log School Lunch </th>
   <th style="text-align:center;"> SNAP </th>
   <th style="text-align:center;"> Log SNAP </th>
  </tr>
 </thead>
<tbody>
  <tr>
   <td style="text-align:left;"> Years post treatment = 0 </td>
   <td style="text-align:center;"> 0.000 </td>
   <td style="text-align:center;"> 0.001 </td>
   <td style="text-align:center;"> 0.006 </td>
   <td style="text-align:center;"> 0.001 </td>
   <td style="text-align:center;"> 0.001 </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.004) </td>
   <td style="text-align:center;"> (0.028) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.024) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 1 </td>
   <td style="text-align:center;"> 0.000* </td>
   <td style="text-align:center;"> 0.006 </td>
   <td style="text-align:center;"> 0.039 </td>
   <td style="text-align:center;"> 0.004 </td>
   <td style="text-align:center;"> 0.024 </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.004) </td>
   <td style="text-align:center;"> (0.026) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.025) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 2 </td>
   <td style="text-align:center;"> 0.001* </td>
   <td style="text-align:center;"> 0.009 </td>
   <td style="text-align:center;"> 0.058 </td>
   <td style="text-align:center;"> 0.006 </td>
   <td style="text-align:center;"> 0.037 </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.007) </td>
   <td style="text-align:center;"> (0.057) </td>
   <td style="text-align:center;"> (0.004) </td>
   <td style="text-align:center;"> (0.035) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 3 </td>
   <td style="text-align:center;"> 0.001** </td>
   <td style="text-align:center;"> 0.031*** </td>
   <td style="text-align:center;"> 0.200*** </td>
   <td style="text-align:center;"> 0.020*** </td>
   <td style="text-align:center;"> 0.156*** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.009) </td>
   <td style="text-align:center;"> (0.071) </td>
   <td style="text-align:center;"> (0.006) </td>
   <td style="text-align:center;"> (0.047) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 4 </td>
   <td style="text-align:center;"> 0.000** </td>
   <td style="text-align:center;"> 0.026** </td>
   <td style="text-align:center;"> 0.193*** </td>
   <td style="text-align:center;"> 0.014*** </td>
   <td style="text-align:center;"> 0.106** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.011) </td>
   <td style="text-align:center;"> (0.066) </td>
   <td style="text-align:center;"> (0.005) </td>
   <td style="text-align:center;"> (0.042) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 5 </td>
   <td style="text-align:center;"> 0.000** </td>
   <td style="text-align:center;"> 0.027** </td>
   <td style="text-align:center;"> 0.193*** </td>
   <td style="text-align:center;"> 0.015** </td>
   <td style="text-align:center;"> 0.103** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.012) </td>
   <td style="text-align:center;"> (0.074) </td>
   <td style="text-align:center;"> (0.006) </td>
   <td style="text-align:center;"> (0.050) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 6 </td>
   <td style="text-align:center;"> 0.001*** </td>
   <td style="text-align:center;"> 0.031*** </td>
   <td style="text-align:center;"> 0.229*** </td>
   <td style="text-align:center;"> 0.024*** </td>
   <td style="text-align:center;"> 0.196*** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.025) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.021) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 7 </td>
   <td style="text-align:center;"> 0.001*** </td>
   <td style="text-align:center;"> 0.006* </td>
   <td style="text-align:center;"> 0.123*** </td>
   <td style="text-align:center;"> 0.022*** </td>
   <td style="text-align:center;"> 0.177*** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.025) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.022) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 8 </td>
   <td style="text-align:center;"> 0.000 </td>
   <td style="text-align:center;"> 0.012*** </td>
   <td style="text-align:center;"> 0.151*** </td>
   <td style="text-align:center;"> 0.024*** </td>
   <td style="text-align:center;"> 0.190*** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.004) </td>
   <td style="text-align:center;"> (0.027) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.023) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 9 </td>
   <td style="text-align:center;"> 0.000* </td>
   <td style="text-align:center;"> 0.040*** </td>
   <td style="text-align:center;"> 0.393*** </td>
   <td style="text-align:center;"> 0.019*** </td>
   <td style="text-align:center;"> 0.148*** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.004) </td>
   <td style="text-align:center;"> (0.030) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.023) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 10 </td>
   <td style="text-align:center;"> 0.000 </td>
   <td style="text-align:center;"> 0.054*** </td>
   <td style="text-align:center;"> 0.595*** </td>
   <td style="text-align:center;"> 0.016*** </td>
   <td style="text-align:center;"> 0.128*** </td>
  </tr>
  <tr>
   <td style="text-align:left;">  </td>
   <td style="text-align:center;"> (0) </td>
   <td style="text-align:center;"> (0.004) </td>
   <td style="text-align:center;"> (0.041) </td>
   <td style="text-align:center;"> (0.003) </td>
   <td style="text-align:center;"> (0.022) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Years post treatment = 11 </td>
   <td style="text-align:center;"> 0.001*** </td>
   <td style="text-align:center;"> 0.124*** </td>
   <td style="text-align:center;"> 1.049*** </td>
   <td style="text-align:center;"> 0.065*** </td>
   <td style="text-align:center;"> 0.534*** </td>
  </tr>
  <tr>
   <td style="text-align:left;box-shadow: 0px 1.5px">  </td>
   <td style="text-align:center;box-shadow: 0px 1.5px"> (0) </td>
   <td style="text-align:center;box-shadow: 0px 1.5px"> (0.008) </td>
   <td style="text-align:center;box-shadow: 0px 1.5px"> (0.098) </td>
   <td style="text-align:center;box-shadow: 0px 1.5px"> (0.014) </td>
   <td style="text-align:center;box-shadow: 0px 1.5px"> (0.117) </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Observations </td>
   <td style="text-align:center;"> 976,648 </td>
   <td style="text-align:center;"> 976,648 </td>
   <td style="text-align:center;"> 976,648 </td>
   <td style="text-align:center;"> 976,648 </td>
   <td style="text-align:center;"> 976,648 </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Cohort FE </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Year FE </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
   <td style="text-align:center;"> X </td>
  </tr>
</tbody>
<tfoot><tr><td style="padding: 0; " colspan="100\%">
<sup></sup> * p </td></tr></tfoot>
</table>\\
\bottomrule
\end{tabular}
\begin{tablenotes}
\small
\item[1] \footnotesize{Each column is the results of the extended two-way fixed effects estimation. 
                      Standard errors are clustered on the county level.}
\item[2] \footnotesize{The samples include first, second, third, and fourth+ generation Hispanic children ages 17 and below who live in intact families. 
                      A first-generation Hispanic child is one that is born in a Spanish-speaking country. 
                      A second-generation Hispanic child is one that is born in the United States with at least one parent born in a Spanish-speaking country.
                      Third-generation Hispanic immigrant children are native-born with native-born parents and at least one grandparent is born in a Spanish-speaking country.
                      country.
                      Fourth-generation+ are native born with native-born parents, all grandparents are born in the United States, and one parent self-reported Hispanic identity.}
\item[3] \footnotesize{Data source is the 1994-2019 Current Population Survey.}
\end{tablenotes}
\end{threeparttable}}
\end{table}
vincentarelbundock commented 5 months ago

I do not believe that modelsummary has changed anything in its treatment of kableExtra, but I can't be 100% sure because there were lots of changes in 2.0.0. Note that kableExtra also had a few changes recently.

Your example is not reproducible and it is extremely complicated. Could you isolate the actually problematic behavior and supply a minimal reproducible example?

hhadah commented 5 months ago

Here is a minimal reproducible example. The code runs perfectly until modelsummary is pipped into kableExtra function. It used to be the case that you could pipe a modelsummary(output="latex") into the kableExtra functions. Now the following code would produce the following error below. Editing modelsummary(output="latex") to modelsummary(output="kableExtra") would get the code to run but the save_kable output becomes an empty latex document.

# Data for linear regression (using the mtcars dataset)
library(modelsummary)
library(kableExtra)
data(mtcars)
linear_model <- lm(mpg ~ wt + qsec, data = mtcars)

regression_tab  <- modelsummary(
  linear_model, 
  stars       = c('***' = 0.01, '**' = 0.05, '*' = 0.1),
  escape      = F,
  output     = "latex") |>
  kable_styling(latex_options = c("scale_down", "HOLD_position")) |> 
  footnote(number = c("\\\\footnotesize{Each column is the results of the extended two-way fixed effects estimation. 
                      Standard errors are clustered on the county level.}"),
           footnote_as_chunk = F, title_format = c("italic"),
           escape = F, threeparttable = T, fixed_small_size = T)

regression_tab %>%
  save_kable(file.path(tables_wd,"tab01-reg.tex"))

Error:

Error in if (kable_format %in% c("pipe", "markdown")) { : 
  argument is of length zero
vincentarelbundock commented 5 months ago

Try:

options(modelsummary_factory_latex = "kableExtra")

and output="latex"

hhadah commented 5 months ago

That did it! Thank you @vincentarelbundock for your help and the modelsummary (and other functions). They are life savers!

vincentarelbundock commented 5 months ago

Success!!