blob: 7300eb31dae45e56176efcf7127f29cc1bbe9baf [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")) {
Hao Zhu401ebd82018-01-14 17:10:20 -050037 warning("Please specify format in kable. kableExtra can customize either ",
38 "HTML or LaTeX outputs. See https://haozhu233.github.io/kableExtra/ ",
39 "for details.")
Hao Zhu62cdde52017-05-20 22:16:03 -040040 return(kable_input)
41 }
Hao Zhu49483bf2017-09-12 11:21:00 -040042 if (is.null(index)) {
43 if (kable_format == "html") {
44 return(group_rows_html(kable_input, group_label, start_row, end_row,
45 label_row_css, escape))
46 }
47 if (kable_format == "latex") {
48 return(group_rows_latex(kable_input, group_label, start_row, end_row,
49 latex_gap_space, escape))
50 }
51 } else {
52 index <- group_row_index_translator(index)
53 out <- kable_input
54 if (kable_format == "html") {
55 for (i in 1:nrow(index)) {
56 out <- group_rows_html(out, index$header[i],
57 index$start[i], index$end[i],
58 label_row_css, escape)
59 }
60 }
61 if (kable_format == "latex") {
62 for (i in 1:nrow(index)) {
63 out <- group_rows_latex(out, index$header[i],
64 index$start[i], index$end[i],
65 latex_gap_space, escape)
66 }
67 }
68 return(out)
Hao Zhu62cdde52017-05-20 22:16:03 -040069 }
Hao Zhu49483bf2017-09-12 11:21:00 -040070}
71
72group_row_index_translator <- function(index) {
73 index <- standardize_header_input(index)
74 index$start <- cumsum(c(1, index$colspan))[1:length(index$colspan)]
75 index$end <- cumsum(index$colspan)
76 index$header <- trimws(index$header)
77 index <- index[index$header != "", ]
78 return(index)
Hao Zhu62cdde52017-05-20 22:16:03 -040079}
80
Hao Zhud972e7f2017-05-22 13:27:15 -040081group_rows_html <- function(kable_input, group_label, start_row, end_row,
Hao Zhuac7e70f2017-08-02 00:18:36 -040082 label_row_css, escape) {
Hao Zhu62cdde52017-05-20 22:16:03 -040083 kable_attrs <- attributes(kable_input)
Hao Zhu558c72f2017-07-24 15:12:00 -040084 kable_xml <- read_kable_as_xml(kable_input)
Hao Zhu62cdde52017-05-20 22:16:03 -040085 kable_tbody <- xml_tpart(kable_xml, "tbody")
86
Hao Zhuac7e70f2017-08-02 00:18:36 -040087 if (escape) {
88 group_label <- escape_html(group_label)
89 }
90
Hao Zhu62cdde52017-05-20 22:16:03 -040091 group_header_rows <- attr(kable_input, "group_header_rows")
92 group_seq <- seq(start_row, end_row)
93 if (!is.null(group_header_rows)) {
94 group_seq <- positions_corrector(group_seq, group_header_rows,
95 length(xml_children(kable_tbody)))
96 }
97
98 # Insert a group header row
99 starting_node <- xml_child(kable_tbody, group_seq[1])
100 kable_ncol <- length(xml_children(starting_node))
101 group_header_row_text <- paste0(
Hao Zhud972e7f2017-05-22 13:27:15 -0400102 '<tr groupLength="', length(group_seq), '"><td colspan="', kable_ncol,
103 '" style="', label_row_css, '"><strong>', group_label,
104 "</strong></td></tr>"
Hao Zhu62cdde52017-05-20 22:16:03 -0400105 )
106 group_header_row <- read_xml(group_header_row_text, options = "COMPACT")
107 xml_add_sibling(starting_node, group_header_row, .where = "before")
108
109 # add indentations to items
Hao Zhuf2dfd142017-07-24 14:43:28 -0400110 out <- as_kable_xml(kable_xml)
Hao Zhu62cdde52017-05-20 22:16:03 -0400111 attributes(out) <- kable_attrs
112 attr(out, "group_header_rows") <- c(attr(out, "group_header_rows"), group_seq[1])
Hao Zhu49483bf2017-09-12 11:21:00 -0400113 out <- add_indent_html(out, positions = seq(start_row, end_row))
Hao Zhu62cdde52017-05-20 22:16:03 -0400114 return(out)
115}
Hao Zhud972e7f2017-05-22 13:27:15 -0400116
Hao Zhufc14c9b2017-05-22 14:03:22 -0400117group_rows_latex <- function(kable_input, group_label, start_row, end_row,
Hao Zhuac7e70f2017-08-02 00:18:36 -0400118 gap_space, escape) {
Hao Zhud972e7f2017-05-22 13:27:15 -0400119 table_info <- magic_mirror(kable_input)
Hao Zhud2c0f732017-08-26 10:40:14 -0400120 out <- enc2utf8(as.character(kable_input))
Hao Zhud972e7f2017-05-22 13:27:15 -0400121
Hao Zhu064990d2017-10-17 18:08:42 -0400122 if (table_info$duplicated_rows) {
123 dup_fx_out <- fix_duplicated_rows_latex(out, table_info)
124 out <- dup_fx_out[[1]]
125 table_info <- dup_fx_out[[2]]
126 }
127
Hao Zhuac7e70f2017-08-02 00:18:36 -0400128 if (escape) {
129 group_label <- escape_latex(group_label)
130 group_label <- gsub("\\\\", "\\\\\\\\", group_label)
131 }
132
Hao Zhud972e7f2017-05-22 13:27:15 -0400133 # Add group label
134 rowtext <- table_info$contents[start_row + 1]
135 if (table_info$booktabs) {
136 new_rowtext <- paste0(
137 "\\\\addlinespace[", gap_space, "]\n",
138 "\\\\multicolumn{", table_info$ncol, "}{l}{\\\\textbf{", group_label,
139 "}}\\\\\\\\\n",
140 rowtext
141 )
Hao Zhud972e7f2017-05-22 13:27:15 -0400142 } else {
143 rowtext <- paste0("\\\\hline\n", rowtext)
144 new_rowtext <- paste0(
145 "\\\\hline\n\\\\multicolumn{", table_info$ncol, "}{l}{\\\\textbf{",
146 group_label, "}}\\\\\\\\\n", rowtext
147 )
148 }
149 out <- sub(rowtext, new_rowtext, out)
Hao Zhu8f202992017-07-15 02:20:18 -0400150 out <- gsub("\\\\addlinespace\n", "", out)
Hao Zhud2c0f732017-08-26 10:40:14 -0400151 out <- structure(out, format = "latex", class = "knitr_kable")
Hao Zhu32f43f72017-06-20 18:24:54 -0400152 table_info$group_rows_used <- TRUE
153 attr(out, "kable_meta") <- table_info
Hao Zhu49483bf2017-09-12 11:21:00 -0400154 out <- add_indent_latex(out, seq(start_row, end_row))
Hao Zhud972e7f2017-05-22 13:27:15 -0400155 return(out)
156}