Search code examples
rggplot2bar-chartaxis-labelsx-axis

add multi x-axis ggplot2 or customized labels to stack bar plot


Hei,

I am trying to add informations of the depth range integrated (1) on the x-axis or (2) on the top of stack bars of the graph (or something similar). See the pictures below.

enter image description here

with the labels: enter image description here

(1) not sure how to do it.

(2) To plot the infos on top of stacked bar I have first create the variable:

depth_int <- c("5-200 m", "5-181 m", "5-200 m", "5-200 m", "5-155 m", "5-200 m", "5-200 m", 
             "5-90 m", "10-60 m", "10-90 m", "10-30 m", "20-90 m", "0.5-30 m", "0.5-90 m", "0.5-90 m")

and by using this function on my ggplot(): geom_text(aes(label = depth_int), hjust = 0, position = "stack")

I get this error: Error: Aesthetics must be either length 1 or the same as the data (164): label

(I guess its because the stacked bar is a combination of the taxa and then you cannot plot only 1 value on the top of the bar per stations (e.g., P1).

-

this is my script:

ggplot(df, aes(x=locationID, fill=class, y = V1))+
  geom_bar(stat="identity", position = "stack", width = 0.9)+
  facet_grid(. ~ expedition, scales="free_x") +
  #scale_fill_manual(values = default_colors, labels= c("","",""))          #default_colors<-c("#F8766D", "#00BA38", "#619CFF")
  # change the label names in the legend
  labs(title = "Taxa abundance per depth integrated", fill = "Taxa", 
       x= bquote('Stations'),
       y= bquote('Abundance'~(cells~m^-3)))+ 
  theme_minimal()+
    theme(panel.grid.major.y = element_line(size = 0.5, linetype = 'solid',
                                          colour = "grey75"),
          panel.grid.minor.y = element_line(size = 0.5, linetype = 'solid',
                                            colour = "grey75"),
          panel.grid.major.x = element_blank(),
        panel.background = element_rect(fill = "white", colour = "grey75"),
        axis.text.x = element_text(colour="black",size=7),
        axis.text.y = element_text(colour="black",size=10),
        axis.title.x = element_text(colour="black",size=10),
        axis.title.y = element_text(colour="black",size=10),
        plot.title = element_text(colour = "black", size = 10, face = "bold"),
        legend.position = "right",
        legend.text = element_text(size=10),
        legend.title = element_text(size = 11, hjust =0.5,  vjust = 3, face = "bold"),
        legend.key.size = unit(10,"point"),
        legend.spacing.y = unit(-.25,"cm"),
        legend.margin=margin(0,5,8,5), 
        legend.box.margin=margin(-10,10,-3,10),
        legend.box.spacing = unit(0.5,"cm"),
        plot.margin = margin(2,2,0,0)) 

Solution

  • Simply passing a vector labels to geom_text will not work. To achieve your desired result you could

    1. Summarise your data to get the total height of the bars by location and expedition

    2. Add your labels to the summarised dataframe. To this end I use a dataframe of labels by location and expedition which I join to the summarised df.

    3. Use the summarised dataframe as data in geom_text

    Note: As the summarised df has no column class I have put fill=class as a local aes inside geom_bar.

    Using some random example data try this:

    library(ggplot2)
    library(tidyr)
    library(dplyr)
    
    # Random example dataset
    set.seed(42)
    df <- data.frame(
      locationID = c(sample(LETTERS[1:4], 50, replace = TRUE), sample(LETTERS[5:8], 50, replace = TRUE)),
      class = sample(letters[1:4], 100, replace = TRUE),
      expedition = rep(paste("expedition", 1:2), each = 50),
      V1 = runif(100)
    )
    
    # Make a dataframe of labels by expedition and locationID
    depth_int <- tidyr::expand(df, nesting(expedition, locationID))
    depth_int$label <- c("5-200 m", "5-181 m", "5-200 m", "5-200 m", "5-90 m", "10-60 m", "10-90 m", "10-30 m")
    depth_int
    #> # A tibble: 8 x 3
    #>   expedition   locationID label  
    #>   <chr>        <chr>      <chr>  
    #> 1 expedition 1 A          5-200 m
    #> 2 expedition 1 B          5-181 m
    #> 3 expedition 1 C          5-200 m
    #> 4 expedition 1 D          5-200 m
    #> 5 expedition 2 E          5-90 m 
    #> 6 expedition 2 F          10-60 m
    #> 7 expedition 2 G          10-90 m
    #> 8 expedition 2 H          10-30 m
    
    # Summarise your data to get the height of the stacked bar and join the labels
    df_labels <- df %>% 
      group_by(locationID, expedition) %>% 
      summarise(V1 = sum(V1)) %>% 
      left_join(depth_int, by = c("locationID", "expedition"))
    
    ggplot(df, aes(x=locationID, y = V1))+
      geom_bar(aes(fill=class), stat="identity", position = "stack", width = 0.9) +
      # Use summarised data to put the labels on top of the stacked bars
      geom_text(data = df_labels, aes(label = label), vjust = 0, nudge_y = 1) +
      facet_grid(. ~ expedition, scales="free_x") +
      labs(title = "Taxa abundance per depth integrated", 
           fill = "Taxa", 
           x= bquote('Stations'),
           y= bquote('Abundance'~(cells~m^-3)))+ 
      theme_minimal()+
      theme(panel.grid.major.y = element_line(size = 0.5, linetype = 'solid',
                                              colour = "grey75"),
            panel.grid.minor.y = element_line(size = 0.5, linetype = 'solid',
                                              colour = "grey75"),
            panel.grid.major.x = element_blank(),
            panel.background = element_rect(fill = "white", colour = "grey75"),
            axis.text.x = element_text(colour="black",size=7),
            axis.text.y = element_text(colour="black",size=10),
            axis.title.x = element_text(colour="black",size=10),
            axis.title.y = element_text(colour="black",size=10),
            plot.title = element_text(colour = "black", size = 10, face = "bold"),
            legend.position = "right",
            legend.text = element_text(size=10),
            legend.title = element_text(size = 11, hjust =0.5,  vjust = 3, face = "bold"),
            legend.key.size = unit(10,"point"),
            legend.spacing.y = unit(-.25,"cm"),
            legend.margin=margin(0,5,8,5), 
            legend.box.margin=margin(-10,10,-3,10),
            legend.box.spacing = unit(0.5,"cm"),
            plot.margin = margin(2,2,0,0))