/nested-table.R

https://github.com/jthomasmock/reactable-presentation · R · 287 lines · 188 code · 76 blank · 23 comment · 7 complexity · 7b41986411a4d7b7227c81c64b99c143 MD5 · raw file

  1. library(tidyverse)
  2. library(reactable)
  3. library(htmltools)
  4. # Read in the data --------------------------------------------------------
  5. raw_pets <- read_csv("animal-complaints.csv") %>%
  6. janitor::clean_names()
  7. glimpse(raw_pets)
  8. # Make it wider for a table -----------------------------------------------
  9. wide_data <- raw_pets %>%
  10. separate(date_received, into = c("month", "year"), sep = " ") %>%
  11. mutate(electoral_division = str_extract(electoral_division, "[:digit:]+") %>%
  12. as.integer(),
  13. month_name = factor(month, levels = month.name, labels = month.abb),
  14. month_num = match(month, month.name)) %>%
  15. count(year, month_name, complaint_type, animal_type) %>%
  16. arrange(month_name, year) %>%
  17. pivot_wider(names_from = month_name, values_from = n) %>%
  18. filter(animal_type == "dog", between(year, 2014, 2019)) %>%
  19. select(-animal_type) %>%
  20. arrange(complaint_type)
  21. wide_data %>%
  22. print(n = 36)
  23. # Make pretty colors ------------------------------------------------------
  24. make_color_pal <- function(colors, bias = 1) {
  25. get_color <- colorRamp(colors, bias = bias)
  26. function(x) rgb(get_color(x), maxColorValue = 255)
  27. }
  28. green_pal <- make_color_pal(c("#ffffff", "#f2fbd2", "#c9ecb4", "#93d3ab", "#35b0ab"), bias = 2)
  29. green_pal(0.1)
  30. green_pal(0.9)
  31. # Something more helpful --------------------------------------------------
  32. scales::show_col("blue3")
  33. scales::show_col("red4")
  34. # Using our palette -------------------------------------------------------
  35. green_pal(0.1) %>% scales::show_col()
  36. green_pal(0.5) %>% scales::show_col()
  37. green_pal(0.9) %>% scales::show_col()
  38. # Not the most efficient --------------------------------------------------
  39. seq(0.01, 0.99, 0.05)
  40. seq(0.01, 0.99, 0.05) %>%
  41. green_pal() %>%
  42. scales::show_col()
  43. # Basic table -------------------------------------------------------------
  44. wide_data %>%
  45. reactable()
  46. # Too much data -----------------------------------------------------------
  47. wide_sum_df <- wide_data %>%
  48. select(complaint_type, everything()) %>%
  49. group_by(complaint_type) %>%
  50. summarize(year = paste(min(year), str_sub(max(year), -2), sep = " - "),
  51. across(c(Jan:Dec), mean)) %>%
  52. ungroup() %>%
  53. mutate(across(c(Jan:Dec), ~round(.x, digits = 1)))
  54. wide_sum_df
  55. wide_sum_df %>%
  56. reactable()
  57. # More complex ------------------------------------------------------------
  58. wide_sum_df %>%
  59. reactable(
  60. compact = TRUE,
  61. defaultColDef = colDef(
  62. minWidth = 60,
  63. align = "right",
  64. format = colFormat(digits = 1)
  65. ),
  66. list(
  67. complaint_type = colDef("Complaint",
  68. width = 200,
  69. align = "left"
  70. ),
  71. year = colDef("Year",
  72. width = 80,
  73. align = "left"
  74. )))
  75. # Let's add some more data back in ----------------------------------------
  76. wide_data %>%
  77. filter(complaint_type == "Attack") %>%
  78. select(complaint_type, everything())
  79. unique(wide_data$complaint_type)
  80. # Select by position ------------------------------------------------------
  81. unique(wide_data$complaint_type)[1]
  82. unique(wide_data$complaint_type)[2]
  83. unique(wide_data$complaint_type)[3]
  84. unique(wide_data$complaint_type)[4]
  85. more_data <- wide_data %>%
  86. filter(complaint_type == wide_data$complaint_type[1]) %>%
  87. select(complaint_type, everything())
  88. more_data
  89. # Basic more data plot ----------------------------------------------------
  90. reactable(more_data)
  91. # More complexity ---------------------------------------------------------
  92. reactable(
  93. more_data,
  94. defaultColDef = colDef(
  95. minWidth = 60,
  96. style = function(value){
  97. if (!is.numeric(value)) return()
  98. tall_in_data <- more_data %>%
  99. pivot_longer(cols = Jan:Dec,
  100. names_to = "months",
  101. values_to = "values") %>%
  102. pull(values)
  103. normalized <- (value - min(tall_in_data)) / (max(tall_in_data) - min(tall_in_data))
  104. color <- green_pal(normalized)
  105. list(background = color)
  106. }),
  107. columns = list(
  108. year = colDef("Year"),
  109. complaint_type = colDef("",width = 250)))
  110. # Ok let's break that down! -----------------------------------------------
  111. tall_ex_data <- more_data %>%
  112. pivot_longer(cols = Jan:Dec,
  113. names_to = "months",
  114. values_to = "values") %>%
  115. pull(values)
  116. tall_ex_data
  117. normalized_ex <- (tall_ex_data - min(tall_ex_data)) / (max(tall_ex_data) - min(tall_ex_data))
  118. normalized_ex
  119. color_ex <- green_pal(normalized_ex)
  120. color_ex
  121. color_ex %>% scales::show_col()
  122. # OK big step! ------------------------------------------------------------
  123. # Let's put everything back together
  124. # Remember this? ----------------------------------------------------------
  125. wide_sum_df %>%
  126. reactable(
  127. defaultColDef = colDef(
  128. minWidth = 60,
  129. align = "right",
  130. format = colFormat(digits = 1)
  131. ),
  132. list(
  133. complaint_type = colDef("Complaint",
  134. width = 200,
  135. align = "left"
  136. ),
  137. year = colDef("Year",
  138. width = 80,
  139. align = "left"
  140. )))
  141. # And this? ---------------------------------------------------------------
  142. reactable(
  143. more_data,
  144. defaultColDef = colDef(
  145. minWidth = 60,
  146. style = function(value){
  147. if (!is.numeric(value)) return()
  148. tall_in_data <- more_data %>%
  149. pivot_longer(cols = Jan:Dec,
  150. names_to = "months",
  151. values_to = "values") %>%
  152. pull(values)
  153. normalized <- (value - min(tall_in_data)) / (max(tall_in_data) - min(tall_in_data))
  154. color <- green_pal(normalized)
  155. list(background = color)
  156. }),
  157. columns = list(
  158. year = colDef("Year"),
  159. complaint_type = colDef("",width = 250)))
  160. # Let's put them together! ------------------------------------------------
  161. # To put them together, we'll need to use a function
  162. # We just adjusted our code from previously
  163. nest_table <- function(index){
  164. # filter to just the complaint_type of interest
  165. in_data <- wide_data %>%
  166. filter(complaint_type == unique(wide_data$complaint_type)[index]) %>%
  167. select(complaint_type, everything())
  168. htmltools::div(
  169. style = list("padding: 30px", "align: right"),
  170. reactable(
  171. defaultSortOrder = "desc",
  172. in_data,
  173. defaultColDef = colDef(
  174. minWidth = 40,
  175. style = function(value){
  176. if (!is.numeric(value)) return()
  177. tall_in_data <- in_data %>%
  178. pivot_longer(cols = Jan:Dec,
  179. names_to = "months",
  180. values_to = "values") %>%
  181. pull(values)
  182. normalized <- (value - min(tall_in_data)) / (max(tall_in_data) - min(tall_in_data))
  183. color <- green_pal(normalized)
  184. list(background = color)
  185. }),
  186. columns = list(
  187. year = colDef("Year"),
  188. complaint_type = colDef("",width = 250))))
  189. }
  190. # Use the function --------------------------------------------------------
  191. wide_sum_df %>%
  192. reactable(
  193. compact = TRUE,
  194. defaultSortOrder = "desc",
  195. defaultColDef = colDef(
  196. minWidth = 40,
  197. align = "right",
  198. format = colFormat(digits = 1)
  199. ),
  200. columns = list(
  201. complaint_type = colDef("Complaint",
  202. width = 200,
  203. align = "left"
  204. ),
  205. year = colDef("Year",
  206. width = 80,
  207. align = "left"
  208. )),
  209. details = function(index){
  210. nest_table(index)
  211. })