You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
6.0KB

  1. #' Insert a vector y inside another vector x at position
  2. #'
  3. #' The vectors in the y list will be inserted
  4. #' at positions respectively *after* the x[position] element of x
  5. #'
  6. #' @param x A vector to be inserted into
  7. #' @param y A vector or list of vectors to insert into x
  8. #' @param position The position / vector of positions to insert vector(s) y in vector x
  9. #' @return The combined vector
  10. insert <- function(x, y, position) {
  11. # y is supposed to be a list of vectors. If it is a single vector, make it a simple list containing that vector
  12. if (!is.list(y)) y <- list(y)
  13. # Stop if there is not as many positions as vectors to insert
  14. stopifnot(length(y) == length(position))
  15. # Create an empty return vector that will contain the partition of x and the inserts
  16. result <- vector("list", 2 * length(position) + 1)
  17. # Split x in groups between the insert positions
  18. old <- split(x, cumsum(seq_along(x) %in% (position + 1)))
  19. # Insert the x splits at odd positions in result
  20. result[seq(from = 1, by = 2, length.out = length(old))] <- old
  21. # Insert the y inserts at even positions in results
  22. result[c(F, T)] <- y
  23. # Return a simple vector
  24. unlist(result)
  25. }
  26. #' Set the "desctable" class to the passed object
  27. #'
  28. #' @param x Object to set the "desctable" class to
  29. #' @return The object with the class "desctable"
  30. set_desctable_class <- function(x) {
  31. class(x) <- "desctable"
  32. x
  33. }
  34. #' Parse a formula
  35. #'
  36. #' Parse a formula defining the conditions to pick a stat/test
  37. #'
  38. #' Parse a formula defining the conditions to pick a stat/test
  39. #' and return the function to use.
  40. #' The formula is to be given in the form of
  41. #' conditional ~ T | F
  42. #' and conditions can be nested such as
  43. #' conditional1 ~ (conditional2 ~ T | F) | F
  44. #' The FALSE option can be omitted, and the TRUE can be replaced with NA
  45. #'
  46. #' @param x The variable to test it on
  47. #' @param f A formula to parse
  48. #' @return A function to use as a stat/test
  49. parse_formula <- function(x, f) {
  50. parse_f <- function(x) {
  51. if (length(x) == 1) as.character(x)
  52. else {
  53. if (as.character(x[[1]]) == "~") {
  54. paste0("if (", parse_f(x[[2]]), "(x)) ",
  55. "{",
  56. parse_f(x[[3]]),
  57. "}")
  58. } else if (as.character(x[[1]]) == "|") {
  59. paste0(parse_f(x[[2]]),
  60. "} else ",
  61. "{",
  62. parse_f(x[[3]]))
  63. } else if (as.character(x[[1]]) == "(") {
  64. parse_f(x[[2]])
  65. }
  66. }
  67. }
  68. eval(parse(text = parse_f(f)))
  69. }
  70. #' Build the header for pander
  71. #'
  72. #' @param head A headerList object
  73. #' @return A names vector
  74. head_pander <- function(head) {
  75. if (is.integer(head[[1]])) {
  76. head %>%
  77. names %>%
  78. lapply(function(x){c(x, rep("", head[[x]] - 1))}) %>%
  79. unlist()
  80. } else {
  81. paste(head %>%
  82. names() %>%
  83. lapply(function(x){c(x, rep("", attr(head[[x]], "colspan") - 1))}) %>%
  84. unlist(),
  85. head %>%
  86. lapply(head_pander) %>%
  87. unlist(),
  88. sep = "<br/>")
  89. }
  90. }
  91. #' Build the header for datatable
  92. #'
  93. #' @param head A headerList object
  94. #' @return An htmltools$tags object containing the header
  95. head_datatable <- function(head) {
  96. TRs <- list()
  97. while (is.list(head[[1]])) {
  98. TR <- purrr::map2(names(head), lapply(head, attr, "colspan"), ~htmltools::tags$th(.x, colspan = .y))
  99. TRs <- c(TRs, list(TR))
  100. head <- purrr::flatten(head)
  101. }
  102. c(TRs, list(purrr::map2(names(head), head, ~htmltools::tags$th(.x, colspan = .y))))
  103. }
  104. #' Build the header for dataframe
  105. #'
  106. #' @param head A headerList object
  107. #' @return A names vector
  108. head_dataframe <- function(head) {
  109. if (is.integer(head[[1]])) {
  110. head %>%
  111. names() %>%
  112. lapply(function(x){rep(x, head[[x]])}) %>%
  113. unlist()
  114. } else {
  115. paste(head %>%
  116. names() %>%
  117. lapply(function(x){rep(x, attr(head[[x]], "colspan"))}) %>%
  118. unlist(),
  119. head %>%
  120. lapply(head_pander) %>%
  121. unlist(),
  122. sep = " / ")
  123. }
  124. }
  125. #' Build header
  126. #'
  127. #' Take a desctable object and create a suitable header for the mentionned output.
  128. #' Output can be one of "pander", "datatable", or "dataframe".
  129. #'
  130. #' @param desctable A desctable object
  131. #' @param output An output format for the header
  132. #' @return A header object in the output format
  133. header <- function(desctable, output = c("pander", "datatable", "dataframe")) {
  134. desctable[-1] %>%
  135. flatten_desctable() %>%
  136. data.frame(check.names = F) %>%
  137. names() -> nm
  138. desctable <- desctable[-1]
  139. if (length(desctable) == 1) {
  140. if (output == "datatable") {
  141. c("\u00A0", nm) %>%
  142. lapply(htmltools::tags$th) %>%
  143. htmltools::tags$tr() %>%
  144. htmltools::tags$thead() %>%
  145. htmltools::tags$table(class = "display")
  146. } else c("\u00A0", nm)
  147. } else {
  148. head <- headerList(desctable)
  149. if (output == "pander") {
  150. c("\u00A0", head_pander(head) %>%
  151. paste(nm, sep = "<br/>"))
  152. } else if (output == "datatable") {
  153. head <- c(head_datatable(head), list(nm %>% lapply(htmltools::tags$th)))
  154. head[[1]] <- c(list(htmltools::tags$th(rowspan = length(head))), head[[1]])
  155. head %>%
  156. lapply(htmltools::tags$tr) %>%
  157. htmltools::tags$thead() %>%
  158. htmltools::tags$table(class = "display")
  159. } else if (output == "dataframe") {
  160. c("\u00A0", head_dataframe(head) %>% paste(nm, sep = " / "))
  161. }
  162. }
  163. }
  164. #' Build a header list object
  165. #'
  166. #' @param desctable A desctable
  167. #' @return A nested list of headers with colspans
  168. headerList <- function(desctable) {
  169. if (is.data.frame(desctable)) length(desctable)
  170. else {
  171. rec <- lapply(desctable, headerList)
  172. if (is.integer(rec[[1]])) attr(rec, "colspan") <- rec %>% unlist() %>% sum()
  173. else attr(rec, "colspan") <- rec %>% lapply(attr, "colspan") %>% unlist() %>% sum()
  174. rec
  175. }
  176. }
  177. #' Flatten a desctable to a dataframe recursively
  178. #'
  179. #' @param desctable A desctable object
  180. #' @return A flat dataframe
  181. flatten_desctable <- function(desctable) {
  182. if (is.data.frame(desctable)) desctable
  183. else {
  184. desctable %>%
  185. lapply(flatten_desctable) %>%
  186. Reduce(f = cbind)
  187. }
  188. }