blob: a4293e65e73d9af83b4534e6f2b4a07e668b6a2b [file] [log] [blame]
Hao Zhu2a87e8e2017-06-14 15:49:33 -04001#' Collapse repeat rows to multirow cell
2#'
Hao Zhu1111f722017-06-14 15:58:40 -04003#' @description Experimenting. Don't use it in production.
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, ]
36 if (sum(matrix_row) != length(matrix_row)) {
37 target_row <- xml_child(kable_tbody, i)
38 row_node_rm_count <- 0
39 for (j in 1:length(matrix_row)) {
40 if (matrix_row[j] != 1) {
41 collapsing_col <- as.numeric(sub("x", "", names(matrix_row)[j])) -
42 row_node_rm_count
43 target_cell <- xml_child(target_row, collapsing_col)
44 if (matrix_row[j] == 0) {
45 xml_remove(target_cell)
46 row_node_rm_count <- row_node_rm_count + 1
47 } else {
48 xml_attr(target_cell, "rowspan") <- matrix_row[j]
49 xml_attr(target_cell, "style") <- paste0(
50 xml_attr(target_cell, "style"),
51 "vertical-align: middle !important;")
52 }
53 }
54 }
55 }
56 }
57
58 out <- structure(as.character(kable_xml), format = "html",
59 class = "knitr_kable")
60 attributes(out) <- kable_attrs
61 return(out)
62}
63
Hao Zhuf4b35292017-06-25 22:38:37 -100064collapse_row_matrix <- function(kable_dt, columns, html = T) {
65 if (html) {
66 column_block <- function(x) c(x, rep(0, x - 1))
67 } else {
68 column_block <- function(x) c(rep(0, x - 1), x)
69 }
70 mapping_matrix <- list()
71 for (i in columns) {
72 mapping_matrix[[paste0("x", i)]] <- unlist(lapply(
73 rle(kable_dt[, i])$length, column_block))
74 }
75 mapping_matrix <- data.frame(mapping_matrix)
76 return(mapping_matrix)
77}
78
Hao Zhu2a87e8e2017-06-14 15:49:33 -040079collapse_rows_latex <- function(kable_input, columns) {
Hao Zhuf4b35292017-06-25 22:38:37 -100080 table_info <- magic_mirror(kable_input)
81 if (is.null(columns)) {
82 columns <- seq(1, table_info$ncol)
83 }
84 if (!table_info$booktabs) {
85 warning("add_header_left only supports LaTeX table with booktabs. Please",
86 " use kable(..., booktabs = T) in your kable function.")
87 }
88 out <- as.character(kable_input)
89 contents <- table_info$contents
90 kable_dt <- kable_dt_latex(contents)
91 collapse_matrix <- collapse_row_matrix(kable_dt, columns, html = F)
92
93 new_kable_dt <- kable_dt
94 new_contents <- c()
95 for (j in seq(1:ncol(collapse_matrix))) {
96 column_align <- table_info$align_vector_origin[columns[j]]
97 column_width <- ifelse(
98 is.null(table_info$column_width[[paste0("column_", columns[j])]]),
99 "*", table_info$column_width[paste0("column_", columns[j])])
100 for (i in seq(1:nrow(collapse_matrix))) {
101 new_kable_dt[i, j] <- collapse_new_dt_item(
102 kable_dt[i, j], collapse_matrix[i, j], column_width, align = column_align
103 )
104 }
105 }
106 for (i in seq(1:nrow(collapse_matrix))) {
107 new_contents[i] <- paste0(new_kable_dt[i, ], collapse = " & ")
108 out <- sub(contents[i + 1], new_contents[i], out)
109 }
110
111 out <- structure(out, format = "latex", class = "knitr_kable")
112 table_info$collapse_rows <- TRUE
113 attr(out, "kable_meta") <- table_info
114 return(out)
115}
116
117kable_dt_latex <- function(x) {
118 data.frame(do.call(rbind, str_split(x[-1], " & ")), stringsAsFactors = FALSE)
119}
120
121collapse_new_dt_item <- function(x, span, width = NULL, align) {
122 if (span == 0) return("")
123 if (span == 1) return(x)
124 out <- paste0(
125 "\\\\multirow\\{", -span, "\\}\\{",
126 ifelse(is.null(width), "\\*", width),
127 "\\}\\{",
128 switch(align,
129 "l" = "\\\\raggedright\\\\arraybackslash ",
130 "c" = "\\\\centering\\\\arraybackslash ",
131 "r" = "\\\\raggedleft\\\\arraybackslash "),
132 x, "\\}"
133 )
134 return(out)
Hao Zhu2a87e8e2017-06-14 15:49:33 -0400135}