├── Inventory Planning and Managing ├── Economic Order Quantity.py └── Economic Production Quantity.py ├── Linear Programming ├── Maximization Problem.py └── Minimization Problem.py ├── Operations Management └── OEE.py ├── Quality Control Charts ├── Images │ ├── Constants.png │ ├── Target IX chart and MR chart.png │ ├── Z chart and MW chart.png │ ├── Z-bar chart and W chart.png │ ├── c chart.png │ ├── dpmo chart.png │ ├── np chart.png │ ├── p chart.png │ ├── u chart.png │ ├── x chart and mR chart.png │ ├── x-bar chart and R chart.png │ └── x-bar chart and s chart.png ├── Process Stability Control - Western Electric.py ├── Process Stability Control.py ├── Target IX chart and MR chart.py ├── Z chart and MW chart.py ├── Z-bar chart and W chart.py ├── c-chart.py ├── dmpo chart.py ├── np-chart.py ├── p-chart.py ├── u-chart.py ├── x chart and mR chart.py ├── x-bar chart and R chart.py └── x-bar chart and s chart.py ├── Queue Length Analysis ├── CustomersInQueueOverTime.PNG ├── QueueLengthAnalysis.py └── orders.csv ├── README.md ├── Six Sigma ├── Gage Run Chart │ ├── GageRunChart.py │ └── msa_2_crossed.xlsx ├── Pareto Chart │ └── Pareto Chart.py ├── Probability Sampling │ ├── Cluster Sampling.py │ ├── Complete Code.py │ ├── Measure Mean Comparison per Sampling Method.py │ ├── Products Data Frame with Stata.py │ ├── Products Data Frame.py │ ├── Simple Random Sampling.py │ ├── Stratified Random Sampling.py │ └── Systematic Sampling.py └── Process Capability Analysis │ └── Process Capability Analysis.py └── Total Productive Maintenance ├── Reliability Analysis - Serial Systems.py ├── Reliability Analysis - Single Component.py └── failure_times.csv /Inventory Planning and Managing/Economic Order Quantity.py: -------------------------------------------------------------------------------- 1 | ### ECONOMIC ORDER QUANTITY ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | %matplotlib inline 7 | 8 | # Define EOQ function 9 | def EOQ(S, D, H): 10 | 11 | """ 12 | Economic Order Quantity 13 | 14 | Arguments: 15 | S: ordering cost 16 | D: annual quantity demanded 17 | H: holding cost per unit 18 | 19 | Returns: 20 | [Q, number_of_orders, time_between_cycles, annual ordering cost, annual holding cost, annual total cost] 21 | 22 | """ 23 | 24 | # Validate that all function arguments are non-negative 25 | if(S>0 and D>0 and H>0): 26 | 27 | Q = (np.sqrt(2*S*D/H)) 28 | number_of_orders = D/Q 29 | time_between_cycles = 12/number_of_orders 30 | AOC = D/Q*S 31 | AHC = Q/2*H 32 | ATC = AOC+AHC 33 | 34 | return [Q, number_of_orders, time_between_cycles, AOC, AHC, ATC] 35 | 36 | else: 37 | print("Error. All function arguments must be non-negative.") 38 | 39 | # Run example 40 | EOQ(10,2400,0.3) 41 | 42 | # Create period list and append values 43 | period = [0, 2] 44 | while period[-1] < 12: 45 | period.append(period[-1]) 46 | period.append(period[-1]+2) 47 | 48 | # Create inventory list and append values 49 | inventory = [400, 0] 50 | while len(inventory) < len(period): 51 | inventory.append(400) 52 | inventory.append(0) 53 | 54 | # Plot inventory level graph 55 | plt.figure(figsize=(15,5)) 56 | plt.plot(period,inventory) 57 | plt.xlabel("Month") 58 | plt.ylabel("Inventory Level") 59 | plt.title("Economic Order Quantity") 60 | -------------------------------------------------------------------------------- /Inventory Planning and Managing/Economic Production Quantity.py: -------------------------------------------------------------------------------- 1 | ### ECONOMIC PRODUCTION QUANTITY ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | %matplotlib inline 7 | 8 | # Define EPQ function 9 | def EPQ(D, P, K, H): 10 | 11 | """ 12 | Economic Production Quantity 13 | 14 | Arguments: 15 | D: annual quantity demanded 16 | K: cost of production run 17 | H: holding cost per unit 18 | P: production rate 19 | 20 | Returns: 21 | [Q, cycle length, number_of_production_runs, production run length, demand period length, annual production cost, annual holding cost, maximum inventory level, annual total cost] 22 | 23 | """ 24 | 25 | if(D>0 and P>0 and K>0 and H>0): 26 | 27 | Q = (np.sqrt((2*D*K)/(H*(1-(D/P))))) 28 | T = round(Q/D*12,2) 29 | number_of_production_runs = D/Q 30 | Tp = round(Q/P*12,2) 31 | Td = round((Q/D-Q/P)*12,2) 32 | APC = number_of_production_runs*K 33 | AHC = Q/2*(1-(D/P))*H 34 | Imax = Q*(1-(D/P)) 35 | ATC = APC+AHC 36 | 37 | return[Q , T, number_of_production_runs, Tp, Td, APC, AHC, Imax, ATC] 38 | 39 | else: 40 | print("Error. All function arguments must be non-negative.") 41 | 42 | # Run example 43 | EPQ(200,1000,100,5) 44 | 45 | # Create period list and append values 46 | period = [0] 47 | while period[-1] < 12: 48 | period.append(period[-1] + 1.2) 49 | period.append(period[-1] + 4.8) 50 | 51 | # Create inventory list and append values 52 | inventory = [0] 53 | while len(inventory) < len(period): 54 | inventory.append(80) 55 | inventory.append(0) 56 | 57 | # Plot inventory level graph 58 | plt.figure(figsize=(15,5)) 59 | plt.plot(period,inventory) 60 | plt.plot([0,1.2],[0,100], linestyle="dashed", label="Production Rate") 61 | plt.xlabel("Month") 62 | plt.ylabel("Inventory Level") 63 | plt.title("Economic Production Quantity") 64 | plt.legend() 65 | -------------------------------------------------------------------------------- /Linear Programming/Maximization Problem.py: -------------------------------------------------------------------------------- 1 | ### LINEAR PROGRAMMING - MAXIMIZATION PROBLEM ### 2 | 3 | # Example: 4 | 5 | # max z = 5x1 + 7x2 6 | # s.t. 7 | # 1x1 + 0x2 <= 16 8 | # 2x1 + 3x2 <= 19 9 | # 1x1 + 1x2 <= 8 10 | # x1 , x2 >= 0 11 | 12 | # Import required libraries 13 | import numpy as np 14 | from scipy.optimize import linprog 15 | 16 | # Set the inequality constraints matrix 17 | # Note: the inequality constraints must be in the form of <= 18 | A = np.array([[1, 0], [2, 3], [1, 1], [-1, 0], [0, -1]]) 19 | 20 | # Set the inequality constraints vector 21 | b = np.array([16, 19, 8, 0, 0]) 22 | 23 | # Set the coefficients of the linear objective function vector 24 | # Note: when maximizing, change the signs of the c vector coefficient 25 | c = np.array([-5, -7]) 26 | 27 | # Solve linear programming problem 28 | res = linprog(c, A_ub=A, b_ub=b) 29 | 30 | # Print results 31 | print('Optimal value:', round(res.fun*-1, ndigits=2), 32 | '\nx values:', res.x, 33 | '\nNumber of iterations performed:', res.nit, 34 | '\nStatus:', res.message) 35 | -------------------------------------------------------------------------------- /Linear Programming/Minimization Problem.py: -------------------------------------------------------------------------------- 1 | ### LINEAR PROGRAMMING - MINIMIZATION PROBLEM ### 2 | 3 | # Example: 4 | 5 | # min z = 10x1 + 15x2 + 25x3 6 | # s.t. 7 | # 1x1 + 1x2 + 1x3 >= 1000 8 | # 1x1 - 2x2 + 0x3 >= 0 9 | # 0x1 + 0x2 + 1x3 >= 340 10 | # x1 , x2 , x3 >= 0 11 | 12 | # Import required libraries 13 | import numpy as np 14 | from scipy.optimize import linprog 15 | 16 | # Set the inequality constraints matrix 17 | # Note: the inequality constraints must be in the form of <= 18 | A = np.array([[-1, -1, -1], [-1, 2, 0], [0, 0, -1], [-1, 0, 0], [0, -1, 0], [0, 0, -1]]) 19 | 20 | # Set the inequality constraints vector 21 | b = np.array([-1000, 0, -340, 0, 0, 0]) 22 | 23 | # Set the coefficients of the linear objective function vector 24 | c = np.array([10, 15, 25]) 25 | 26 | # Solve linear programming problem 27 | res = linprog(c, A_ub=A, b_ub=b) 28 | 29 | # Print results 30 | print('Optimal value:', round(res.fun, ndigits=2), 31 | '\nx values:', res.x, 32 | '\nNumber of iterations performed:', res.nit, 33 | '\nStatus:', res.message) 34 | -------------------------------------------------------------------------------- /Operations Management/OEE.py: -------------------------------------------------------------------------------- 1 | ### OVERALL EQUIPMENT EFFECTIVENESS ### 2 | 3 | # Import required libraries 4 | import matplotlib.pyplot as plt 5 | %matplotlib inline 6 | 7 | # Define ggplot style sheet 8 | plt.style.use('ggplot') 9 | 10 | # Define OEE function 11 | def oee(A, B, C, D, E, F): 12 | 13 | ''' 14 | Overall Equipment Effectiveness components: 15 | 16 | A : Total available time 17 | B : Run time 18 | C : Production capacity 19 | D : Actual production 20 | E : Product output 21 | F : Actual good products 22 | 23 | ''' 24 | 25 | # Components validation 26 | if B <= A and D <= C and E == D and F <= E: 27 | 28 | # Define a list for profit percentages 29 | 30 | # % Total available time 31 | Profits=[100] 32 | # % Run time 33 | Profits.append(B/A*100) 34 | # % Production capacity 35 | Profits.append(B/A*100) 36 | # % Actual production 37 | Profits.append(D/C*Profits[2]) 38 | # % Product output 39 | Profits.append(D/C*Profits[2]) 40 | # % Actual good products 41 | Profits.append(F/E*Profits[4]) 42 | 43 | # Define a list for losses percentages 44 | Loss=[0] 45 | # % Time losses 46 | Loss.append(Profits[0]-Profits[1]) 47 | Loss.append(Profits[1]-Profits[2]) 48 | # % Speed losses 49 | Loss.append(Profits[2]-Profits[3]) 50 | Loss.append(Profits[3]-Profits[4]) 51 | # % Defective units 52 | Loss.append(Profits[4]-Profits[5]) 53 | 54 | # Define list of indexes 55 | ind=[0, 1, 2, 3, 4, 5] 56 | 57 | # Define bars width 58 | width=0.85 59 | 60 | # Define figure size 61 | plt.figure(figsize=(20, 10)) 62 | 63 | # Plot profits bars 64 | plt.barh(ind, Profits, width, color='#03C03C') 65 | 66 | # Plot losses bars 67 | plt.barh(ind, Loss, width, left=Profits, color='red') 68 | 69 | # Plot OEE universal benchmarks 70 | plt.axvline(85, linestyle='dashed', color='black', label='World Class OEE') 71 | plt.axvline(60, linestyle='dashdot', color='black', label='Typical OEE') 72 | plt.axvline(40, linestyle='dotted', color='black', label='Low OEE') 73 | 74 | # Invert y-axis 75 | plt.gca().invert_yaxis() 76 | 77 | # Hide y-axis labels 78 | plt.yticks([]) 79 | 80 | # Add OEE components 81 | plt.text(0, 0, 'Total Available Time', horizontalalignment='left') 82 | plt.text(0, 1, 'Run Time', horizontalalignment='left') 83 | plt.text(Profits[1], 1, 'Time Losses', horizontalalignment='left') 84 | plt.text(0, 2, 'Production Capacity', horizontalalignment='left') 85 | plt.text(0, 3, 'Actual Production', horizontalalignment='left') 86 | plt.text(Profits[3], 3, 'Speed Losses', horizontalalignment='left') 87 | plt.text(0, 4, 'Product Output', horizontalalignment='left') 88 | plt.text(0, 5, 'Actual Good Products', horizontalalignment='left') 89 | plt.text(Profits[5], 5, 'Defective Units', horizontalalignment='left') 90 | 91 | # Add x-axis label 92 | plt.xlabel('Percentage') 93 | 94 | # Add plot title 95 | plt.title('Overall Equipment Efficiency') 96 | 97 | # Show plot legends 98 | plt.legend() 99 | 100 | # Print OEE components 101 | return(print('Overall Equipment Effectiveness: {}%'.format(round(Profits[-1],2)))) 102 | 103 | # Errors validation 104 | if B > A: 105 | print('Error. The run time cannot be greater than the total available time.') 106 | if D > C: 107 | print('Error. The actual production cannot be greater than the production capacity.') 108 | if E != D: 109 | print('Error. The product output must be equal to actual production.') 110 | if F > E: 111 | print('Error. The actual good products cannot be greater than the product output.') 112 | -------------------------------------------------------------------------------- /Quality Control Charts/Images/Constants.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/Constants.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/Target IX chart and MR chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/Target IX chart and MR chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/Z chart and MW chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/Z chart and MW chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/Z-bar chart and W chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/Z-bar chart and W chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/c chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/c chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/dpmo chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/dpmo chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/np chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/np chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/p chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/p chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/u chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/u chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/x chart and mR chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/x chart and mR chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/x-bar chart and R chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/x-bar chart and R chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Images/x-bar chart and s chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Quality Control Charts/Images/x-bar chart and s chart.png -------------------------------------------------------------------------------- /Quality Control Charts/Process Stability Control - Western Electric.py: -------------------------------------------------------------------------------- 1 | ### PROCESS STABILITY CONTROL ### 2 | 3 | # CONTROL CHART RULES: 4 | 5 | # Rule #1: Beyond limits - one or more points are beyond the control limits 6 | # Rule #2: Zone A - 2 consecutive points in Zone A or beyond 7 | # Rule #3: Zone B - 4 out of 5 consecutive points in Zone B or beyond 8 | # Rule #4: Zone C - 8 or more consecutive points on one side of the center line (in Zone C or beyond) 9 | 10 | # Import required libraries 11 | import numpy as np 12 | import pandas as pd 13 | import statistics 14 | import hvplot 15 | import hvplot.pandas 16 | 17 | # Set a random seed 18 | np.random.seed(42) 19 | 20 | # Define sample size 21 | sample_size=5 22 | 23 | # Generate normal distributed measures 24 | data = np.round(np.random.normal(loc=50, scale=25, size=100),2) 25 | 26 | # Generate sample groups 27 | sample_group = np.repeat(range(1,21),sample_size) 28 | 29 | # Define data frame 30 | df = pd.DataFrame({'data':data, 'sample_group':sample_group}) 31 | df.head() 32 | 33 | # Group masures by sample groups (x_bar) 34 | df_grouped = df.groupby('sample_group').mean() 35 | 36 | # Rename x-bar column 37 | df_grouped.columns = ['x_bar'] 38 | df_grouped.head() 39 | 40 | # Add R (range) column 41 | df_max = df.groupby('sample_group').max() 42 | df_min = df.groupby('sample_group').min() 43 | df_grouped['R'] = df_max['data'] - df_min['data'] 44 | df_grouped.head() 45 | 46 | # Get control limits 47 | df_grouped['x_bar_bar'] = statistics.mean(df_grouped['x_bar']) 48 | df_grouped['UCL'] = statistics.mean(df_grouped['x_bar'])+(0.577*statistics.mean(df_grouped['R'])) 49 | df_grouped['+2s'] = (df_grouped['UCL']-df_grouped['x_bar_bar'])/3*2+df_grouped['x_bar_bar'] 50 | df_grouped['+1s'] = (df_grouped['UCL']-df_grouped['x_bar_bar'])/3*1+df_grouped['x_bar_bar'] 51 | df_grouped['-1s'] = df_grouped['x_bar_bar']-(df_grouped['UCL']-df_grouped['x_bar_bar'])/3*1 52 | df_grouped['-2s'] = df_grouped['x_bar_bar']- (df_grouped['UCL']-df_grouped['x_bar_bar'])/3*2 53 | df_grouped['LCL'] = statistics.mean(df_grouped['x_bar'])-(0.577*statistics.mean(df_grouped['R'])) 54 | df_grouped.head() 55 | 56 | # Plot x-bar control chart 57 | 58 | # Line chart 59 | line_plot = df_grouped.hvplot.line( 60 | x='sample_group', 61 | y=['x_bar','UCL','+2s','+1s','x_bar_bar','-1s','-2s','LCL'], 62 | xlabel="Sample Group", 63 | title="x-bar chart", 64 | height=500, 65 | width=1000) 66 | 67 | # Scatter plot 68 | scatter_plot = df_grouped.hvplot.scatter( 69 | x='sample_group', 70 | y=['x_bar','UCL','+2s','+1s','x_bar_bar','-1s','-2s','LCL'], 71 | xlabel="Sample Group", 72 | title="x-bar chart", 73 | height=500, 74 | width=1000) 75 | 76 | # Merge line chart and scatter plot into a single plot 77 | x_bar_chart = line_plot*scatter_plot 78 | x_bar_chart 79 | 80 | # Control chart rules lists setup 81 | R1_lower = [] 82 | R1_upper = [] 83 | R2_lower = ['-'] 84 | R2_upper = ['-'] 85 | R3_lower = ['-','-','-','-'] 86 | R3_upper = ['-','-','-','-'] 87 | R4_lower = ['-','-','-','-','-','-','-'] 88 | R4_upper = ['-','-','-','-','-','-','-'] 89 | 90 | # Rule 1 - Lower 91 | for x in df_grouped['x_bar']: 92 | if x < df_grouped['LCL'][1]: 93 | R1_lower.append(False) 94 | else: 95 | R1_lower.append(True) 96 | 97 | # Rule 1 - Upper 98 | for x in df_grouped['x_bar']: 99 | if x > df_grouped['UCL'][1]: 100 | R1_upper.append(False) 101 | else: 102 | R1_upper.append(True) 103 | 104 | # Rule 2 - Lower 105 | for i in range(2,len(df_grouped)+1): 106 | if df_grouped["x_bar"][i] < df_grouped["-2s"][i] and df_grouped["x_bar"][i-1] < df_grouped["-2s"][i-1]: 107 | R2_lower.append(False) 108 | else: 109 | R2_lower.append(True) 110 | 111 | # Rule 2 - Upper 112 | for i in range(2,len(df_grouped)+1): 113 | if df_grouped["x_bar"][i] > df_grouped["+2s"][i] and df_grouped["x_bar"][i-1] > df_grouped["+2s"][i-1]: 114 | R2_upper.append(False) 115 | else: 116 | R2_upper.append(True) 117 | 118 | # Rule 3 - Lower 119 | for i in range(5, len(df_grouped['x_bar'])+1): 120 | if((df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1]) or 121 | (df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i] < df_grouped['-1s'][i]) or 122 | (df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1] and df_grouped['x_bar'][i] < df_grouped['-1s'][i]) or 123 | (df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1] and df_grouped['x_bar'][i] < df_grouped['-1s'][i]) or 124 | (df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1] and df_grouped['x_bar'][i] < df_grouped['-1s'][i])): 125 | R3_lower.append(False) 126 | else: 127 | R3_lower.append(True) 128 | i+=1 129 | 130 | # Rule 3 - Upper 131 | for i in range(5, len(df_grouped['x_bar'])+1): 132 | if((df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1]) or 133 | (df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i] > df_grouped['+1s'][i]) or 134 | (df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1] and df_grouped['x_bar'][i] > df_grouped['+1s'][i]) or 135 | (df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1] and df_grouped['x_bar'][i] > df_grouped['+1s'][i]) or 136 | (df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1] and df_grouped['x_bar'][i] > df_grouped['+1s'][i])): 137 | R3_upper.append(False) 138 | else: 139 | R3_upper.append(True) 140 | i+=1 141 | 142 | # Rule 4 - Lower 143 | for i in range(8,len(df_grouped)+1): 144 | if (df_grouped["x_bar"][i-8:i] < df_grouped["x_bar_bar"][1]).all(): 145 | R4_lower.append(False) 146 | else: 147 | R4_lower.append(True) 148 | 149 | # Rule 4 - Upper 150 | for i in range(8,len(df_grouped)+1): 151 | if (df_grouped["x_bar"][i-8:i] > df_grouped["x_bar_bar"][1]).all(): 152 | R4_upper.append(False) 153 | else: 154 | R4_upper.append(True) 155 | 156 | # Define outcomes data frame 157 | analysis = pd.DataFrame({'R1_lower':R1_lower, 158 | 'R1_upper':R1_upper, 159 | 'R2_lower':R2_lower, 160 | 'R2_upper':R2_upper, 161 | 'R3_lower':R3_lower, 162 | 'R3_upper':R3_upper, 163 | 'R4_lower':R4_lower, 164 | 'R4_upper':R4_upper}) 165 | 166 | analysis.index = df_grouped.index 167 | analysis 168 | 169 | # Look for at least one False value in each of the control chart rules 170 | analysis.all() 171 | -------------------------------------------------------------------------------- /Quality Control Charts/Process Stability Control.py: -------------------------------------------------------------------------------- 1 | ### PROCESS STABILITY CONTROL ### 2 | 3 | # CONTROL CHART RULES: 4 | 5 | # Rule #1: Beyond limits - One or more points beyond the control limits 6 | # Rule #2: Zone A - 2 out of 3 consecutive points in Zone A or beyond 7 | # Rule #3: Zone B - 4 out of 5 consecutive points in Zone B or beyond 8 | # Rule #4: Zone C - 7 or more consecutive points on one side of the average (in Zone C or beyond) 9 | # Rule #5: Trend - 7 consecutive points trending up or trending down 10 | # Rule #6: Mixture - 8 consecutive points with no points in Zone C 11 | # Rule #7: Stratification - 15 consecutive points in Zone C 12 | # Rule #8: Over-control - 14 consecutive points alternating up and down 13 | 14 | # Source: SPC for Excel 15 | # URL: https://www.spcforexcel.com/knowledge/control-chart-basics/control-chart-rules-interpretation 16 | 17 | # Import required libraries 18 | import numpy as np 19 | import pandas as pd 20 | import statistics 21 | import hvplot 22 | import hvplot.pandas 23 | 24 | # Set a random seed 25 | np.random.seed(42) 26 | 27 | # Define sample size 28 | sample_size=5 29 | 30 | # Generate normal distributed measures 31 | data = np.round(np.random.normal(loc=50, scale=10, size=100),2) 32 | 33 | # Generate sample groups 34 | sample_group = np.repeat(range(1,21),sample_size) 35 | 36 | # Define data frame 37 | df = pd.DataFrame({'data':data, 'sample_group':sample_group}) 38 | df.head() 39 | 40 | # Group masures by sample groups (x_bar) 41 | df_grouped = df.groupby('sample_group').mean() 42 | 43 | # Rename x-bar column 44 | df_grouped.columns = ['x_bar'] 45 | df_grouped.head() 46 | 47 | # Add R (range) column 48 | df_max = df.groupby('sample_group').max() 49 | df_min = df.groupby('sample_group').min() 50 | df_grouped['R'] = df_max['data'] - df_min['data'] 51 | df_grouped.head() 52 | 53 | # Get control limits 54 | df_grouped['x_bar_bar'] = statistics.mean(df_grouped['x_bar']) 55 | df_grouped['UCL'] = statistics.mean(df_grouped['x_bar'])+(0.577*statistics.mean(df_grouped['R'])) 56 | df_grouped['+2s'] = (df_grouped['UCL']-df_grouped['x_bar_bar'])/3*2+df_grouped['x_bar_bar'] 57 | df_grouped['+1s'] = (df_grouped['UCL']-df_grouped['x_bar_bar'])/3*1+df_grouped['x_bar_bar'] 58 | df_grouped['-1s'] = df_grouped['x_bar_bar']-(df_grouped['UCL']-df_grouped['x_bar_bar'])/3*1 59 | df_grouped['-2s'] = df_grouped['x_bar_bar']- (df_grouped['UCL']-df_grouped['x_bar_bar'])/3*2 60 | df_grouped['LCL'] = statistics.mean(df_grouped['x_bar'])-(0.577*statistics.mean(df_grouped['R'])) 61 | df_grouped.head() 62 | 63 | # Plot x-bar control chart 64 | 65 | # Line chart 66 | line_plot = df_grouped.hvplot.line( 67 | x='sample_group', 68 | y=['x_bar','UCL','+2s','+1s','x_bar_bar','-1s','-2s','LCL'], 69 | xlabel="Sample Group", 70 | title="x-bar chart", 71 | height=500, 72 | width=1000) 73 | 74 | # Scatter plot 75 | scatter_plot = df_grouped.hvplot.scatter( 76 | x='sample_group', 77 | y=['x_bar','UCL','+2s','+1s','x_bar_bar','-1s','-2s','LCL'], 78 | xlabel="Sample Group", 79 | title="x-bar chart", 80 | height=500, 81 | width=1000) 82 | 83 | # Merge line chart and scatter plot into a single plot 84 | x_bar_chart = line_plot*scatter_plot 85 | x_bar_chart 86 | 87 | # Control chart rules lists setup 88 | R1_lower = [] 89 | R1_upper = [] 90 | R2_lower = ['-','-'] 91 | R2_upper = ['-','-'] 92 | R3_lower = ['-','-','-','-'] 93 | R3_upper = ['-','-','-','-'] 94 | R4_lower = ['-','-','-','-','-','-'] 95 | R4_upper = ['-','-','-','-','-','-'] 96 | R5_down = ['-','-','-','-','-','-'] 97 | R5_up = ['-','-','-','-','-','-'] 98 | R6 = ['-','-','-','-','-','-','-'] 99 | R7 = ['-','-','-','-','-','-','-','-','-','-','-','-','-','-'] 100 | R8 = ['-','-','-','-','-','-','-','-','-','-','-','-','-'] 101 | 102 | # Rule 1 - Lower 103 | for x in df_grouped['x_bar']: 104 | if x < df_grouped['LCL'][1]: 105 | R1_lower.append(False) 106 | else: 107 | R1_lower.append(True) 108 | 109 | # Rule 1 - Upper 110 | for x in df_grouped['x_bar']: 111 | if x > df_grouped['UCL'][1]: 112 | R1_upper.append(False) 113 | else: 114 | R1_upper.append(True) 115 | 116 | # Rule 2 - Lower 117 | i = 3 118 | while i <= len(df_grouped['x_bar']): 119 | if((df_grouped['x_bar'][i] < df_grouped['-2s'][i] and df_grouped['x_bar'][i-1] < df_grouped['-2s'][i-1]) or 120 | (df_grouped['x_bar'][i-1] < df_grouped['-2s'][i-1] and df_grouped['x_bar'][i-2] < df_grouped['-2s'][i-2]) or 121 | (df_grouped['x_bar'][i] < df_grouped['-2s'][i] and df_grouped['x_bar'][i-2] < df_grouped['-2s'][i-2])): 122 | R2_lower.append(False) 123 | else: 124 | R2_lower.append(True) 125 | i+=1 126 | 127 | # Rule 2 - Upper 128 | i = 3 129 | while i <= len(df_grouped['x_bar']): 130 | if((df_grouped['x_bar'][i] > df_grouped['+2s'][i] and df_grouped['x_bar'][i-1] > df_grouped['+2s'][i-1]) or 131 | (df_grouped['x_bar'][i-1] > df_grouped['+2s'][i-1] and df_grouped['x_bar'][i-2] > df_grouped['+2s'][i-2]) or 132 | (df_grouped['x_bar'][i] > df_grouped['+2s'][i] and df_grouped['x_bar'][i-2] > df_grouped['+2s'][i-2])): 133 | R2_upper.append(False) 134 | else: 135 | R2_upper.append(True) 136 | i+=1 137 | 138 | # Rule 3 - Lower 139 | i = 5 140 | while i <= len(df_grouped['x_bar']): 141 | if((df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1]) or 142 | (df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i] < df_grouped['-1s'][i]) or 143 | (df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1] and df_grouped['x_bar'][i] < df_grouped['-1s'][i]) or 144 | (df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] and df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1] and df_grouped['x_bar'][i] < df_grouped['-1s'][i]) or 145 | (df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] and df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] and df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1] and df_grouped['x_bar'][i] < df_grouped['-1s'][i])): 146 | R3_lower.append(False) 147 | else: 148 | R3_lower.append(True) 149 | i+=1 150 | 151 | # Rule 3 - Upper 152 | i = 5 153 | while i <= len(df_grouped['x_bar']): 154 | if((df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1]) or 155 | (df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i] > df_grouped['+1s'][i]) or 156 | (df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1] and df_grouped['x_bar'][i] > df_grouped['+1s'][i]) or 157 | (df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4] and df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1] and df_grouped['x_bar'][i] > df_grouped['+1s'][i]) or 158 | (df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3] and df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2] and df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1] and df_grouped['x_bar'][i] > df_grouped['+1s'][i])): 159 | R3_upper.append(False) 160 | else: 161 | R3_upper.append(True) 162 | i+=1 163 | 164 | # Rule 4 - Lower 165 | i = 7 166 | while i <= len(df_grouped['x_bar']): 167 | if(df_grouped['x_bar'][i] < df_grouped['x_bar_bar'][i] and 168 | df_grouped['x_bar'][i-1] < df_grouped['x_bar_bar'][i-1] and 169 | df_grouped['x_bar'][i-2] < df_grouped['x_bar_bar'][i-2] and 170 | df_grouped['x_bar'][i-3] < df_grouped['x_bar_bar'][i-3] and 171 | df_grouped['x_bar'][i-4] < df_grouped['x_bar_bar'][i-4] and 172 | df_grouped['x_bar'][i-5] < df_grouped['x_bar_bar'][i-5] and 173 | df_grouped['x_bar'][i-6] < df_grouped['x_bar_bar'][i-6]): 174 | R4_lower.append(False) 175 | else: 176 | R4_lower.append(True) 177 | i+=1 178 | 179 | # Rule 4 - Upper 180 | i = 7 181 | while i <= len(df_grouped['x_bar']): 182 | if(df_grouped['x_bar'][i] > df_grouped['x_bar_bar'][i] and 183 | df_grouped['x_bar'][i-1] > df_grouped['x_bar_bar'][i-1] and 184 | df_grouped['x_bar'][i-2] > df_grouped['x_bar_bar'][i-2] and 185 | df_grouped['x_bar'][i-3] > df_grouped['x_bar_bar'][i-3] and 186 | df_grouped['x_bar'][i-4] > df_grouped['x_bar_bar'][i-4] and 187 | df_grouped['x_bar'][i-5] > df_grouped['x_bar_bar'][i-5] and 188 | df_grouped['x_bar'][i-6] > df_grouped['x_bar_bar'][i-6]): 189 | R4_upper.append(False) 190 | else: 191 | R4_upper.append(True) 192 | i+=1 193 | 194 | # Rule 5 - Trend Down 195 | i = 7 196 | while i <= len(df_grouped['x_bar']): 197 | if(df_grouped['x_bar'][i] < df_grouped['x_bar'][i-1] and 198 | df_grouped['x_bar'][i-1] < df_grouped['x_bar'][i-2] and 199 | df_grouped['x_bar'][i-2] < df_grouped['x_bar'][i-3] and 200 | df_grouped['x_bar'][i-3] < df_grouped['x_bar'][i-4] and 201 | df_grouped['x_bar'][i-4] < df_grouped['x_bar'][i-5] and 202 | df_grouped['x_bar'][i-5] < df_grouped['x_bar'][i-6]): 203 | R5_down.append(False) 204 | else: 205 | R5_down.append(True) 206 | i+=1 207 | 208 | # Rule 5 - Trend Up 209 | i = 7 210 | while i <= len(df_grouped['x_bar']): 211 | if(df_grouped['x_bar'][i] > df_grouped['x_bar'][i-1] and 212 | df_grouped['x_bar'][i-1] > df_grouped['x_bar'][i-2] and 213 | df_grouped['x_bar'][i-2] > df_grouped['x_bar'][i-3] and 214 | df_grouped['x_bar'][i-3] > df_grouped['x_bar'][i-4] and 215 | df_grouped['x_bar'][i-4] > df_grouped['x_bar'][i-5] and 216 | df_grouped['x_bar'][i-5] > df_grouped['x_bar'][i-6]): 217 | R5_up.append(False) 218 | else: 219 | R5_up.append(True) 220 | i+=1 221 | 222 | # Rule 6 223 | i = 8 224 | while i <= len(df_grouped['x_bar']): 225 | if((df_grouped['x_bar'][i] < df_grouped['-1s'][i] or df_grouped['x_bar'][i] > df_grouped['+1s'][i]) and 226 | (df_grouped['x_bar'][i-1] < df_grouped['-1s'][i-1] or df_grouped['x_bar'][i-1] > df_grouped['+1s'][i-1]) and 227 | (df_grouped['x_bar'][i-2] < df_grouped['-1s'][i-2] or df_grouped['x_bar'][i-2] > df_grouped['+1s'][i-2]) and 228 | (df_grouped['x_bar'][i-3] < df_grouped['-1s'][i-3] or df_grouped['x_bar'][i-3] > df_grouped['+1s'][i-3]) and 229 | (df_grouped['x_bar'][i-4] < df_grouped['-1s'][i-4] or df_grouped['x_bar'][i-4] > df_grouped['+1s'][i-4]) and 230 | (df_grouped['x_bar'][i-5] < df_grouped['-1s'][i-5] or df_grouped['x_bar'][i-5] > df_grouped['+1s'][i-5]) and 231 | (df_grouped['x_bar'][i-6] < df_grouped['-1s'][i-6] or df_grouped['x_bar'][i-6] > df_grouped['+1s'][i-6]) and 232 | (df_grouped['x_bar'][i-7] < df_grouped['-1s'][i-7] or df_grouped['x_bar'][i-7] > df_grouped['+1s'][i-7])): 233 | R6.append(False) 234 | else: 235 | R6.append(True) 236 | i+=1 237 | 238 | # Rule 7 239 | i = 15 240 | while i <= len(df_grouped['x_bar']): 241 | if(((df_grouped['x_bar'][i] < df_grouped['x_bar_bar'][i] and df_grouped['x_bar'][i] > df_grouped['-1s'][i]) or (df_grouped['x_bar'][i] > df_grouped['x_bar_bar'][i] and df_grouped['x_bar'][i] < df_grouped['+1s'][i])) and 242 | ((df_grouped['x_bar'][i-1] < df_grouped['x_bar_bar'][i-1] and df_grouped['x_bar'][i-1] > df_grouped['-1s'][i-1]) or (df_grouped['x_bar'][i-1] > df_grouped['x_bar_bar'][i-1] and df_grouped['x_bar'][i-1] < df_grouped['+1s'][i-1])) and 243 | ((df_grouped['x_bar'][i-2] < df_grouped['x_bar_bar'][i-2] and df_grouped['x_bar'][i-2] > df_grouped['-1s'][i-2]) or (df_grouped['x_bar'][i-2] > df_grouped['x_bar_bar'][i-2] and df_grouped['x_bar'][i-2] < df_grouped['+1s'][i-2])) and 244 | ((df_grouped['x_bar'][i-3] < df_grouped['x_bar_bar'][i-3] and df_grouped['x_bar'][i-3] > df_grouped['-1s'][i-3]) or (df_grouped['x_bar'][i-3] > df_grouped['x_bar_bar'][i-3] and df_grouped['x_bar'][i-3] < df_grouped['+1s'][i-3])) and 245 | ((df_grouped['x_bar'][i-4] < df_grouped['x_bar_bar'][i-4] and df_grouped['x_bar'][i-4] > df_grouped['-1s'][i-4]) or (df_grouped['x_bar'][i-4] > df_grouped['x_bar_bar'][i-4] and df_grouped['x_bar'][i-4] < df_grouped['+1s'][i-4])) and 246 | ((df_grouped['x_bar'][i-5] < df_grouped['x_bar_bar'][i-5] and df_grouped['x_bar'][i-5] > df_grouped['-1s'][i-5]) or (df_grouped['x_bar'][i-5] > df_grouped['x_bar_bar'][i-5] and df_grouped['x_bar'][i-5] < df_grouped['+1s'][i-5])) and 247 | ((df_grouped['x_bar'][i-6] < df_grouped['x_bar_bar'][i-6] and df_grouped['x_bar'][i-6] > df_grouped['-1s'][i-6]) or (df_grouped['x_bar'][i-6] > df_grouped['x_bar_bar'][i-6] and df_grouped['x_bar'][i-6] < df_grouped['+1s'][i-6])) and 248 | ((df_grouped['x_bar'][i-7] < df_grouped['x_bar_bar'][i-7] and df_grouped['x_bar'][i-7] > df_grouped['-1s'][i-7]) or (df_grouped['x_bar'][i-7] > df_grouped['x_bar_bar'][i-7] and df_grouped['x_bar'][i-7] < df_grouped['+1s'][i-7])) and 249 | ((df_grouped['x_bar'][i-8] < df_grouped['x_bar_bar'][i-8] and df_grouped['x_bar'][i-8] > df_grouped['-1s'][i-8]) or (df_grouped['x_bar'][i-8] > df_grouped['x_bar_bar'][i-8] and df_grouped['x_bar'][i-8] < df_grouped['+1s'][i-8])) and 250 | ((df_grouped['x_bar'][i-9] < df_grouped['x_bar_bar'][i-9] and df_grouped['x_bar'][i-9] > df_grouped['-1s'][i-9]) or (df_grouped['x_bar'][i-9] > df_grouped['x_bar_bar'][i-9] and df_grouped['x_bar'][i-9] < df_grouped['+1s'][i-9])) and 251 | ((df_grouped['x_bar'][i-10] < df_grouped['x_bar_bar'][i-10] and df_grouped['x_bar'][i-10] > df_grouped['-1s'][i-10]) or (df_grouped['x_bar'][i-10] > df_grouped['x_bar_bar'][i-10] and df_grouped['x_bar'][i-10] < df_grouped['+1s'][i-10])) and 252 | ((df_grouped['x_bar'][i-11] < df_grouped['x_bar_bar'][i-11] and df_grouped['x_bar'][i-11] > df_grouped['-1s'][i-11]) or (df_grouped['x_bar'][i-11] > df_grouped['x_bar_bar'][i-11] and df_grouped['x_bar'][i-11] < df_grouped['+1s'][i-11])) and 253 | ((df_grouped['x_bar'][i-12] < df_grouped['x_bar_bar'][i-12] and df_grouped['x_bar'][i-12] > df_grouped['-1s'][i-12]) or (df_grouped['x_bar'][i-12] > df_grouped['x_bar_bar'][i-12] and df_grouped['x_bar'][i-12] < df_grouped['+1s'][i-12])) and 254 | ((df_grouped['x_bar'][i-13] < df_grouped['x_bar_bar'][i-13] and df_grouped['x_bar'][i-13] > df_grouped['-1s'][i-13]) or (df_grouped['x_bar'][i-13] > df_grouped['x_bar_bar'][i-13] and df_grouped['x_bar'][i-13] < df_grouped['+1s'][i-13])) and 255 | ((df_grouped['x_bar'][i-14] < df_grouped['x_bar_bar'][i-14] and df_grouped['x_bar'][i-14] > df_grouped['-1s'][i-14]) or (df_grouped['x_bar'][i-14] > df_grouped['x_bar_bar'][i-14] and df_grouped['x_bar'][i-14] < df_grouped['+1s'][i-14]))): 256 | R7.append(False) 257 | else: 258 | R7.append(True) 259 | i+=1 260 | 261 | # Rule #8 262 | i = 14 263 | while i <= len(df_grouped['x_bar']): 264 | if(((df_grouped['x_bar'][i] > df_grouped['x_bar'][i-1]) and 265 | (df_grouped['x_bar'][i-1] < df_grouped['x_bar'][i-2]) and 266 | (df_grouped['x_bar'][i-2] > df_grouped['x_bar'][i-3]) and 267 | (df_grouped['x_bar'][i-3] < df_grouped['x_bar'][i-4]) and 268 | (df_grouped['x_bar'][i-4] > df_grouped['x_bar'][i-5]) and 269 | (df_grouped['x_bar'][i-5] < df_grouped['x_bar'][i-6]) and 270 | (df_grouped['x_bar'][i-6] > df_grouped['x_bar'][i-7]) and 271 | (df_grouped['x_bar'][i-7] < df_grouped['x_bar'][i-8]) and 272 | (df_grouped['x_bar'][i-8] > df_grouped['x_bar'][i-9]) and 273 | (df_grouped['x_bar'][i-9] < df_grouped['x_bar'][i-10]) and 274 | (df_grouped['x_bar'][i-10] > df_grouped['x_bar'][i-11]) and 275 | (df_grouped['x_bar'][i-11] < df_grouped['x_bar'][i-12]) and 276 | (df_grouped['x_bar'][i-12] > df_grouped['x_bar'][i-13])) or 277 | ((df_grouped['x_bar'][i] < df_grouped['x_bar'][i-1]) and 278 | (df_grouped['x_bar'][i-1] > df_grouped['x_bar'][i-2]) and 279 | (df_grouped['x_bar'][i-2] < df_grouped['x_bar'][i-3]) and 280 | (df_grouped['x_bar'][i-3] > df_grouped['x_bar'][i-4]) and 281 | (df_grouped['x_bar'][i-4] < df_grouped['x_bar'][i-5]) and 282 | (df_grouped['x_bar'][i-5] > df_grouped['x_bar'][i-6]) and 283 | (df_grouped['x_bar'][i-6] < df_grouped['x_bar'][i-7]) and 284 | (df_grouped['x_bar'][i-7] > df_grouped['x_bar'][i-8]) and 285 | (df_grouped['x_bar'][i-8] < df_grouped['x_bar'][i-9]) and 286 | (df_grouped['x_bar'][i-9] > df_grouped['x_bar'][i-10]) and 287 | (df_grouped['x_bar'][i-10] < df_grouped['x_bar'][i-11]) and 288 | (df_grouped['x_bar'][i-11] > df_grouped['x_bar'][i-12]) and 289 | (df_grouped['x_bar'][i-12] < df_grouped['x_bar'][i-13]))): 290 | R8.append(False) 291 | else: 292 | R8.append(True) 293 | i+=1 294 | 295 | # Define outcomes data frame 296 | analysis = pd.DataFrame({'R1_lower':R1_lower, 297 | 'R1_upper':R1_upper, 298 | 'R2_lower':R2_lower, 299 | 'R2_upper':R2_upper, 300 | 'R3_lower':R3_lower, 301 | 'R3_upper':R3_upper, 302 | 'R4_lower':R4_lower, 303 | 'R4_upper':R4_upper, 304 | 'R5_down':R5_down, 305 | 'R5_up':R5_up, 306 | 'R6':R6, 307 | 'R7':R7, 308 | 'R8':R8}) 309 | analysis.index = df_grouped.index 310 | analysis 311 | 312 | # Look for at least one False value in each of the control chart rules 313 | analysis.all() 314 | -------------------------------------------------------------------------------- /Quality Control Charts/Target IX chart and MR chart.py: -------------------------------------------------------------------------------- 1 | ### Target IX chart and MR chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | from matplotlib.ticker import MaxNLocator 8 | import statistics 9 | 10 | # Set random seed 11 | np.random.seed(42) 12 | 13 | # Define sample number 14 | sample = pd.Series(np.arange(0,18)) 15 | 16 | # Set part number 17 | part = pd.Series(np.repeat([1,2,3],6)) 18 | 19 | # Set readings for each part 20 | reading_part_1 = pd.Series(np.random.normal(loc=150, scale=2, size=6)) 21 | reading_part_2 = pd.Series(np.random.normal(loc=200, scale=2, size=6)) 22 | reading_part_3 = pd.Series(np.random.normal(loc=348, scale=2, size=6)) 23 | reading = pd.concat([reading_part_1, reading_part_2, reading_part_3], ignore_index=True) 24 | 25 | # Create a data frame with the sample number, part, and readings 26 | df = pd.DataFrame({'sample':sample, 'part':part, 'reading':reading}) 27 | 28 | # Define the target for each part 29 | df['target'] = pd.Series(np.repeat([150,200,350],6)) 30 | 31 | # Obtain the deviation from each sample 32 | df['deviation'] = (df['reading'] - df['target']) 33 | 34 | # Define list variable for moving ranges 35 | MR = [np.nan] 36 | 37 | # Obtain and append moving ranges 38 | for i in range(1,len(df)): 39 | MR.append(abs(df['deviation'][i] - df['deviation'][i-1])) 40 | i += 1 41 | df['MR'] = MR 42 | 43 | #Plot Target IX and MR charts 44 | fig, axs = plt.subplots(2, figsize=(15,15), sharex=True) 45 | 46 | axs[0].plot(df['deviation'], linestyle='-', marker='o', color='black') 47 | axs[0].axhline(0, color='grey') 48 | axs[0].axhline(df['deviation'].mean(), color='blue') 49 | axs[0].axhline(df['deviation'].mean()+(2.66*df['MR'].mean()), color='red', linestyle='dashed') 50 | axs[0].axhline(df['deviation'].mean()-(2.66*df['MR'].mean()), color='red', linestyle='dashed') 51 | axs[0].set_title('Target IX Chart') 52 | axs[0].set(xlabel='Sample', ylabel='Target Deviation') 53 | axs[0].xaxis.set_major_locator(MaxNLocator(integer=True)) 54 | axs[0].yaxis.set_major_locator(MaxNLocator(integer=False)) 55 | 56 | axs[1].plot(df['MR'], linestyle='-', marker='o', color='black') 57 | axs[1].axhline(df['MR'].mean(), color='blue') 58 | axs[1].axhline(df['MR'].mean()*3.267, color='red', linestyle='dashed') 59 | axs[1].axhline(df['MR'].mean()*0, color='red', linestyle='dashed') 60 | axs[1].set_title('MR Chart') 61 | axs[1].set(xlabel='Sample', ylabel='Moving Range') 62 | axs[1].yaxis.set_major_locator(MaxNLocator(integer=False)) 63 | 64 | # Validate points out of control limits on Target IX chart 65 | i = 0 66 | control = True 67 | for deviation in df['deviation']: 68 | if deviation > df['deviation'].mean()+(2.66*df['MR'].mean()) or deviation < df['deviation'].mean()-(2.66*df['MR'].mean()): 69 | print('Sample', i, 'out of cotrol limits!') 70 | control = False 71 | i += 1 72 | if control == True: 73 | print('All Target IX points within control limits.') 74 | 75 | # Validate points out of control limits on MR chart 76 | i = 0 77 | control = True 78 | for MR in df['MR']: 79 | if MR > df['MR'].mean()*3.267 or MR < df['MR'].mean()*0: 80 | print('Sample', i, 'range out of cotrol limits!') 81 | control = False 82 | i += 1 83 | if control == True: 84 | print('All MR points within control limits.') 85 | -------------------------------------------------------------------------------- /Quality Control Charts/Z chart and MW chart.py: -------------------------------------------------------------------------------- 1 | ### Z chart and MW chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | from matplotlib.ticker import MaxNLocator 8 | import statistics 9 | 10 | # Set random seed 11 | np.random.seed(42) 12 | 13 | # Define sample number 14 | sample = pd.Series(np.arange(0,16)) 15 | 16 | # Set readings 17 | X = pd.Series(np.random.normal(loc=39, scale=1, size=16)) 18 | 19 | # Define the target Xbar 20 | target_x = pd.Series(np.repeat(39,16)) 21 | 22 | # Define the target Rbar 23 | target_R = pd.Series(np.repeat(0.6,16)) 24 | 25 | # Create a data frame with the sample number, readings, target Xbar and target Rbar 26 | df = pd.DataFrame({'sample':sample, 'X':X, 'target_Xbar':target_x, 'target_Rbar':target_R}) 27 | 28 | # Obtain the Z values 29 | df['Z'] = (df['X']-df['target_Xbar'])/df['target_Rbar'] 30 | 31 | # Define list variable for moving ranges 32 | MW = [np.nan] 33 | 34 | # Obtain and append moving ranges 35 | i = 1 36 | for data in range(1, len(df)): 37 | MW.append(abs(df['Z'][i] - df['Z'][i-1])) 38 | i += 1 39 | df['MW'] = MW 40 | 41 | #Plot Z and MW charts 42 | fig, axs = plt.subplots(2, figsize=(15,15), sharex=True) 43 | 44 | axs[0].plot(df['Z'], linestyle='-', marker='o', color='black') 45 | axs[0].axhline(0, color='blue') 46 | axs[0].axhline(2.66, color='red', linestyle='dashed') 47 | axs[0].axhline(-2.66, color='red', linestyle='dashed') 48 | axs[0].set_title('Z Chart') 49 | axs[0].set(xlabel='Sample', ylabel='Z') 50 | axs[0].xaxis.set_major_locator(MaxNLocator(integer=True)) 51 | axs[0].yaxis.set_major_locator(MaxNLocator(integer=False)) 52 | 53 | axs[1].plot(df['MW'], linestyle='-', marker='o', color='black') 54 | axs[1].axhline(1, color='blue') 55 | axs[1].axhline(3.27, color='red', linestyle='dashed') 56 | axs[1].axhline(0, color='red', linestyle='dashed') 57 | axs[1].set_title('MW Chart') 58 | axs[1].set(xlabel='Sample', ylabel='W') 59 | 60 | # Validate points out of control limits on Z chart 61 | i = 0 62 | control = True 63 | for Z in df['Z']: 64 | if Z > 2.66 or Z < -2.66: 65 | print('Sample', i, 'out of cotrol limits!') 66 | control = False 67 | i += 1 68 | if control == True: 69 | print('All Z points within control limits.') 70 | 71 | # Validate points out of control limits on MW chart 72 | i = 0 73 | control = True 74 | for MW in df['MW']: 75 | if MW > 3.27 or MW < 0: 76 | print('Sample', i, 'out of cotrol limits!') 77 | control = False 78 | i += 1 79 | if control == True: 80 | print('All MW points within control limits.') 81 | -------------------------------------------------------------------------------- /Quality Control Charts/Z-bar chart and W chart.py: -------------------------------------------------------------------------------- 1 | ### Z-bar chart and W chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | from matplotlib.ticker import MaxNLocator 8 | import statistics 9 | 10 | # Set random seed 11 | np.random.seed(42) 12 | 13 | # Define sample number 14 | sample = pd.Series(np.arange(0,12)) 15 | 16 | # Set part number 17 | part = pd.Series(np.repeat([1,2],6)) 18 | 19 | # Set readings for parts 1 20 | reading_part_1_x1 = pd.Series(np.random.normal(loc=5, scale=1, size=6)) 21 | reading_part_1_x2 = pd.Series(np.random.normal(loc=5, scale=1, size=6)) 22 | reading_part_1_x3 = pd.Series(np.random.normal(loc=5, scale=1, size=6)) 23 | 24 | # Set readings for parts 2 25 | reading_part_2_x1 = pd.Series(np.random.normal(loc=7, scale=1, size=6)) 26 | reading_part_2_x2 = pd.Series(np.random.normal(loc=7, scale=1, size=6)) 27 | reading_part_2_x3 = pd.Series(np.random.normal(loc=7, scale=1, size=6)) 28 | 29 | # Concatenate readings for each trial 30 | x1 = pd.concat([reading_part_1_x1, reading_part_2_x1], ignore_index=True) 31 | x2 = pd.concat([reading_part_1_x2, reading_part_2_x2], ignore_index=True) 32 | x3 = pd.concat([reading_part_1_x3, reading_part_2_x3], ignore_index=True) 33 | 34 | # Define the target for each part 35 | target = pd.Series(np.repeat([5,7],6)) 36 | 37 | # Create a data frame with the sample number, part, target and readings for each trial 38 | df = pd.DataFrame({'sample':sample, 'part':part, 'target_Xbar2':target, 'x1':x1, 'x2':x2, 'x3':x3}) 39 | 40 | # Obtain the X-bar 41 | df['X-bar'] = df[['x1','x2','x3']].mean(axis=1) 42 | 43 | # Obtain the sample range 44 | range = [] 45 | for i in df['sample']: 46 | range.append(max(df['x1'][i],df['x2'][i],df['x3'][i]) - min(df['x1'][i],df['x2'][i],df['x3'][i])) 47 | df['R'] = range 48 | 49 | # Define Upper Specification Limit for Part 1 50 | USL_part_1 = 5.5 51 | 52 | # Define Lower Specification Limit for Part 1 53 | LSL_part_1 = 4.5 54 | 55 | # Define process Cp for part 1 56 | Cp_1 = 1 57 | 58 | # Define Upper Specification Limit for Part 2 59 | USL_part_2 = 7.5 60 | 61 | # Define Lower Specification Limit for Part 2 62 | LSL_part_2 = 6.5 63 | 64 | # Define process Cp for part 2 65 | Cp_2 = 1 66 | 67 | # Obtain the target Rbar 68 | target_range_part_1 = pd.Series((np.repeat(1.693*((USL_part_1-LSL_part_1))/(6*Cp_1),6))) 69 | target_range_part_2 = pd.Series((np.repeat(1.693*((USL_part_2-LSL_part_2))/(6*Cp_2),6))) 70 | df['target_Rbar'] = pd.concat([target_range_part_1, target_range_part_2], ignore_index=True) 71 | 72 | # Obtain the Z value 73 | df['Z'] = ((df['X-bar']-df['target_Xbar2'])/df['target_Rbar']) 74 | 75 | # Obtain the W value 76 | df['W'] = df['target_Rbar']/df['R'] 77 | 78 | #Plot Zbar and W charts 79 | fig, axs = plt.subplots(2, figsize=(15,15), sharex=True) 80 | 81 | axs[0].plot(df['Z'], linestyle='-', marker='o', color='black') 82 | axs[0].axhline(0, color='blue') 83 | axs[0].axhline(1.023, color='red', linestyle='dashed') 84 | axs[0].axhline(-1.023, color='red', linestyle='dashed') 85 | axs[0].set_title('Z-bar Chart') 86 | axs[0].set(xlabel='Sample', ylabel='Z') 87 | axs[0].xaxis.set_major_locator(MaxNLocator(integer=True)) 88 | axs[0].yaxis.set_major_locator(MaxNLocator(integer=False)) 89 | 90 | axs[1].plot(df['W'], linestyle='-', marker='o', color='black') 91 | axs[1].axhline(1, color='blue') 92 | axs[1].axhline(2.574, color='red', linestyle='dashed') 93 | axs[1].axhline(0, color='red', linestyle='dashed') 94 | axs[1].set_title('W Chart') 95 | axs[1].set(xlabel='Sample', ylabel='W') 96 | 97 | # Validate points out of control limits on Z-bar chart 98 | i = 0 99 | control = True 100 | for Z in df['Z']: 101 | if Z > 1.023 or Z < -1.023: 102 | print('Sample', i, 'out of cotrol limits!') 103 | control = False 104 | i += 1 105 | if control == True: 106 | print('All Z-bar points within control limits.') 107 | 108 | # Validate points out of control limits on W chart 109 | i = 0 110 | control = True 111 | for W in df['W']: 112 | if W > 2.574 or W < 0: 113 | print('Sample', i, 'out of cotrol limits!') 114 | control = False 115 | i += 1 116 | if control == True: 117 | print('All W points within control limits.') 118 | -------------------------------------------------------------------------------- /Quality Control Charts/c-chart.py: -------------------------------------------------------------------------------- 1 | ### c-chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import statistics 8 | 9 | # Set random seed 10 | np.random.seed(42) 11 | 12 | # Create dummy data 13 | c = {'defects':np.random.randint(0,5,10).tolist(), 14 | 'group_size':np.repeat(10,10).tolist()} 15 | 16 | # Convert data to data frame 17 | c = pd.DataFrame(c) 18 | 19 | # Plot c-chart 20 | plt.figure(figsize=(15,7.5)) 21 | plt.plot(c['defects'], linestyle='-', marker='o', color='black') 22 | plt.axhline(statistics.mean(c['defects'])+3*np.sqrt(statistics.mean(c['defects'])), color='red', linestyle='dashed') 23 | plt.axhline(statistics.mean(c['defects'])-3*np.sqrt(statistics.mean(c['defects'])), color='red', linestyle='dashed') 24 | plt.axhline(statistics.mean(c['defects']), color='blue') 25 | plt.ylim(bottom=0) 26 | plt.title('c Chart') 27 | plt.xlabel('Group') 28 | plt.ylabel('Defect Count') 29 | 30 | # Validate points out of control limits 31 | i = 0 32 | control = True 33 | for group in c['defects']: 34 | if group > statistics.mean(c['defects'])+3*np.sqrt(statistics.mean(c['defects'])) or group < statistics.mean(c['defects'])-3*np.sqrt(statistics.mean(c['defects'])): 35 | print('Group', i, 'out of defects cotrol limits!') 36 | control = False 37 | i += 1 38 | if control == True: 39 | print('All points within control limits.') 40 | -------------------------------------------------------------------------------- /Quality Control Charts/dmpo chart.py: -------------------------------------------------------------------------------- 1 | ### dpmo chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | from matplotlib.ticker import MaxNLocator 8 | import statistics 9 | 10 | # Set random seed 11 | np.random.seed(42) 12 | 13 | # Define sample size 14 | sample_size = 10 15 | 16 | # Set the opportunities for defects per unit 17 | defects_opportunities = 2751 18 | 19 | # Define sample number 20 | sample = pd.Series(np.arange(0,40)) 21 | 22 | # Specify the number of defects found per sample 23 | defects_found = pd.Series(np.random.randint(0,7,40)) 24 | 25 | # Obtain the defects per unit 26 | dpu = defects_found/sample_size 27 | 28 | # Obtain the defects per million opportunities 29 | dpmo = (dpu/defects_opportunities)*1000000 30 | 31 | # Create a data frame with the sample number, defects found, dpu and dpmo 32 | df = pd.DataFrame({'sample_number':sample, 'defects_found':defects_found, 'dpu':dpu, 'dpmo':dpmo}) 33 | 34 | # Plot dpmo chart 35 | plt.figure(figsize=(15,7.5)) 36 | plt.plot(df['dpmo'], linestyle='-', marker='o', color='black') 37 | plt.axhline(statistics.mean(df['dpmo']), color='blue') 38 | plt.axhline(statistics.mean(df['dpmo'])+3000*(np.sqrt((statistics.mean(df['dpmo']/(sample_size*defects_opportunities))))), color='red', linestyle='dashed') 39 | plt.axhline(max(0,statistics.mean(df['dpmo'])-3000*(np.sqrt((statistics.mean(df['dpmo']/(sample_size*defects_opportunities)))))), color='red', linestyle='dashed') 40 | plt.title('dpmo Chart') 41 | plt.xlabel('Sample') 42 | plt.ylabel('Defects per Million Opportunities') 43 | 44 | # Validate points out of control limits 45 | i = 0 46 | control = True 47 | for dpmo in df['dpmo']: 48 | if dpmo > statistics.mean(df['dpmo'])+3000*(np.sqrt((statistics.mean(df['dpmo']/(sample_size*defects_opportunities))))) or dpmo < max(0,statistics.mean(df['dpmo'])-3000*(np.sqrt((statistics.mean(df['dpmo']/(sample_size*defects_opportunities)))))): 49 | print('Sample', i, 'out of cotrol limits!') 50 | control = False 51 | i += 1 52 | if control == True: 53 | print('All dpmo points within control limits.') 54 | -------------------------------------------------------------------------------- /Quality Control Charts/np-chart.py: -------------------------------------------------------------------------------- 1 | ### np-chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import statistics 8 | 9 | # Set random seed 10 | np.random.seed(42) 11 | 12 | # Create dummy data 13 | data = {'defects':np.random.randint(1,5,10).tolist(), 14 | 'group_size':np.repeat(10,10).tolist()} 15 | 16 | # Convert data to data frame 17 | data = pd.DataFrame(data) 18 | 19 | # Add 'np' column to data frame 20 | data['np'] = data['defects']/data['group_size'] 21 | 22 | # Plot np-chart 23 | plt.figure(figsize=(15,7.5)) 24 | plt.plot(data['np'], linestyle='-', marker='o', color='black') 25 | plt.axhline(statistics.mean(data['np'])+3*(np.sqrt((statistics.mean(data['np'])*(1-statistics.mean(data['np'])))/statistics.mean(data['group_size']))), color='red', linestyle='dashed') 26 | plt.axhline(statistics.mean(data['np'])-3*(np.sqrt((statistics.mean(data['np'])*(1-statistics.mean(data['np'])))/statistics.mean(data['group_size']))), color='red', linestyle='dashed') 27 | plt.axhline(statistics.mean(data['np']), color='blue') 28 | plt.ylim(bottom=0) 29 | plt.title('np Chart') 30 | plt.xlabel('Group') 31 | plt.ylabel('Fraction Defective') 32 | 33 | # Validate points out of control limits 34 | i = 0 35 | control = True 36 | for group in data['np']: 37 | if group > (statistics.mean(data['np'])+3*(np.sqrt((statistics.mean(data['np'])*(1-statistics.mean(data['np'])))/statistics.mean(data['group_size'])))) or group < (statistics.mean(data['np'])-3*(np.sqrt((statistics.mean(data['np'])*(1-statistics.mean(data['np'])))/statistics.mean(data['group_size'])))): 38 | print('Group', i, 'out of fraction defective cotrol limits!') 39 | control = False 40 | i += 1 41 | if control == True: 42 | print('All points within control limits.') 43 | -------------------------------------------------------------------------------- /Quality Control Charts/p-chart.py: -------------------------------------------------------------------------------- 1 | ### p-chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import statistics 8 | 9 | # Set random seed 10 | np.random.seed(42) 11 | 12 | # Create dummy data 13 | p = {'defects':np.random.randint(1,5,10).tolist(), 14 | 'group_size':np.random.randint(10,15,10).tolist()} 15 | 16 | # Convert data to data frame 17 | p = pd.DataFrame(p) 18 | 19 | # Add 'p' column to data frame 20 | p['p'] = p['defects']/p['group_size'] 21 | 22 | # Plot p-chart 23 | plt.figure(figsize=(15,7.5)) 24 | plt.plot(p['p'], linestyle='-', marker='o', color='black') 25 | plt.step(x=range(0,len(p['p'])), y=statistics.mean(p['p'])+3*(np.sqrt((statistics.mean(p['p'])*(1-statistics.mean(p['p'])))/(p['group_size']))), color='red', linestyle='dashed') 26 | plt.step(x=range(0,len(p['p'])), y=statistics.mean(p['p'])-3*(np.sqrt((statistics.mean(p['p'])*(1-statistics.mean(p['p'])))/(p['group_size']))), color='red', linestyle='dashed') 27 | plt.axhline(statistics.mean(p['p']), color='blue') 28 | plt.ylim(bottom=0) 29 | plt.title('p Chart') 30 | plt.xlabel('Group') 31 | plt.ylabel('Fraction Defective') 32 | 33 | # Validate points out of control limits 34 | i = 0 35 | control = True 36 | for group in p['p']: 37 | if group > (statistics.mean(p['p'])+3*(np.sqrt((statistics.mean(p['p'])*(1-statistics.mean(p['p'])))/statistics.mean(p['group_size'])))) or group < (statistics.mean(p['p'])-3*(np.sqrt((statistics.mean(p['p'])*(1-statistics.mean(p['p'])))/statistics.mean(p['group_size'])))): 38 | print('Group', i, 'out of fraction defective cotrol limits!') 39 | control = False 40 | i += 1 41 | if control == True: 42 | print('All points within control limits.') 43 | -------------------------------------------------------------------------------- /Quality Control Charts/u-chart.py: -------------------------------------------------------------------------------- 1 | ### u-chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import statistics 8 | 9 | # Set random seed 10 | np.random.seed(42) 11 | 12 | # Create dummy data 13 | u = {'defects':np.random.randint(1,5,10).tolist(), 14 | 'group_size':np.random.randint(10,15,10).tolist()} 15 | 16 | # Convert data to data frame 17 | u = pd.DataFrame(u) 18 | 19 | # Add 'u' column to data frame 20 | u['u'] = u['defects']/u['group_size'] 21 | 22 | # Plot u-chart 23 | plt.figure(figsize=(15,7.5)) 24 | plt.plot(u['u'], linestyle='-', marker='o', color='black') 25 | plt.step(x=range(0, len(u['u'])), y=u['u'].mean()+3*np.sqrt(u['u'].mean()/u['group_size']), color='red', linestyle='dashed') 26 | plt.step(x=range(0, len(u['u'])), y=u['u'].mean()-3*np.sqrt(u['u'].mean()/u['group_size']), color='red', linestyle='dashed') 27 | plt.axhline(statistics.mean(u['u']), color='blue') 28 | plt.ylim(bottom=0) 29 | plt.title('u Chart') 30 | plt.xlabel('Group') 31 | plt.ylabel('Fraction Defective') 32 | 33 | # Validate points out of control limits 34 | i = 0 35 | control = True 36 | for group in u['u']: 37 | if group > u['u'].mean()+3*np.sqrt(u['u'].mean()/u['group_size'][i]) or group < u['u'].mean()-3*np.sqrt(u['u'].mean()/u['group_size'][i]): 38 | print('Group', i, 'out of fraction defective cotrol limits!') 39 | control = False 40 | i += 1 41 | if control == True: 42 | print('All points within control limits.') 43 | -------------------------------------------------------------------------------- /Quality Control Charts/x chart and mR chart.py: -------------------------------------------------------------------------------- 1 | ### x-mR chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import statistics 8 | 9 | # Set random seed 10 | np.random.seed(42) 11 | 12 | # Create dummy data 13 | x = pd.Series(np.random.normal(loc=10, scale=2, size=10)) 14 | 15 | # Define list variable for moving ranges 16 | MR = [np.nan] 17 | 18 | # Create dummy data 19 | x = pd.Series(np.random.normal(loc=10, scale=2, size=10)) 20 | 21 | # Define list variable for moving ranges 22 | MR = [np.nan] 23 | 24 | # Get and append moving ranges 25 | i = 1 26 | for data in range(1, len(x)): 27 | MR.append(abs(x[i] - x[i-1])) 28 | i += 1 29 | 30 | # Convert list to pandas Series objects 31 | MR = pd.Series(MR) 32 | 33 | # Concatenate mR Series with and rename columns 34 | data = pd.concat([x,MR], axis=1).rename(columns={0:"x", 1:"mR"}) 35 | 36 | # Plot x and mR charts 37 | fig, axs = plt.subplots(2, figsize=(15,15), sharex=True) 38 | 39 | # x chart 40 | axs[0].plot(data['x'], linestyle='-', marker='o', color='black') 41 | axs[0].axhline(statistics.mean(data['x']), color='blue') 42 | axs[0].axhline(statistics.mean(data['x'])+3*statistics.mean(data['mR'][1:len(data['mR'])])/1.128, color = 'red', linestyle = 'dashed') 43 | axs[0].axhline(statistics.mean(data['x'])-3*statistics.mean(data['mR'][1:len(data['mR'])])/1.128, color = 'red', linestyle = 'dashed') 44 | axs[0].set_title('Individual Chart') 45 | axs[0].set(xlabel='Unit', ylabel='Value') 46 | 47 | # mR chart 48 | axs[1].plot(data['mR'], linestyle='-', marker='o', color='black') 49 | axs[1].axhline(statistics.mean(data['mR'][1:len(data['mR'])]), color='blue') 50 | axs[1].axhline(statistics.mean(data['mR'][1:len(data['mR'])])+3*statistics.mean(data['mR'][1:len(data['mR'])])*0.8525, color='red', linestyle ='dashed') 51 | axs[1].axhline(statistics.mean(data['mR'][1:len(data['mR'])])-3*statistics.mean(data['mR'][1:len(data['mR'])])*0.8525, color='red', linestyle ='dashed') 52 | axs[1].set_ylim(bottom=0) 53 | axs[1].set_title('mR Chart') 54 | axs[1].set(xlabel='Unit', ylabel='Range') 55 | 56 | # Validate points out of control limits for x chart 57 | i = 0 58 | control = True 59 | for unit in data['x']: 60 | if unit > statistics.mean(data['x'])+3*statistics.mean(data['mR'][1:len(data['mR'])])/1.128 or unit < statistics.mean(data['x'])-3*statistics.mean(data['mR'][1:len(data['mR'])])/1.128: 61 | print('Unit', i, 'out of control limits!') 62 | control = False 63 | i += 1 64 | if control == True: 65 | print('All points within control limits.') 66 | 67 | # Validate points out of control limits for mR chart 68 | i = 0 69 | control = True 70 | for unit in data['mR']: 71 | if unit > statistics.mean(data['mR'][1:len(data['mR'])])+3*statistics.mean(data['mR'][1:len(data['mR'])])*0.8525 or unit < statistics.mean(data['mR'][1:len(data['mR'])])-3*statistics.mean(data['mR'][1:len(data['mR'])])*0.8525: 72 | print('Unit', i, 'out of control limits!') 73 | control = False 74 | i += 1 75 | if control == True: 76 | print('All points within control limits.') 77 | -------------------------------------------------------------------------------- /Quality Control Charts/x-bar chart and R chart.py: -------------------------------------------------------------------------------- 1 | ### x-bar chart and R chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import statistics 8 | 9 | # Set random seed 10 | np.random.seed(42) 11 | 12 | # Create dummy data 13 | x = np.array([list(np.random.normal(loc=10, scale=2, size=5)), 14 | list(np.random.normal(loc=10, scale=2, size=5)), 15 | list(np.random.normal(loc=10, scale=2, size=5)), 16 | list(np.random.normal(loc=10, scale=2, size=5)), 17 | list(np.random.normal(loc=10, scale=2, size=5)), 18 | list(np.random.normal(loc=17, scale=2, size=5)), 19 | list(np.random.normal(loc=10, scale=2, size=5)), 20 | list(np.random.normal(loc=10, scale=2, size=5)), 21 | list(np.random.normal(loc=10, scale=2, size=5)), 22 | list(np.random.normal(loc=10, scale=2, size=5))]) 23 | 24 | # Define list variable for groups means 25 | x_bar = [] 26 | 27 | # Define list variable for groups ranges 28 | r = [] 29 | 30 | # Get and append groups means and ranges 31 | for group in x: 32 | x_bar.append(group.mean()) 33 | r.append(group.max() - group.min()) 34 | 35 | # Plot x-bar and R charts 36 | fig, axs = plt.subplots(2, figsize=(15,15)) 37 | 38 | # x-bar chart 39 | axs[0].plot(x_bar, linestyle='-', marker='o', color='black') 40 | axs[0].axhline((statistics.mean(x_bar)+0.577*statistics.mean(r)), color='red', linestyle='dashed') 41 | axs[0].axhline((statistics.mean(x_bar)-0.577*statistics.mean(r)), color='red', linestyle='dashed') 42 | axs[0].axhline((statistics.mean(x_bar)), color='blue') 43 | axs[0].set_title('x-bar Chart') 44 | axs[0].set(xlabel='Group', ylabel='Mean') 45 | 46 | # R chart 47 | axs[1].plot(r, linestyle='-', marker='o', color='black') 48 | axs[1].axhline((2.574*statistics.mean(r)), color='red', linestyle='dashed') 49 | axs[1].axhline((0*statistics.mean(r)), color='red', linestyle='dashed') 50 | axs[1].axhline((statistics.mean(r)), color='blue') 51 | axs[1].set_ylim(bottom=0) 52 | axs[1].set_title('R Chart') 53 | axs[1].set(xlabel='Group', ylabel='Range') 54 | 55 | # Validate points out of control limits for x-bar chart 56 | i = 0 57 | control = True 58 | for group in x_bar: 59 | if group > statistics.mean(x_bar)+0.577*statistics.mean(r) or group < statistics.mean(x_bar)-0.577*statistics.mean(r): 60 | print('Group', i, 'out of mean control limits!') 61 | control = False 62 | i += 1 63 | if control == True: 64 | print('All points within control limits.') 65 | 66 | # Validate points out of control limits for R chart 67 | i = 0 68 | control = True 69 | for group in r: 70 | if group > 2.574*statistics.mean(r): 71 | print('Group', i, 'out of range cotrol limits!') 72 | control = False 73 | i += 1 74 | if control == True: 75 | print('All points within control limits.') 76 | -------------------------------------------------------------------------------- /Quality Control Charts/x-bar chart and s chart.py: -------------------------------------------------------------------------------- 1 | ### x-bar chart and R chart ### 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import statistics 8 | 9 | # Set random seed 10 | np.random.seed(42) 11 | 12 | # Create dummy data 13 | x = np.array([list(np.random.normal(loc=10, scale=2, size=11)), 14 | list(np.random.normal(loc=10, scale=2, size=11)), 15 | list(np.random.normal(loc=10, scale=2, size=11)), 16 | list(np.random.normal(loc=10, scale=2, size=11)), 17 | list(np.random.normal(loc=10, scale=2, size=11)), 18 | list(np.random.normal(loc=10, scale=2, size=11)), 19 | list(np.random.normal(loc=10, scale=2, size=11)), 20 | list(np.random.normal(loc=13, scale=2, size=11)), 21 | list(np.random.normal(loc=10, scale=2, size=11)), 22 | list(np.random.normal(loc=10, scale=2, size=11))]) 23 | 24 | # Define list variable for groups means 25 | x_bar = [] 26 | 27 | # Define list variable for groups standard deviation 28 | s = [] 29 | 30 | # Get and append groups means and standard deviations 31 | for group in x: 32 | x_bar.append(group.mean()) 33 | s.append(np.std(group)) 34 | 35 | # Plot x-bar and s charts 36 | fig, axs = plt.subplots(2, figsize=(15,15)) 37 | 38 | # x-bar chart 39 | axs[0].plot(x_bar, linestyle='-', marker='o', color='black') 40 | axs[0].axhline((statistics.mean(x_bar)+0.927*statistics.mean(s)), color='red', linestyle='dashed') 41 | axs[0].axhline((statistics.mean(x_bar)-0.927*statistics.mean(s)), color='red', linestyle='dashed') 42 | axs[0].axhline((statistics.mean(x_bar)), color='blue') 43 | axs[0].set_title('x-bar Chart') 44 | axs[0].set(xlabel='Group', ylabel='Mean') 45 | 46 | # s chart 47 | axs[1].plot(s, linestyle='-', marker='o', color='black') 48 | axs[1].axhline((1.649*statistics.mean(s)), color='red', linestyle='dashed') 49 | axs[1].axhline((0.321*statistics.mean(s)), color='red', linestyle='dashed') 50 | axs[1].axhline((statistics.mean(s)), color='blue') 51 | axs[1].set_title('s Chart') 52 | axs[1].set(xlabel='Group', ylabel='Range') 53 | 54 | # Validate points out of control limits for x-bar chart 55 | i = 0 56 | control = True 57 | for group in x_bar: 58 | if group > statistics.mean(x_bar)+0.927*statistics.mean(s) or group < statistics.mean(x_bar)-0.927*statistics.mean(s): 59 | print('Group', i, 'out of mean control limits!') 60 | control = False 61 | i += 1 62 | if control == True: 63 | print('All points within control limits.') 64 | 65 | # Validate points out of control limits for s chart 66 | i = 0 67 | control = True 68 | for group in s: 69 | if group > 1.649*statistics.mean(s) or group < 0.321*statistics.mean(s): 70 | print('Group', i, 'out of standard deviation cotrol limits!') 71 | control = False 72 | i += 1 73 | if control == True: 74 | print('All points within control limits.') 75 | -------------------------------------------------------------------------------- /Queue Length Analysis/CustomersInQueueOverTime.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Queue Length Analysis/CustomersInQueueOverTime.PNG -------------------------------------------------------------------------------- /Queue Length Analysis/QueueLengthAnalysis.py: -------------------------------------------------------------------------------- 1 | ### QUEUE LENGTH ANALYSIS ### 2 | 3 | # Import libraries and dependencies 4 | import datetime 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | import seaborn as sns 8 | 9 | # Read csv file 10 | df = pd.read_csv('orders.csv') 11 | 12 | # Convert OrderCreatedLocalDateTime column to datetime type 13 | df['OrderCreatedLocalDateTime'] = pd.to_datetime(df['OrderCreatedLocalDateTime']) 14 | 15 | # Calculate TimeOutOfQueue column 16 | OrderCreatedORSTTime_column = [] 17 | for i in range(len(df)): 18 | OrderCreatedORSTTime_column.append(df.iloc[i]['OrderCreatedLocalDateTime'] + datetime.timedelta(seconds=int(df.iloc[i]['OrderProcessingTime']))) 19 | df['TimeOutOfQueue'] = OrderCreatedORSTTime_column 20 | 21 | # Calculate timeframe from input file on a minute basis 22 | # If the timespan from the source file is less than a day 23 | if (pd.to_datetime(df.iloc[-1,0]) - pd.to_datetime(df.iloc[0,0])).days == 0: 24 | timeframe = pd.date_range( 25 | df.iloc[0,0], 26 | periods=60*24, 27 | freq='1min' 28 | ) 29 | # If the timespan from the source file is more than a day 30 | else: 31 | timeframe = pd.date_range( 32 | df.iloc[0,0], 33 | periods=(pd.to_datetime(df.iloc[-1,0]) - pd.to_datetime(df.iloc[0,0])).days*60*24, 34 | freq='1min' 35 | ) 36 | 37 | # Create 'minutes' pandas data frame 38 | minutes_df = pd.DataFrame({'Business Date':timeframe}) 39 | 40 | # Add Date column 41 | minutes_df['Date'] = minutes_df['Business Date'].dt.date 42 | 43 | # Add Time column 44 | minutes_df['Time'] = minutes_df['Business Date'].dt.time 45 | 46 | # Initialize date ranges list 47 | date_ranges_list = [] 48 | 49 | # Loop through all the orders placed on the source file 50 | for i, row in df.iterrows(): 51 | 52 | # Get Start/End Time 53 | enter_t = row["OrderCreatedLocalDateTime"].replace(second=0, microsecond=0) 54 | exit_t = row["TimeOutOfQueue"].replace(second=0, microsecond=0) 55 | 56 | # Skip orders served within the same minute 57 | if enter_t == exit_t: 58 | continue 59 | 60 | # Get the date ranges in minutes between Enter/Exit 61 | date_range = pd.date_range( 62 | start=enter_t, 63 | end=exit_t, 64 | freq="1min", 65 | inclusive="right", 66 | ).tz_localize(None) 67 | 68 | # Append the list of date ranges 69 | date_ranges_list.append(pd.Series(date_range)) 70 | 71 | # Accumulate all date ranges 72 | all_date_ranges = pd.concat(date_ranges_list) 73 | 74 | # Count the number of occurences for a date/time 75 | orders_counts = all_date_ranges.value_counts().sort_index().to_frame(name="Orders") 76 | 77 | # Merge the orders count into the minutes table 78 | minutes_df = pd.merge( 79 | minutes_df, 80 | orders_counts, 81 | how="left", 82 | left_on="Business Date", 83 | right_index=True 84 | ) 85 | 86 | # Clear empty cells 87 | minutes_df.fillna(0, inplace=True) 88 | 89 | # Convert Orders column to int type 90 | minutes_df['Orders'] = minutes_df['Orders'].astype('int') 91 | 92 | # Create heatmap 93 | sns.heatmap(minutes_df.groupby([minutes_df['Business Date'].dt.date, minutes_df['Business Date'].dt.strftime('%H:00')]) 94 | ['Orders'].mean() 95 | .rename_axis(index=['Date','Hour']) 96 | .unstack(level=0), 97 | cmap='coolwarm' 98 | ) 99 | 100 | # Add title 101 | plt.title('Customers in Queue Over Time') 102 | 103 | # Show figure 104 | plt.show() 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python for Industrial Engineering 2 | This repository contains Python codes for industrial and system engineering applications. 3 | 4 | ## Requirements 5 | 6 | * [Python 3.7](https://www.python.org/) 7 | 8 | ## Libraries 9 | 10 | * [NumPy](https://numpy.org) 11 | * [pandas](https://pandas.pydata.org/) 12 | * [SciPy](https://www.scipy.org/) 13 | * [statistics](https://docs.python.org/3/library/statistics.html) 14 | * [matplotlib](https://matplotlib.org/3.2.1/index.html) 15 | * [seaborn](https://seaborn.pydata.org/) 16 | * [scikit-learn](https://scikit-learn.org/stable/) 17 | * [reliability](https://reliability.readthedocs.io/en/latest/index.html#) 18 | * [hvplot](https://hvplot.holoviz.org/) 19 | 20 | --- 21 | 22 | ### Roberto Salazar 23 | 24 | **Email:** rsalaza4@binghamton.edu | [LinkedIn](https://www.linkedin.com/in/roberto-salazar-reyna/) | [Medium](https://robertosalazarr.medium.com/) 25 | -------------------------------------------------------------------------------- /Six Sigma/Gage Run Chart/GageRunChart.py: -------------------------------------------------------------------------------- 1 | ### GAGE RUN CHART ### 2 | 3 | # Import libraries and dependencies 4 | import numpy as np 5 | import pandas as pd 6 | import seaborn as sns 7 | import matplotlib.pyplot as plt 8 | 9 | # Open Excel file 10 | data = pd.read_excel("msa_2_crossed.xlsx") 11 | 12 | # Sort values by Part and Operator 13 | data = data.sort_values(["Part","Operator"]) 14 | 15 | # Reset index 16 | data.reset_index(inplace=True, drop=True) 17 | 18 | # Display top rows 19 | data.head() 20 | 21 | # Add Replicate column 22 | data["Replicate"] = list(np.arange(1, data["Part"].value_counts()[1]+1))*(int(len(data["Part"])/data["Part"].value_counts()[1])) 23 | 24 | # Display top rows 25 | data.head() 26 | 27 | # Build seaborn replot 28 | g = sns.relplot( 29 | data=data, 30 | x="Replicate", 31 | y="Value", 32 | hue="Operator", 33 | style="Operator", 34 | col="Part", 35 | col_wrap=5, 36 | aspect=0.7 37 | ) 38 | 39 | # Add subtitle 40 | g.fig.suptitle("Gage Run Chart by Part, Operator", fontsize=16) 41 | 42 | # Add horizontal reference line, color, dashes style and axis labels 43 | g.map(plt.axhline, y=data["Value"].mean(), color=".7", dashes=(2, 1), zorder=0).set_axis_labels("Operator", "Value") 44 | -------------------------------------------------------------------------------- /Six Sigma/Gage Run Chart/msa_2_crossed.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsalaza4/Python-for-Industrial-Engineering/42f40926739282f3bc0bb905e8dd83aeedb5dbcc/Six Sigma/Gage Run Chart/msa_2_crossed.xlsx -------------------------------------------------------------------------------- /Six Sigma/Pareto Chart/Pareto Chart.py: -------------------------------------------------------------------------------- 1 | ### PARETO CHART ### 2 | 3 | # Import required libraries 4 | import pandas as pd 5 | import matplotlib.pyplot as plt 6 | from matplotlib.ticker import PercentFormatter 7 | 8 | # Build data frame 9 | df = pd.DataFrame({"error": [92, 83, 76, 59, 53, 27, 16, 9, 7, 4, 3, 1]}) 10 | 11 | # Reset the indexes 12 | df.index = ["Dose missed", "Wrong time", "Wrong drug", "Over dose", "Wrong patient", "Wrong route", "Wrong calculation", "Duplicated drugs", "Under dose", "Wrong IV rate", "Technique error", "Unauthorized drug"] 13 | 14 | # Sort values in descending order 15 | df = df.sort_values(by='error', ascending=False) 16 | 17 | # Add cumulative percentage column 18 | df["cum_percentage"] = round(df["error"].cumsum()/df["error"].sum()*100,2) 19 | 20 | # Set figure and axis 21 | fig, ax = plt.subplots(figsize=(22,10)) 22 | 23 | # Plot bars (i.e. frequencies) 24 | ax.bar(df.index, df["error"]) 25 | ax.set_title("Pareto Chart") 26 | ax.set_xlabel("Medication Error") 27 | ax.set_ylabel("Frequency"); 28 | 29 | # Second y axis (i.e. cumulative percentage) 30 | ax2 = ax.twinx() 31 | ax2.plot(df.index, df["cum_percentage"], color="red", marker="D", ms=7) 32 | ax2.axhline(80, color="orange", linestyle="dashed") 33 | ax2.yaxis.set_major_formatter(PercentFormatter()) 34 | ax2.set_ylabel("Cumulative Percentage"); 35 | 36 | # Display data frame 37 | df 38 | -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Cluster Sampling.py: -------------------------------------------------------------------------------- 1 | def cluster_sampling(df, number_of_clusters): 2 | 3 | try: 4 | # Divide the units into cluster of equal size 5 | df['cluster_id'] = np.repeat([range(1,number_of_clusters+1)],len(df)/number_of_clusters) 6 | 7 | # Create an empty list 8 | indexes = [] 9 | 10 | # Append the indexes from the clusters that meet the criteria 11 | # For this formula, clusters id must be an even number 12 | for i in range(0,len(df)): 13 | if df['cluster_id'].iloc[i]%2 == 0: 14 | indexes.append(i) 15 | cluster_sample = df.iloc[indexes] 16 | return(cluster_sample) 17 | 18 | except: 19 | print("The population cannot be divided into clusters of equal size!") 20 | 21 | # Obtain a cluster sample and save it in a new variable 22 | cluster_sample = cluster_sampling(df,5) 23 | 24 | # Save the sample mean in a separate variable 25 | cluster_mean = round(cluster_sample['measure'].mean(),3) 26 | 27 | # View sampled data frame 28 | cluster_sample 29 | -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Complete Code.py: -------------------------------------------------------------------------------- 1 | # PROBABILITY SAMPLING # 2 | 3 | # Import required libraries 4 | import numpy as np 5 | import pandas as pd 6 | 7 | # Set random seed 8 | np.random.seed(42) 9 | 10 | # Define total number of products 11 | number_of_products = 10 12 | 13 | # Create data dictionary 14 | data = {'product_id':np.arange(1, number_of_products+1).tolist(), 15 | 'measure':np.round(np.random.normal(loc=10, scale=0.5, size=number_of_products),3)} 16 | 17 | # Transform dictionary into a data frame 18 | df = pd.DataFrame(data) 19 | 20 | # Store the real mean in a separate variable 21 | real_mean = round(df['measure'].mean(),3) 22 | 23 | # View data frame 24 | df 25 | 26 | # SIMPLE RANDOM SAMPLING 27 | 28 | # Obtain simple random sample 29 | simple_random_sample = df.sample(n=4).sort_values(by='product_id') 30 | 31 | # Save the sample mean in a separate variable 32 | simple_random_mean = round(simple_random_sample['measure'].mean(),3) 33 | 34 | # View sampled data frame 35 | simple_random_sample 36 | 37 | # SYSTEMATIC SAMPLING 38 | 39 | # Define systematic sampling function 40 | def systematic_sampling(df, step): 41 | 42 | indexes = np.arange(0,len(df),step=step) 43 | systematic_sample = df.iloc[indexes] 44 | return systematic_sample 45 | 46 | # Obtain a systematic sample and save it in a new variable 47 | systematic_sample = systematic_sampling(df, 3) 48 | 49 | # Save the sample mean in a separate variable 50 | systematic_mean = round(systematic_sample['measure'].mean(),3) 51 | 52 | # View sampled data frame 53 | systematic_sample 54 | 55 | # CLUSTER SAMPLING 56 | 57 | def cluster_sampling(df, number_of_clusters): 58 | 59 | try: 60 | # Divide the units into cluster of equal size 61 | df['cluster_id'] = np.repeat([range(1,number_of_clusters+1)],len(df)/number_of_clusters) 62 | 63 | # Create an empty list 64 | indexes = [] 65 | 66 | # Append the indexes from the clusters that meet the criteria 67 | # For this formula, clusters id must be an even number 68 | for i in range(0,len(df)): 69 | if df['cluster_id'].iloc[i]%2 == 0: 70 | indexes.append(i) 71 | cluster_sample = df.iloc[indexes] 72 | return(cluster_sample) 73 | 74 | except: 75 | print("The population cannot be divided into clusters of equal size!") 76 | 77 | # Obtain a cluster sample and save it in a new variable 78 | cluster_sample = cluster_sampling(df,5) 79 | 80 | # Save the sample mean in a separate variable 81 | cluster_mean = round(cluster_sample['measure'].mean(),3) 82 | 83 | # View sampled data frame 84 | cluster_sample 85 | 86 | # STRATIFIED RANDOM SAMPLING 87 | 88 | # Create data dictionary 89 | data = {'product_id':np.arange(1, number_of_products+1).tolist(), 90 | 'product_strata':np.repeat([1,2], number_of_products/2).tolist(), 91 | 'measure':np.round(np.random.normal(loc=10, scale=0.5, size=number_of_products),3)} 92 | 93 | # Transform dictionary into a data frame 94 | df = pd.DataFrame(data) 95 | 96 | # View data frame 97 | df 98 | 99 | # # Import StratifiedShuffleSplit 100 | from sklearn.model_selection import StratifiedShuffleSplit 101 | 102 | # Set the split criteria 103 | split = StratifiedShuffleSplit(n_splits=1, test_size=4) 104 | 105 | # Perform data frame split 106 | for x, y in split.split(df, df['product_strata']): 107 | stratified_random_sample = df.iloc[y].sort_values(by='product_id') 108 | 109 | # View sampled data frame 110 | stratified_random_sample 111 | 112 | # Obtain the sample mean for each group 113 | stratified_random_sample.groupby('product_strata').mean().drop(['product_id'],axis=1) 114 | 115 | # MEAURE MEAN COMPARISON PER SAMPLING METHOD 116 | 117 | # Create a dictionary with the mean outcomes for each sampling method and the real mean 118 | outcomes = {'sample_mean':[simple_random_mean,systematic_mean,cluster_mean], 119 | 'real_mean':real_mean} 120 | 121 | # Transform dictionary into a data frame 122 | outcomes = pd.DataFrame(outcomes, index=['Simple Random Sampling','Systematic Sampling','Cluster Sampling']) 123 | 124 | # Add a value corresponding to the absolute error 125 | outcomes['abs_error'] = abs(outcomes['real_mean'] - outcomes['sample_mean']) 126 | 127 | # Sort data frame by absolute error 128 | outcomes.sort_values(by='abs_error') 129 | -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Measure Mean Comparison per Sampling Method.py: -------------------------------------------------------------------------------- 1 | # Create a dictionary with the mean outcomes for each sampling method and the real mean 2 | outcomes = {'sample_mean':[simple_random_mean,systematic_mean,cluster_mean], 3 | 'real_mean':real_mean} 4 | 5 | # Transform dictionary into a data frame 6 | outcomes = pd.DataFrame(outcomes, index=['Simple Random Sampling','Systematic Sampling','Cluster Sampling']) 7 | 8 | # Add a value corresponding to the absolute error 9 | outcomes['abs_error'] = abs(outcomes['real_mean'] - outcomes['sample_mean']) 10 | 11 | # Sort data frame by absolute error 12 | outcomes.sort_values(by='abs_error') -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Products Data Frame with Stata.py: -------------------------------------------------------------------------------- 1 | # Create data dictionary 2 | data = {'product_id':np.arange(1, number_of_products+1).tolist(), 3 | 'product_strata':np.repeat([1,2], number_of_products/2).tolist(), 4 | 'measure':np.round(np.random.normal(loc=10, scale=0.5, size=number_of_products),3)} 5 | 6 | # Transform dictionary into a data frame 7 | df = pd.DataFrame(data) 8 | 9 | # View data frame 10 | df -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Products Data Frame.py: -------------------------------------------------------------------------------- 1 | # Import required libraries 2 | import numpy as np 3 | import pandas as pd 4 | 5 | # Set random seed 6 | np.random.seed(42) 7 | 8 | # Define total number of products 9 | number_of_products = 10 10 | 11 | # Create data dictionary 12 | data = {'product_id':np.arange(1, number_of_products+1).tolist(), 13 | 'measure':np.round(np.random.normal(loc=10, scale=0.5, size=number_of_products),3)} 14 | 15 | # Transform dictionary into a data frame 16 | df = pd.DataFrame(data) 17 | 18 | # Store the real mean in a separate variable 19 | real_mean = round(df['measure'].mean(),3) 20 | 21 | # View data frame 22 | df -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Simple Random Sampling.py: -------------------------------------------------------------------------------- 1 | # Obtain simple random sample 2 | simple_random_sample = df.sample(n=4).sort_values(by='product_id') 3 | 4 | # Save the sample mean in a separate variable 5 | simple_random_mean = round(simple_random_sample['measure'].mean(),3) 6 | 7 | # View sampled data frame 8 | simple_random_sample -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Stratified Random Sampling.py: -------------------------------------------------------------------------------- 1 | # Import StratifiedShuffleSplit 2 | from sklearn.model_selection import StratifiedShuffleSplit 3 | 4 | # Set the split criteria 5 | split = StratifiedShuffleSplit(n_splits=1, test_size=4) 6 | 7 | # Perform data frame split 8 | for x, y in split.split(df, df['product_strata']): 9 | stratified_random_sample = df.iloc[y].sort_values(by='product_id') 10 | 11 | # View sampled data frame 12 | stratified_random_sample 13 | 14 | # Obtain the sample mean for each group 15 | stratified_random_sample.groupby('product_strata').mean().drop(['product_id'],axis=1) -------------------------------------------------------------------------------- /Six Sigma/Probability Sampling/Systematic Sampling.py: -------------------------------------------------------------------------------- 1 | # Define systematic sampling function 2 | def systematic_sampling(df, step): 3 | 4 | indexes = np.arange(0,len(df),step=step) 5 | systematic_sample = df.iloc[indexes] 6 | return systematic_sample 7 | 8 | # Obtain a systematic sample and save it in a new variable 9 | systematic_sample = systematic_sampling(df, 3) 10 | 11 | # Save the sample mean in a separate variable 12 | systematic_mean = round(systematic_sample['measure'].mean(),3) 13 | 14 | # View sampled data frame 15 | systematic_sample 16 | -------------------------------------------------------------------------------- /Six Sigma/Process Capability Analysis/Process Capability Analysis.py: -------------------------------------------------------------------------------- 1 | ### PROCESS CAPABILITY ANALYSIS ### 2 | 3 | # Import required libraries 4 | import pandas as pd 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | import seaborn as sns 8 | from scipy.stats import norm 9 | 10 | # Set specification limits 11 | target = 5 12 | LSL = 3 13 | USL = 7 14 | 15 | # Generate normally distributed data points 16 | data = np.random.normal(loc=target,scale=1,size=100) 17 | 18 | # Generate probability density function 19 | x = np.linspace(min(data), max(data), 1000) 20 | y = norm.pdf(x, loc=5, scale=1) 21 | 22 | # Plot histogram for data along with probability density functions and specification limits 23 | plt.figure(figsize=(15,10)) 24 | plt.hist(data, color="lightgrey", edgecolor="black", density=True) 25 | sns.kdeplot(data, color="blue", label="Density ST") 26 | plt.plot(x, y, linestyle="--", color="black", label="Theorethical Density ST") 27 | plt.axvline(LSL, linestyle="--", color="red", label="LSL") 28 | plt.axvline(USL, linestyle="--", color="orange", label="USL") 29 | plt.axvline(target, linestyle="--", color="green", label="Target") 30 | plt.title('Process Capability Analysis') 31 | plt.xlabel("Measure") 32 | plt.ylabel("") 33 | plt.yticks([]) 34 | plt.legend() 35 | plt.show() 36 | 37 | # Calculate Cp 38 | Cp = (USL-LSL)/(6*np.std(data)) 39 | 40 | # Calculate Cpk 41 | Cpk = min((USL-data.mean())/(3*data.std()), (data.mean()-LSL)/(3*data.std())) 42 | 43 | # Calculate z-value 44 | z = min((USL-data.mean())/(data.std()), (data.mean()-LSL)/(data.std())) 45 | 46 | # Get data summary statistics 47 | num_samples = len(data) 48 | sample_mean = data.mean() 49 | sample_std = data.std() 50 | sample_max = data.max() 51 | sample_min = data.min() 52 | sample_median = np.median(data) 53 | 54 | # Get percentage of data points outside of specification limits 55 | pct_below_LSL = len(data[data < LSL])/len(data)*100 56 | pct_above_USL = len(data[data > USL])/len(data)*100 57 | 58 | # Write .txt file with results 59 | with open('process_results.txt', "w") as results: 60 | results.write("PROCESS CAPABILITY ANALYSIS\n") 61 | 62 | results.write("-----------------------------------\n") 63 | results.write(f"Specifications\n") 64 | results.write(f"\nTaget: {target}\n") 65 | results.write(f"LSL: {LSL}\n") 66 | results.write(f"USL: {USL}\n") 67 | 68 | results.write("-----------------------------------\n") 69 | results.write(f"Indices\n") 70 | results.write(f"\nCp: {round(Cp,2)}\n") 71 | results.write(f"Cpk: {round(Cpk,2)}\n") 72 | results.write(f"z: {round(z,2)}\n") 73 | 74 | results.write("-----------------------------------\n") 75 | results.write(f"Summary Statistics\n") 76 | results.write(f"\nNumber of samples: {round(num_samples,2)}\n") 77 | results.write(f"Sample mean: {round(sample_mean,2)}\n") 78 | results.write(f"Sample std: {round(sample_std,2)}\n") 79 | results.write(f"Sample max: {round(sample_max,2)}\n") 80 | results.write(f"Sample min: {round(sample_min,2)}\n") 81 | results.write(f"Sample median: {round(sample_median,2)}\n") 82 | 83 | results.write(f"Percentage of data points below LSL: {round(pct_below_LSL,2)}%\n") 84 | results.write(f"Percentage of data points above USL: {round(pct_above_USL,2)}%\n") 85 | -------------------------------------------------------------------------------- /Total Productive Maintenance/Reliability Analysis - Serial Systems.py: -------------------------------------------------------------------------------- 1 | ### RELIABILITY ANALYSIS - SERIAL SYSTEMS ### 2 | 3 | # Import required libraries 4 | import path 5 | import numpy as np 6 | import pandas as pd 7 | import reliability 8 | from reliability.Fitters import Fit_Everything 9 | 10 | # Load data 11 | Path = "failure_times.csv" 12 | df = pd.read_csv(Path) 13 | df.head() 14 | 15 | # Get summary statistics 16 | df.describe() 17 | 18 | # Get the number of components in series to be analyzed 19 | number_of_components = len(df.columns) 20 | print(f"Number of components to be analyzed: {number_of_components}\n") 21 | 22 | # Create a list to store the components individual reliabilities 23 | reliabilities = [] 24 | 25 | # Create a list to store the reliabilities times of interest for each component 26 | times = [] 27 | 28 | # Iterate over each component (i.e. column from the dataframe) and determine its realibility at time 't' 29 | for i in range(len(df.columns)): 30 | 31 | # Data Validation 32 | # Validate that there are no negative failure times 33 | error = False 34 | for data_point in df.iloc[:,i]: 35 | if data_point < 0: 36 | error = True 37 | 38 | # Print error message if at least one negative failure time is present for the given component 39 | if error == True: 40 | print(f"Error: {df.columns[i]} has at least one negative failure time. Negative values are not accepted. The analysis cannot move forward.") 41 | 42 | # Perform reliability analysis 43 | if error == False: 44 | 45 | # Print component being analyzed 46 | print(f"Reliability analysis {df.columns[i]}:\n") 47 | 48 | # Fit all probability distributions available from 'reliability' library 49 | output = Fit_Everything(failures=df.iloc[:,i].dropna().tolist(), show_probability_plot=False, show_PP_plot=False) 50 | 51 | # Define the probability distribution that best fitted the failure times for the given component 52 | output.best_distribution.plot() 53 | 54 | # Define the desired time of failure 't' 55 | t = float(input("Type in the desired time before failure: ")) 56 | 57 | # Time 't' validation 58 | # Validate that no negative time was inserted 59 | while t<0 : 60 | print("Error: negative value insterted. Please insert a positive value greater than 0:") 61 | t = float(input("Type in the desired time before failure: ")) 62 | 63 | # If the best fitted distribution was Beta with 2 parameters, the time 't' cannot be greater than 1 64 | if output.best_distribution_name == 'Beta_2P': 65 | while t>1: 66 | print("Error: for Beta distributions the range of the values must be within 0 and 1.") 67 | t = float(input("Type in the desired time before failure: ")) 68 | 69 | # Append failure time in times list 70 | times.append(t) 71 | 72 | # Get component reliability 73 | component_reliability = output.best_distribution.SF(t) 74 | 75 | # Append component reliability in reliabilities list 76 | reliabilities.append(component_reliability) 77 | 78 | print("\n--------------------------------------------------------------------------------------------------------------------\n") 79 | 80 | # Calculate the system reliability 81 | system_reliability = np.prod(reliabilities) 82 | 83 | # Print system reliability result 84 | print(f"RESULTS:\n\nSystem Reliability for {len(df.columns)} components in series: {round(system_reliability*100,2)}%") 85 | print("") 86 | 87 | # Print system components individual reliabilities 88 | for i in range(len(reliabilities)): 89 | print(f"Reliability of {df.columns[i]} at time {times[i]}: {round(reliabilities[i]*100,2)}%") 90 | -------------------------------------------------------------------------------- /Total Productive Maintenance/Reliability Analysis - Single Component.py: -------------------------------------------------------------------------------- 1 | ### RELIABILITY ANALYSIS - SINGLE COMPONENT ### 2 | 3 | # Import required libraries 4 | import matplotlib.pyplot as plt 5 | %matplotlib inline 6 | import scipy 7 | import scipy.stats 8 | import warnings 9 | warnings.filterwarnings("ignore") 10 | 11 | # Step 1: Obtain failure times 12 | 13 | # For the following example, 1000 random points will be generated from a known distribution (i.e. beta distribution) 14 | # However, most likely the distribution following the data will be unknown 15 | 16 | # Specify the sample size 17 | size = 1000 18 | x = scipy.arange(size) 19 | 20 | # Generate the data 21 | y = scipy.stats.beta.rvs(6, 2, size=size, random_state=40)*50 # real data from unknown distribution would be here 22 | 23 | # Step 1: Identify the distribution that best fits the data 24 | 25 | # Build histogram 26 | plt.figure(figsize=(20,10)) 27 | h = plt.hist(y, bins=range(51)) 28 | 29 | # List the distributions to be fitted 30 | dist_names = ['alpha', 'beta', 'expon', 'gamma', 'norm', 'rayleigh'] 31 | 32 | # Fit the distributions to the data and plot their probability density functions 33 | for dist_name in dist_names: 34 | dist = getattr(scipy.stats, dist_name) 35 | param = dist.fit(y) 36 | pdf_fitted = dist.pdf(x, *param[:-2], loc=param[-2], scale=param[-1])*size 37 | plt.plot(pdf_fitted, label=dist_name) 38 | plt.xlim(0,50) 39 | plt.legend(loc='upper left') 40 | plt.title("Failure Times Distribution") 41 | plt.xlabel("Failure Time") 42 | plt.ylabel("Frequency") 43 | plt.show() 44 | 45 | # Step 3: Obtain the distribution parameters 46 | 47 | # Define the distribution with the best fit 48 | dist = getattr(scipy.stats, 'beta') 49 | 50 | # Fit the distribution to the data 51 | param = dist.fit(y) 52 | 53 | # Distribution parameters (e.g. 'a', 'b' for beta distribution) 54 | args = param[:-2] 55 | 56 | # Location parameter 57 | loc = param[-2] 58 | 59 | # Scale parameter 60 | scale = param[-1] 61 | 62 | # Step 4: Obtain probabilities 63 | 64 | # Probability of failure before time t 65 | print("Probability of failure before time 40:", round(scipy.stats.beta.cdf(40, *args, loc=loc, scale=scale)*100,2),"%") 66 | 67 | # Reliability estimation after time t 68 | print("Reliability Estimation at time 40:", round(scipy.stats.beta.sf(40, *args, loc=loc, scale=scale)*100,2),"%") 69 | -------------------------------------------------------------------------------- /Total Productive Maintenance/failure_times.csv: -------------------------------------------------------------------------------- 1 | machine 1,machine 2,machine 3 2 | 18.48356355,20.97390261,20.28037226 3 | 20.70819228,23.19203503,18.34168567 4 | 18.20676379,17.62221231,12.16723531 5 | 21.80164819,15.9528419,13.65075827 6 | 23.97154032,17.5690592,14.13218551 7 | 17.52800729,20.61650708,11.27192972 8 | 21.41705036,19.18223783,20.1554724 9 | 19.85743239,19.0247768,13.3237784 10 | 25.45480665,21.27874065,13.78223631 11 | 16.06167506,15.71199865,14.93287311 12 | 23.03282518,20.25527326,12.03248625 13 | 18.43568976,24.09464223,18.45001037 14 | 17.83646239,20.56675854,10.93391381 15 | 17.79559965,20.15000014,8.123765564 16 | 18.49444483,17.55391256,12.20738883 17 | 21.44789932,22.55383317,17.69901072 18 | 19.5888964,15.83504472,15.30969595 19 | 22.18284319,19.55561786,16.14692413 20 | 14.02042269,19.5943725,13.85289403 21 | 16.66262369,14.89626859,14.63298595 22 | 16.62769186,18.75793044,15.75983963 23 | 20.07242812,20.25900325,6.075965921 24 | 22.21985694,21.18885814,18.5085764 25 | 16.95554803,24.16623535,12.98502948 26 | 26.16580338,20.31995409,7.644450236 27 | 17.35643761,19.95895658,17.91121085 28 | 21.87482665,15.15730903,11.17318687 29 | 17.77309649,18.71450396,8.830033594 30 | 16.11474916,20.4485662,14.07693953 31 | 24.39268533,22.54443534,17.80897495 32 | 18.31699211,20.78746224,12.87682108 33 | 16.43660613,22.81365772,18.04072569 34 | 24.16935602,20.42947515,16.35466625 35 | 24.07630312,22.01603826,16.4333104 36 | 25.48886602,23.39467447,22.03879956 37 | 15.35049909,19.86485697,20.74539191 38 | 19.77491746,25.93247976,21.38314613 39 | 18.67636977,18.71419109,16.76260281 40 | 24.91490147,19.31921451,13.26941409 41 | 17.85131498,16.71239305,11.96235112 42 | 20.44449243,24.11486744,18.11408411 43 | 25.09181685,18.56311959,10.53553775 44 | 17.30319734,14.61501287,16.63967442 45 | 19.01981433,17.97735541,10.56281072 46 | 24.86114561,23.77027031,21.29728798 47 | 19.05505164,20.46465185,15.28879359 48 | 15.69649983,15.61618239,16.06843599 49 | 23.60024061,19.06193154,15.78165061 50 | 14.40578779,20.4232276,16.48642183 51 | 20.19797434,21.45251006,10.29089566 52 | 20.36294533,23.26614012,13.84637367 53 | 26.50828053,17.13948771,19.03852497 54 | 24.76473336,17.77561889,10.43388717 55 | 24.65867098,17.52435806,21.16790616 56 | 23.64998491,20.62976498,17.1077891 57 | 17.77130579,20.29928915,16.31924327 58 | 22.06399343,17.22655759,24.1057651 59 | 20.80682194,19.70138747,17.60082835 60 | 19.70459645,18.94662288,12.34561289 61 | 21.18036699,19.82279233,10.49612694 62 | 21.55385468,20.05526327,16.75955797 63 | 15.2705366,18.41982967,9.344049473 64 | 26.75860238,22.8225523,15.10204159 65 | 15.08232988,22.51777308,13.15569411 66 | 16.15089976,23.56069119,16.66908208 67 | 24.79156036,16.13872957,19.14577915 68 | 21.08100327,18.95822522,13.52171414 69 | 21.36342735,21.26365386,18.40080559 70 | 22.24810983,20.89226124,20.47912456 71 | 24.04876202,21.94696985,16.00927995 72 | 18.37535909,18.84464986,17.46256709 73 | 21.30346107,12.93475619,12.70223308 74 | 17.20169918,16.29731438,14.77068566 75 | 21.28513627,23.85417736,16.11676182 76 | 14.69022732,22.0057078,11.03681751 77 | 19.99494335,17.74397248,20.19693364 78 | 22.91169083,20.77998147,22.22439884 79 | 17.29082512,21.07576758,12.82467139 80 | 20.4840148,21.75597432,8.286265608 81 | 18.32226952,24.98393738,10.65099115 82 | 21.36318319,21.32885067,15.27698588 83 | 20.6552918,24.39042924, 84 | 22.11993313,23.94908554, 85 | 20.9982867,21.87267744, 86 | 25.17314124,18.64165701, 87 | 22.93226184,20.24424312, 88 | 18.5781615,20.16135397, 89 | 22.26943167,21.18676876, 90 | 15.67973194,25.54150821, 91 | 17.45536888,18.42940618, 92 | 21.1652411,17.99569316, 93 | 16.58397719,20.49645474, 94 | 19.83369401,21.06695203, 95 | 21.6435946,26.12622617, 96 | 25.10414717,16.36330457, 97 | 19.30213481,14.74480171, 98 | 23.53974149,24.51861753, 99 | 21.96221913,25.89588476, 100 | 16.64882925,24.19769889, 101 | 15.41797559,18.496006, --------------------------------------------------------------------------------