Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

299 lines
9.8KB

  1. #' Gets the ... names
  2. #'
  3. #' Used to get the -year
  4. #'
  5. #' @param ... arguments
  6. #'
  7. #' @return a vector of the names of ...
  8. #'
  9. #' @examples
  10. #' x <- 1:10
  11. #' y <- 1
  12. #' get_quos_names(-x)
  13. #' get_quos_names(x:y)
  14. get_quos_names <- function(...) {
  15. q <- quos(...)
  16. sapply(q, function(i) as.character(i[[2]]))
  17. }
  18. #' Parses a simple vector so that it looks like its input
  19. #'
  20. #' @param x a vector
  21. #'
  22. #' @return a string
  23. #'
  24. #' @examples
  25. #' dput_parser("x")
  26. #' dput_parser(c("x", "y"))
  27. dput_parser <- function(x) {
  28. ifelse(length(x) == 1,
  29. sprintf("'%s'", x),
  30. paste0("c(",
  31. paste(sprintf("'%s'", x), collapse = ", "),
  32. ")"))
  33. }
  34. #' Adds color to processed tidy data
  35. #'
  36. #' @param x a processed data-frame as outputted by process_long or process_wide
  37. #' @param key_values the unique key-values
  38. #' @param color_fun the color function
  39. #' @param color_header the color for the header
  40. #' @param ... not used
  41. #'
  42. #' @return a data-frame with the colors
  43. #'
  44. #' @examples
  45. #' NULL
  46. add_color_tidyr <- function(x, key_values,
  47. color_fun = scales::brewer_pal(type = "qual", "Set1"),
  48. color_header = "#737373",
  49. color_id = "#d0d0d0") {
  50. color_dict <- color_fun(3)
  51. names(color_dict) <- c("id", "key", "value")
  52. x %>% mutate(.color = ifelse(.id_map == ".header" & !.val %in% key_values,
  53. color_header,
  54. color_dict[.type]))
  55. }
  56. #' Processes a wide dataframe and converts it into a dataset that can be plotted
  57. #'
  58. #' @param x a wide data frame
  59. #' @param ids a vector of id-variables that are already in the tidy-format
  60. #' @param key a vector of key-variables
  61. #' @param color_id the color for the id-body
  62. #' @param ...
  63. #'
  64. #' @return
  65. #'
  66. #' @examples
  67. #' wide <- data_frame(
  68. #' year = 2010:2011,
  69. #' Alice = c(105, 110),
  70. #' Bob = c(100, 97),
  71. #' Charlie = c(90, 95)
  72. #' )
  73. #' process_wide(wide, ids = "year", key = "person")
  74. #' process_wide(wide, ids = "year", key = "person") %>% static_plot
  75. process_wide <- function(x, ids, key, color_id = "lightgray", ...) {
  76. if (!all(ids %in% names(x)))
  77. stop("all ids must be in x")
  78. nr <- nrow(x)
  79. nc <- ncol(x)
  80. key_values <- names(x)
  81. key_values <- key_values[!key_values %in% ids]
  82. id_values <- x %>% select(one_of(ids))
  83. id_values <- id_values %>% gather(key = ".key_map", value = ".id_map")
  84. x <- x %>% mutate(.r = row_number()) %>%
  85. unite(one_of(ids), col = ".id_map", remove = F)
  86. x <- x %>%
  87. gather(key = ".col", value = ".val", names(x) %>% str_subset("^[^\\.]")) %>%
  88. mutate(.key_map = .col,
  89. .type = ifelse(.col %in% ids, "id", "value"),
  90. .val = as.character(.val),
  91. .x = rep(1:nc, each = nr),
  92. .y = -rep(1:nr, nc))
  93. # the .key_map == ids need to be redirected to the key-values and multiplied
  94. ids_key_map <- tidyr::crossing(.key_map = key_values, .col = ids)
  95. x <- bind_rows(
  96. x %>% filter(!.key_map %in% ids),
  97. x %>% filter(.key_map %in% ids) %>% select(-.key_map) %>% left_join(ids_key_map, by = ".col")
  98. )
  99. # due to the untidiness of the wide-data, we need to treat the keys in th header
  100. # specially
  101. key_mapper <- tidyr::crossing(id_values %>% select(.id_map),
  102. .key_map = key_values) %>%
  103. mutate(.id_map = as.character(.id_map))
  104. key_frame <- data_frame(.r = 0, .col = key_values,
  105. .val = key_values, .x = 1:length(key_values) + length(ids),
  106. .y = 0, .type = "key", .key_map = key_values) %>%
  107. left_join(key_mapper, by = ".key_map")
  108. # add headers
  109. x <- x %>% bind_rows(
  110. data_frame(.id_map = ".header", .r = 0, .col = ids, .val = ids,
  111. .x = 1:length(ids), .y = 0, .type = "id", .key_map = key_values),
  112. key_frame,
  113. .
  114. ) %>%
  115. unite(.id_map, .key_map, .val, col = ".id", remove = F)
  116. x %>% add_color_tidyr(key_values = key_values) %>%
  117. mutate(.alpha = ifelse(.id_map == ".header", 1, 0.6))
  118. }
  119. #' Processes a long dataframe and converts it into a dataset that can be plotted
  120. #'
  121. #' @param x a long data frame
  122. #' @param ids a vector of id-variables that are already in the tidy-format
  123. #' @param key a vector of key-variables
  124. #' @param ...
  125. #'
  126. #' @return
  127. #'
  128. #' @examples
  129. #' long <- data_frame(
  130. #' year = c(2010L, 2011L, 2010L, 2011L, 2010L, 2011L),
  131. #' person = c("Alice", "Alice", "Bob", "Bob", "Charlie", "Charlie"),
  132. #' sales = c(105, 110, 100, 97, 90, 95)
  133. #' )
  134. #' process_long(long, ids = "year", key = "person", value = "sales")
  135. #' process_long(long, ids = "year", key = "person", value = "sales") %>% static_plot
  136. process_long <- function(x, ids, key, value, ...) {
  137. if (!all(c(ids, key, value) %in% names(x)))
  138. stop("all ids, key, and value must be names of x")
  139. nr <- nrow(x)
  140. nc <- ncol(x)
  141. x <- x %>% mutate(.r = row_number()) %>%
  142. unite(ids, col = ".id_map", remove = F) %>%
  143. unite(key, col = ".key_map", remove = F)
  144. key_values <- x %>% pull(key) %>% unique()
  145. type_dict <- c(rep("id", length(ids)), rep("key", length(key)), rep("value", length(value)))
  146. names(type_dict) <- c(rep(ids, length(ids)), rep(key, length(key)), rep(value, length(value)))
  147. x <- x %>%
  148. gather(key = ".col", value = ".val", names(x) %>% str_subset("^[^\\.]")) %>%
  149. mutate(
  150. .x = rep(1:nc, each = nr),
  151. .y = -rep(1:nr, nc),
  152. .type = type_dict[.col],
  153. .val = as.character(.val)
  154. ) %>%
  155. bind_rows(
  156. tidyr::crossing(.id_map = ".header", .r = 0, .col = ids, .val = ids,
  157. .x = 1:length(ids), .y = 0, .type = "id",
  158. .key_map = key_values),
  159. data_frame(.id_map = ".header", .r = 0, .col = key, .val = key,
  160. .x = 1 + 1:length(key), .y = 0, .type = "key",
  161. .key_map = key_values),
  162. data_frame(.id_map = ".header", .r = 0, .col = value, .val = value,
  163. .x = 1 + length(key) + 1:length(value), .y = 0, .type = "value",
  164. .key_map = "value"),
  165. .
  166. ) %>%
  167. unite(.id_map, .key_map, .val, col = ".id", remove = F)
  168. x %>% add_color_tidyr(key_values = key_values) %>%
  169. mutate(.alpha = ifelse(.id_map == ".header", 1, 0.6))
  170. }
  171. #' Animates a gather or spread function
  172. #'
  173. #' internally used by animate_spread and animate_gather
  174. #'
  175. #' @param lhs the (processed) dataset on the left-side
  176. #' @param rhs the (processed) dataset on the right-side
  177. #' @param sequence a named vector of the sequence titles
  178. #' (current_state, final_state, operation, and reverse_operation)
  179. #' @param key_values the unique key-values
  180. #' @param export the export type, either gif, first or last. The latter two
  181. #' export ggplots of the first/last state of the join
  182. #' @param detailed boolean value if the animation should show one step for each
  183. #' key value
  184. #' @param ... further arguments passed to animate_plot
  185. #'
  186. #' @return the plot or the gif
  187. #'
  188. #' @examples
  189. #' NULL
  190. gather_spread <- function(lhs, rhs, sequence, key_values, export, detailed, ...) {
  191. # lhs is the one state of the df
  192. # rhs is the target state
  193. # animate the four steps: inital with sequence[["current_state]],
  194. # transformations by the unique key-values with sequence[["operation"]],
  195. # final with sequence[["final_state"]]
  196. # and back transformation with sequence[["reverse_operation]]
  197. # have lhs and rhs in the right format: preprocessed with ids, .x, .y etc.
  198. # have a color function that makes coloring easier
  199. # transformations: for each key-variable: respective ids "fly in", keys fly in and ids fly in (all in one step for one key. i.e., Alice)
  200. # how much is the rhs to the left of lhs?
  201. xshift <- 2
  202. state_start <- lhs %>% mutate(.frame = 0)
  203. step_0 <- lhs %>% mutate(.frame = 1)
  204. state_end <- rhs %>% mutate(.frame = length(key_values) + 2, .x = .x + max(lhs$.x) + xshift)
  205. if (detailed) {
  206. # take one instance of the first headers
  207. start_headers <- lhs %>% filter(.id_map == ".header" & !.val %in% key_values) %>%
  208. group_by(.col, .val) %>% slice(1) %>% ungroup()
  209. end_headers <- state_end %>% filter(.id_map == ".header")
  210. # for each unique key-value move the respective entries
  211. keys_to_shift <- lhs %>% filter(.key_map %in% key_values)
  212. keys_shifted <- lhs[0, ]
  213. key_steps <- lhs[0, ]
  214. i <- 1
  215. for (keyval in key_values) {
  216. i <- i + 1
  217. keys_shifted <- bind_rows(keys_shifted, filter(state_end, .key_map == keyval))
  218. keys_to_shift <- keys_to_shift %>% filter(.key_map != keyval)
  219. if (keyval == key_values[length(key_values)]) {
  220. # in the last round, we dont want to save the start headers
  221. start_headers <- NULL
  222. }
  223. round_n <- bind_rows(end_headers, start_headers,
  224. keys_shifted, keys_to_shift) %>% mutate(.frame = i)
  225. key_steps <- bind_rows(key_steps, round_n)
  226. }
  227. anim_df <- bind_rows(state_start, step_0, key_steps, state_end)
  228. # form the .frame as proper factors
  229. frame_labels <- c(
  230. sequence[["current_state"]],
  231. paste(sequence[["operation"]], key_values),
  232. sequence[["final_state"]],
  233. sequence[["reverse_operation"]]
  234. )
  235. title_string <- "{gsub('\\\\) [a-zA-Z]+$', ')', previous_state)}"
  236. } else {
  237. anim_df <- bind_rows(state_start, state_end)
  238. frame_labels <- c(
  239. sequence[["operation"]],
  240. sequence[["reverse_operation"]]
  241. )
  242. title_string <- "{ifelse(transitioning, previous_state, ifelse(grepl('gather', next_state), 'Wide', 'Long'))}"
  243. }
  244. frame_levels <- anim_df$.frame %>% unique()
  245. anim_df <- anim_df %>%
  246. mutate(.frame = factor(.frame,
  247. levels = frame_levels,
  248. labels = frame_labels))
  249. if (export == "gif") {
  250. animate_plot(anim_df, title = title_string) #...
  251. } else if (export == "first") {
  252. static_plot(state_start) #....
  253. } else if (export == "last") {
  254. static_plot(state_end) #....
  255. }
  256. # open issues: ... doesnt work properly.
  257. # especially if the id-arguments are passed in the gather-style, i.e., -year, or year:var
  258. }