Hao Zhu | 2a87e8e | 2017-06-14 15:49:33 -0400 | [diff] [blame] | 1 | #' Collapse repeat rows to multirow cell |
| 2 | #' |
Hao Zhu | 1111f72 | 2017-06-14 15:58:40 -0400 | [diff] [blame] | 3 | #' @description Experimenting. Don't use it in production. |
Hao Zhu | f4b3529 | 2017-06-25 22:38:37 -1000 | [diff] [blame^] | 4 | #' @export |
| 5 | collapse_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 Zhu | 2a87e8e | 2017-06-14 15:49:33 -0400 | [diff] [blame] | 9 | 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 | |
| 22 | collapse_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 Zhu | f4b3529 | 2017-06-25 22:38:37 -1000 | [diff] [blame^] | 28 | if (is.null(columns)) { |
| 29 | columns <- seq(1, ncol(kable_dt)) |
| 30 | } |
Hao Zhu | 2a87e8e | 2017-06-14 15:49:33 -0400 | [diff] [blame] | 31 | 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 Zhu | f4b3529 | 2017-06-25 22:38:37 -1000 | [diff] [blame^] | 64 | collapse_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 Zhu | 2a87e8e | 2017-06-14 15:49:33 -0400 | [diff] [blame] | 79 | collapse_rows_latex <- function(kable_input, columns) { |
Hao Zhu | f4b3529 | 2017-06-25 22:38:37 -1000 | [diff] [blame^] | 80 | 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 | |
| 117 | kable_dt_latex <- function(x) { |
| 118 | data.frame(do.call(rbind, str_split(x[-1], " & ")), stringsAsFactors = FALSE) |
| 119 | } |
| 120 | |
| 121 | collapse_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 Zhu | 2a87e8e | 2017-06-14 15:49:33 -0400 | [diff] [blame] | 135 | } |