rggplot2graph

Animate Pyramid Population in R


I want to make an animate population pyramid in R.Data here https://drive.google.com/file/d/1bo2veDvh5c2d5qH4YePUgrEu8WDAeHRq/view?usp=sharing

I found a code and used it but I receive this error.

Error in pivot_longer(): ! Can't select columns past the end. ℹ Locations 2 and 3 don't exist. ℹ There is only 1 column. Run rlang::last_trace() to see where the error occurred.

library(dplyr)
library(tidyr)
library(stringr) # for string manipulation
library(gganimate) # create animation


d <- read.csv(
    "/Users/Merry/Downloads/germany_population_all_years.csv", 
    # remove the "X" letters otherwise added to headers started with numbers
    check.names = F
)


head(d, n = 3)


# Tidy up
d.tidy <- d %>% as_tibble() %>% 
    pivot_longer(-c(1:3), values_to = "population", names_to = "age")




# Male on the positive range of y axis, and female on the negative range. 
# Increase the bar length by 80 units to offset the coverage of the central banner.
d.tidy <- d.tidy %>% as_tibble() %>% 
    mutate(population = ifelse(gender == "f", - population - 80, population + 80 ))




# Display the population pyramid in order of age.
d.tidy$age <- factor(d.tidy$age, levels = colnames(d)[-c(1:3)])




# Highlight the population difference between genders.
d.tidy <- d.tidy %>% 
    group_by(year, age) %>%
    mutate(pop.min = min(abs(population)), 
           # female in negative values, male in positive values
           pop.min = ifelse(gender == "m", pop.min, -pop.min))




# Make bars of men in blue, and women in red. 
# Make the past data (before 2024) in dark color, and future data in brighter color. 
d.tidy <- d.tidy %>% 
    mutate(time.gender = str_c(past.future, "_", gender))


my.colors <- c("past_m" = "steelblue4", "past_f" = "red4",
               "future_m" = "steelblue1", "future_f" = "red1")


# now ready for visualization!
head(d.tidy, n = 4) 


### Visualization ----------------------------------------------------


# Optional: create subset to make it easier for code development and troubleshooting
d.tidy <- d.tidy # %>% filter(year %in% c(1980))




# Create bar plots with flipped axes. Pyramids of all years data are now superimposed.
p1 <- d.tidy %>% 
    ggplot(aes(x = age, y = population, fill = time.gender)) +
    geom_col(position = "identity", width = 1) +
    coord_flip(clip = "off") +
    scale_fill_manual(values = my.colors)
p1




# Highlight population difference between genders.
p2 <- p1 + 
    geom_col(aes(y = pop.min), 
             fill = "white", alpha = .5, width = 1,
             position = "identity")  
p2




# Create a central banner overlay on the bars.
p3 <- p2 + 
    annotate(geom = "rect", # create rectangles
             xmin = -Inf, xmax = Inf, ymin = 80, ymax = -80,
             fill = "floralwhite")
p3




# Label with ages in the central banner
p4 <- p3 +
    annotate(geom = "text",
             x = seq(0, 100, 5), y = 0, 
             label = seq(0, 100, 5),
             size = 3, fontface = "bold") 
p4




# Revise the axial labels and titles.
breaks <- seq(0, 700, by = 100)
breaks.updated <- c(breaks + 80, - breaks - 80)


p5 <- p4 + 
    scale_y_continuous(
        breaks = breaks.updated,
        labels = function(x) {abs(x) - 80 }) + 
    labs(y = "Female (in thousands)                 Male (in thousands)   ",
         title = "Simulated year",
         subtitle = "{frame_time}") 
p5


# Revise the theme. 
p6 <- p5 + 
    theme_void() + # an empty canvas, with removal of all axis elements
    theme(
        axis.text.x = element_text(size = 10, color = "snow4", margin = margin(t = 5)),
        axis.title.x = element_text(face = "bold", margin = margin(t = 5, b = 5)),
        plot.title = element_text(hjust = .1, vjust = -10, size = 10),
        plot.subtitle = element_text(hjust = .1, vjust = -6, face = "bold", size = 20),
        legend.position = "none")


p6




# Create animation.
p7 <- p6 + transition_time(time = year)
p7


# Save the animation
anim_save(filename = "Population pyramid animation.gif", 
          path = "graphics") ```

Solution

  • There are 3 things you need to change in your code which should make it work:

    I would specify that sep = ; in your .csv, as it looks like based on the error message, the csv is not being imported correctly, as the program is looking to separate by , and not ;. Without sep = ;, I get:

    > head(d, n = 1)
                                                            year;gender;0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;22;23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40;41;42;43;44;45;46;47;48;49;50;51;52;53;54;55;56;57;58;59;60;61;62;63;64;65;66;67;68;69;70;71;72;73;74;75;76;77;78;79;80;81;82 ...
    1 1992;m;147;146;214;191;211;231;223;209;190;195;233;217;209;177;202;191;143;185;134;162;169;171;195;218;224;122;102;120;110;106;174;179;157;190;214;207;267;193;183;174;150;175;165;119;123;109;90;91;74;89;96;95;88;84;92;93;77;87;70;82;55;64;56;63;70;53;56;62;44;43;31;32;29;13;13;15;21;20;22;22;15;16;15;3;7;25
    

    With sep = ;, I get:

    > head(d, n = 1)
      year gender   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35
    1 1992      m 147 146 214 191 211 231 223 209 190 195 233 217 209 177 202 191 143 185 134 162 169 171 195 218 224 122 102 120 110 106 174 179 157 190 214 207
       36  37  38  39  40  41  42  43  44  45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    1 267 193 183 174 150 175 165 119 123 109 90 91 74 89 96 95 88 84 92 93 77 87 70 82 55 64 56 63 70 53 56 62 44 43 31 32 29 13 13 15 21 20 22 22 15 16 15  3  7
      85
    1 25
    

    Secondly, I would change the # Tidy Up line of code to have -c(1:2) from -c(1:3), otherwise age 0 will get removed:

    d.tidy <- d %>% as_tibble() %>% 
      pivot_longer(-c(1:2), values_to = "population", names_to = "age")
    

    Finally, the past.future variable is not defined. You should change from

    d.tidy <- d.tidy %>% 
      mutate(time.gender = str_c(past.future, "_", gender))
    

    to:

    d.tidy <- d.tidy %>% 
      mutate(past.future = ifelse(year < 2024, "past", "future")) %>%
      mutate(time.gender = str_c(past.future, "_", gender))
    

    You should get a working gif from these 3 changes.