I need to make "stacked bar plot" using ggplot2

38 Views Asked by At

I need to make a "stacked bar plot" using ggplot2 foe following data (similar to the image below)

stacked bar plot- what i needed `

State <- c("All India", "Arunachal Pradesh", "Gujarat", "Jammu & Kashmir", "Madhya Pradesh", "Maharashtra", "Meghalaya", "Mizoram", "Orissa", "Punjab", "Tamil Nadu", "Uttar Pradesh", "West Bengal")
Mean <- c(217, 227, 214, 217, 217, 204, 209, 215, 223, 243, 220, 230, 210)
Percentile_5th <- c(163, 193, 162, 185, 177, 146, 169, 173, 167, 183, 156, 163, 170)
Percentile_95th <- c(271, 262, 265, 249, 258, 261, 249, 257, 280, 302, 284, 297, 249)


I need to make it for all the states ("All India", "Arunachal Pradesh", "Gujarat", "Jammu & Kashmir", "Madhya Pradesh", "Maharashtra", "Meghalaya", "Mizoram", "Orissa", "Punjab", "Tamil Nadu", "Uttar Pradesh", "West Bengal")

Also I need to any two state ("All India", "Punjab")

I have tried to make it for two state using following code

data <- data.frame(
      State = rep(c("All India",  "Punjab"   ), each = 3),
     Metric = rep(c("5th Percentile", "Mean", "95th Percentile"), 2),
     Value = c(163, 217, 271, 183, 243, 302)  # 5th P, Mean increment, 95th P increment for each state
 )

ggplot(data, aes(x = State, y = Value, fill = Metric)) +
    geom_bar(stat = "identity") +
    geom_text(aes(label = Value), position = position_stack(vjust = 0.5)) +
    scale_fill_manual(values = c("5th Percentile" = "orange", "Mean" = "blue", "95th Percentile" = "green")) +
    labs(title = "Stacked Bar Plot for All India and Punjab", 
         x = "State", 
         y = "Values", 
         fill = "Metric") +
    theme_minimal()

The output I received is

The output plot

The plot I need (as I said earlier) is the 5th percentile at the bottom of the bar, the next mean at the middle of the bar, and the 95th at the top of the bar. But it is not giving like that. Also, I need to make a similar plot for all states. Kindly help me

1

There are 1 best solutions below

3
stefan On

The first issue could be easily fixed by converting your metric column to a factor with the order of the levels set in your desired order. Second, to create a similar plot for each of your states put your data in a data frame, reshape to long format, put your plotting code inside a function, filter the data for the desired state and finally loop over all states using e.g. lapply:

library(ggplot2)
library(tidyr)

data <- data.frame(
  State = State,
  Mean = Mean,
  Percentile_5th = Percentile_5th,
  Percentile_95th = Percentile_95th
)

data_long <- data |>
  tidyr::pivot_longer(
    -State,
    names_to = "Metric",
    values_to = "Value"
  )

data_long$Metric <- factor(
  data_long$Metric,
  levels = rev(c("Percentile_5th", "Mean", "Percentile_95th")),
  labels = rev(c("5th Percentile", "Mean", "95th Percentile"))
)

plot_fun <- function(state) {
  data_long |>
    subset(
      State %in% c("All India", state)
    ) |>
    ggplot(aes(x = State, y = Value, fill = Metric)) +
    geom_col(
      color = "white",
      linewidth = .25,
      width = .75
    ) +
    geom_text(aes(label = Value),
      position = position_stack(vjust = 0.5)
    ) +
    scale_fill_manual(
      values = c(
        "5th Percentile" = "orange",
        "Mean" = "blue",
        "95th Percentile" = "forestgreen"
      )
    ) +
    scale_y_continuous(expand = c(0, 0, .05, 0)) +
    labs(
      title = paste0(
        "Stacked Bar Plot for All India and ",
        state
      ),
      x = "State",
      y = "Values",
      fill = "Metric"
    ) +
    theme_bw() +
    theme(
      panel.grid = element_blank()
    )
}

# For all states run:
# lapply(State[-1], plot_fun)

lapply(State[-1][1:2], plot_fun)
#> [[1]]

#> 
#> [[2]]