Tuesday, 30 May 2017

Manpower Planning using Shiny in R

Manpower  planning is an operational problem that requires determining number of manpower for performing standardised work. The post focuses on implementation of such system with use of functional programming in R. Shiny is a powerful library available in R that helps in intertwining code with user interface (ui) for building a GUI (graphical user interface). We will describe R scripts for server and ui as we go along with the implementation. 



Script for ui in R
First step is to load the shiny package.

library(shiny)

Next define the UI for random distribution application

shinyUI(fluidPage(

The title of application in GUI window is created as:

titlePanel("Workstation Manpower Planning"),

The sidebar layout is created with controls to select the random distribution type and number of observations to generate. Note that the use of the br() element is to introduce extra vertical spacing. In side bar panel, input files are loaded as csv. Product changeover data sets account for change over time in switching from one product to another product. ProductChangeover.csv file consists of workstation, from product, to product and associated changeover time. Demand file has associated demand for the three products. Work station available time depicts the time available at workstation in a shift, by removing the downtime. Work content provides statistics for operation time of different products at various workstations. The statistics are mean and standard deviation of set up and manual time, while fixed processing times on machines, if any. Data sets are shown at end of posts for your understanding.




  sidebarLayout(
    sidebarPanel(
      fileInput('WCfile', 'Select the Workcontent.csv file',
                accept=c('text/csv','text/comma-separated-values,text/plain','.csv')),
      tags$hr(),
      fileInput('COfile', 'Select the ProductChangeover.csv file',
                accept=c('text/csv','text/comma-separated-values,text/plain','.csv')),
      tags$hr(),
      fileInput('DEMfile', 'Select the Demand.csv file',
                accept=c('text/csv','text/comma-separated-values,text/plain','.csv')),
      tags$hr(),
      fileInput('WSATfile', 'Select the Workstationavailtimes.csv file',
                accept=c('text/csv','text/comma-separated-values,text/plain','.csv')),
      tags$hr(),
      helpText("All .csv files must be uploaded in required format under headers")
    ),

Next, we have the main panel that is declare in tab form with three categories: first one is output table on planned manpower allocation at the workstations, second tab is balancing efficiency plot and third tab is plotting of product mix losses.

    mainPanel(
      tabsetPanel(type="tab",
                  tabPanel("Summary - Workstation Planned Manpower",dataTableOutput("mytable")),
                  tabPanel("Plot- Workstation Manpower Efficiency", plotOutput("myplot")),
                  tabPanel("Plot - Workstation Product Mix Loss", plotOutput("myplot1"))
                  )
   
    )
  ))

)


Script for server in R:

First we load the shiny library:

library(shiny)

Next, we create a function under shinyServer that declares input from ui script.

shinyServer(function(input, output) {

  observe({
    WCfile = input$WCfile
    COfile = input$COfile
    DEMfile = input$DEMfile
    WSATfile = input$WSATfile
    if (is.null(WCfile) || is.null(COfile) || is.null(DEMfile) || is.null(WSATfile)) {
      return(NULL)
    }

The input data is stored in different data frames from csv files for work content, product changeover times, demand and workstation available times.
 
    workcontent.df <- data.frame(read.csv(WCfile$datapath))
    productco.df <- data.frame(read.csv(COfile$datapath))
    dem.df <- data.frame(read.csv(DEMfile$datapath))
    wsavailtim.df <- data.frame(read.csv(WSATfile$datapath))
 
Step 1 calculations require determining maximum manual work content for each workstation among all products which is MAX(Manual time + Set-up time) + MAX(Change-over time)

 
    workcontent.df$totalmanualtime <- workcontent.df$Mean_Set.up + workcontent.df$SD_Set.up + workcontent.df$Mean_Manual + workcontent.df$SD_Manual
    co.df <- data.frame(Workstation=productco.df$Workstation, time=productco.df$Changeover_time)
    maxcotime.df <- aggregate(.~Workstation,co.df,FUN = max)
    wc.df <- data.frame(Workstation=workcontent.df$Workstation,time=workcontent.df$totalmanualtime)
    maxwc.df <- aggregate(.~Workstation,wc.df,FUN = max)
    aggregwc.df <- rbind(maxwc.df,maxcotime.df)
    manualwc.df <- aggregate(.~Workstation,aggregwc.df,FUN = sum)
 

Step 2 Calculations is to determine maximum production units for each workstation i.e. SUM(Workstation production units for all products)
 
    wswc.df <-data.frame(Workstation=workcontent.df$Workstation,Product=workcontent.df$Product)
    demwspr.df <- merge(wswc.df,dem.df,by="Product")
    aggrdemws.df <- data.frame(Workstation=demwspr.df$Workstation,Demand=demwspr.df$Demand)
    aggrdemws.df <- aggregate(.~Workstation,aggrdemws.df,FUN = sum)
 
 
Step 3 calculations determine cycle time for each workstation i.e. Max production units / Available time. Step 4 determines number of workers for each workstation as ROUNDUP(Max work content/Cycle time) and Step 5 determine Manpower Efficiency for each workstation as (# Operators/(Max work content/Cycle time))
 
    cycletime.df <- merge(wsavailtim.df,aggrdemws.df,by="Workstation")
    cycletime.df$ct <- cycletime.df$Demand/cycletime.df$Availabletime
    NoofOper.df <- merge(manualwc.df,cycletime.df,by="Workstation")
    NoofOper.df$Operators <- ceiling(NoofOper.df$time/NoofOper.df$ct)
    NoofOper.df$Efficiency <- round(((NoofOper.df$time/NoofOper.df$ct)/NoofOper.df$Operators)*100,0)
    NoofOpersop.df <- data.frame(Workstation = NoofOper.df$Workstation, Number_Of_Manpower = NoofOper.df$Operators,Manpower_Efficiency = paste(NoofOper.df$Efficiency,"%"))
    NoofOpersef.df <- data.frame(Workstation = NoofOper.df$Workstation, Manpower_Efficiency = NoofOper.df$Efficiency)
 
Step 6 calculates product mix loss percentage for the workstations. First, calculate the manual time by adding mean and standard deviations of manual time and set up time. This will aid in design of workstation for maximum time. Next, multiply the total work content with demand of the products for the workstations. After that, get the total work content on the workstations, obtained by summing it up for all products. The design of workstation requires work content that is multiplication of Step 1 and Step 2 time and quantities. The product mix loss percentage is [1 - (Actual Work content / Design Work content)].
 
    wcwspr <- data.frame(Workstation = workcontent.df$Workstation,Product = workcontent.df$Product, time_des = workcontent.df$totalmanualtime)
    wcwspr <- merge(wcwspr,dem.df,by="Product")
    wcwspr$ActualWC <- wcwspr$time_des * wcwspr$Demand
    aggrwcws <- aggregate(.~Workstation,data = wcwspr,FUN = sum)
    aggrwcws <- merge(aggrwcws,manualwc.df,by="Workstation")
    aggrwcws$DesgnWC <- aggrwcws$time * aggrwcws$Demand
    aggrwcws$ProductMixLoss <- (1 - (aggrwcws$ActualWC/aggrwcws$DesgnWC)) * 100
    aggrwcws$ProductMixLoss <- round(aggrwcws$ProductMixLoss)
    aggrwcwsref <- data.frame(Workstation = aggrwcws$Workstation, Product_Mix_Loss = paste(aggrwcws$ProductMixLoss,"%"))
    aggrwcwsplot <- data.frame(Workstation = aggrwcws$Workstation, Product_Mix_Loss = aggrwcws$ProductMixLoss)

Merge two data frames to get number of operators by workstation.
 
    NoofOpersop.df <- merge(NoofOpersop.df,aggrwcwsref,by="Workstation")
 
 
Finally, delete unwanted data frames
 
    rm(list=c("aggrdemws.df","aggregwc.df","co.df","cycletime.df","demwspr.df","manualwc.df","maxcotime.df","maxwc.df","wc.df","wswc.df"))
 
 
The last step is to display and plot the output for the three tabs. First, render data table to display number of operators, load lattice package and plot bar chart for efficiency and product mix loss for the workstations.
 
    output$mytable <- renderDataTable({NoofOpersop.df})
 
    library(lattice)
    output$myplot <- renderPlot({
     barchart(Manpower_Efficiency~Workstation,data=NoofOpersef.df,label = NoofOpersef.df$Manpower_Efficiency,col="grey",main="Workstation Manpower Efficiency",xlab = "Workstation",ylab = "Efficiency (%)",ylim=c(0,100))
    })
 
    output$myplot1 <- renderPlot({
      barchart(Product_Mix_Loss ~ Workstation,data=aggrwcwsplot,xlab="Workstation",ylab="Product Loss Percentage",col="blue",ylim=c(0,100))
    })
 
  })

})

Datasets (.csv)

1. Workstationavailtimes.csv


Workstation Availabletime
WS1 420
WS2 420
WS3 420
WS4 420

2. Workcontent.csv

Workstation Product Mean_Set-up SD_Set-up Mean_Manual SD_Manual Machine Time
WS1 A 11 3 28 11 5
WS1 B 4 1 12 3 6
WS1 C 3 1 8 2 4
WS2 A 5 2 12 10 12
WS2 B 7 3 15 5 8
WS3 A 5 3 37 26 0
WS4 A 14 5 34 6 24
3. Demand.csv

Product  Demand
A 4000
B 2500
C 1000

4. ProductChangeover.csv

Workstation From_Product To_Product Changeover_time
WS1 A B 4
WS1 B A 2
WS1 B C 1
WS1 C B 3
WS1 A C 2
WS1 C A 3
WS2 A B 5
WS2 B A 8

Output

The code will run the app to give three outputs on Summary, Plot of workstation manpower efficiency and Plot of workstation product mix loss.

No comments:

Post a Comment