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.

358 lines
11KB

  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(DATA_DIR))
  14. fs::dir_create(here::here(ANIM_OUT))
  15. DOWNLOAD_DATA <- TRUE
  16. download_file <- function(filename, url, basedir) {
  17. filename <- gsub(" ", "_", filename)
  18. filename <- gsub("/", "-", filename)
  19. fs::dir_create(basedir)
  20. download.file(url, destfile = fs::path(basedir, filename))
  21. }
  22. if (DOWNLOAD_DATA) {
  23. ## ----download-popest-tables-1900-1980, eval=FALSE------------------------
  24. x <- xml2::read_html("https://www2.census.gov/programs-surveys/popest/tables/1900-1980/national/asrh/")
  25. xx <-
  26. data_frame(
  27. filename = rvest::html_nodes(x, "a") %>% rvest::html_text(),
  28. url = rvest::html_nodes(x, "a") %>% rvest::html_attr("href")
  29. ) %>%
  30. filter(str_detect(url, "\\.csv")) %>%
  31. mutate(url = str_c(
  32. "https://www2.census.gov/programs-surveys/popest/tables/1900-1980/national/asrh/",
  33. url
  34. ))
  35. pwalk(xx, download_file, basedir = here::here(DATA_DIR, "1900-1980"))
  36. ## ----download-popest-tables-1980-1990, eval=FALSE------------------------
  37. xml2::read_html("https://www.census.gov/data/datasets/time-series/demo/popest/1980s-national.html") %>%
  38. rvest::html_nodes(".list.section .uscb-text-link") %>%
  39. rvest::html_attrs() %>%
  40. purrr::transpose() %>%
  41. as_tibble() %>%
  42. unnest() %>%
  43. select(filename = name, url = href) %>%
  44. mutate(url = paste0("https:", url)) %>%
  45. pwalk(download_file, basedir = here::here(DATA_DIR, "1980-1990"))
  46. ## ----download-popest-tables-1990-2000, eval=FALSE------------------------
  47. xml2::read_html("https://www.census.gov/data/datasets/time-series/demo/popest/intercensal-1990-2000-national.html") %>%
  48. rvest::html_nodes("#listArticlesContainer_list_0 .uscb-text-link") %>%
  49. rvest::html_attrs() %>%
  50. purrr::transpose() %>%
  51. as_tibble() %>%
  52. unnest() %>%
  53. select(filename = name, url = href) %>%
  54. mutate(url = paste0("https:", url),
  55. filename = paste0(filename, ".csv")) %>%
  56. pwalk(download_file, basedir = here::here(DATA_DIR, "1990-2000"))
  57. ## ----download-popest-tables-2000-2010----
  58. ## "2000-2010/us-est00int-01.xls"
  59. download_file(
  60. "us-est00int-alldata-5yr.csv",
  61. "https://www2.census.gov/programs-surveys/popest/datasets/2000-2010/intercensal/national/us-est00int-alldata-5yr.csv",
  62. here::here(DATA_DIR, "2000-2010")
  63. )
  64. ## ----download-popest-tables-2010----
  65. ## "2010-2017/PEP_2017_PEPSYASEXN_with_ann.csv"
  66. ## Download manually from https://factfinder.census.gov/bkmk/table/1.0/en/PEP/2017/PEPSYASEXN/0100000US
  67. ## ----download-popest-projections
  68. ## "np2017_d1.csv"
  69. download_file(
  70. "np2017_d1.csv",
  71. "https://www2.census.gov/programs-surveys/popproj/datasets/2017/2017-popproj/np2017_d1.csv",
  72. here::here(DATA_DIR)
  73. )
  74. }
  75. ## ----import-historical---------------------------------------------------
  76. pop <- list()
  77. import_pre1980 <- function(file) {
  78. # cli::cat_bullet(file)
  79. year <- str_extract(file, "\\d{4}\\.csv")
  80. year <- as.integer(sub("\\.csv", "", year))
  81. skip <- if (year >= 1960) 8 else 9
  82. x <- read_csv(file, col_names = FALSE, skip = skip,
  83. col_types = cols_only(
  84. X1 = col_guess(), X2 = col_guess(),
  85. X3 = col_guess(), X4 = col_guess()
  86. ))
  87. colnames(x) <- c("Age", "Total", "Total Male", "Total Female")
  88. x <- x[1:max(which(x$Age %in% c("75+", "85+"))), ]
  89. stopifnot(nrow(x) %in% c(76L, 86L))
  90. x %>%
  91. mutate(
  92. Year = year,
  93. Age = sub("\\+", "", Age),
  94. Age = as.integer(Age)
  95. ) %>%
  96. select(Year, everything())
  97. }
  98. pop[["1900-1980"]] <- fs::dir_ls(
  99. here::here(DATA_DIR, "1900-1980")
  100. ) %>%
  101. map_dfr(import_pre1980)
  102. import_1980_1990 <- function(file) {
  103. read_fwf(
  104. file,
  105. col_positions = fwf_positions(
  106. start = c(3, 5, 7, 12, 22, 32),
  107. end = c(4, 6, 9, 20, 30, 40),
  108. col_names = c("Month", "Year", "Age", "Total", "Total Male", "Total Female")
  109. ),
  110. skip = 0
  111. ) %>%
  112. filter(Age < 101, Month == 4, Year == min(Year)) %>%
  113. mutate(Year = Year + 1900)
  114. }
  115. pop[["1980-1990"]] <- fs::dir_ls(
  116. here::here(DATA_DIR, "1980-1990"),
  117. regexp = "TXT"
  118. ) %>%
  119. map_dfr(import_1980_1990)
  120. import_1990_2000 <- function(file) {
  121. x <- read_csv(file, col_names = FALSE, skip = 3)
  122. colnames(x) <- c("Month", "Age", "Total", "Total Male", "Total Female")
  123. x %>%
  124. filter(
  125. !is.na(Total),
  126. !str_detect(Age, "All")
  127. ) %>%
  128. mutate(Month = lubridate::parse_date_time(Month, "mdy")) %>%
  129. filter(
  130. lubridate::month(Month) == 4
  131. ) %>%
  132. mutate(
  133. Year = lubridate::year(Month),
  134. Age = str_remove(Age, "\\+"),
  135. Age = as.integer(Age)
  136. ) %>%
  137. select(Year, everything(), -Month)
  138. }
  139. pop[["1990-2000"]] <- fs::dir_ls(
  140. here::here(DATA_DIR, "1990-2000"),
  141. regexp = "\\d{4}\\.csv"
  142. ) %>%
  143. map_dfr(import_1990_2000)
  144. # pop[["2000-2010"]] <- read_csv(
  145. # here::here(DATA_DIR, "2000-2010/us-est00int-alldata.csv")
  146. # ) %>%
  147. # select(Month = MONTH, Year = YEAR, Age = AGE,
  148. # Total = TOT_POP, `Total Male` = TOT_MALE,
  149. # `Total Female` = TOT_FEMALE) %>%
  150. # filter(Year > 2000, Age <= 125)
  151. pop[["2000-2010"]] <- readxl::read_xls(
  152. here::here(DATA_DIR, "2000-2010/us-est00int-01.xls"),
  153. sheet = "Sheet1"
  154. ) %>%
  155. mutate(age_label = Age, Age = str_extract(Age, "\\d{1,3}"), Age = as.integer(Age) - str_detect(age_label, "Under") * 5) %>%
  156. select(-age_label) %>%
  157. gather(Year, value, -1:-2) %>%
  158. spread(Group, value) %>%
  159. mutate(Year = as.integer(Year)) %>%
  160. filter(Year > 2000)
  161. pop[["2010-2017"]] <- readr::read_csv(
  162. here::here(DATA_DIR, "2010-2017/PEP_2017_PEPSYASEXN_with_ann.csv"),
  163. skip = 1
  164. ) %>%
  165. select(-1:-4) %>%
  166. gather(key, value, -Sex) %>%
  167. filter(str_detect(key, "Population Estimate")) %>%
  168. mutate(key = str_remove(key, "Pop.+\\) - "), key = str_remove(key, " Total - ")) %>%
  169. filter(!str_detect(key, "Total|Median")) %>%
  170. separate(key, c("Year", "Age"), sep = ";") %>%
  171. mutate(
  172. Year = as.integer(Year),
  173. Age = sub("+", "", Age, fixed = TRUE),
  174. Age = as.integer(Age),
  175. Sex = recode(
  176. Sex,
  177. "Both Sexes" = "Total",
  178. "Female" = "Total Female",
  179. "Male" = "Total Male"
  180. )
  181. ) %>%
  182. spread(Sex, value)
  183. # pop[["2018"]] <- readr::read_tsv(
  184. # here::here(DATA_DIR, "national-population-projections_2014-2060.txt")
  185. # ) %>%
  186. # select(-1) %>%
  187. # filter(!is.na(Year)) %>%
  188. # select(contains("Code"), contains("Pop")) %>%
  189. # {colnames(.) <- sub(" Code", "", colnames(.)); .} %>%
  190. # group_by(Year, Age) %>%
  191. # mutate(Total = sum(`Projected Populations`)) %>%
  192. # ungroup() %>%
  193. # spread(Gender, `Projected Populations`) %>%
  194. # mutate(
  195. # Age = sub("+", "", Age, fixed = TRUE),
  196. # Age = as.integer(Age)
  197. # ) %>%
  198. # filter(Year > 2017)
  199. pop[["2018"]] <-
  200. readr::read_csv(
  201. here::here(DATA_DIR, "np2017_d1.csv")
  202. ) %>%
  203. gather(Age, n, -SEX:-YEAR) %>%
  204. mutate(SEX = c("Total", "Total Male", "Total Female")[SEX + 1]) %>%
  205. spread(SEX, n) %>%
  206. filter(Age != "TOTAL_POP") %>%
  207. mutate(
  208. Age = sub("POP_", "", Age, fixed = TRUE),
  209. Age = as.integer(Age)
  210. ) %>%
  211. select(Year = YEAR, Age, starts_with("Total"))
  212. ## ------------------------------------------------------------------------
  213. ga <-
  214. pop %>%
  215. bind_rows() %>%
  216. mutate(age_group_int = as.integer(Age) %/% 5) %>%
  217. group_by(age_group_int) %>%
  218. mutate(
  219. # age_group = paste(min(Age), max(Age), sep = " - "),
  220. age_group = paste(min(Age)),
  221. age_group = ifelse(Age >= 100, "100+", age_group)
  222. ) %>%
  223. ungroup() %>%
  224. arrange(age_group_int, Year) %>%
  225. mutate(age_group = fct_inorder(age_group)) %>%
  226. select(-age_group_int) %>%
  227. group_by(Year, age_group) %>%
  228. summarize(Total = sum(Total)) %>%
  229. group_by(Year) %>%
  230. mutate(GrandTotal = sum(Total)) %>%
  231. ungroup() %>%
  232. mutate(Total = Total/GrandTotal) %>%
  233. arrange(Year, age_group) %>% View()
  234. # complete(Year, age_group, fill = list(Total = 0)) %>%
  235. # filter(Year > 2000) %>%
  236. {
  237. ggplot(.) +
  238. # aes(age_group, Total) +
  239. # geom_vline(xintercept = c(1980, 1990), color = "grey50") +
  240. # geom_line(aes(group = age_group)) +
  241. # geom_point(size = 0.2) +
  242. # geom_col(fill = "grey40") +
  243. # facet_wrap(~ Year) +
  244. # coord_flip() +
  245. # scale_y_continuous(labels = function(x) {grkmisc::pretty_num(x, decimal_digits = 0)}) +
  246. geom_segment(aes(yend = age_group, y = age_group, xend = Total), x = 0, size = 5) +
  247. geom_segment(aes(yend = age_group, y = age_group, xend = Total),
  248. x = 0,
  249. size = 5,
  250. color = "grey40",
  251. alpha = 0.25,
  252. data = filter(., Year == min(Year)) %>% select(-Year)
  253. ) +
  254. # annotate("text", label = "{closest_state}", x = max(.$Total) * 0.8, y = 20, size = 12) +
  255. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  256. theme(panel.grid.major.y = element_blank()) +
  257. scale_x_continuous(labels = scales::percent, expand = c(0, 0)) +
  258. # labs(x = "Percent of Population", y = NULL) +
  259. theme(axis.title.x = element_text(hjust = 0.5, size = 30)) +
  260. labs(x = "{closest_state}", y = NULL) +
  261. gganimate::transition_time(Year) +
  262. gganimate::transition_states(Year, 1, 0, wrap = FALSE) +
  263. gganimate::ease_aes("linear") +
  264. gganimate::enter_fade()
  265. }
  266. anim_save(
  267. here::here(ANIM_OUT, "pop-anim.mp4"),
  268. animate(ga, width = 1040*2, height = 480*2, res = 144,
  269. fps = 20, nframes = 161*2,
  270. renderer = av_renderer())
  271. )
  272. pop %>%
  273. bind_rows() %>%
  274. mutate(age_group_int = as.integer(Age) %/% 5) %>%
  275. select(1:3, age_group_int) %>%
  276. filter(age_group_int > 5) %>%
  277. mutate(
  278. old = age_group_int >= 13,
  279. old = c("Adult", "Older Adult")[old + 1]
  280. ) %>%
  281. group_by(Year, old) %>%
  282. summarize(n = sum(Total)) %>%
  283. spread(old, n) %>%
  284. {
  285. ggplot(.) +
  286. aes(x = Year) +
  287. geom_segment(aes(xend = Year, y = Adult, yend = `Older Adult`)) +
  288. geom_point(aes(y = Adult), color = grkmisc::moffitt_colors$blue) +
  289. geom_point(aes(y = `Older Adult`), color = grkmisc::moffitt_colors$orange)
  290. }
  291. ga2 <-
  292. pop %>%
  293. bind_rows() %>%
  294. mutate(age_group_int = as.integer(Age) %/% 5) %>%
  295. select(1:3, age_group_int) %>%
  296. filter(age_group_int >= 3) %>%
  297. mutate(
  298. old = age_group_int >= 13,
  299. old = c("Adult", "Older Adult")[old + 1]
  300. ) %>%
  301. group_by(Year, old) %>%
  302. summarize(n = sum(Total)) %>%
  303. spread(old, n) %>%
  304. mutate(ratio = `Older Adult` / Adult) %>%
  305. {
  306. ggplot(.) +
  307. aes(Year, ratio) +
  308. geom_point() +
  309. geom_point(data = filter(., Year == 2018), color = "red") +
  310. labs(y = "Dependency Ratio", x = NULL) +
  311. theme_minimal(base_size = 16, base_family = "Fira Sans Condensed") +
  312. scale_y_continuous(labels = scales::percent) +
  313. gganimate::transition_states(Year, 1, 0, FALSE) +
  314. gganimate::shadow_mark()
  315. }
  316. anim_save(
  317. here::here(ANIM_OUT, "population-projections/pop-dependency.mp4"),
  318. animate(ga2, width = 1040*2, height = 480*2, res = 144,
  319. fps = 18, nframes = 161,
  320. renderer = av_renderer())
  321. )