# Load the required package to read CSV files
library(readr)
## Warning: package 'readr' was built under R version 4.5.2
# 1. Load the dataset into R
# Make sure the CSV file is in the same folder as your Quarto document!
df <- read.csv("garments_worker_productivity.csv")
# 2. Check the structure to see measurement types (numeric vs. character)
str(df)
## 'data.frame': 1197 obs. of 15 variables:
## $ date : chr "1/1/2015" "1/1/2015" "1/1/2015" "1/1/2015" ...
## $ quarter : chr "Quarter1" "Quarter1" "Quarter1" "Quarter1" ...
## $ department : chr "sweing" "finishing " "sweing" "sweing" ...
## $ day : chr "Thursday" "Thursday" "Thursday" "Thursday" ...
## $ team : int 8 1 11 12 6 7 2 3 2 1 ...
## $ targeted_productivity: num 0.8 0.75 0.8 0.8 0.8 0.8 0.75 0.75 0.75 0.75 ...
## $ smv : num 26.16 3.94 11.41 11.41 25.9 ...
## $ wip : int 1108 NA 968 968 1170 984 NA 795 733 681 ...
## $ over_time : int 7080 960 3660 3660 1920 6720 960 6900 6000 6900 ...
## $ incentive : int 98 0 50 50 50 38 0 45 34 45 ...
## $ idle_time : num 0 0 0 0 0 0 0 0 0 0 ...
## $ idle_men : int 0 0 0 0 0 0 0 0 0 0 ...
## $ no_of_style_change : int 0 0 0 0 0 0 0 0 0 0 ...
## $ no_of_workers : num 59 8 30.5 30.5 56 56 8 57.5 55 57.5 ...
## $ actual_productivity : num 0.941 0.886 0.801 0.801 0.8 ...
# 3. Generate summary statistics for every column
summary(df)
## date quarter department day
## Length:1197 Length:1197 Length:1197 Length:1197
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## team targeted_productivity smv wip
## Min. : 1.000 Min. :0.0700 Min. : 2.90 Min. : 7.0
## 1st Qu.: 3.000 1st Qu.:0.7000 1st Qu.: 3.94 1st Qu.: 774.5
## Median : 6.000 Median :0.7500 Median :15.26 Median : 1039.0
## Mean : 6.427 Mean :0.7296 Mean :15.06 Mean : 1190.5
## 3rd Qu.: 9.000 3rd Qu.:0.8000 3rd Qu.:24.26 3rd Qu.: 1252.5
## Max. :12.000 Max. :0.8000 Max. :54.56 Max. :23122.0
## NA's :506
## over_time incentive idle_time idle_men
## Min. : 0 Min. : 0.00 Min. : 0.0000 Min. : 0.0000
## 1st Qu.: 1440 1st Qu.: 0.00 1st Qu.: 0.0000 1st Qu.: 0.0000
## Median : 3960 Median : 0.00 Median : 0.0000 Median : 0.0000
## Mean : 4567 Mean : 38.21 Mean : 0.7302 Mean : 0.3693
## 3rd Qu.: 6960 3rd Qu.: 50.00 3rd Qu.: 0.0000 3rd Qu.: 0.0000
## Max. :25920 Max. :3600.00 Max. :300.0000 Max. :45.0000
##
## no_of_style_change no_of_workers actual_productivity
## Min. :0.0000 Min. : 2.00 Min. :0.2337
## 1st Qu.:0.0000 1st Qu.: 9.00 1st Qu.:0.6503
## Median :0.0000 Median :34.00 Median :0.7733
## Mean :0.1504 Mean :34.61 Mean :0.7351
## 3rd Qu.:0.0000 3rd Qu.:57.00 3rd Qu.:0.8503
## Max. :2.0000 Max. :89.00 Max. :1.1204
##
# Load the knitr package for clean table formatting
library(knitr)
# Build the table data
summary_table <- data.frame(
Variable = c("date", "quarter", "department", "day", "team", "targeted_productivity",
"smv", "wip", "over_time", "incentive", "idle_time", "idle_men",
"no_of_style_change", "no_of_workers", "actual_productivity"),
Type = c("Categorical", "Categorical", "Categorical", "Categorical", "Categorical",
"Numeric", "Numeric", "Numeric", "Numeric", "Numeric", "Numeric", "Numeric",
"Numeric", "Numeric", "Numeric"),
Stats = c("59 unique dates", "Mode: Quarter1", "Mode: sewing", "Mode: Wednesday",
"Min: 1, Max: 12", "Mean: 0.730", "Mean: 15.06, Max: 54.56", "506 Missing (NAs)",
"Mean: 4567.4", "Mean: 38.21", "Mean: 0.73", "Mean: 0.37, Max: 45",
"Mean: 0.15, Max: 2", "Mean: 34.61", "Mean: 0.735"),
Description = c("Date of production", "A portion of the month", "Associated department",
"Day of the week", "The specific team number", "Target productivity set",
"Standard Minute Value", "Work in progress", "Overtime in minutes",
"Financial incentive", "Time production interrupted", "Number of idle workers",
"Changes in garment style", "Total workers on team", "Actual measured productivity"),
Rationale = c("Exclude: Too granular for a general predictive model.",
"Include: Predictor for seasonal/time trends.",
"Include: Departments have different baselines.",
"Include: Identifies daily productivity dips.",
"Include: Teams may perform differently.",
"Include: Strongly tied to actual outcomes.",
"Include: Represents garment complexity.",
"Exclude: 506 NAs. Imputing could skew models.",
"Include: Captures extra effort/fatigue.",
"Include: Directly impacts motivation.",
"Include: Negative impact on productivity.",
"Include: Represents wasted labor capacity.",
"Include: Style changes slow production.",
"Include: Scales with production capacity.",
"Include: Target Variable to predict.")
)
# Output the clean table
kable(summary_table,
col.names = c("Variable", "Measurement Type", "Summary Stats", "Description", "Decision & Rationale"),
align = "l",
caption = "Task 1: Variable Summary and Rationale")
| Variable | Measurement Type | Summary Stats | Description | Decision & Rationale |
|---|---|---|---|---|
| date | Categorical | 59 unique dates | Date of production | Exclude: Too granular for a general predictive model. |
| quarter | Categorical | Mode: Quarter1 | A portion of the month | Include: Predictor for seasonal/time trends. |
| department | Categorical | Mode: sewing | Associated department | Include: Departments have different baselines. |
| day | Categorical | Mode: Wednesday | Day of the week | Include: Identifies daily productivity dips. |
| team | Categorical | Min: 1, Max: 12 | The specific team number | Include: Teams may perform differently. |
| targeted_productivity | Numeric | Mean: 0.730 | Target productivity set | Include: Strongly tied to actual outcomes. |
| smv | Numeric | Mean: 15.06, Max: 54.56 | Standard Minute Value | Include: Represents garment complexity. |
| wip | Numeric | 506 Missing (NAs) | Work in progress | Exclude: 506 NAs. Imputing could skew models. |
| over_time | Numeric | Mean: 4567.4 | Overtime in minutes | Include: Captures extra effort/fatigue. |
| incentive | Numeric | Mean: 38.21 | Financial incentive | Include: Directly impacts motivation. |
| idle_time | Numeric | Mean: 0.73 | Time production interrupted | Include: Negative impact on productivity. |
| idle_men | Numeric | Mean: 0.37, Max: 45 | Number of idle workers | Include: Represents wasted labor capacity. |
| no_of_style_change | Numeric | Mean: 0.15, Max: 2 | Changes in garment style | Include: Style changes slow production. |
| no_of_workers | Numeric | Mean: 34.61 | Total workers on team | Include: Scales with production capacity. |
| actual_productivity | Numeric | Mean: 0.735 | Actual measured productivity | Include: Target Variable to predict. |
Bivariate Associations
# Load the necessary libraries
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(GGally)
## Warning: package 'GGally' was built under R version 4.5.3
## Loading required package: ggplot2
## Warning: package 'ggplot2' was built under R version 4.5.3
# Clean the dataset based on our Task 1 decisions
df_clean <- df %>%
select(-date, -wip) %>%
mutate(across(c(quarter, department, day, team), as.factor))
# Matrix 1: Core Categories (Department & Quarter)
ggpairs(df_clean %>% select(actual_productivity, department, quarter),
title = "Matrix 1: Core Categories vs Productivity") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
# Matrix 2: Targets and Time (Targeted Prod. & SMV)
ggpairs(df_clean %>% select(actual_productivity, targeted_productivity, smv),
title = "Matrix 2: Targets and Time vs Productivity") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Matrix 3: Extra Effort (Overtime & Incentives)
ggpairs(df_clean %>% select(actual_productivity, over_time, incentive),
title = "Matrix 3: Overtime and Incentives vs Productivity") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Matrix Explanations Matrix 1: Core Categories vs. Productivity This
matrix visualizes how categorical variables impact our target variable
using boxplots and density curves.
Department: The boxplots reveal the median differences and spread of productivity between departments (e.g., sewing vs. finishing). If one department has a significantly higher median or tighter interquartile range, it indicates more consistent performance.
Quarter: Comparing the quarters allows us to observe if productivity fluctuates based on the time of the month, helping identify potential seasonal fatigue or end-of-month pushes.
Matrix 2: Targets and Time vs. Productivity This matrix examines the continuous production metrics using scatterplots and correlation coefficients.
Targeted Productivity: We expect a strong positive correlation here, as teams generally aim to meet their assigned goals. The scatterplot helps identify if teams consistently underperform or overperform their specific targets.
SMV (Standard Minute Value): This represents the allocated time for a task. Observing its relationship with actual productivity shows whether more complex, time-consuming garments (higher SMV) negatively impact the final productivity rate.
Matrix 3: Overtime and Incentives vs. Productivity This matrix highlights the impact of extra effort and financial motivation.
Incentives: The data shows a slight positive correlation (0.077) between financial incentives and actual productivity. However, the density plot is highly right-skewed, meaning most production runs receive little to no incentive, with only a few extreme outliers.
Overtime: Overtime has a slight negative correlation (-0.054) with productivity. This suggests that simply working longer hours does not necessarily equate to higher efficiency, possibly due to worker fatigue.
Predictive Modeling & Resampling
To predict actual productivity, we are comparing a Baseline Linear Regression model against two regularized models (Ridge and LASSO). Regularization helps prevent overfitting by penalizing overly complex models, which is particularly useful when dealing with highly correlated predictors.
To ensure our models are robust and generalize well to new data, we evaluate their Root Mean Squared Error (RMSE) across three different resampling techniques: 1. 10-Fold Cross-Validation: Splits the data into 10 equal parts, training on 9 and testing on 1, rotating until all parts are tested. 2. Leave-One-Out Cross-Validation (LOOCV): The most exhaustive method, where the model is trained on all data points except one, iterating through every single observation in the dataset. 3. Bootstrapping: Creates 100 random samples (with replacement) from the dataset to train the model, providing a strong measure of stability.
# Load required library for regularized regression
library(glmnet)
## Warning: package 'glmnet' was built under R version 4.5.3
## Loading required package: Matrix
## Loaded glmnet 5.0
# Prepare matrix for glmnet (automatically handles dummy variables for categories)
x <- model.matrix(actual_productivity ~ ., data = df_clean)[, -1]
y <- df_clean$actual_productivity
n <- nrow(x)
set.seed(42)
# ==========================================
# 1. BASELINE LINEAR MODEL
# ==========================================
# K-Fold (10-fold)
k <- 10
folds <- sample(1:k, n, replace = TRUE)
baseline_kfold <- mean(sapply(1:k, function(i) {
fit <- lm(actual_productivity ~ ., data = df_clean[folds != i, ])
pred <- predict(fit, df_clean[folds == i, ])
sqrt(mean((df_clean$actual_productivity[folds == i] - pred)^2))
}))
# LOOCV (Using the hat-values shortcut to save computing time)
fit_full <- lm(actual_productivity ~ ., data = df_clean)
baseline_loocv <- sqrt(mean((residuals(fit_full) / (1 - hatvalues(fit_full)))^2))
# Bootstrap (100 iterations)
baseline_boot <- mean(sapply(1:100, function(i) {
idx <- sample(1:n, n, replace = TRUE)
fit <- lm(actual_productivity ~ ., data = df_clean[idx, ])
pred <- predict(fit, df_clean)
sqrt(mean((df_clean$actual_productivity - pred)^2))
}))
# ==========================================
# 2. RIDGE REGRESSION (alpha = 0)
# ==========================================
# K-Fold
ridge_cv_kfold <- cv.glmnet(x, y, alpha = 0, nfolds = 10)
ridge_kfold <- sqrt(min(ridge_cv_kfold$cvm))
# LOOCV
ridge_cv_loocv <- cv.glmnet(x, y, alpha = 0, nfolds = n, grouped = FALSE)
ridge_loocv <- sqrt(min(ridge_cv_loocv$cvm))
# Bootstrap (100 iterations)
ridge_boot <- mean(sapply(1:100, function(i) {
idx <- sample(1:n, n, replace = TRUE)
fit <- cv.glmnet(x[idx, ], y[idx], alpha = 0, nfolds = 5)
pred <- predict(fit, s = "lambda.min", newx = x)
sqrt(mean((y - pred)^2))
}))
# ==========================================
# 3. LASSO REGRESSION (alpha = 1)
# ==========================================
# K-Fold
lasso_cv_kfold <- cv.glmnet(x, y, alpha = 1, nfolds = 10)
lasso_kfold <- sqrt(min(lasso_cv_kfold$cvm))
# LOOCV
lasso_cv_loocv <- cv.glmnet(x, y, alpha = 1, nfolds = n, grouped = FALSE)
lasso_loocv <- sqrt(min(lasso_cv_loocv$cvm))
# Bootstrap (100 iterations)
lasso_boot <- mean(sapply(1:100, function(i) {
idx <- sample(1:n, n, replace = TRUE)
fit <- cv.glmnet(x[idx, ], y[idx], alpha = 1, nfolds = 5)
pred <- predict(fit, s = "lambda.min", newx = x)
sqrt(mean((y - pred)^2))
}))
# ==========================================
# FINAL RESULTS TABLE
# ==========================================
results_table <- data.frame(
Model = c("Baseline (Linear)", "Ridge Regression", "LASSO Regression"),
K_Fold_RMSE = round(c(baseline_kfold, ridge_kfold, lasso_kfold), 4),
LOOCV_RMSE = round(c(baseline_loocv, ridge_loocv, lasso_loocv), 4),
Bootstrap_RMSE = round(c(baseline_boot, ridge_boot, lasso_boot), 4)
)
print(results_table)
## Model K_Fold_RMSE LOOCV_RMSE Bootstrap_RMSE
## 1 Baseline (Linear) 0.1460 0.1467 0.1477
## 2 Ridge Regression 0.1481 0.1474 0.1472
## 3 LASSO Regression 0.1461 0.1466 0.1448
The RMSE scores are all incredibly close (hovering between 0.145 and 0.149). This means that all three models have a similar level of accuracy when predicting productivity.
LASSO Regression performed slightly better during the Bootstrap resampling (with the lowest RMSE of 0.1458), but the Baseline Linear model actually held its own very well across the board.
This report analyzes the garments_worker_productivity
dataset to identify key drivers of manufacturing efficiency and to build
predictive models for actual productivity.
To establish a robust foundation for modeling, the dataset underwent
targeted cleaning: * Feature Selection: The
date and wip (work in progress) columns were
removed to focus on relevant operational metrics and mitigate excessive
missing values. * Data Integrity: All remaining rows
with missing values (NAs) were omitted to ensure complete cases for
cross-validation. * Categorical Formatting: Core
categorical variables (department, quarter,
day, and team) were explicitly converted into
factors to ensure they were correctly interpreted as dummy variables
during regression modeling.
To evaluate the relationships between productivity and other operational features without visual overcrowding, the bivariate analysis was segmented into three focused matrices: * Core Categories: Analyzed the distribution of productivity across departments and quarters, highlighting median performance differences. * Targets and Time: Evaluated the linear relationships between allocated time (SMV), target productivity goals, and the actual productivity achieved. * Operational Effort: Assessed the impact of overtime and financial incentives, revealing a slight positive correlation between incentives and productivity (0.077), alongside a highly skewed distribution of bonus payouts.
Three regression models were developed to predict actual productivity: a Baseline Linear Model, Ridge Regression (\(\alpha = 0\)), and LASSO Regression (\(\alpha = 1\)). To rigorously evaluate model stability and prevent overfitting, each model’s Root Mean Squared Error (RMSE) was calculated across three distinct resampling methodologies: * 10-Fold Cross-Validation: Partitioning the dataset into 10 rotating training and testing sets. * Leave-One-Out Cross-Validation (LOOCV): Iteratively testing on a single observation while training on the remainder of the dataset. * Bootstrapping: Utilizing 100 iterations of random sampling with replacement to validate performance stability.
Conclusion: All three models performed with high similarity, yielding RMSE scores clustering tightly between 0.145 and 0.149 across all resampling methods, with the LASSO model demonstrating a negligible edge during bootstrap validation.