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.

346 satır
10KB

  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 = color_dict[.type])
  53. }
  54. #' Processes a wide dataframe and converts it into a dataset that can be plotted
  55. #'
  56. #' @param x a wide data frame
  57. #' @param ids a vector of id-variables that are already in the tidy-format
  58. #' @param key a vector of key-variables
  59. #' @param color_id the color for the id-body
  60. #' @param ...
  61. #'
  62. #' @return
  63. #'
  64. #' @examples
  65. #' wide <- data_frame(
  66. #' year = 2010:2011,
  67. #' Alice = c(105, 110),
  68. #' Bob = c(100, 97),
  69. #' Charlie = c(90, 95)
  70. #' )
  71. #' process_wide(wide, ids = "year", key = "person")
  72. #' process_wide(wide, ids = "year", key = "person") %>% static_plot
  73. process_wide <- function(x, ids, key, color_id = "lightgray", ...) {
  74. if (!all(ids %in% names(x)))
  75. stop("all ids must be in x")
  76. nr <- nrow(x)
  77. nc <- ncol(x)
  78. key_values <- names(x)
  79. key_values <- key_values[!key_values %in% ids]
  80. id_values <- x %>% select(one_of(ids))
  81. id_values <- id_values %>% gather(key = ".key_map", value = ".id_map")
  82. x <- x %>% mutate(.r = row_number()) %>%
  83. unite(one_of(ids), col = ".id_map", remove = F)
  84. x <- x %>%
  85. gather(key = ".col", value = ".val", names(x) %>% str_subset("^[^\\.]")) %>%
  86. mutate(.key_map = .col,
  87. .type = ifelse(.col %in% ids, "id", "value"),
  88. .val = as.character(.val),
  89. .x = rep(1:nc, each = nr),
  90. .y = -rep(1:nr, nc),
  91. .header = F)
  92. # make sure that we have one id value per key
  93. tmp <- x %>% filter(.key_map %in% ids)
  94. x <- bind_rows(
  95. left_join(tmp %>% select(-.key_map),
  96. tmp %>% select(.id_map) %>% crossing(.key_map = key_values),
  97. by = ".id_map"),
  98. x %>% filter(!.key_map %in% ids)
  99. )
  100. # add header:
  101. crosser <- crossing(.id_map = as.character(id_values$.id_map),
  102. .key_map = key_values)
  103. key_header <- data_frame(
  104. .key_map = key_values,
  105. .r = 0,
  106. .col = key_values,
  107. .val = key_values,
  108. .type = "key",
  109. .x = length(ids) + 1:length(key_values),
  110. .y = 0,
  111. .header = TRUE) %>%
  112. left_join(crosser, by = ".key_map")
  113. id_header <- left_join(
  114. data_frame(.id_map = ids,
  115. .r = 0,
  116. .col = ids,
  117. .val = ids,
  118. .type = "id",
  119. .x = 1:length(ids),
  120. .y = 0,
  121. .header = TRUE),
  122. crossing(.id_map = ids, .key_map = key_values),
  123. by = ".id_map"
  124. )
  125. x <- bind_rows(id_header, key_header, x)
  126. x <- x %>% unite(.key_map, .id_map, .val, col = ".id", remove = F)
  127. x %>%
  128. add_color_tidyr(key_values = key_values) %>%
  129. mutate(.alpha = ifelse(.header == TRUE, 1, 0.6))
  130. }
  131. #' Processes a long dataframe and converts it into a dataset that can be plotted
  132. #'
  133. #' @param x a long data frame
  134. #' @param ids a vector of id-variables that are already in the tidy-format
  135. #' @param key a vector of key-variables
  136. #' @param ...
  137. #'
  138. #' @return
  139. #'
  140. #' @examples
  141. #' long <- data_frame(
  142. #' year = c(2010L, 2011L, 2010L, 2011L, 2010L, 2011L),
  143. #' person = c("Alice", "Alice", "Bob", "Bob", "Charlie", "Charlie"),
  144. #' sales = c(105, 110, 100, 97, 90, 95)
  145. #' )
  146. #' process_long(long, ids = "year", key = "person", value = "sales")
  147. #' process_long(long, ids = "year", key = "person", value = "sales") %>% static_plot
  148. process_long <- function(x, ids, key, value, ...) {
  149. if (!all(c(ids, key, value) %in% names(x)))
  150. stop("all ids, key, and value must be names of x")
  151. nr <- nrow(x)
  152. nc <- ncol(x)
  153. xn <- names(x)
  154. x <- x %>% mutate(.r = row_number()) %>%
  155. unite(ids, col = ".id_map", remove = F) %>%
  156. unite(key, col = ".key_map", remove = F)
  157. key_values <- x %>% pull(key) %>% unique()
  158. type_dict <- c(rep("id", length(ids)), rep("key", length(key)), rep("value", length(value)))
  159. names(type_dict) <- c(rep(ids, length(ids)), rep(key, length(key)), rep(value, length(value)))
  160. x_dict <- 1:nc
  161. names(x_dict) <- xn
  162. x <- x %>%
  163. gather(key = ".col", value = ".val", names(x) %>% str_subset("^[^\\.]")) %>%
  164. mutate(
  165. .x = x_dict[.col],
  166. .y = -rep(1:nr, nc),
  167. .type = type_dict[.col],
  168. .val = as.character(.val),
  169. .header = FALSE
  170. )
  171. # add headers:
  172. id_headers <- crossing(.id_map = ids, # x$.id_map %>% unique()
  173. .key_map = key_values,
  174. ) %>%
  175. mutate(
  176. .r = 0,
  177. .col = "id",
  178. .val = .id_map,
  179. .x = x_dict[.val],
  180. .y = 0,
  181. .type = "id",
  182. .header = TRUE
  183. )
  184. x <- x %>%
  185. add_row(
  186. .before = T,
  187. .id_map = c(rep("key", length(key)), rep("value", length(value))),
  188. .key_map = c(rep("key", length(key)), rep("value", length(value))),
  189. .r = 0,
  190. .col = c(rep("key", length(key)), rep("value", length(value))),
  191. .val = c(key, value),
  192. .x = length(ids) + 1:length(c(key, value)),
  193. .y = 0,
  194. .type = c(rep("key", length(key)), rep("value", length(value))),
  195. .header = TRUE
  196. )
  197. x <- bind_rows(id_headers, x)
  198. x <- x %>%
  199. unite(.key_map, .id_map, .val, col = ".id", remove = F)
  200. x %>% add_color_tidyr(key_values = key_values) %>%
  201. mutate(.alpha = ifelse(.header == TRUE, 1, 0.6))
  202. }
  203. #' Animates a gather or spread function
  204. #'
  205. #' internally used by animate_spread and animate_gather
  206. #'
  207. #' @param lhs the (processed) dataset on the left-side
  208. #' @param rhs the (processed) dataset on the right-side
  209. #' @param sequence a named vector of the sequence titles
  210. #' (current_state, final_state, operation, and reverse_operation)
  211. #' @param key_values the unique key-values
  212. #' @param export the export type, either gif, first or last. The latter two
  213. #' export ggplots of the first/last state of the join
  214. #' @param detailed boolean value if the animation should show one step for each
  215. #' key value
  216. #' @param ... further arguments passed to animate_plot
  217. #'
  218. #' @return the plot or the gif
  219. #'
  220. #' @examples
  221. #' NULL
  222. gather_spread <- function(lhs, rhs, sequence, key_values, export, detailed, ...) {
  223. # lhs is the one state of the df
  224. # rhs is the target state
  225. # animate the four steps: inital with sequence[["current_state]],
  226. # transformations by the unique key-values with sequence[["operation"]],
  227. # final with sequence[["final_state"]]
  228. # and back transformation with sequence[["reverse_operation]]
  229. # have lhs and rhs in the right format: preprocessed with ids, .x, .y etc.
  230. # have a color function that makes coloring easier
  231. # 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)
  232. # how much is the rhs to the left of lhs?
  233. if (!detailed) {
  234. anim_df <- bind_rows(
  235. lhs %>% mutate(.frame = 0),
  236. rhs %>% mutate(.frame = 1)
  237. )
  238. frame_labels <- c(sequence[["operation"]], sequence[["reverse_operation"]])
  239. title_string <- "{ifelse(transitioning, previous_state, ifelse(grepl('gather', next_state), 'Wide', 'Long'))}"
  240. } else {
  241. xshift <- 2
  242. rhs <- rhs %>% mutate(.x = .x + max(lhs$.x) + xshift)
  243. # the header rows
  244. header_start <- lhs %>% filter(.header == TRUE, !.key_map %in% key_values)
  245. header_end <- rhs %>% filter(.header == TRUE)
  246. state_start <- lhs %>% mutate(.frame = 0)
  247. state_end <- rhs %>% mutate(.frame = length(key_values) + 2)
  248. step_0 <- lhs %>% mutate(.frame = 1)
  249. # for each unique key-value move the respective entries
  250. keys_remaining <- lhs %>% filter(.key_map %in% key_values)
  251. keys_shifted <- lhs[0, ]
  252. key_steps <- lhs[0, ]
  253. f <- 1
  254. ids_remaining <- lhs %>% filter(.type == "id" & .header == FALSE)
  255. for (keyval in key_values) {
  256. f <- f + 1
  257. move_rhs <- rhs %>% filter(.key_map == keyval)
  258. keys_remaining <- keys_remaining %>% filter(.key_map != keyval)
  259. if (keyval == key_values[length(key_values)]) {
  260. header_start <- NULL
  261. }
  262. hd <- header_end %>% filter(.key_map == keyval |
  263. (.type %in% c("key", "value") &
  264. .col %in% c("key", "value")))
  265. keys_shifted <- bind_rows(keys_shifted, move_rhs)
  266. round_n <- bind_rows(header_start, hd,
  267. keys_remaining, keys_shifted) %>%
  268. mutate(.frame = f)
  269. key_steps <- bind_rows(key_steps, round_n)
  270. }
  271. anim_df <- bind_rows(state_start, step_0, key_steps, state_end)
  272. # form the .frame as proper factors
  273. frame_labels <- c(
  274. sequence[["current_state"]],
  275. paste(sequence[["operation"]], key_values),
  276. sequence[["final_state"]],
  277. sequence[["reverse_operation"]]
  278. )
  279. title_string <- "{gsub('\\\\) [a-zA-Z]+$', ')', previous_state)}"
  280. }
  281. frame_levels <- anim_df$.frame %>% unique()
  282. anim_df <- anim_df %>%
  283. mutate(.frame = factor(.frame,
  284. levels = frame_levels,
  285. labels = frame_labels))
  286. if (export == "gif") {
  287. animate_plot(anim_df, title = title_string) #...
  288. } else if (export == "first") {
  289. static_plot(state_start) #....
  290. } else if (export == "last") {
  291. static_plot(state_end) #....
  292. }
  293. # open issues: ... doesnt work properly.
  294. # especially if the id-arguments are passed in the gather-style, i.e., -year, or year:var
  295. }