blob: 0004ec89a36b3cb5833f48b479f7bc90e3fb9794 [file] [log] [blame]
Hao Zhu3166f062017-06-26 07:51:46 -10001#' Collapse repeated rows to multirow cell
Hao Zhu2a87e8e2017-06-14 15:49:33 -04002#'
Hao Zhu3166f062017-06-26 07:51:46 -10003#' @description
Hao Zhuf4b35292017-06-25 22:38:37 -10004#' @export
5collapse_rows <- function(kable_input, columns = NULL) {
6 # if (is.null(columns)) {
7 # stop("Please specify numeric positions of columns you want to collapse.")
8 # }
Hao Zhu2a87e8e2017-06-14 15:49:33 -04009 kable_format <- attr(kable_input, "format")
10 if (!kable_format %in% c("html", "latex")) {
11 message("Currently generic markdown table using pandoc is not supported.")
12 return(kable_input)
13 }
14 if (kable_format == "html") {
15 return(collapse_rows_html(kable_input, columns))
16 }
17 if (kable_format == "latex") {
18 return(collapse_rows_latex(kable_input, columns))
19 }
20}
21
22collapse_rows_html <- function(kable_input, columns) {
23 kable_attrs <- attributes(kable_input)
24 kable_xml <- read_xml(as.character(kable_input), options = "COMPACT")
25 kable_tbody <- xml_tpart(kable_xml, "tbody")
26
27 kable_dt <- rvest::html_table(xml2::read_html(as.character(kable_input)))[[1]]
Hao Zhuf4b35292017-06-25 22:38:37 -100028 if (is.null(columns)) {
29 columns <- seq(1, ncol(kable_dt))
30 }
Hao Zhu2a87e8e2017-06-14 15:49:33 -040031 kable_dt$row_id <- rownames(kable_dt)
32 collapse_matrix <- collapse_row_matrix(kable_dt, columns)
33
34 for (i in 1:nrow(collapse_matrix)) {
35 matrix_row <- collapse_matrix[i, ]
Hao Zhu3166f062017-06-26 07:51:46 -100036 target_row <- xml_child(kable_tbody, i)
37 row_node_rm_count <- 0
38 for (j in 1:length(matrix_row)) {
39 collapsing_col <- as.numeric(sub("x", "", names(matrix_row)[j])) -
40 row_node_rm_count
41 target_cell <- xml_child(target_row, collapsing_col)
42 if (matrix_row[j] == 0) {
43 xml_remove(target_cell)
44 row_node_rm_count <- row_node_rm_count + 1
45 } else if (matrix_row[j] != 1) {
46 xml_attr(target_cell, "rowspan") <- matrix_row[j]
47 xml_attr(target_cell, "style") <- paste0(
48 xml_attr(target_cell, "style"),
49 "vertical-align: middle !important;")
Hao Zhu2a87e8e2017-06-14 15:49:33 -040050 }
51 }
52 }
53
54 out <- structure(as.character(kable_xml), format = "html",
55 class = "knitr_kable")
56 attributes(out) <- kable_attrs
57 return(out)
58}
59
Hao Zhuf4b35292017-06-25 22:38:37 -100060collapse_row_matrix <- function(kable_dt, columns, html = T) {
61 if (html) {
62 column_block <- function(x) c(x, rep(0, x - 1))
63 } else {
64 column_block <- function(x) c(rep(0, x - 1), x)
65 }
66 mapping_matrix <- list()
67 for (i in columns) {
68 mapping_matrix[[paste0("x", i)]] <- unlist(lapply(
69 rle(kable_dt[, i])$length, column_block))
70 }
71 mapping_matrix <- data.frame(mapping_matrix)
72 return(mapping_matrix)
73}
74
Hao Zhu2a87e8e2017-06-14 15:49:33 -040075collapse_rows_latex <- function(kable_input, columns) {
Hao Zhuf4b35292017-06-25 22:38:37 -100076 table_info <- magic_mirror(kable_input)
77 if (is.null(columns)) {
78 columns <- seq(1, table_info$ncol)
79 }
80 if (!table_info$booktabs) {
81 warning("add_header_left only supports LaTeX table with booktabs. Please",
82 " use kable(..., booktabs = T) in your kable function.")
83 }
84 out <- as.character(kable_input)
85 contents <- table_info$contents
86 kable_dt <- kable_dt_latex(contents)
87 collapse_matrix <- collapse_row_matrix(kable_dt, columns, html = F)
88
89 new_kable_dt <- kable_dt
90 new_contents <- c()
91 for (j in seq(1:ncol(collapse_matrix))) {
92 column_align <- table_info$align_vector_origin[columns[j]]
93 column_width <- ifelse(
94 is.null(table_info$column_width[[paste0("column_", columns[j])]]),
95 "*", table_info$column_width[paste0("column_", columns[j])])
96 for (i in seq(1:nrow(collapse_matrix))) {
97 new_kable_dt[i, j] <- collapse_new_dt_item(
98 kable_dt[i, j], collapse_matrix[i, j], column_width, align = column_align
99 )
100 }
101 }
102 for (i in seq(1:nrow(collapse_matrix))) {
103 new_contents[i] <- paste0(new_kable_dt[i, ], collapse = " & ")
104 out <- sub(contents[i + 1], new_contents[i], out)
105 }
106
107 out <- structure(out, format = "latex", class = "knitr_kable")
108 table_info$collapse_rows <- TRUE
109 attr(out, "kable_meta") <- table_info
110 return(out)
111}
112
113kable_dt_latex <- function(x) {
114 data.frame(do.call(rbind, str_split(x[-1], " & ")), stringsAsFactors = FALSE)
115}
116
117collapse_new_dt_item <- function(x, span, width = NULL, align) {
118 if (span == 0) return("")
119 if (span == 1) return(x)
120 out <- paste0(
121 "\\\\multirow\\{", -span, "\\}\\{",
122 ifelse(is.null(width), "\\*", width),
123 "\\}\\{",
124 switch(align,
125 "l" = "\\\\raggedright\\\\arraybackslash ",
126 "c" = "\\\\centering\\\\arraybackslash ",
127 "r" = "\\\\raggedleft\\\\arraybackslash "),
128 x, "\\}"
129 )
130 return(out)
Hao Zhu2a87e8e2017-06-14 15:49:33 -0400131}