2

I'm new to R and in fact this is my first post on SO. Apologies if my description and layout isn't ideal - all feedback is welcome.

My problem is that I want to create separate plots from a tibble of data where each plot is for a different country but of the same variables. I then want to save each plot separately as a png.

I am most familiar with tidyverse and ggplot to tidy data and then visualise it. I have created a tibble that has 32,454 observations of 4 variables. The data consists of 200 different countries. I wish to create separate geom_bar plots for each country, for each of the variables (Gov_bal, Priv_bal, Ext_bal). I wish to stack the values of each variable for each year and then identify them by fill.

I have looked here https://stats.stackexchange.com/questions/19213/generate-separate-plots-for-each-group-of-records-in-dataset; here Save multiple ggplots using a for loop and here Plot one histogram(separate) for each variable in the column but I haven't been able to achieve what I want.

Here is an example of my data. I'm sure there's a better way I could replicate it but this is the gist of it.

Country <- c("Aus", "Aus", "Aus", "Aus", "USA", "USA", "USA", "USA", "UK", "UK", "UK", "UK")
Year <- c("1990", "1991", "1992", "1993", "1990", "1991", "1992", "1993", "1990", "1991", "1992", "1993")
Gov_bal <- c(5, 6, 5, 8, 8, 9, 5, 4, 6, 7, 4, 8)
Priv_bal <- c(3, 5, 4, 2, 6, 8, 5, 3, 2, 3, 6, 5)
Ext_bal <- c(2, -1, -2, 4, 5, 1, 3, 7, 4, 2, 3, 1)

sect_balances <- data.frame(Country, Year, Gov_bal, Priv_bal, Ext_bal)

sect_balances <- sect_balances %>% pivot_longer(Gov_bal:Ext_bal, names_to = "Sector", values_to = "Value")

The plot I want for each country looks like this. (I've used filter(Country == "Aus") to just select one country for this example but I want a function/solution that does this automatically for me.)

sect_balances %>% filter(Country == "Aus") %>% 
  ggplot(aes(x = Year, y = Value, fill = Sector)) +
  geom_bar(position = "stack", stat = "identity") +
    labs(x = "Year",
         y = "Per cent of GDP",
         title = "Australia") +
  theme_classic()

I am also aware that I can use facet_wrap to display all plots by "Country" but I want to save each plot individually.

My problem is that I wish to create a loop or some other solution that cycles through the different countries and creates separate geom_bar plots. So for my above example of data I want a code that creates three separate geom_bar plots (e.g. one each for "Aus", "USA", "UK") and then saves each plot separately. Obviously for my actual data I want a code that can do this for 200 different countries.

I have tried this code but to be honest I don't have my head around the functions loop or map. I need to do some more reading but any help would be great.

for (i in Country) {
  country_id <- subset(sect_balances, Country == i) 
  p <- ggplot(country_id, aes(x = Year, y = Value)) + geom_bar(position = "stack",
                                                               stat = "identity") 
  png(paste("plot_", i, ".png", sep = ""), width = 600, height = 500, res = 120) 
  print(p) 
  dev.off() 
}

I can't remember where I found this code that I adjusted, but unfortunately it is not a solution to my problem.

Many thanks

cromj006
  • 35
  • 3

3 Answers3

1

Without resorting to additional packages like ggforce, you could write a function to plot and save the graph for a single country like so:

plot_country <- function(x = "Aus") {
  tmp <- sect_balances %>%
         filter(Country == x)
  p <- ggplot(tmp, aes(x = Year, y = Value, fill = Sector)) +
       geom_bar(position = "stack", stat = "identity") +
       labs(x = "Year", y = "Per cent of GDP", title = x) +
       theme_classic()
  ggsave(p, file=paste0(x, ".png"))
}

Then just loop over country names:

for (k in unique(sect_balances$Country)) {
  plot_country(k)
}

The embrace operator is described in the "programming with dplyr" vignette:

https://dplyr.tidyverse.org/articles/programming.html

Vincent
  • 15,809
  • 7
  • 37
  • 39
  • Thanks @Vincent for the quick response. Your code worked really well - thank you. However, when I apply it to my actual dataset I get the error "Error in unique(Country) : object 'Country' not found". I'm wondering if that is because in my dataset the "Country" variable has been parsed as a character? Any ideas? – cromj006 Nov 27 '20 at 01:47
  • Here, it works because you defined `Country` as its own vector. My guess is you would have to modify your code with `unique(sect_balances$Country)` to retrieve the country abbreviations from the actual column of your dataset. – Vincent Nov 27 '20 at 01:51
  • 1
    Wow, you're totally right @RonakShah . I need to take a break ;) – Vincent Nov 27 '20 at 02:44
1

If you want to plot each country separately instead of using for loop you can split the data into list of dataframes and plot each one individually.

library(tidyverse)

sect_balances %>%
  group_split(Country) %>%
  map(~ggplot(.x) + aes(x = Year, y = Value, fill = Sector) +
      geom_bar(position = "stack", stat = "identity") +
      labs(x = "Year",
           y = "Per cent of GDP",
           title = first(.x$Country)) +
      theme_classic()) -> list_plot

list_plot has list of plots and you can access each one of them individually as list_plot[[1]], list_plot[[2]] and so on.

If you want to save them as separate plots on your device you can add additional ggsave command in the code as :

sect_balances %>%
  group_split(Country) %>%
  map(~{ggplot(.x) + aes(x = Year, y = Value, fill = Sector) +
        geom_bar(position = "stack", stat = "identity") +
        labs(x = "Year",
             y = "Per cent of GDP",
             title = first(.x$Country)) +
        theme_classic() -> plot
      ggsave(paste0(first(.x$Country), '_plot.png'), plot)
      })
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
0

Here is one way to do it, in which we make a ggplot object and then using ggforce::facet_wrap_paginate to loop through the countries to print the result.

library(tidyr)
library(dplyr)
library(ggplot2)
library(ggforce)

p = sect_balances %>%  
  ggplot(aes(x = Year, y = Value, fill = Sector)) +
  geom_bar(position = "stack", stat = "identity") +
  labs(x = "Year",
       y = "Per cent of GDP")

uni_country = sort(unique(sect_balances$Country))

for (i in seq_along(uni_country)) {
  print(p + 
    facet_wrap_paginate(facets = ~Country, 1, 1, page = i) + 
    ggsave(paste0("my_plot_", uni_country[i], ".png")))
}
Cole
  • 11,130
  • 1
  • 9
  • 24
  • Thanks @Cole for the quick response. Your code worked (i.e. I didn't get any errors but I couldn't then see the plots it had created. Also it didn't save them anywhere. The `facet_wrap_paginate` looks like an interesting tool. Can you save plots separately using it? – cromj006 Nov 27 '20 at 01:49
  • I verified and using ```ggsave()``` works. Note, because the faceting orders alphabetically, I sorted the ```uni_country``` so that you could include the country name in the file you save. Edit: I also just saw you do not see any plots. It seemed to work at some point but I am no longer sure. I will edit the post when/if I find the fix. – Cole Nov 27 '20 at 02:06
  • Thanks I'll have another look at your solution but @Vincent has worked for me. Thank you for taking the time to help me though – cromj006 Nov 27 '20 at 02:20