Paula
Paula

Reputation: 107

ggplot code works for a single list element for not in for loop

I have a list of data frames and want to make a plot of each of them, for which I want to have a single code. The code I wrote works when I work with each data frame separately, but not when I use a for-loop, I can't find out what's going on.

library(dplyr)
#example data set:
df1 <- data.frame(colA=c("AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ"),
                  colB=sample(1:100,10),
                  colC=1:10,
                  colD=c(22,13,13,5,4,4,3,2,2,2),
                  colE=c("1.AA","2.AB","3.AC","4.AD","5.AE","6.AF","7.AG","8.AH","9.AI","10.AJ"))

df2 <- data.frame(colA=c("BA","AA","AC","AD","AE","AF","AG","AH","AI","AJ"),
                  colB=sample(1:100,10),
                  colC=1:10,
                  colD=c(13,11,8,8,6,4,4,3,2,2),
                  colE=c("1.BA","2.AA","3.AC","4.AD","5.AE","6.AF","7.AG","8.AH","9.AI","10.AJ"))                 

df3 <- data.frame(colA=c("CA","CB","AC","AD","AA","AF","AG","AH","AI","AJ"),
                  colB=sample(1:100,10),
                  colC=1:10,
                  colD=c(13,11,8,8,6,4,4,3,2,2),
                  colE=c("1.CA","2.CB","3.AC","4.AD","5.AA","6.AF","7.AG","8.AH","9.AI","10.AJ"))

toy_top_list <- list(df1, df2, df3) 
names(toy_top_list) <- c("a","b","c")

#code for plotting one of the data frames:
#first setting some custom values for colors and labels:
slice_colors_a <- ifelse(toy_top_list[[1]][1]=="AA", "red","grey")
perc21_a <- toy_top_list[[1]] %>% filter(colA=="AA") %>% select(colD)
my_label_a <- ifelse(toy_top_list[[1]][1]=="AA", paste(perc21_a,"%",sep = ""),"")

#then the plot itself, for one of the data frames
toy_top_list[[1]] %>% ggplot(aes(x="",y=colD,fill=colE,width=1)) +
  geom_col(col="white", width = 2) + 
  scale_fill_manual(values=slice_colors_a, name="colA") +
  geom_text(aes(x=1.3,label=my_label_a),
            position=position_stack(vjust=0.5),
            col="white", fontface="bold") +
  ggtitle(names(toy_top_list)[1]) +
  theme(axis.title=element_blank(),
        axis.ticks = element_blank(),
        axis.text = element_blank(),
        panel.grid=element_blank(),
        panel.background = element_rect(fill = "white"),
        plot.margin = unit(c(3,1,3,1), "pt")) +
  coord_polar("y",direction = -1)

And with this I get what I want: enter image description here

#Now I want to automate this with a for-loop
#I create a list where I will store each plot
toy_pie_list <- vector(mode = "list",length=length(toy_top_list))

#now the for loop
for(i in 1:length(toy_top_list)){
  slice_colors_i <- ifelse(toy_top_list[[i]][1]=="AA", "red","grey")
  perc21_i <- toy_top_list[[i]] %>% filter(colA=="AA") %>% select(colD)
  my_label_i <- ifelse(toy_top_list[[i]][1]=="AA", paste(perc21_i,"%",sep = ""),"")
  toy_pie_list[[i]] <- toy_top_list[[i]] %>% ggplot(aes(x="",y=colD,fill=colE,width=1)) +
    geom_col(col="white", width = 2) + 
    scale_fill_manual(values=slice_colors_i, name="colA") +
    geom_text(aes(x=1.3,label=my_label_i),
              position=position_stack(vjust=0.5),
              col="white", fontface="bold") +
    ggtitle(names(toy_top_list)[i]) +
    theme(axis.title=element_blank(),
          axis.ticks = element_blank(),
          axis.text = element_blank(),
          panel.grid=element_blank(),
          panel.background = element_rect(fill = "white"),
          plot.margin = unit(c(3,1,3,1), "pt")) +
    coord_polar("y",direction = -1) 
}

names(toy_pie_list) <- names(toy_top_list)

toy_pie_list$a

But the plots it has created are not what I wanted: enter image description here

When I copy paste the code using the different list indices, I get the plots I want for each data frame, but when I use the for-loop the plots created are completely different. Can anybody please help?

Upvotes: 1

Views: 45

Answers (1)

stefan
stefan

Reputation: 123963

I have not checked out what exactly goes wrong with your for loop. However, in general I would suggest to put the plotting code inside a function if you want to make several plots using a loop. Makes the code much easier to read and to check.

Additionally my approach

  1. Adds columns for the label and the colors based on your condition ifelse(colA == "AA", ...

  2. Uses a named color vector to make sure that colors are assigned to the right values

library(dplyr)
library(ggplot2)

# example data set:
df1 <- data.frame(
  colA = c("AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ"),
  colB = sample(1:100, 10),
  colC = 1:10,
  colD = c(22, 13, 13, 5, 4, 4, 3, 2, 2, 2),
  colE = c("1.AA", "2.AB", "3.AC", "4.AD", "5.AE", "6.AF", "7.AG", "8.AH", "9.AI", "10.AJ")
)

df2 <- data.frame(
  colA = c("BA", "AA", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ"),
  colB = sample(1:100, 10),
  colC = 1:10,
  colD = c(13, 11, 8, 8, 6, 4, 4, 3, 2, 2),
  colE = c("1.BA", "2.AA", "3.AC", "4.AD", "5.AE", "6.AF", "7.AG", "8.AH", "9.AI", "10.AJ")
)

df3 <- data.frame(
  colA = c("CA", "CB", "AC", "AD", "AA", "AF", "AG", "AH", "AI", "AJ"),
  colB = sample(1:100, 10),
  colC = 1:10,
  colD = c(13, 11, 8, 8, 6, 4, 4, 3, 2, 2),
  colE = c("1.CA", "2.CB", "3.AC", "4.AD", "5.AA", "6.AF", "7.AG", "8.AH", "9.AI", "10.AJ")
)

toy_top_list <- list(df1, df2, df3)
names(toy_top_list) <- c("a", "b", "c")

toy_pie_list <- vector(mode = "list", length = length(toy_top_list))

plot_pie <- function(d, title) {
  # Add colums with the colors and the labels
  d <- d %>% 
    mutate(color = ifelse(colA == "AA", "red", "grey"),
           label = ifelse(colA == "AA", paste0(colD, "%"), ""))
  
  # Make a named vector of colors
  slice_colors <- select(d, colE, color) %>% 
    tibble::deframe()

  ggplot(d, aes(x = "", y = colD, fill = colE)) +
    geom_col(col = "white", width = 2) +
    scale_fill_manual(values = slice_colors, name = "colA") +
    geom_text(aes(x = 1.3, label = label),
      position = position_stack(vjust = 0.5),
      col = "white", fontface = "bold"
    ) +
    ggtitle(title) +
    theme(
      axis.title = element_blank(),
      axis.ticks = element_blank(),
      axis.text = element_blank(),
      panel.grid = element_blank(),
      panel.background = element_rect(fill = "white"),
      plot.margin = unit(c(3, 1, 3, 1), "pt")
    ) +
    coord_polar("y", direction = -1)
}

# for loop
for(i in names(toy_top_list)){
  toy_pie_list[[i]] <- plot_pie(toy_top_list[[i]], i)
}
# or do it with lapply
toy_pie_list <- lapply(names(toy_top_list), function(x) plot_pie(toy_top_list[[x]], x))
# set names
toy_pie_list <- setNames(toy_pie_list, names(toy_top_list))

toy_pie_list$c

Upvotes: 2

Related Questions