淺談統計製程管制(SPC)&小小DIY
Statistic Process Control (SPC)
因為工作是個製程仔,所以每天最常看到的就是這個叫SPC的東西…
SPC中文叫統計製程管制,當然叫SPC比較簡潔乾脆,主要用途是將要監控的參數標準化後,依照時間順序呈現在圖表上,當參數發生異常變動時,可由SPC chart觀察出來,
當工程師發現管制圖異常,就要趕緊介入查看參數的異常原因。(不小心就說出我的工作日常QQ)
管制圖?怎麼管?
SPC的歷史就不贅述了,餵狗第一條就有了~想說的是如果你要監控理應穩定的參數隨著時間的表現,就可以用SPC來查看這項參數的變動情況。就好比下面這張圖:
譬如今天我們要生產一支iphone,供應商需要製造它螢幕上的玻璃,那麼在生產的過程中,為了確保玻璃面板的厚度、長度或寬度的參數不會異常,都會對這些參數訂一個目標值(Target),想盡辦法將生產出來的貨品,其量測到的參數越接近target越好。將這些在生產中量測到的這些貨品參數對target值標準化後,以時間軸繪製出來,就能得到上圖。
而這張圖畫出來,就可以發現他之所以能「管」,便是建立在中央極限定理上。一道穩定的製程,在製造手續、製造機器都受到穩定控制的情況下,產品的參數便如同隨機變數,在經過收集多筆資料後,應當符合常態分布,上面講到的Target就是此道生產流程的目標值,理應同為數據的平均值。而SPC chart則是將這多筆的資料以時間這個維度做展開。如果我們將上圖的資料繪成組體圖表示的話,你會看到:
既然是數據組會呈現常態分佈,那麼必然會有機率出現資料點在平均值的三倍標準差之外,就像圖一的那些紅點所標示的。也因此,通常我們會以此製程的3倍標準差,當作管制圖的管制界線(Control limit)。當生產中量測到的參數超出Control limit,就會說這批貨的此項參數Out-Of-Control (OOC),而將此批貨的生產視為出現異常。
動手做-產生一組模擬的SPC table
先別一次說太多SPC的東西,太無聊了(工作日常誰想知道XD)
來聊聊上面這些圖怎麼畫吧~畢竟別的沒有,用python畫圖我還算蠻精的(?!)
不過在畫圖之前,總得先有data吧?所以這篇先來講準備資料的部分。
我們根據SPC的理論,需要先產生一組以常態分配產生的亂數,最後搞到dataframe裡面,形成一組SPC 資料表。
所以先把python 資料科學的兩寶(pandas, numpy),還有其他起手式也import進來。
接著,我先把如何產生這張SPC table的一些參數,用 SPCDataConfig
這個class定義起來,之後可以讓其他控制項讀取這個config class來決定要做的事情。
這裡面,資料是由numpy的random.randn
函數以 \( (\mu, \sigma)=(0, 1) \) 常態分布所產生的。而其他的一些基本input參數如下:
start_date_str
,end_date_str
: 文字格式的資料起始/最後時間,格式須遵守’YYYY/mm/dd HH:MM:SS.f’ctype
: SPC chart要監控的指標統計參數,本文以每筆資料均為該批貨物量測到的參數之平均值為例。cnt
: 資料總筆數data_order
: 資料的數量級。0.1, 0.01, 0.001, …依此類推cl
: Control limit值,user根據data_order做相應的輸入。target
: 指標的target,本例中為0。
同時,因為start_date_str
, end_date_str
的輸入格式,資料表中的時間格式也將遵循同樣格式,故在轉化為datetime型態的變數時,使用dt_fmt定義相同格式,作為格式引數。
最後,為了在開始~結束的時間內隨機佈下時間撮點,所以再將datetime格式的時間參數數值化。
import pandas as pd
import numpy as np
import os, sys, time, random
from datetime import datetime
class SPCDataConfig:
def __init__(self, start_date_str, end_date_str, ctype, cnt, data_order, cl, target):
self.start_date_str = start_date_str
self.end_date_str = end_date_str
self.ctype = ctype
self.cnt = cnt
self.data_order = data_order
self.cl = cl
self.target = target
self.dt_fmt = '%Y/%m/%d %H:%M:%S.%f'
self.start_date_dt = datetime.strptime(self.start_date_str, self.dt_fmt)
self.end_date_dt = datetime.strptime(self.end_date_str, self.dt_fmt)
self.start_date_num = int(self.start_date_dt.timestamp())
self.end_date_num = int(self.end_date_dt.timestamp())
接著,我將create一系列的class來作為SPC table從無到有的各個元件,
資料時序隨機產生器
在DataTimeGenerator
中,我用前一節提到的起始/最後時間數值化的結果,產生指定數量的隨機時間數值,並依遞增排序。
class DataTimeGenerator:
def __init__(self, cfg):
self.cfg = cfg
def gen(self):
time_series_num = np.random.randint(self.cfg.start_date_num, self.cfg.end_date_num, self.cfg.cnt)
time_series_num = np.sort(time_series_num)
time_series = [datetime.fromtimestamp(x) for x in time_series_num]
return time_series
指標數值隨機產生器
在RawDataGenerator
中,同樣以隨機值產生SPC所要監控的指標,並搭配data_order
控制指標的數量級。
class RawDataGenerator:
def __init__(self, cfg):
self.cfg = cfg
def gen(self):
values = np.random.randn(self.cfg.cnt) * self.cfg.data_order
return values
Control Limit / Target 產生器
這兩個class,簡單地以np.ones()
函數,產生指定control limit 或target的一維陣列。
class ControlLimitGenerator:
def __init__(self, cfg):
self.cfg = cfg
def gen(self):
CL = self.cfg.cl
UCL = np.ones(self.cfg.cnt) * CL
LCL = np.ones(self.cfg.cnt) * -CL
return UCL, LCL
class TargetGenerator:
def __init__(self, cfg):
self.cfg = cfg
def gen(self):
TARGET = np.ones(self.cfg.cnt) * self.cfg.target
return TARGET
貨品編號產生器
構想是想讓SPC chart裡每一批接受監控的貨物,各自有一個名稱,也就是貨號。一批貨中,通常含有數量不一的產品,通常基於產能考量,只會抽檢該批貨中的其中一片去監控製程穩定度。所以這邊LotIdGenerator
會產生每一個時間點,受量測的貨物貨號,與產品編號。至於編號格式嘛…
而這批貨底下可能也會有為數不同的產品,所以再以貨號為基底,小數點後兩碼為產品編號:
class LotIdGenerator:
def __init__(self, cfg):
self.cfg = cfg
def gen(self):
LETTER = ['W', 'X', 'Y', 'Z']
lotid = ['A21{0:s}{0:s}{1:03d}'.format(LETTER[random.randint(0, 3)], random.randint(1, 999)) for x in range(self.cfg.cnt)]
itemid = ['{0:02d}'.format(random.randint(1, 25)) for x in range(self.cfg.cnt)]
lot_info = pd.DataFrame({
'Lot': lotid, 'Item2': itemid
}, columns=['Lot', 'Item2'])
lot_info["Lot_ID"] = lot_info['Lot']
lot_info["Item_ID"] = lot_info['Lot'].str.cat(lot_info['Item2'].values, sep='.')
return lot_info
結合所有產生器
定義完所有產生器,最後再用一個整合的產生器,去驅動各個產生器產生數列,並組合成dataframe輸出。
接著就可以定義好你想要的SPC data參數後,匯入Config物件,
再call SPCDataGenerator
來產生SPC table
class SPCDataGenerator:
def __init__(self, cfg):
self.cfg = cfg
def gen(self):
TS = DataTimeGenerator(self.cfg)
RD = RawDataGenerator(self.cfg)
CL = ControlLimitGenerator(self.cfg)
TG = TargetGenerator(self.cfg)
LI = LotIdGenerator(self.cfg)
time_series = TS.gen()
values = RD.gen()
UCL, LCL = CL.gen()
target = TG.gen()
lot_info = LI.gen()
self.df = pd.DataFrame({
'Data_Time': time_series,
'Lot_ID': lot_info['Lot_ID'].values,
'Item_ID': lot_info['Item_ID'].values,
'Value': values,
'Target': target,
'UCL': UCL,
'LCL': LCL,
})
self.df = self.df[['Data_Time', 'Lot_ID', 'Item_ID', 'Value', 'Target', 'UCL', 'LCL']]
return self.df
# define configurations:
data_cnt = 100
chart_type = 'MEAN'
spc_cfg = SPCDataConfig('2020/11/17 00:00:00.0', '2020/12/18 00:00:00.0', ctype=chart_type, cnt=data_cnt, data_order=0.1, cl=0.3, target=0)
SPC = SPCDataGenerator(spc_cfg)
spc_df = SPC.gen()
於是可以得到下方的輸出結果~
要把pandas 的dataframe給格式化print出來,可以使用tabulate套件
from tabulate import tabulate print(tabulate(spc_df.head(), headers='keys', tablefmt='psql'))
+----+---------+---------------------+----------+------------+-------------+----------+-------+-------+-------+-------+
| | index | Data_Time | Lot_ID | Item_ID | Value | Target | USL | UCL | LCL | LSL |
|----+---------+---------------------+----------+------------+-------------+----------+-------+-------+-------+-------|
| 0 | 0 | 2020-11-17 07:24:41 | A21W641 | A21W641.10 | 0.0935012 | 0 | 0.3 | 0.2 | -0.2 | -0.3 |
| 1 | 1 | 2020-11-17 07:52:37 | A21Z267 | A21Z267.06 | -0.140788 | 0 | 0.3 | 0.2 | -0.2 | -0.3 |
| 2 | 2 | 2020-11-17 10:33:47 | A21W999 | A21W999.06 | -0.00277936 | 0 | 0.3 | 0.2 | -0.2 | -0.3 |
| 3 | 3 | 2020-11-17 15:18:14 | A21Z412 | A21Z412.05 | -0.196211 | 0 | 0.3 | 0.2 | -0.2 | -0.3 |
| 4 | 4 | 2020-11-17 18:54:46 | A21W311 | A21W311.16 | 0.116503 | 0 | 0.3 | 0.2 | -0.2 | -0.3 |
+----+---------+---------------------+----------+------------+-------------+----------+-------+-------+-------+-------+
產生資料的部分就到這裡,畫圖的部份富堅到下次吧!
如果這篇文章對你有所幫助,就幫按個讚吧!
留言