Search code examples
rggplot2bar-chartr-plotly

Create Bullet Graph using ggplot2 or plotly in R


I'm trying to find out a way to build Bullet chart for a categorical and a numerical variable. My original datasample looks like the one below:

| Name   | count |
|--------|-------|
| Alex   | 89    |
| Bob    | 85    |
| David  | 76    |
| Mike   | 64    |
| Jess   | 50    |
| Steve  | 45    |
| Elina  | 29    |
| Ethan  | 20    |
| Jordan | 10    |
| Jim    | 5     |

It is replicated as

name = c("Alex","Bob","David","Mike","Jess","Steve","Elina","Ethan","Jordan","Jim")
count= c(89,85,76,64,50,45,29,20,10,5)
data= data.frame(name, count, stringsAsFactors = TRUE)

I don't know how to create target/threshold values and then a bullet graph. I tried referencing this link Create with ggplot2 a barplot with bar bullet in it, but I'm unable to get proper graph with it. Can someone please help me out to create bullet graph based on this dataset.

The output should be something similar to the image below: enter image description here


Solution

  • To do that need to create a dummy data that represent the rank for each name as below

    name = c("Alex","Bob","David","Mike","Jess","Steve","Elina","Ethan","Jordan","Jim")
    count= c(89,85,76,64,50,45,29,20,10,5)
    data = data.frame(name, count, stringsAsFactors = TRUE)
    
    library(dplyr)
    library(ggplot2)
    
    data <- data %>%
      mutate(width = seq(.8, .1, length.out = nrow(data)))
    
    bullet_base <- data.frame(rank = c("Poor", "Ok", "Good", "Excellent"),
      value = c(20, 20, 20, 40))
    bullet_base_rep <- 
      do.call("rbind", replicate(nrow(data), bullet_base, simplify = FALSE)) %>%
      mutate(name = sort(rep(data$name, 4) ))
    
    head(bullet_base_rep, 10)
    #>         rank value  name
    #> 1       Poor    20  Alex
    #> 2         Ok    20  Alex
    #> 3       Good    20  Alex
    #> 4  Excellent    40  Alex
    #> 5       Poor    20   Bob
    #> 6         Ok    20   Bob
    #> 7       Good    20   Bob
    #> 8  Excellent    40   Bob
    #> 9       Poor    20 David
    #> 10        Ok    20 David
    

    Now we just need to graph them with the right color. I didn't have the white line as you didn't provide any information about what should use to draw the cross-line for each bar of this graph.

    bullet_colors <- c("#E9FFE3", "#A3D694", "#61AB40", "#318100")
    names(bullet_colors) <- c("Poor", "Ok", "Good", "Excellent")
    
    ggplot() +
      geom_bar(data = bullet_base_rep, 
        aes(x = name, y = value, fill = rank), stat = "identity",
        position = "stack") +
      geom_bar(data = data, 
        aes(x = name, y = count), fill = "black", width = .2,
        stat = "identity") +
      scale_fill_manual(values = bullet_colors) +
      coord_flip(expand = FALSE)
    

    Created on 2021-05-29 by the reprex package (v2.0.0)

    # Change the order of Excellent -> Poor
    bullet_base <- data.frame(rank = c("Excellent", "Good", "Ok", "Poor"),
      value = c(20, 20, 20, 40))
    bullet_base_rep <- 
      do.call("rbind", replicate(nrow(data), bullet_base, simplify = FALSE)) %>%
      mutate(name = sort(rep(data$name, 4) ))
    
    # For drawing in certain order change the value to factor type
    # ggplot will draw the last level first when draw graph
    bullet_base_rep$rank <- factor(bullet_base_rep$rank,
      levels = c("Poor", "Ok", "Good", "Excellent"))
    head(bullet_base_rep, 10)
    #>         rank value  name
    #> 1  Excellent    20  Alex
    #> 2       Good    20  Alex
    #> 3         Ok    20  Alex
    #> 4       Poor    40  Alex
    #> 5  Excellent    20   Bob
    #> 6       Good    20   Bob
    #> 7         Ok    20   Bob
    #> 8       Poor    40   Bob
    #> 9  Excellent    20 David
    #> 10      Good    20 David
    
    bullet_colors <- c("#E9FFE3", "#A3D694", "#61AB40", "#318100")
    names(bullet_colors) <- c("Poor", "Ok", "Good", "Excellent")
    
    ggplot() +
      geom_bar(data = bullet_base_rep, 
        aes(x = name, y = value, fill = rank), stat = "identity",
        position = "stack") +
      geom_bar(data = data, 
        aes(x = name, y = count), fill = "black", width = .2,
        stat = "identity") +
      scale_fill_manual(values = bullet_colors) +
      coord_flip(expand = FALSE)
    

    Created on 2021-05-30 by the reprex package (v2.0.0)