R Shiny Gantt Chart: How to Modernize Planning Management in Pharma

[This article was first published on Tag: r - Appsilon | Enterprise R Shiny Dashboards, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
R Shiny Gantt Chart for planning management in pharmaceutical logistics

We live in a constantly and rapidly evolving environment, which means that effective planning management is crucial for companies. This is particularly true in the pharmaceutical sector, which involves stringent regulations, research and development timelines, and intricate supply chains. Traditional planning methods often fall short in providing a comprehensive overview and real-time insights into complex projects.

This is where Gantt charts, combined with the power of R Shiny, come into play. They offer a way to modernize and optimize planning management, and in this article, we will demonstrate precisely how. By the end of the article, you will have a complete R Shiny application with options to add or remove tasks from a Gantt chart.

But first, what is a Gantt chart? That’s what we’ll answer in the following section.

Want to create automated data quality reports in R and R Shiny? Try data.validator by Appsilon – you’ll never look back.

Table of contents:


What Is a Gantt Chart and How Is It Used in Planning Management?

A Gantt chart is an excellent tool in planning management as it visually represents project schedules, tasks, and timelines. It provides a comprehensive overview of a project’s progress and helps project managers and stakeholders effectively allocate resources, track milestones, and manage dependencies. By displaying tasks and their interrelationships in a timeline format, Gantt charts enable teams with a quick overview to plan, monitor, and control projects more efficiently.

Here’s one example of a Gantt chart, specifically tied to the film production industry:

Image 1 - Gantt chart example (source: wikimedia.org)

Image 1 – Gantt chart example (source: wikimedia.org)

As you can see, a Gantt chart consists of horizontal bars that represent individual tasks or activities within a project. These bars are plotted along a horizontal axis, which allows stakeholders to understand the sequential order and duration of tasks. The length of each bar corresponds to the duration of the task, while the position along the timeline indicates the start and end dates

Each bar represents a single task, and multiple tasks can be happening at the same time. In the above example, you can see how the Pre-Production task consists of 5 different tasks and some of them have an overlap in the time frame. To be more precise, the “Researching interview partners” task begins at the end of the “Write shooting script” task.

Gantt Charts for Project Management Use

This article will focus on a Gantt chart application in the pharmaceutical industry. It’s a good example, but it’s not limited to any specific industry. The tutorial below is a good introduction to Gantt charts for project managers and stakeholders to visualize the various stages of drug development, identify dependencies between tasks, and ensure that all activities are executed in a timely manner.

For instance, in the pharmaceutical sector, Gantt charts can be beneficial for managing clinical trials. These trials involve multiple stages, such as patient recruitment, protocol development, data collection, analysis, and reporting. By utilizing a Gantt chart, project managers can identify potential bottlenecks, allocate resources effectively, and track the progress of each stage. This ensures that trials are conducted efficiently, adhering to strict timelines and regulatory requirements.

That’s about enough for the introduction to Gantt charts and the theory, so next we’ll dive deep into practical use cases. First, you’ll learn how to visualize Gantt charts with R’s ggplot2 package.

Gantt Charts in R – How to Create a Gantt Chart with ggplot2

Now that you understand the importance of Gantt charts in planning management, let’s explore how to create a Gantt chart using the popular R package – ggplot2.

For starters, we’ll import the R packages and create some dummy data. Each task for the Gantt chart will have a name, start date, and end date. For example, we’ll make a dummy project in the pharmaceutical industry with 5 tasks: Research, Clinical Trials, Regulatory Approval, Manufacturing, and Marketing:

library(tidyverse)
library(ggplot2)


# Individual tasks formatted as a data.frame
tasks <- data.frame(
  Task = c("Research", "Clinical Trials", "Regulatory Approval", "Manufacturing", "Marketing"),
  StartDate = as.Date(c("2023-05-01", "2023-07-01", "2024-01-01", "2024-03-01", "2024-06-01")),
  EndDate = as.Date(c("2023-06-30", "2023-12-31", "2024-06-30", "2024-12-31", "2025-06-30"))
)

tasks

Here’s what the resulting data.frame looks like:

Image 2 - Dummy tasks for a pharmaceutical industry project

Image 2 – Dummy tasks for a pharmaceutical industry project

We can now create a basic Gantt chart using ggplot2. We’ll use the geom_segment() function to draw horizontal bars for each task based on their start and end dates. Here’s the code:

ggplot(tasks, aes(x = StartDate, xend = EndDate, y = fct_rev(fct_inorder(Task)), yend = Task)) + 
  geom_segment()

And we get a Gantt chart back, but the visuals leave a lot to be desired:

Image 3 - A basic Gantt chart with ggplot2

Image 3 – A basic Gantt chart with ggplot2

Now to enhance the visuals, we’ll add colors to individual tasks using the color parameter within geom_segment(). We’ll also increase the line width by altering the linewidth parameter.

The colors vector holds hexadecimal values for a monochromatic blue color palette:

colors <- c("#deecfb", "#bedaf7", "#7ab3ef", "#368ce7", "#1666ba")
  
ggplot(tasks, aes(x = StartDate, xend = EndDate, y = fct_rev(fct_inorder(Task)), yend = Task)) + 
  geom_segment(linewidth = 35, color = colors)

The resulting Gantt chart is much more visually appealing now:

Image 4 - Adding colors to individual tasks

Image 4 – Adding colors to individual tasks

But we can take the whole thing to the next level. The following code snippet shows you how to add titles, labels, and adjust the overall theme. In short, it makes the Gantt chart much more presentable:

ggplot(tasks, aes(x = StartDate, xend = EndDate, y = fct_rev(fct_inorder(Task)), yend = Task)) + 
  geom_segment(linewidth = 35, color = colors) + 
  labs(
    title = "Pharma Company Gantt Chart",
    x = "Duration",
    y = "Task"
  ) + 
  theme_bw() + 
  theme(legend.position = "none") + 
  theme(
    plot.title = element_text(size = 20),
    axis.text.x = element_text(size = 12),
    axis.text.y = element_text(size = 12, angle = 45)
  )

Here’s what you’ll see in the visualizations panel:

Image 5 - Finalizing the visuals of a Gantt chart

Image 5 – Finalizing the visuals of a Gantt chart

The Gantt chart is looking great now but isn’t interactive in any way. We’ll change that in the next section, where we’ll create an R Shiny application around it.

R Shiny Gantt Chart – How to Build a Planning Management App

This section will take you from a static chart to a fully-working R Shiny application that allows you to add and remove elements from Gantt charts through interactive controls. It will take some work to make everything work, so follow the code section by section.

We’ll place comments in the code only for those sections that have changed from the previous one. That’s how you’ll recognize where to add, delete, or change the code.

Let’s start with a basic example of a R Shiny application with input controls and output sections for tables and charts.

Building a Basic R Shiny App for Planning Management

We’ll start simple and build an R Shiny application with input controls and the output contains and display only static data. You won’t be able to add or remove tasks in this section, but understanding this one is mandatory before implementing more advanced features.

This is what you’ll need regarding library imports, so stick them to the top of your R script:

library(tidyverse)
library(ggplot2)
library(DT)
library(shiny)

Now let’s write the UI. We’ll have a simple page with a sidebar and a main content section. The sidebar will have input elements for task name, start date, end date, and an action button that adds the task to the list. Again, we won’t implement the logic for adding the task to the list in this section.

The main panel of the app will have a task table view which will render a DT table with a list of tasks. Below the table, we’ll also have a chart view that will show our Gantt chart.

Don’t know how to display table data in R? Here are top packages for visualizing table data in R and R Shiny.

Here’s the entire code snippet for the Shiny app UI:

# Shiny app UI
ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      tags$h3("R Shiny Task Scheduling"),
      tags$hr(),

      # Controls for task name, start & end date
      textInput(inputId = "inTaskName", label = "Task:", placeholder = "e.g., Marketing"),
      dateInput(inputId = "inStartDate", value = Sys.Date(), min = Sys.Date(), label = "Start Date:"),
      dateInput(inputId = "inEndDate", value = Sys.Date() + 10, min = Sys.Date() + 1, label = "End Date:"),

      # Button that adds the task
      actionButton(inputId = "btn", label = "Add Task")
    ),
    mainPanel(
      # Table output
      tags$h3("Task Table View"),
      tags$hr(),
      DTOutput(outputId = "tableTasks"),

      # Chart output
      tags$h3("Task Chart View"),
      tags$hr(),
      plotOutput(outputId = "plotTasks")
    )
  )
)

And now onto the server logic. We’ll make an initial data.frame of tasks as a reactive value and then display it both as a table and as a Gantt chart.

The code for the table is new, so make sure you understand it. On the contrary, the code for the Gantt chart is almost identical to the one from the previous section:

server <- function(input, output) {
  df <- reactiveValues(
    data = data.frame(
      Task = c("Research", "Clinical Trials", "Regulatory Approval"),
      StartDate = as.Date(c("2023-05-01", "2023-07-01", "2024-01-01")),
      EndDate = as.Date(c("2023-06-30", "2023-12-31", "2024-06-30"))
    )
  )

  # Table output
  output$tableTasks <- renderDT({
    datatable(
      data = df$data,
      colnames = c("Task", "Start Date", "End Date"),
      filter = "top"
    )
  })

  # Chart output
  output$plotTasks <- renderPlot({
    ggplot(df$data, aes(x = StartDate, xend = EndDate, y = fct_rev(fct_inorder(Task)), yend = Task)) +
      geom_segment(linewidth = 10, color = "#0198f9") +
      labs(
        title = "Pharma Company Gantt Chart",
        x = "Duration",
        y = "Task"
      ) +
      theme_bw() +
      theme(legend.position = "none") +
      theme(
        plot.title = element_text(size = 20),
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12, angle = 45)
      )
  })
}


shinyApp(ui = ui, server = server)

We now have both the UI and server code, which means we can run the R Shiny app. Here’s what it looks like:

Image 6 - Basic R Shiny app for planning management

Image 6 – Basic R Shiny app for planning management

The UI controls are there and both the table and the chart display correctly. The only problem is – The input controls don’t work. You’ll learn how to enable adding tasks in the following section.

How to Add Tasks to a Gantt Chart in Shiny

You don’t have to tweak a thing in the Shiny app UI to enable adding tasks to a task data.frame. Everything happens in the server() function.

Here’s a brief overview of the code changes we have to make:

  • Attach a reactive observer to the action button
    • Fetch the current values from the input elements (task name, start date, end date)
    • Check if current values are not null
    • If not null, make a new data.frame that matches the structure of the existing one
    • Use the rbind() function to concatenate two data.frame objects. This will append the new task to the end of the existing ones
    • Sort the data.frame by StartDate to preserve the ordering

And that’s it! It’s just one task with multiple steps, and all the code changes happen in server() below the initial data declaration.

Here’s the code snippet for the entire R Shiny app:

library(tidyverse)
library(ggplot2)
library(DT)
library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      tags$h3("R Shiny Task Scheduling"),
      tags$hr(),
      textInput(inputId = "inTaskName", label = "Task:", placeholder = "e.g., Marketing"),
      dateInput(inputId = "inStartDate", value = Sys.Date(), min = Sys.Date(), label = "Start Date:"),
      dateInput(inputId = "inEndDate", value = Sys.Date() + 10, min = Sys.Date() + 1, label = "End Date:"),
      actionButton(inputId = "btn", label = "Add Task")
    ),
    mainPanel(
      tags$h3("Task Table View"),
      tags$hr(),
      DTOutput(outputId = "tableTasks"),
      tags$h3("Task Chart View"),
      tags$hr(),
      plotOutput(outputId = "plotTasks")
    )
  )
)


server <- function(input, output) {
  df <- reactiveValues(
    data = data.frame(
      Task = c("Research", "Clinical Trials", "Regulatory Approval"),
      StartDate = as.Date(c("2023-05-01", "2023-07-01", "2024-01-01")),
      EndDate = as.Date(c("2023-06-30", "2023-12-31", "2024-06-30"))
    )
  )

  # ADD TASK
  observeEvent(input$btn, {
    task_name <- input$inTaskName
    task_start_date <- input$inStartDate
    task_end_date <- input$inEndDate

    # Check if not null
    if (!is.null(task_name) && !is.null(task_start_date) && !is.null(task_end_date)) {
      # Make a new row
      new_row <- data.frame(Task = task_name, StartDate = task_start_date, EndDate = task_end_date, stringsAsFactors = FALSE)
      # Add row to the existing dataframe
      df$data <- rbind(df$data, new_row)
      # Sort the dataframe by StartDate
      df$data <- df$data[order(df$data$StartDate), ]
    }
  })

  output$tableTasks <- renderDT({
    datatable(
      data = df$data,
      colnames = c("Task", "Start Date", "End Date"),
      filter = "top"
    )
  })

  output$plotTasks <- renderPlot({
    ggplot(df$data, aes(x = StartDate, xend = EndDate, y = fct_rev(fct_inorder(Task)), yend = Task)) +
      geom_segment(linewidth = 10, color = "#0198f9") +
      labs(
        title = "Pharma Company Gantt Chart",
        x = "Duration",
        y = "Task"
      ) +
      theme_bw() +
      theme(legend.position = "none") +
      theme(
        plot.title = element_text(size = 20),
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12, angle = 45)
      )
  })
}


shinyApp(ui = ui, server = server)

You can now launch the app and experiment with task addition:

Image 7 - Adding tasks to the Gantt chart in R Shiny

Image 7 – Adding tasks to the Gantt chart in R Shiny

You can further improve this solution by adding start date and end date checks. For example, the end date can’t be lower than the start date, and we currently don’t implement this check. Consider this as a homework assignment.

Up next, let’s see how to delete a task from the task list.

How to Delete Tasks from an R Shiny Gantt Chart

Deleting a task will be much more challenging than adding it. There are just much more moving parts – we need an additional button for each table row, and we need to keep track of the row number.

Let’s follow the same convention and declare a list of code changes we have to implement:

  • Add an ID column and a Remove button to the table
    • Add an ID column based on the row_number() function. Make sure the column is added as the first column of the data.frame
    • Add a Remove button to each row by monitoring the current ID value
  • Change task addition logic to accommodate new columns
    • Calculate the new row ID – The largest from the data.frame plus one
    • Add a Remove button for the given ID
  • Attach a reactive observer to the Remove button
    • Use the - notation to remove a row at a given index location (ID)
    • Reorder the data.frame by start date

It’s a lot of code changes, so don’t sweat it if you can’t figure everything out. Just copy our code snippet from below:

library(tidyverse)
library(ggplot2)
library(DT)
library(shiny)
library(glue)


ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      tags$h3("R Shiny Task Scheduling"),
      tags$hr(),
      textInput(inputId = "inTaskName", label = "Task:", placeholder = "e.g., Marketing"),
      dateInput(inputId = "inStartDate", value = Sys.Date(), min = Sys.Date(), label = "Start Date:"),
      dateInput(inputId = "inEndDate", value = Sys.Date() + 10, min = Sys.Date() + 1, label = "End Date:"),
      actionButton(inputId = "btn", label = "Add Task")
    ),
    mainPanel(
      tags$h3("Task Table View"),
      tags$hr(),
      DTOutput(outputId = "tableTasks"),
      tags$h3("Task Chart View"),
      tags$hr(),
      plotOutput(outputId = "plotTasks")
    )
  )
)


server <- function(input, output) {
  df <- reactiveValues(
    data = data.frame(
      Task = c("Research", "Clinical Trials", "Regulatory Approval"),
      StartDate = as.Date(c("2023-05-01", "2023-07-01", "2024-01-01")),
      EndDate = as.Date(c("2023-06-30", "2023-12-31", "2024-06-30"))
    ) %>%
      # Add an ID column - used later to remove row with certain ID
      mutate(ID = row_number(), .before = Task) %>%
      # Add a column with a custom Remove button
      mutate(
        Remove = glue('<button id="custom_btn" onclick="Shiny.onInputChange(\'button_id\', \'{ID}\')">Remove</button>')
      )
  )

  observeEvent(input$btn, {
    task_name <- input$inTaskName
    task_start_date <- input$inStartDate
    task_end_date <- input$inEndDate

    if (!is.null(task_name) && !is.null(task_start_date) && !is.null(task_end_date)) {
      # We also need a new row ID
      new_id <- max(df$data$ID) + 1
      new_row <- data.frame(
        # Row ID
        ID = new_id,
        Task = task_name,
        StartDate = task_start_date,
        EndDate = task_end_date,
        # Remove button
        Remove = glue('<button id="custom_btn" onclick="Shiny.onInputChange(\'button_id\', \'{new_id}\')">Remove</button>'),
        stringsAsFactors = FALSE
      )
      df$data <- rbind(df$data, new_row)
      df$data <- df$data[order(df$data$ID), ]
    }
  })

  # REMOVE A TASK
  observeEvent(input$button_id, {
    # Remove a row from the data.frame
    df$data <- df$data[-c(as.integer(input$button_id)), ]
    # Sort the dataframe by StartDate
    df$data <- df$data[order(df$data$StartDate), ]
  })

  output$tableTasks <- renderDT({
    datatable(data = df$data, escape = FALSE)
  })

  output$plotTasks <- renderPlot({
    ggplot(df$data, aes(x = StartDate, xend = EndDate, y = fct_rev(fct_inorder(Task)), yend = Task)) +
      geom_segment(linewidth = 10, color = "#0198f9") +
      labs(
        title = "Pharma Company Gantt Chart",
        x = "Duration",
        y = "Task"
      ) +
      theme_bw() +
      theme(legend.position = "none") +
      theme(
        plot.title = element_text(size = 20),
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12, angle = 45)
      )
  })
}


shinyApp(ui = ui, server = server)

Our R Shiny application for planning management is almost done. Here’s what it looks like:

Image 8 - Removing tasks from a Gantt chart in R Shiny

Image 8 – Removing tasks from a Gantt chart in R Shiny

Finally, let’s address the general app styles.

Styling an R Shiny Gantt Planning Management Application with CSS

Functionallity-wise, our app is ready to ship. But it looks terrible. In this section, we’ll show you how to style it with a couple of lines of CSS code.

To start, create a www folder right where your app.R script file is saved, and then create a main.css file inside it. Paste the following contents:

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Poppins', sans-serif;
  background-color: #f2f2f2;
}

h1, h2, h3, h4, h5, h6 {
  font-weight: 700;
}

.sidebar {
  height: 100%;
  background-color: #ffffff;
  margin: 1.5rem 0;
  border-radius: 1rem;
}

.card {
  background-color: #ffffff;
  padding: 1rem 2rem;
  border-radius: 1rem;
  border: 1px solid #d3d3d3;
  margin: 1.5rem 0;
}

This set of CSS stylings will apply the Poppins font to the entire application, and take care of the background colors and general margins. It’s not a lot, but just enough to make the application stand out.

We’re not done yet. The next step is to add the main.css to our Shiny app. To do so, add a link to the main.css file inside the Shiny app UI. While there, we’ll also surround the main panel elements into two distinct div‘s, so we can better style them with CSS.

Here’s the entire code snippet:

library(tidyverse)
library(ggplot2)
library(DT)
library(shiny)
library(glue)


ui <- fluidPage(
  # Link to CSS file
  tags$head(
    tags$link(rel = "stylesheet", type = "text/css", href = "main.css")
  ),
  sidebarLayout(
    sidebarPanel(
      class = "sidebar",
      tags$h3("R Shiny Task Scheduling"),
      tags$hr(),
      textInput(inputId = "inTaskName", label = "Task:", placeholder = "e.g., Marketing"),
      dateInput(inputId = "inStartDate", value = Sys.Date(), min = Sys.Date(), label = "Start Date:"),
      dateInput(inputId = "inEndDate", value = Sys.Date() + 10, min = Sys.Date() + 1, label = "End Date:"),
      actionButton(inputId = "btn", label = "Add Task")
    ),
    mainPanel(
      # Surround the elements with a DIV element that has a class name of "card"
      tags$div(
        class = "card",
        tags$h3("Task Table View"),
        tags$hr(),
        DTOutput(outputId = "tableTasks")
      ),
      # Surround the elements with a DIV element that has a class name of "card"
      tags$div(
        class = "card",
        tags$h3("Task Chart View"),
        tags$hr(),
        plotOutput(outputId = "plotTasks")
      )
    )
  )
)


server <- function(input, output) {
  df <- reactiveValues(
    data = data.frame(
      Task = c("Research", "Clinical Trials", "Regulatory Approval"),
      StartDate = as.Date(c("2023-05-01", "2023-07-01", "2024-01-01")),
      EndDate = as.Date(c("2023-06-30", "2023-12-31", "2024-06-30"))
    ) %>%
      mutate(ID = row_number(), .before = Task) %>%
      mutate(
        Remove = glue('<button id="custom_btn" onclick="Shiny.onInputChange(\'button_id\', \'{ID}\')">Remove</button>')
      )
  )

  observeEvent(input$btn, {
    task_name <- input$inTaskName
    task_start_date <- input$inStartDate
    task_end_date <- input$inEndDate

    if (!is.null(task_name) && !is.null(task_start_date) && !is.null(task_end_date)) {
      new_id <- max(df$data$ID) + 1
      new_row <- data.frame(
        ID = new_id,
        Task = task_name,
        StartDate = task_start_date,
        EndDate = task_end_date,
        Remove = glue('<button id="custom_btn" onclick="Shiny.onInputChange(\'button_id\', \'{new_id}\')">Remove</button>'),
        stringsAsFactors = FALSE
      )
      df$data <- rbind(df$data, new_row)
      df$data <- df$data[order(df$data$ID), ]
    }
  })

  observeEvent(input$button_id, {
    output$text <- renderText(glue("Row number {input$button_id} is selected recently"))
    df$data <- df$data[-c(as.integer(input$button_id)), ]
    df$data <- df$data[order(df$data$StartDate), ]
  })

  output$tableTasks <- renderDT({
    datatable(data = df$data, escape = FALSE)
  })

  output$plotTasks <- renderPlot({
    ggplot(df$data, aes(x = StartDate, xend = EndDate, y = fct_rev(fct_inorder(Task)), yend = Task)) +
      geom_segment(linewidth = 10, color = "#0198f9") +
      labs(
        title = "Pharma Company Gantt Chart",
        x = "Duration",
        y = "Task"
      ) +
      theme_bw() +
      theme(legend.position = "none") +
      theme(
        plot.title = element_text(size = 20),
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12, angle = 45)
      )
  })
}


shinyApp(ui = ui, server = server)

Our application is now complete:

Image 9 - Completed R Shiny application for planning management

Image 9 – Completed R Shiny application for planning management

Simple, yet effective. Let’s make a short recap next.


Summing up R Shiny Gantt Charts

In this article, we have explored the power of R Shiny and Gantt charts in revolutionizing planning management, specifically in the context of the pharmaceutical industry. We started by understanding the importance of planning management and how Gantt charts serve as a valuable tool for visualizing project timelines and tasks. You’ve learned how to make Gantt charts in R with ggplot2 and how to make an entire application around them with R Shiny. A lot for a single read.

Through the use of R Shiny, teams can create powerful planning management apps tailored to their specific needs. These apps provide a comprehensive overview of projects, facilitate collaboration, and enable data-driven decision-making. Stakeholders can gain insights, identify bottlenecks, allocate resources effectively, and make timely adjustments to ensure project success.

If your company needs a custom-tailored R Shiny application, make sure to reach out to Appsilon.

The post appeared first on appsilon.com/blog/.

To leave a comment for the author, please follow the link and comment on their blog: Tag: r - Appsilon | Enterprise R Shiny Dashboards.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)