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ů.

411 lines
14KB

  1. ## ----setup, include=FALSE------------------------------------------------
  2. knitr::opts_chunk$set(echo=FALSE)
  3. ## ----library-------------------------------------------------------------
  4. library(dplyr)
  5. library(purrr)
  6. library(tidyr)
  7. library(stringr)
  8. library(readr)
  9. library(forcats)
  10. library(gganimate)
  11. DATA_DIR <- "data-raw"
  12. ANIM_OUT <- "anim"
  13. fs::dir_create(here::here(ANIM_OUT))
  14. pop <- readRDS(here::here("data", "pop_estimates_by_age.rds"))
  15. # Functions ---------------------------------------------------------------
  16. scale_abs_percent <- function(x, ...) {
  17. scales::percent(abs(x), ...)
  18. }
  19. pretty_num_abs <- function(x, decimal_digits = 0, ...) {
  20. grkmisc::pretty_num(abs(x), decimal_digits = decimal_digits, ...)
  21. }
  22. create_animation <- function(gganim, filename, base_dir = ANIM_OUT,
  23. output_format = c("mp4", "gif"),
  24. delete_frames = FALSE,
  25. nframes = 161, fps = 25, loop = FALSE,
  26. width = 2080, height = 960, res = 144) {
  27. grkmisc::logger(msg = glue::glue("Creating {filename} frames"))
  28. ga_files <-
  29. animate(gganim,
  30. width = width, height = height, res = res, fps = fps, nframes = nframes,
  31. renderer = file_renderer(here::here(ANIM_OUT, "frames"), filename, overwrite = TRUE))
  32. first_last <- first_last_new <- ga_files[c(1, length(ga_files))]
  33. first_last_new[1] <- sub("\\d+\\.png", "_first.png", first_last[1])
  34. first_last_new[2] <- sub("\\d+\\.png", "_last.png", first_last[2])
  35. fs::file_copy(first_last, fs::path(ANIM_OUT, fs::path_file(first_last_new)), overwrite = TRUE)
  36. if ("mp4" %in% output_format) {
  37. grkmisc::logger(msg = glue::glue("Writing MP4 {filename} animation"))
  38. av::av_encode_video(
  39. ga_files,
  40. here::here(ANIM_OUT, fs::path(filename, ext = "mp4")),
  41. framerate = fps,
  42. verbose = TRUE
  43. )
  44. }
  45. if ("gif" %in% output_format) {
  46. grkmisc::logger(msg = glue::glue("Writing gif {filename} animation"))
  47. gifski::gifski(
  48. ga_files,
  49. here::here(ANIM_OUT, fs::path(filename, ext = "gif")),
  50. width = width, height = height, delay = 1/fps, loop = loop
  51. )
  52. }
  53. if (delete_frames) unlink(ga_files)
  54. }
  55. # Pyramid Animation -------------------------------------------------------
  56. age_groups <- paste(seq(0, 100, 5))
  57. age_groups[length(age_groups)] <- paste0(age_groups[length(age_groups)], "+")
  58. pop_age <-
  59. pop %>%
  60. bind_rows() %>%
  61. mutate(age_group_int = as.integer(Age) %/% 5) %>%
  62. group_by(age_group_int) %>%
  63. mutate(age_group = min(Age)) %>%
  64. ungroup() %>%
  65. select(-age_group_int) %>%
  66. group_by(Year, age_group) %>%
  67. summarize(Total = sum(Total)) %>%
  68. group_by(Year) %>%
  69. mutate(GrandTotal = sum(Total)) %>%
  70. ungroup() %>%
  71. mutate(Total = Total/GrandTotal) %>%
  72. arrange(Year, age_group) %>%
  73. complete(Year, age_group, fill = list(Total = 0)) %>%
  74. # filter(Year %in% c(1930, 2000, 2018, 2030)) %>%
  75. mutate(
  76. xend = Total,
  77. x = -xend,
  78. future = Year > 2018,
  79. dependent = age_group < 15 | age_group > 64
  80. )
  81. ga <-
  82. ggplot(pop_age) +
  83. aes(x, xend = xend, y = age_group, yend = age_group) +
  84. geom_segment(aes(color = future), size = 5) +
  85. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  86. scale_x_continuous(labels = scale_abs_percent, expand = c(0, 0, 0, 0)) +
  87. scale_y_continuous(breaks = seq(0, 100, 5), labels = age_groups) +
  88. scale_color_manual(values = c("grey40", "#00589a"), guide = FALSE) +
  89. labs(x = "Percent of Population", y = NULL,
  90. caption = "Source: U.S. Census Bureau") +
  91. ggtitle("{closest_state}") +
  92. gganimate::transition_time(Year) +
  93. gganimate::transition_states(Year, 1, 1, wrap = FALSE) +
  94. gganimate::ease_aes("linear") +
  95. gganimate::enter_fade() +
  96. theme(
  97. panel.grid.major.y = element_blank(),
  98. panel.grid.minor.y = element_blank(),
  99. panel.grid.minor.x = element_blank(),
  100. plot.caption = element_text(color = "grey50"),
  101. plot.title = element_text(hjust = 0.5, size = 30)
  102. )
  103. create_animation(ga, "pop-anim", fps = 25, loop = FALSE)
  104. # Pyramid Static Images ---------------------------------------------------
  105. gg_first_underlay <-
  106. pop_age %>%
  107. filter(Year %in% c(min(Year), max(Year))) %>%
  108. ggplot() +
  109. aes(x, xend = xend, y = age_group, yend = age_group) +
  110. geom_segment(aes(color = future), size = 5) +
  111. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  112. scale_x_continuous(labels = scale_abs_percent, expand = c(0, 0, 0, 0)) +
  113. scale_y_continuous(breaks = seq(0, 100, 5), labels = age_groups) +
  114. scale_color_manual(values = c("grey90", "#00589a"), guide = FALSE) +
  115. # scale_alpha_discrete(range = c(0.75, 1)) +
  116. labs(x = "Percent of Population", y = NULL,
  117. caption = "Source: U.S. Census Bureau") +
  118. ggtitle(max(pop_age$Year)) +
  119. theme(
  120. panel.grid.major.y = element_blank(),
  121. panel.grid.minor.y = element_blank(),
  122. panel.grid.minor.x = element_blank(),
  123. plot.caption = element_text(color = "grey50"),
  124. plot.title = element_text(hjust = 0.5, size = 30)
  125. )
  126. ggsave(
  127. fs::path(ANIM_OUT, "pop-anim_first-last", ext = "png"),
  128. gg_first_underlay,
  129. width = 28.89/2, height = 13.33/2, dpi = 144, device = "png", scale = 1
  130. )
  131. gg_dependency_ratio <-
  132. pop_age %>%
  133. filter(Year %in% c(max(Year))) %>%
  134. ggplot() +
  135. aes(x, xend = xend, y = age_group, yend = age_group) +
  136. geom_segment(aes(color = dependent), size = 5) +
  137. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  138. scale_x_continuous(labels = scale_abs_percent, expand = c(0, 0, 0, 0),
  139. limits = c(min(pop_age$x), max(pop_age$xend))) +
  140. scale_y_continuous(breaks = seq(0, 100, 5), labels = age_groups) +
  141. scale_color_manual(values = c("#bec3c3", "#00589a"), guide = FALSE) +
  142. labs(x = "Percent of Population", y = NULL,
  143. caption = "Source: U.S. Census Bureau") +
  144. ggtitle(max(pop_age$Year)) +
  145. theme(
  146. panel.grid.major.y = element_blank(),
  147. panel.grid.minor.y = element_blank(),
  148. panel.grid.minor.x = element_blank(),
  149. plot.caption = element_text(color = "grey50"),
  150. plot.title = element_text(hjust = 0.5, size = 30)
  151. )
  152. ggsave(
  153. fs::path(ANIM_OUT, "pop-anim_dependency-ratio", ext = "png"),
  154. gg_dependency_ratio,
  155. width = 28.89/2, height = 13.33/2, dpi = 144, device = "png", scale = 1
  156. )
  157. # Pyramid Absolute Animation -------------------------------------------------------
  158. age_groups <- paste(seq(0, 100, 5))
  159. age_groups[length(age_groups)] <- paste0(age_groups[length(age_groups)], "+")
  160. pop_age_abs <-
  161. pop %>%
  162. bind_rows() %>%
  163. mutate(age_group_int = as.integer(Age) %/% 5) %>%
  164. group_by(age_group_int) %>%
  165. mutate(age_group = min(Age)) %>%
  166. ungroup() %>%
  167. select(-age_group_int) %>%
  168. group_by(Year, age_group) %>%
  169. summarize(Total = sum(Total)) %>%
  170. # group_by(Year) %>%
  171. # mutate(GrandTotal = sum(Total)) %>%
  172. ungroup() %>%
  173. # mutate(Total = Total/GrandTotal) %>%
  174. arrange(Year, age_group) %>%
  175. complete(Year, age_group, fill = list(Total = 0)) %>%
  176. # filter(Year %in% c(1930, 2000, 2018, 2030)) %>%
  177. mutate(
  178. xend = Total,
  179. x = -xend,
  180. future = Year > 2018,
  181. dependent = age_group < 15 | age_group > 64
  182. )
  183. ga_abs <-
  184. ggplot(pop_age_abs) +
  185. aes(x, xend = xend, y = age_group, yend = age_group) +
  186. geom_segment(aes(color = future), size = 5) +
  187. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  188. scale_x_continuous(labels = pretty_num_abs, expand = c(0, 0, 0, 0)) +
  189. scale_y_continuous(breaks = seq(0, 100, 5), labels = age_groups) +
  190. scale_color_manual(values = c("grey40", "#00589a"), guide = FALSE) +
  191. labs(x = "Total Age Group Population", y = NULL,
  192. caption = "Source: U.S. Census Bureau") +
  193. ggtitle("{closest_state}") +
  194. gganimate::transition_time(Year) +
  195. gganimate::transition_states(Year, 1, 1, wrap = FALSE) +
  196. gganimate::ease_aes("linear") +
  197. gganimate::enter_fade() +
  198. theme(
  199. panel.grid.major.y = element_blank(),
  200. panel.grid.minor.y = element_blank(),
  201. panel.grid.minor.x = element_blank(),
  202. plot.caption = element_text(color = "grey50"),
  203. plot.title = element_text(hjust = 0.5, size = 30)
  204. )
  205. create_animation(ga_abs, "pop-anim-abs", fps = 25, nframes = 321, loop = FALSE)
  206. # Pyramid Abs Static Images ---------------------------------------------------
  207. gg_first_underlay <-
  208. pop_age_abs %>%
  209. filter(Year %in% c(min(Year), max(Year))) %>%
  210. ggplot() +
  211. aes(x, xend = xend, y = age_group, yend = age_group) +
  212. geom_segment(
  213. data = filter(pop_age_abs, Year == max(Year)),
  214. color = "#00589a",
  215. size = 5,
  216. ) +
  217. geom_segment(
  218. data = filter(pop_age_abs, Year == min(Year)),
  219. color = "#bec3c3",
  220. size = 5,
  221. ) +
  222. annotate("text", x = 0, y = 0, label = "1900", vjust = 0,
  223. family = "Fira Sans Condensed", size = 12) +
  224. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  225. scale_x_continuous(labels = pretty_num_abs, expand = c(0, 0, 0, 0)) +
  226. scale_y_continuous(breaks = seq(0, 100, 5), labels = age_groups) +
  227. # scale_color_manual(values = c("grey90", "#00589a"), guide = FALSE) +
  228. # scale_alpha_discrete(range = c(0.75, 1)) +
  229. labs(x = "Total Age Group Population", y = NULL,
  230. caption = "Source: U.S. Census Bureau") +
  231. ggtitle(max(pop_age$Year)) +
  232. theme(
  233. panel.grid.major.y = element_blank(),
  234. panel.grid.minor.y = element_blank(),
  235. panel.grid.minor.x = element_blank(),
  236. plot.caption = element_text(color = "grey50"),
  237. plot.title = element_text(hjust = 0.5, size = 30)
  238. )
  239. ggsave(
  240. fs::path(ANIM_OUT, "pop-anim-abs_first-last", ext = "png"),
  241. gg_first_underlay,
  242. width = 28.89/2, height = 13.33/2, dpi = 144, device = "png", scale = 1
  243. )
  244. gg_dependency_ratio <-
  245. pop_age_abs %>%
  246. filter(Year %in% c(max(Year))) %>%
  247. ggplot() +
  248. aes(x, xend = xend, y = age_group, yend = age_group) +
  249. geom_segment(aes(color = dependent), size = 5) +
  250. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  251. scale_x_continuous(labels = pretty_num_abs, expand = c(0, 0, 0, 0),
  252. limits = c(min(pop_age_abs$x), max(pop_age_abs$xend))) +
  253. scale_y_continuous(breaks = seq(0, 100, 5), labels = age_groups) +
  254. scale_color_manual(values = c("#bec3c3", "#00589a"), guide = FALSE) +
  255. labs(x = "Total Age Group Population", y = NULL,
  256. caption = "Source: U.S. Census Bureau") +
  257. ggtitle(max(pop_age$Year)) +
  258. theme(
  259. panel.grid.major.y = element_blank(),
  260. panel.grid.minor.y = element_blank(),
  261. panel.grid.minor.x = element_blank(),
  262. plot.caption = element_text(color = "grey50"),
  263. plot.title = element_text(hjust = 0.5, size = 30)
  264. )
  265. ggsave(
  266. fs::path(ANIM_OUT, "pop-anim-abs_dependency-ratio", ext = "png"),
  267. gg_dependency_ratio,
  268. width = 28.89/2, height = 13.33/2, dpi = 144, device = "png", scale = 1
  269. )
  270. # Elder Dependency Animation ----------------------------------------------
  271. pop %>%
  272. bind_rows() %>%
  273. mutate(age_group_int = as.integer(Age) %/% 5) %>%
  274. select(1:3, age_group_int) %>%
  275. filter(age_group_int > 5) %>%
  276. mutate(
  277. old = age_group_int >= 13,
  278. old = c("Adult", "Older Adult")[old + 1]
  279. ) %>%
  280. group_by(Year, old) %>%
  281. summarize(n = sum(Total)) %>%
  282. spread(old, n) %>%
  283. {
  284. ggplot(.) +
  285. aes(x = Year) +
  286. geom_segment(aes(xend = Year, y = Adult, yend = `Older Adult`)) +
  287. geom_point(aes(y = Adult), color = grkmisc::moffitt_colors$blue) +
  288. geom_point(aes(y = `Older Adult`), color = grkmisc::moffitt_colors$orange, size = 2)
  289. } %>%
  290. invisible()
  291. elder_dependency <-
  292. pop %>%
  293. bind_rows() %>%
  294. mutate(age_group_int = as.integer(Age) %/% 5) %>%
  295. select(1:3, age_group_int) %>%
  296. mutate(
  297. old = case_when(
  298. age_group_int < 3 ~ "Children",
  299. age_group_int >= 13 ~ "Older Adult",
  300. TRUE ~ "Adult"
  301. )
  302. ) %>%
  303. group_by(Year, old) %>%
  304. summarize(n = sum(Total)) %>%
  305. spread(old, n) %>%
  306. mutate(
  307. dependency_elder = `Older Adult` / Adult,
  308. dependency_all = (`Older Adult` + Children) / Adult
  309. )
  310. ga_elder_dep <-
  311. ggplot(elder_dependency) +
  312. aes(Year, dependency_elder) +
  313. geom_point(color = "#00589a") +
  314. geom_point(data = filter(elder_dependency, Year == 2018), size = 4, color = "#eb1455") +
  315. geom_text(
  316. data = filter(elder_dependency, Year == 1900),
  317. aes(label = round(dependency_elder * 100, 1)),
  318. color = "#00589a",
  319. size = 6, hjust = 1.25, vjust = 0.4, family = "Fira Sans Condensed"
  320. ) +
  321. geom_text(
  322. data = filter(elder_dependency, Year == 2018),
  323. aes(label = round(dependency_elder * 100, 1)),
  324. color = "#eb1455",
  325. size = 6, hjust = 1.5, vjust = 0.4, family = "Fira Sans Condensed"
  326. ) +
  327. geom_text(
  328. data = filter(elder_dependency, Year == 2060),
  329. aes(label = round(dependency_elder * 100, 1)),
  330. color = "#00589a",
  331. size = 6, hjust = -0.25, vjust = 0.4, family = "Fira Sans Condensed"
  332. ) +
  333. labs(y = "Dependency Ratio", x = NULL,
  334. caption = "Source: U.S. Census Bureau") +
  335. theme_minimal(base_size = 24, base_family = "Fira Sans Condensed") +
  336. theme(plot.caption = element_text(color = "grey50", size = 14)) +
  337. scale_y_continuous(labels = scales::percent) +
  338. gganimate::transition_states(Year, 1, 0, FALSE) +
  339. gganimate::shadow_mark()
  340. create_animation(ga_elder_dep, "pop-dependency", delete_frames = TRUE, fps = 30, loop = FALSE)
  341. # All Dependency Animation ------------------------------------------------
  342. elder_dep_together <-
  343. elder_dependency %>%
  344. ungroup() %>%
  345. gather(timeseries, ratio, starts_with("depend")) %>%
  346. select(-Adult:-`Older Adult`) %>%
  347. mutate(timeseries = ifelse(timeseries == "dependency_elder", 0, 1))
  348. ga_dep_together <-
  349. ggplot(elder_dep_together) +
  350. aes(Year, ratio, group = Year) +
  351. geom_point(aes(color = paste(timeseries)), show.legend = FALSE) +
  352. scale_color_manual(values = c("#00589a", "#82c878")) +
  353. labs(y = "Dependency Ratio", x = NULL,
  354. caption = "Source: U.S. Census Bureau") +
  355. theme_minimal(base_size = 24, base_family = "Fira Sans Condensed") +
  356. theme(plot.caption = element_text(color = "grey50", size = 14)) +
  357. scale_y_continuous(labels = scales::percent) +
  358. gganimate::transition_states(timeseries, 1, 0, FALSE) +
  359. gganimate::view_follow(fixed_x = TRUE) +
  360. gganimate::ease_aes("sine-in-out")+
  361. gganimate::shadow_mark()
  362. create_animation(ga_dep_together, "pop-dependency-both", delete_frames = TRUE, fps = 30, loop = FALSE)