R Shiny Gantt Chart: How to Modernize Planning Management in Pharma
This article is originally published at https://appsilon.com
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 it Affects Planning Management?
- Gantt Chart in R – How to Create a Gantt Chart with ggplot2
- R Shiny Gantt Chart – How to Build a Planning Management App
- Summing up R Shiny Gantt Chart
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:
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:
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:
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:
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:
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:
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 newdata.frame
that matches the structure of the existing one - Use the
rbind()
function to concatenate twodata.frame
objects. This will append the new task to the end of the existing ones - Sort the
data.frame
byStartDate
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:
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 thedata.frame
- Add a Remove button to each row by monitoring the current ID value
- Add an ID column based on the
- 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
- Calculate the new row ID – The largest from the
- 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
- Use the
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:
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:
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/.
Thanks for visiting r-craft.org
This article is originally published at https://appsilon.com
Please visit source website for post related comments.