library(httr)
library(jsonlite)
library(tidyverse)
library(zeallot)
library(glue)
gender <- c(rep("M", 23), rep("F", 23))
ages <- rep(c("0-5", "5-10", "10-15", "15-20", "15-20", "20-25","20-25", "20-25", "25-30", "30-35",
"35-40", "40-45", "45-50", "50-55", "55-60", "60-65", "60-65", "65-70", "65-70", "70-75",
"75-80", "80-85", "85-90"), 2)
base_vars <- glue("0{03:49}E") %>%
map_chr(function(x) {
if (nchar(x) == 3) x <- glue("0{x}")
x
}) %>%
map_chr(function(x) glue("B01001_{x}")) %>%
discard(~.=="B01001_026E")
urls <- glue("https://api.census.gov/data/2015/acs/acs5?get={base_vars}&for=us")
get_pop_estimate <- function(url) {
Sys.sleep(2)
est <- GET(url) %>%
content("text") %>%
fromJSON() %>%
.[2,1] %>%
as.numeric()
cat(glue("Received population estimate for {url}..."), "\n")
return(est)
}
pop_estimates <- map(urls, get_pop_estimate)
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_003E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_004E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_005E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_006E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_007E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_008E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_009E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_010E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_011E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_012E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_013E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_014E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_015E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_016E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_017E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_018E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_019E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_020E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_021E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_022E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_023E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_024E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_025E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_027E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_028E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_029E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_030E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_031E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_032E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_033E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_034E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_035E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_036E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_037E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_038E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_039E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_040E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_041E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_042E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_043E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_044E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_045E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_046E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_047E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_048E&for=us...
## Received population estimate for https://api.census.gov/data/2015/acs/acs5?get=B01001_049E&for=us...
pop_estimates <- flatten_dbl(pop_estimates)
df_1 <- tibble(
gender = gender,
age = ages,
estimate = pop_estimates
) %>%
group_by(gender, age) %>%
summarize(estimate = sum(estimate)) %>%
ungroup() %>%
spread(gender, estimate) %>%
mutate(larger = if_else(`F` > `M`, "Larger female pop", "Larger male pop")) %>%
gather(gender, estimate, `F`:`M`, -age, -larger)
c(fdf, mdf) %<-% split(df_1, f = df_1$gender)
mdf_lower <- anti_join(mdf, filter(df_1, larger == "Larger male pop"))
mdf_higher <- anti_join(mdf, filter(df_1, larger == "Larger female pop"))
fdf_lower <- anti_join(fdf, filter(df_1, larger == "Larger female pop"))
fdf_higher <- anti_join(fdf, filter(df_1, larger == "Larger male pop"))
names(mdf) <- glue("m_{names(mdf)}")
names(fdf) <- glue("f_{names(fdf)}")
df_2 <- bind_cols(mdf, fdf) %>%
rowwise() %>%
mutate(larger = if_else(m_estimate > f_estimate, "Male", "Female"),
total_est = m_estimate + f_estimate,
min_pop_est = min(m_estimate, f_estimate)) %>%
mutate(remainder_est = max(m_estimate, f_estimate) - min_pop_est)
df_3 <- df_2 %>%
select(age = f_age, larger, min_pop_est, remainder_est) %>%
gather(fill_col, value, min_pop_est:remainder_est, -age, -larger) %>%
unite("fill_col", c("larger", "fill_col"), sep = "_") %>%
mutate(fill_col = recode(
fill_col,
"Female_min_pop_est" = "min_pop_est",
"Male_min_pop_est" = "min_pop_est")) %>%
mutate(age = forcats::fct_relevel(age, "5-10", after = 1))
pretty_num <- partial(prettyNum, big.mark = ",", preserve.width = "none")
library(ggplot2)
ggplot(df_3, aes(age, value)) +
geom_bar(stat = "identity", aes(fill = fill_col), width = 0.95) +
geom_text(data = mdf_lower,
aes(age, estimate, label = pretty_num(estimate)),
size = 3.5, color = "#4d74b7", hjust = 1.2) +
geom_text(data = mdf_higher,
aes(age, estimate, label = pretty_num(estimate)),
size = 3.5, color = "#4d74b7", hjust = -0.2) +
geom_text(data = fdf_lower,
aes(age, estimate, label = pretty_num(estimate)),
size = 3.5, color = "#e60000", hjust = 1.2) +
geom_text(data = fdf_higher,
aes(age, estimate, label = pretty_num(estimate)),
size = 3.5, color = "#e60000", hjust = -0.2) +
labs(y = "",
x = "Age",
title = "US Population by Age and Sex",
subtitle = "This chart compares the estimated female and male populations by age in the United States as of 2015. \nFor each age bracket, red represents a larger female population, blue represents a larger male population, \nand gray represents the smaller of the two. The total estimated population is 316,515,021.",
caption = "Source: American Community Survey") +
coord_flip() +
scale_y_continuous(
breaks = seq(1e6, 12e6, by = 1e6),
labels = paste0(1:12, "M"),
expand = expand_scale(mult = c(0,0.2))
) +
scale_fill_manual(values = c("#e60000", "#4d74b7", "#dddddd")) +
theme_classic() +
theme(legend.position = "none") +
theme(axis.line.x = element_blank()) +
theme(axis.title.y = element_text(vjust = 1, angle = 0)) +
theme(axis.text = element_text(color = "black")) +
theme(plot.margin = unit(c(1, 2, 1.5, 1.2), "cm"))

Original work by Mike Bostock