PhoneTech Assembly Plant — Process Mining Dashboard

Smartphone Assembly Line  |  bupaR Process Analytics
As of: 2026-02-21 08:00 CET
Data period: 2025-09-01 to 2026-02-20 (24 weeks)
Total Cases Processed
4,218
+8.3% vs. prior period
Avg Throughput Time
6.4 hrs
Target: 6.0 hrs  |  +6.7% over
On-Time Completion Rate
81.2%
Target: 90%  |  below target
Rework Rate
12.4%
Target: <8%  |  523 cases reworked
Active Cases (In Progress)
87
Capacity: 120  |  72.5% utilization
R Code
library(bupaverse) # Load the event log phone_log <- read_xes("phonetech_assembly.xes") # Key metrics phone_log %>% n_cases() # Total cases phone_log %>% throughput_time("log", units = "hours") # Avg throughput phone_log %>% number_of_repetitions("log") # Rework rate phone_log %>% filter_processing_time( interval = lubridate::dhours(0), lubridate::dhours(6) ) %>% n_cases() / n_cases(phone_log) # On-time rate
Process Map — Frequency (Absolute)
Fast / On Target
Normal
Bottleneck / Slow
S 4,218 PCB Assembly 4,218 (100%) avg 38 min 4,218 Display Install 4,218 (100%) avg 52 min 4,218 Battery & Power 4,218 (100%) avg 29 min 4,218 Housing Assy 4,218 (100%) avg 44 min 4,218 SW Flashing 4,512 (107%) avg 68 min 4,512 Quality Control 4,741 (112%) avg 85 min 3,695 294 rework 229 rework
Nodes show activity frequency (absolute & relative to total cases). Dashed red lines indicate rework loops. QC frequency >100% due to rework re-entries. SW Flashing is the primary bottleneck (avg 68 min, target 45 min).
R Code
# Frequency process map (absolute counts) phone_log %>% process_map(frequency("absolute")) # Performance process map (mean processing time) phone_log %>% process_map(performance(mean, "mins")) # Combined: node = relative-case frequency, edge = performance phone_log %>% process_map( type_nodes = frequency("relative-case"), type_edges = performance(mean, "mins"), rankdir = "LR" ) # Export phone_log %>% process_map(render = FALSE) %>% export_map("process_map.pdf")
Throughput Time Analysis
Overall Throughput (hours)
Actual: 6.4h
Target: 6.0h
By Product Variant
Standard 5.2h  (target: 5.5h)
Pro 6.8h  (target: 6.5h)
Ultra 8.1h  (target: 7.5h)
Throughput Trend (Last 24 Weeks)
6.0h 6.4h
Sep '25Nov '25Jan '26Feb '26
R Code
# Throughput time at log level phone_log %>% throughput_time("log", units = "hours") # By product variant (case-level attribute) phone_log %>% group_by(product_variant) %>% throughput_time("log", units = "hours") # Plot throughput time distribution phone_log %>% throughput_time("log") %>% plot() # Weekly trend (augment + aggregate) phone_log %>% throughput_time(level = "case", units = "hours") %>% mutate(week = lubridate::floor_date(first_event, "week")) %>% group_by(week) %>% summarise(avg_throughput = mean(throughput_time))
Activity Performance
Activity Freq Rel % Avg Proc Avg Idle Processing Time vs Target Util %
PCB Assembly 4,218 100% 38 min 3 min
88%
Display Install 4,218 100% 52 min 8 min
91%
Battery & Power 4,218 100% 29 min 5 min
79%
Housing Assy 4,218 100% 44 min 6 min
84%
SW Flashing 4,512 107% 68 min 14 min
97%
Quality Control 4,741 112% 85 min 22 min
95%
Bullet graphs: dark bar = actual processing time; black marker = target. Gray bands = poor | satisfactory | good ranges. = exceeds target threshold.
R Code
# Activity frequency phone_log %>% activity_frequency("activity") # Processing time per activity phone_log %>% processing_time("activity", units = "mins") # Idle time per activity phone_log %>% idle_time("resource", units = "mins") # Resource frequency per activity phone_log %>% resource_frequency("resource-activity") # Performance process map phone_log %>% process_map(performance(mean, "mins"))
Trace Analysis — Top Variants
PCB
Display
Battery
Housing
SW Flash
QC
# Trace (Activity Sequence) Cases Rel % Cumul % Note
1
P
D
B
H
S
Q
2,847 67.5% 67.5% Happy path
2
P
D
B
H
S
Q
S
Q
548 13.0% 80.5% SW rework x1
3
P
D
B
H
S
Q
D
S
Q
312 7.4% 87.9% Display rework
4
P
D
B
H
S
Q
S
Q
S
Q
189 4.5% 92.4% SW rework x2
5
P
D
D
B
H
S
Q
142 3.4% 95.7% Display self-loop
6
P
D
B
B
H
S
Q
98 2.3% 98.0% Battery self-loop
7 Other (14 variants) 82 2.0% 100% Rare paths
P=PCB, D=Display, B=Battery, H=Housing, S=SW Flash, Q=QC. Faded segments = rework iterations. 6 variants cover 98% of all cases.
R Code
# Trace variants with frequencies phone_log %>% traces() # Trace coverage (cumulative) phone_log %>% trace_coverage("trace") %>% plot() # Visual trace explorer (top 80% coverage) phone_log %>% trace_explorer(coverage = 0.80) # Detect rework: repetitions and self-loops phone_log %>% number_of_repetitions("activity") phone_log %>% number_of_selfloops("activity") # Filter only traces with rework phone_log %>% filter_activity_frequency(interval = c(2, Inf))
Resource Utilization — Workstations
PCB Line (WS-01..03)
88%
Display Line (WS-04..06)
91%
Battery Station (WS-07..08)
79%
Housing Line (WS-09..11)
84%
SW Flash (WS-12..13)
97%
QC Bay (WS-14..16)
95%
Handover-of-Work Matrix (Frequency)
PCB Display Battery Housing SW Flash QC
PCB
-
4,218
0
0
0
0
Display
0
-
4,218
0
0
0
Battery
0
0
-
4,218
0
0
Housing
0
0
0
-
4,218
0
SW Flash
0
0
0
0
-
4,512
QC
0
229
0
0
294
-
Matrix shows handover count from row activity to column activity. QC row shows rework flows back to Display (229) and SW Flash (294).
R Code
# Resource frequency phone_log %>% resource_frequency("resource") # Resource involvement per case phone_log %>% resource_involvement("resource") # Resource specialisation phone_log %>% resource_specialisation("resource") # Handover-of-work network (visual) phone_log %>% resource_map() # Handover-of-work matrix (tabular) phone_log %>% resource_matrix() %>% plot() # Process matrix (directly-follows) phone_log %>% process_matrix(frequency("absolute")) %>% plot()
Conformance & Quality — Rule Checking
Process Rule Compliance
Rule bupaR Function Conformant Violations
All cases start with PCB Assembly starts("PCB") 4,218 (100%) 0
All cases end with Quality Control ends("QC") 4,218 (100%) 0
PCB always precedes Display Install precedence("PCB","DI") 4,218 (100%) 0
Battery always followed by Housing response("Bat","Hous") 4,218 (100%) 0
SW Flashing occurs at most once contains_exactly("SW",1) 3,481 (82.5%) 737 (17.5%)
QC occurs at most once contains_exactly("QC",1) 3,695 (87.6%) 523 (12.4%)
Throughput time < 8 hours filter_throughput_time() 3,426 (81.2%) 792 (18.8%)
Rework Detection Summary
Rework Type Cases Affected Rate Trend (24 wk)
Repetitions (rework loop) 1,049 24.9%
Self-loops (immediate repeat) 240 5.7%
Bottleneck Cases (Top Offenders)
Case ID Throughput Activities Bottleneck Activity Wait Time
CASE-3847 14.2 hrs 11 QC (x3 iterations) 4.1 hrs
CASE-2156 12.8 hrs 10 SW Flashing (x3) 3.6 hrs
CASE-0891 11.5 hrs 9 Display + SW rework 2.9 hrs
CASE-4102 11.1 hrs 9 QC (x2) + wait 3.3 hrs
R Code
library(processcheckR) # Check multiple rules at once phone_log %>% check_rules( rule1 = starts("PCB Assembly"), rule2 = ends("Quality Control"), rule3 = precedence("PCB Assembly", "Display Install"), rule4 = response("Battery & Power", "Housing Assembly"), rule5 = contains_exactly("SW Flashing", 1), rule6 = contains_exactly("Quality Control", 1) ) # Rework detection phone_log %>% number_of_repetitions("activity", type = "all") phone_log %>% number_of_selfloops("activity", type = "all") phone_log %>% size_of_repetitions("activity") # Filter non-conformant cases phone_log %>% filter_rules(contains_exactly("SW Flashing", 1), reverse = TRUE) # Bottleneck cases (longest throughput) phone_log %>% throughput_time(level = "case", units = "hours") %>% arrange(desc(throughput_time)) %>% head(10)
Dotted Chart — Case Timeline (Sample: 30 Cases)
PCB
Display
Battery
Housing
SW Flash
QC
0h 3h 6h 9h 12h+
Target 6h C-0001 C-0002 C-0003 C-0004 C-0005 C-0006 C-0007 C-0008 C-0009 C-0010 C-0011 C-0012 C-0013 C-0014 C-0015 C-0016 C-0017 C-0018 C-0019 C-0020 C-0021 C-0022 C-0023 C-0024 C-0025 C-0026 C-0027 C-0028 C-0029 C-0030
Sorted by case duration (shortest first). X-axis = relative time since case start. Red-labeled cases exceed the 6-hour target. Faded dots indicate rework iterations. Cases C-0015 and C-0011 are clear outliers with multiple rework cycles.
R Code
# Dotted chart (relative time, sorted by duration) phone_log %>% dotted_chart( x = "relative", sort = "duration", color = activity_id(phone_log) ) # Absolute time view phone_log %>% dotted_chart(x = "absolute", sort = "start") # Intra-day pattern analysis phone_log %>% dotted_chart(x = "relative_day") # Performance spectrum (detailed) phone_log %>% ps_detailed() phone_log %>% ps_aggregated(bins = 30)
Appendix: Event Log Setup in R (bupaR)
The code below shows how to create the PhoneTech assembly event log from raw factory data using bupaR's activitylog format. This log structure powers all the analyses shown above.
library(bupaverse) library(dplyr) library(lubridate) # --- 1. Read raw factory MES data --- raw_data <- read.csv("phonetech_mes_export.csv") %>% mutate( start = ymd_hms(start_timestamp), complete = ymd_hms(complete_timestamp) ) # --- 2. Create bupaR activity log --- phone_log <- raw_data %>% activitylog( case_id = "case_id", activity_id = "activity", resource_id = "workstation", timestamps = c("start", "complete") ) # --- 3. Validate and inspect --- summary(phone_log) n_cases(phone_log) # 4,218 n_activities(phone_log) # 6 n_resources(phone_log) # 16 workstations n_traces(phone_log) # 20 unique trace variants # --- 4. Enrich with product variant --- phone_log <- phone_log %>% left_join(product_info, by = "case_id") %>% re_map(mapping(phone_log)) # --- 5. Data quality checks --- library(daqapo) phone_log %>% detect_time_anomalies() # Negative durations? phone_log %>% detect_incomplete_cases( activities = c("PCB Assembly", "Quality Control") ) phone_log %>% detect_duration_outliers( "SW Flashing", sd_threshold = 3 )
PhoneTech Assembly Plant — Process Mining Dashboard  |  Built with bupaR (bupaverse)  |  Data: Fictional  |  Design: Stephen Few principles