I am struggling to create a latex table that also has the shape of distributions by group in one of the cells, as sparklines. I tried using kintr::kable
and kableExtra
packages with the spec_plot
function. I create a reproducible example, maybe someone can help. Note that the code also uses dplyr
; path
is a string, specifying the location on my local hard drive:
This is the sample of the dataset:
dat_plot <- structure(list(year = c(2015, 2016, 2017, 2018, 2019, 2015, 2016,
2017, 2018, 2019, 2013, 2014, 2015, 2016, 2017), country_name = c("Austria",
"Austria", "Austria", "Austria", "Austria", "Belgium", "Belgium",
"Belgium", "Belgium", "Belgium", "Bulgaria", "Bulgaria", "Bulgaria",
"Bulgaria", "Bulgaria"), no_party_share = c(33.3333333333333,
33.3333333333333, 42.8571428571429, 50, 50, 75, 75, 75, 75, 75,
100, 100, 100, 100, 100), no_party_mean = c(20, 20, 20, 20, 20,
42, 42, 42, 42, 42, 99, 99, 99, 99, 99), no_party_diff = c(50,
50, 50, 50, 50, 75, 75, 75, 75, 75, 20, 20, 20, 20, 20), nonideo_share = c(16.6666666666667,
16.6666666666667, 28.5714285714286, 33.3333333333333, 33.3333333333333,
8.33333333333333, 8.33333333333333, 8.33333333333333, 8.33333333333333,
8.33333333333333, 100, 100, 100, 100, 90), nonideo_mean = c(26,
26, 26, 26, 26, 13, 13, 13, 13, 13, 76, 76, 76, 76, 76), nonideo_diff = c(0,
0, 0, 0, 0, 8, 8, 8, 8, 8, 50, 50, 50, 50, 50), movement_share = c(0,
0, 0, 0, 0, 16.6666666666667, 16.6666666666667, 16.6666666666667,
16.6666666666667, 16.6666666666667, 40, 40, 33.3333333333333,
40, 40), movement_mean = c(0, 0, 0, 0, 0, 13, 13, 13, 13, 13,
39, 39, 39, 39, 39), movement_diff = c(0, 0, 0, 0, 0, 17, 17,
17, 17, 17, 0, 0, 0, 0, 0)), class = c("grouped_df", "tbl_df",
"tbl", "data.frame"), row.names = c(NA, -15L), groups = structure(list(
country_name = c("Austria", "Belgium", "Bulgaria"), .rows = structure(list(
1:5, 6:10, 11:15), ptype = integer(0), class = c("vctrs_list_of",
"vctrs_vctr", "list"))), row.names = c(NA, -3L), class = c("tbl_df",
"tbl", "data.frame"), .drop = TRUE))
Here is what I tried:
no_party_list <- split(dat_plot$no_party_share, dat_plot$country_name)
nonideo_list <- split(dat_plot$nonideo_share, dat_plot$country_name)
movement_list <- split(dat_plot$movement_share, dat_plot$country_name)
options(knitr.kable.NA = "")
mod_res <- dat_plot %>%
select(country_name, no_party_mean, no_party_diff, nonideo_mean, nonideo_diff,
movement_mean, movement_diff) %>%
unique(.) %>%
kbl(caption = "Distribution of brands by country \\label{tab:country_table}",
booktabs = T, format = "latex", col.names = NULL, linesep = "") %>%
# kable_styling(latex_options = c("hold_position")) %>%
kable_styling(latex_options = "HOLD_position") %>% # Change will be here
column_spec(3, image = spec_plot(no_party_list, same_lim = "FALSE")) %>%
column_spec(6, image = spec_plot(no_party_list, same_lim = "FALSE")) %>%
column_spec(9, image = spec_plot(no_party_list, same_lim = "FALSE")) %>%
add_header_above(c(" " = 1, "Mean" = 1, "Diff." = 1, "Shape"=1,
"Mean" = 1, "Diff." = 1, "Shape"=1,
"Mean" = 1, "Diff." = 1, "Shape"=1)) %>%
add_header_above(c(" " = 1, "No party label" = 3, "Nonideo. ref." = 3, "Movement ref." = 3))
writeLines(mod_res, paste0(path, "Paper/Tables/", "/desc_countries_table.tex"))
When it creates the tex file the \includegraphics
refer to an empty path. The spec_plot
somehow does not generate the image files, at least I don't see them locally in the relevant folder. When I try to build it with latex, embedded in a longer latex document where this table should feature, I get a seemingly unrelated error:
Package array Error: Illegal pream-token (N): `c' used. [\begin{tabular}[t]{lr>{}rrr>{}rrNA>{}NA}]
My guess is that saving the table with writeLines
does not properly run the code and does not create local copies of the required images. I also tried save_kable
, but I think that's not the way to go. I would appreciate any help on this!
There are several issues with your code.
There are only seven columns in the dataframe that you are passing to kbl()
, but in column_spec()
you are trying to insert image to the Ninth column, which eventually leads to the latex error Package array Error: Illegal pream-token (N)
In add_header_above()
you are trying to colspan for 10 columns where you have only seven columns.
writeLines()
will not include the necessary packages in the tex file.
Therefore at first we need to create placeholder columns to insert the sparkline images. Also we can create a wrapper function around the spec_plot()
with the dir
and file_type
argument specified so that sparkline images are saved as pdfs
into a local directory of our working directory.
options(knitr.kable.NA = "")
# wrapper function ---------------------------------------------------
spec_plot2 <- function(...) {
kableExtra::spec_plot(dir = "spec_plots", file_type = "pdf",
same_lim = FALSE, ...)
}
# --------------------------------------------------------------------
mod_res <- dat_plot %>%
select(country_name, no_party_mean, no_party_diff, nonideo_mean, nonideo_diff, movement_mean,
movement_diff) %>%
unique() %>%
mutate( no_party_shape = "", nonideo_shape = "", movement_shape = "" ) %>%
select(country_name, matches("no_party"), matches("nonideo"), matches("movement")) %>%
kbl(
caption = "Distribution of brands by country \\label{tab:country_table}",
col.names = c(" ", "Mean", "Diff.", "Shape", "Mean", "Diff.", "Shape",
"Mean", "Diff.", "Shape"),
booktabs = TRUE,
format = "latex",
linesep = ""
) %>%
kable_styling(latex_options = "HOLD_position") %>% # Change will be here
column_spec(4, image = spec_plot2(no_party_list)) %>%
column_spec(7, image = spec_plot2(nonideo_list)) %>%
column_spec(10, image = spec_plot2(movement_list)) %>%
add_header_above(c(" " = 1, "No party label" = 3, "Nonideo. ref." = 3,
"Movement ref." = 3))
Now the mod_res
looks like,
\begin{table}[H]
\caption{Distribution of brands by country \label{tab:country_table}}
\centering
\begin{tabular}[t]{lrr>{}lrr>{}lrr>{}l}
\toprule
\multicolumn{1}{c}{ } & \multicolumn{3}{c}{No party label} & \multicolumn{3}{c}{Nonideo. ref.} & \multicolumn{3}{c}{Movement ref.} \\
\cmidrule(l{3pt}r{3pt}){2-4} \cmidrule(l{3pt}r{3pt}){5-7} \cmidrule(l{3pt}r{3pt}){8-10}
& Mean & Diff. & Shape & Mean & Diff. & Shape & Mean & Diff. & Shape\\
\midrule
Austria & 20 & 50 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_308038534be7.pdf} & 26 & 0 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30806ac26855.pdf} & 0 & 0 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30801a2357ab.pdf}\\
Belgium & 42 & 75 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30802e60317c.pdf} & 13 & 8 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_308031982ca3.pdf} & 13 & 17 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30802f9657b0.pdf}\\
Bulgaria & 99 & 20 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30801e4c5ec8.pdf} & 76 & 50 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30806cb263d5.pdf} & 39 & 0 & \includegraphics[width=0.67in, height=0.17in]{file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30805ced3db5.pdf}\\
\bottomrule
\end{tabular}
\end{table}
and the plots are saved as pdfs in the spec_plots
directory. Now Then changing the absolute paths of pdfs to a relative ones (that is, changing from file:///C:/Users/User/Desktop/rmd_SO/spec_plots/plot_30805ced3db5.pdf
to spec_plots/plot_30805ced3db5.pdf
) and copy-pasting the above tex output with the following packages (By skimming over the tex output we can get the idea which packages we need, to compile this tex output)
\usepackage{booktabs}
\usepackage{array}
\usepackage{float}
\usepackage{graphicx}
in a tex file and compiling the tex file we get the desired table.
main.tex
\documentclass{article}
\usepackage{booktabs}
\usepackage{array}
\usepackage{float}
\usepackage{graphicx}
\begin{document}
\begin{table}[H]
\caption{Distribution of brands by country \label{tab:country_table}}
\centering
\begin{tabular}[t]{lrr>{}lrr>{}lrr>{}l}
\toprule
\multicolumn{1}{c}{ } & \multicolumn{3}{c}{No party label} & \multicolumn{3}{c}{Nonideo. ref.} & \multicolumn{3}{c}{Movement ref.} \\
\cmidrule(l{3pt}r{3pt}){2-4} \cmidrule(l{3pt}r{3pt}){5-7} \cmidrule(l{3pt}r{3pt}){8-10}
& Mean & Diff. & Shape & Mean & Diff. & Shape & Mean & Diff. & Shape\\
\midrule
Austria & 20 & 50 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_308038534be7.pdf} & 26 & 0 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_30806ac26855.pdf} & 0 & 0 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_30801a2357ab.pdf}\\
Belgium & 42 & 75 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_30802e60317c.pdf} & 13 & 8 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_308031982ca3.pdf} & 13 & 17 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_30802f9657b0.pdf}\\
Bulgaria & 99 & 20 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_30801e4c5ec8.pdf} & 76 & 50 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_30806cb263d5.pdf} & 39 & 0 & \includegraphics[width=0.33in, height=0.17in]{spec_plots/plot_30805ced3db5.pdf}\\
\bottomrule
\end{tabular}
\end{table}
\end{document}
(Note that, I have changed the width and height in the \includegraphics
a bit)
Now if we want, we can also get the tex output in a tex file using save_kable()
with the keep_tex
argument set as TRUE
mod_res %>%
save_kable("mod_res.pdf", keep_tex = TRUE)
This will generate the mod_res.tex
file in our working directory with an exclusive list of latex packages (some of which aren't specifically needed for compiling this table).
Though this save_kable
command is not generating the mod_res.pdf
file in my windows machine due to a latex compilation error (which is due to the fact that plot-pdfs paths are appended with file:///
), We are getting the mod_res.tex
file which we want actually.