blob: 0d31bbd622de41e17a7694e6eeab8c106e038c60 [file] [log] [blame]
Hao Zhu62cdde52017-05-20 22:16:03 -04001#' Put a few rows of a table into one category
2#'
Hao Zhubd95bb22017-05-22 16:08:49 -04003#' @description Group a few rows in a table together under a label.
4#'
5#' @param kable_input Output of `knitr::kable()` with `format` specified
6#' @param group_label A character string for the name of the group
7#' @param start_row A numeric value that tells the function in which row the
8#' group starts. Note that the counting excludes header rows and other group
9#' labeling rows
10#' @param end_row A numeric value that tells the function in which row the group
11#' ends.
Hao Zhu49483bf2017-09-12 11:21:00 -040012#' @param index A named vector providing the index for robust row-grouping tasks.
13#' Basically, you can use it in the same way as `add_header_above()`.
Hao Zhubd95bb22017-05-22 16:08:49 -040014#' @param label_row_css A character string for any customized css used for the
15#' labeling row. By default, the labeling row will have a solid black line
16#' underneath. Only useful for HTML documents.
17#' @param latex_gap_space A character value telling LaTeX how large the gap
18#' between the previous row and the group labeling row. Only useful for LaTeX
19#' documents.
Hao Zhuac7e70f2017-08-02 00:18:36 -040020#' @param escape A T/F value showing whether special characters should be
21#' escaped.
Hao Zhubd95bb22017-05-22 16:08:49 -040022#'
Hao Zhu78e61222017-05-24 20:53:35 -040023#' @examples x <- knitr::kable(head(mtcars), "html")
24#' # Put Row 2 to Row 5 into a Group and label it as "Group A"
25#' group_rows(x, "Group A", 2, 5)
26#'
Hao Zhu62cdde52017-05-20 22:16:03 -040027#' @export
Hao Zhu49483bf2017-09-12 11:21:00 -040028group_rows <- function(kable_input, group_label = NULL,
29 start_row = NULL, end_row = NULL,
30 index = NULL,
Hao Zhud972e7f2017-05-22 13:27:15 -040031 label_row_css = "border-bottom: 1px solid;",
Hao Zhu49483bf2017-09-12 11:21:00 -040032 latex_gap_space = "0.3em",
Hao Zhuac7e70f2017-08-02 00:18:36 -040033 escape = TRUE) {
Hao Zhu49483bf2017-09-12 11:21:00 -040034
Hao Zhu62cdde52017-05-20 22:16:03 -040035 kable_format <- attr(kable_input, "format")
36 if (!kable_format %in% c("html", "latex")) {
37 message("Currently generic markdown table using pandoc is not supported.")
38 return(kable_input)
39 }
Hao Zhu49483bf2017-09-12 11:21:00 -040040 if (is.null(index)) {
41 if (kable_format == "html") {
42 return(group_rows_html(kable_input, group_label, start_row, end_row,
43 label_row_css, escape))
44 }
45 if (kable_format == "latex") {
46 return(group_rows_latex(kable_input, group_label, start_row, end_row,
47 latex_gap_space, escape))
48 }
49 } else {
50 index <- group_row_index_translator(index)
51 out <- kable_input
52 if (kable_format == "html") {
53 for (i in 1:nrow(index)) {
54 out <- group_rows_html(out, index$header[i],
55 index$start[i], index$end[i],
56 label_row_css, escape)
57 }
58 }
59 if (kable_format == "latex") {
60 for (i in 1:nrow(index)) {
61 out <- group_rows_latex(out, index$header[i],
62 index$start[i], index$end[i],
63 latex_gap_space, escape)
64 }
65 }
66 return(out)
Hao Zhu62cdde52017-05-20 22:16:03 -040067 }
Hao Zhu49483bf2017-09-12 11:21:00 -040068}
69
70group_row_index_translator <- function(index) {
71 index <- standardize_header_input(index)
72 index$start <- cumsum(c(1, index$colspan))[1:length(index$colspan)]
73 index$end <- cumsum(index$colspan)
74 index$header <- trimws(index$header)
75 index <- index[index$header != "", ]
76 return(index)
Hao Zhu62cdde52017-05-20 22:16:03 -040077}
78
Hao Zhud972e7f2017-05-22 13:27:15 -040079group_rows_html <- function(kable_input, group_label, start_row, end_row,
Hao Zhuac7e70f2017-08-02 00:18:36 -040080 label_row_css, escape) {
Hao Zhu62cdde52017-05-20 22:16:03 -040081 kable_attrs <- attributes(kable_input)
Hao Zhu558c72f2017-07-24 15:12:00 -040082 kable_xml <- read_kable_as_xml(kable_input)
Hao Zhu62cdde52017-05-20 22:16:03 -040083 kable_tbody <- xml_tpart(kable_xml, "tbody")
84
Hao Zhuac7e70f2017-08-02 00:18:36 -040085 if (escape) {
86 group_label <- escape_html(group_label)
87 }
88
Hao Zhu62cdde52017-05-20 22:16:03 -040089 group_header_rows <- attr(kable_input, "group_header_rows")
90 group_seq <- seq(start_row, end_row)
91 if (!is.null(group_header_rows)) {
92 group_seq <- positions_corrector(group_seq, group_header_rows,
93 length(xml_children(kable_tbody)))
94 }
95
96 # Insert a group header row
97 starting_node <- xml_child(kable_tbody, group_seq[1])
98 kable_ncol <- length(xml_children(starting_node))
99 group_header_row_text <- paste0(
Hao Zhud972e7f2017-05-22 13:27:15 -0400100 '<tr groupLength="', length(group_seq), '"><td colspan="', kable_ncol,
101 '" style="', label_row_css, '"><strong>', group_label,
102 "</strong></td></tr>"
Hao Zhu62cdde52017-05-20 22:16:03 -0400103 )
104 group_header_row <- read_xml(group_header_row_text, options = "COMPACT")
105 xml_add_sibling(starting_node, group_header_row, .where = "before")
106
107 # add indentations to items
Hao Zhuf2dfd142017-07-24 14:43:28 -0400108 out <- as_kable_xml(kable_xml)
Hao Zhu62cdde52017-05-20 22:16:03 -0400109 attributes(out) <- kable_attrs
110 attr(out, "group_header_rows") <- c(attr(out, "group_header_rows"), group_seq[1])
Hao Zhu49483bf2017-09-12 11:21:00 -0400111 out <- add_indent_html(out, positions = seq(start_row, end_row))
Hao Zhu62cdde52017-05-20 22:16:03 -0400112 return(out)
113}
Hao Zhud972e7f2017-05-22 13:27:15 -0400114
Hao Zhufc14c9b2017-05-22 14:03:22 -0400115group_rows_latex <- function(kable_input, group_label, start_row, end_row,
Hao Zhuac7e70f2017-08-02 00:18:36 -0400116 gap_space, escape) {
Hao Zhud972e7f2017-05-22 13:27:15 -0400117 table_info <- magic_mirror(kable_input)
Hao Zhud2c0f732017-08-26 10:40:14 -0400118 out <- enc2utf8(as.character(kable_input))
Hao Zhud972e7f2017-05-22 13:27:15 -0400119
Hao Zhuac7e70f2017-08-02 00:18:36 -0400120 if (escape) {
121 group_label <- escape_latex(group_label)
122 group_label <- gsub("\\\\", "\\\\\\\\", group_label)
123 }
124
Hao Zhud972e7f2017-05-22 13:27:15 -0400125 # Add group label
126 rowtext <- table_info$contents[start_row + 1]
127 if (table_info$booktabs) {
128 new_rowtext <- paste0(
129 "\\\\addlinespace[", gap_space, "]\n",
130 "\\\\multicolumn{", table_info$ncol, "}{l}{\\\\textbf{", group_label,
131 "}}\\\\\\\\\n",
132 rowtext
133 )
Hao Zhud972e7f2017-05-22 13:27:15 -0400134 } else {
135 rowtext <- paste0("\\\\hline\n", rowtext)
136 new_rowtext <- paste0(
137 "\\\\hline\n\\\\multicolumn{", table_info$ncol, "}{l}{\\\\textbf{",
138 group_label, "}}\\\\\\\\\n", rowtext
139 )
140 }
141 out <- sub(rowtext, new_rowtext, out)
Hao Zhu8f202992017-07-15 02:20:18 -0400142 out <- gsub("\\\\addlinespace\n", "", out)
Hao Zhud2c0f732017-08-26 10:40:14 -0400143 out <- structure(out, format = "latex", class = "knitr_kable")
Hao Zhu32f43f72017-06-20 18:24:54 -0400144 table_info$group_rows_used <- TRUE
145 attr(out, "kable_meta") <- table_info
Hao Zhu49483bf2017-09-12 11:21:00 -0400146 out <- add_indent_latex(out, seq(start_row, end_row))
Hao Zhud972e7f2017-05-22 13:27:15 -0400147 return(out)
148}