データ駆動型の問題解決フレームワークで品質を飛躍的に向上
シックスシグマ(Six Sigma)は、1980年代にモトローラ社で開発された品質管理手法で、プロセスの変動を統計的に管理し、欠陥を100万機会あたり3.4個(3.4 DPMO)以下に抑えることを目標とします。
シグマレベルは、プロセスの平均から規格限界までの距離を標準偏差(σ)の倍数で表したものです。レベルが高いほど品質が優れています。
| シグマレベル | DPMO | 歩留まり | 品質評価 |
|---|---|---|---|
| 2σ | 308,537 | 69.1% | 非常に悪い |
| 3σ | 66,807 | 93.3% | 悪い |
| 4σ | 6,210 | 99.38% | 普通 |
| 5σ | 233 | 99.977% | 良好 |
| 6σ | 3.4 | 99.99966% | 世界クラス |
欠陥データからDPMOとシグマレベルを計算します。
# ===================================
# Example 1: DPMO (Defects Per Million Opportunities) Calculation
# ===================================
import numpy as np
import pandas as pd
from scipy import stats
import matplotlib.pyplot as plt
from typing import Dict, Tuple
class SigmaLevelCalculator:
"""シグマレベルとDPMOの計算クラス
製造プロセスの品質レベルを評価するための指標を計算。
Parameters:
-----------
shift : float
1.5シグマシフト(長期変動を考慮)
デフォルト: 1.5(モトローラの経験則)
"""
def __init__(self, shift: float = 1.5):
self.shift = shift
def calculate_dpmo(self, defects: int, units: int,
opportunities: int) -> float:
"""DPMOを計算
Parameters:
-----------
defects : int
検出された欠陥数
units : int
検査単位数
opportunities : int
1単位あたりの欠陥機会数
Returns:
--------
dpmo : float
DPMO(100万機会あたりの欠陥数)
"""
total_opportunities = units * opportunities
dpo = defects / total_opportunities # Defects Per Opportunity
dpmo = dpo * 1_000_000
return dpmo
def dpmo_to_sigma(self, dpmo: float,
include_shift: bool = True) -> float:
"""DPMOからシグマレベルを計算
Parameters:
-----------
dpmo : float
DPMO値
include_shift : bool
1.5シグマシフトを考慮するか
Returns:
--------
sigma_level : float
シグマレベル
"""
# DPMOを歩留まり(不良率の補数)に変換
yield_rate = 1 - (dpmo / 1_000_000)
# 正規分布の累積分布関数の逆関数でZ値を計算
# 両側規格の場合、片側のDPMOを使用
z_score = stats.norm.ppf(yield_rate)
# 1.5シグマシフトを考慮
if include_shift:
sigma_level = z_score + self.shift
else:
sigma_level = z_score
return sigma_level
def sigma_to_dpmo(self, sigma_level: float,
include_shift: bool = True) -> float:
"""シグマレベルからDPMOを計算
Parameters:
-----------
sigma_level : float
シグマレベル
include_shift : bool
1.5シグマシフトを考慮するか
Returns:
--------
dpmo : float
DPMO値
"""
# 1.5シグマシフトを考慮
if include_shift:
z_score = sigma_level - self.shift
else:
z_score = sigma_level
# 正規分布の累積分布関数でDPMOを計算
defect_rate = 1 - stats.norm.cdf(z_score)
dpmo = defect_rate * 1_000_000
return dpmo
def calculate_process_sigma(self, data: np.ndarray,
LSL: float, USL: float) -> Dict:
"""プロセスデータから直接シグマレベルを計算
Parameters:
-----------
data : np.ndarray
プロセスデータ
LSL : float
下方規格限界
USL : float
上方規格限界
Returns:
--------
results : dict
シグマレベル、DPMO、プロセス能力指数などを含む辞書
"""
process_mean = data.mean()
process_std = data.std(ddof=1)
# Cpk計算
Cpu = (USL - process_mean) / (3 * process_std)
Cpl = (process_mean - LSL) / (3 * process_std)
Cpk = min(Cpu, Cpl)
# シグマレベル(短期: Cpkベース)
sigma_level_st = 3 * Cpk
# 実際の不良率から長期シグマレベルを計算
defects = np.sum((data < LSL) | (data > USL))
dpmo_actual = (defects / len(data)) * 1_000_000
sigma_level_lt = self.dpmo_to_sigma(dpmo_actual, include_shift=False)
results = {
'process_mean': process_mean,
'process_std': process_std,
'Cpk': Cpk,
'sigma_level_st': sigma_level_st,
'sigma_level_lt': sigma_level_lt,
'dpmo_actual': dpmo_actual,
'defects': defects,
'total_samples': len(data)
}
return results
def plot_sigma_table(self):
"""シグマレベル対応表の可視化"""
sigma_levels = np.arange(1, 7, 0.1)
dpmo_values = [self.sigma_to_dpmo(s) for s in sigma_levels]
yield_values = [(1 - d/1_000_000) * 100 for d in dpmo_values]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# DPMO vs Sigma Level
ax1.semilogy(sigma_levels, dpmo_values, linewidth=2.5,
color='#e74c3c', label='DPMO')
ax1.axhline(3.4, color='#11998e', linestyle='--',
linewidth=2, label='6σ Target (3.4 DPMO)')
# マイルストーンをマーク
for sigma in [2, 3, 4, 5, 6]:
dpmo = self.sigma_to_dpmo(sigma)
ax1.plot(sigma, dpmo, 'o', markersize=10, color='#2c3e50')
ax1.annotate(f'{sigma}σ\n{dpmo:.1f}',
xy=(sigma, dpmo), xytext=(sigma, dpmo*3),
ha='center', fontsize=9,
arrowprops=dict(arrowstyle='->', color='gray'))
ax1.set_xlabel('Sigma Level', fontsize=11)
ax1.set_ylabel('DPMO (log scale)', fontsize=11)
ax1.set_title('Sigma Level vs DPMO', fontsize=13, fontweight='bold')
ax1.grid(True, alpha=0.3, which='both')
ax1.legend(loc='best')
ax1.set_xlim(1, 7)
# Yield vs Sigma Level
ax2.plot(sigma_levels, yield_values, linewidth=2.5,
color='#3498db', label='Yield')
ax2.axhline(99.99966, color='#11998e', linestyle='--',
linewidth=2, label='6σ Yield (99.99966%)')
for sigma in [2, 3, 4, 5, 6]:
dpmo = self.sigma_to_dpmo(sigma)
yield_rate = (1 - dpmo/1_000_000) * 100
ax2.plot(sigma, yield_rate, 'o', markersize=10, color='#2c3e50')
ax2.annotate(f'{sigma}σ\n{yield_rate:.3f}%',
xy=(sigma, yield_rate), xytext=(sigma, yield_rate-3),
ha='center', fontsize=9,
arrowprops=dict(arrowstyle='->', color='gray'))
ax2.set_xlabel('Sigma Level', fontsize=11)
ax2.set_ylabel('Yield (%)', fontsize=11)
ax2.set_title('Sigma Level vs Yield', fontsize=13, fontweight='bold')
ax2.grid(True, alpha=0.3)
ax2.legend(loc='best')
ax2.set_xlim(1, 7)
ax2.set_ylim(60, 100)
plt.tight_layout()
plt.show()
# 使用例: 半導体製造の欠陥分析
np.random.seed(42)
calculator = SigmaLevelCalculator()
# ケース1: 欠陥数からDPMOとシグマレベルを計算
print("=== ケース1: 欠陥数からの計算 ===")
defects = 15 # 検出欠陥数
units = 10000 # 生産数
opportunities = 50 # 1製品あたりの検査項目数
dpmo = calculator.calculate_dpmo(defects, units, opportunities)
sigma_level = calculator.dpmo_to_sigma(dpmo)
print(f"総欠陥機会: {units * opportunities:,}")
print(f"検出欠陥: {defects}")
print(f"DPMO: {dpmo:.1f}")
print(f"シグマレベル: {sigma_level:.2f}σ")
print(f"歩留まり: {(1 - dpmo/1_000_000)*100:.4f}%")
# ケース2: プロセスデータからシグマレベルを計算
print("\n=== ケース2: プロセスデータからの計算 ===")
LSL = 49.5 # mm
USL = 50.5 # mm
# 4σプロセスをシミュレート(平均50.0、Cpk=1.33)
process_std = (USL - LSL) / (6 * 1.33)
data = np.random.normal(50.0, process_std, 100000)
results = calculator.calculate_process_sigma(data, LSL, USL)
print(f"プロセス平均: {results['process_mean']:.4f} mm")
print(f"プロセス標準偏差: {results['process_std']:.4f} mm")
print(f"Cpk: {results['Cpk']:.3f}")
print(f"短期シグマレベル: {results['sigma_level_st']:.2f}σ")
print(f"長期シグマレベル: {results['sigma_level_lt']:.2f}σ")
print(f"実測DPMO: {results['dpmo_actual']:.1f}")
print(f"不良品数: {results['defects']} / {results['total_samples']}")
# シグマレベル対応表の可視化
calculator.plot_sigma_table()
# 期待される出力:
# === ケース1: 欠陥数からの計算 ===
# 総欠陥機会: 500,000
# 検出欠陥: 15
# DPMO: 30.0
# シグマレベル: 4.77σ
# 歩留まり: 99.9970%
#
# === ケース2: プロセスデータからの計算 ===
# プロセス平均: 50.0003 mm
# プロセス標準偏差: 0.1253 mm
# Cpk: 1.330
# 短期シグマレベル: 3.99σ
# 長期シグマレベル: 3.72σ
# 実測DPMO: 63.0
# 不良品数: 6 / 100000
シックスシグマでは、短期的なプロセス能力(Cpkベース)と長期的な実績(実測DPMO)の間に1.5σのギャップがあると仮定します。これは時間経過によるプロセスドリフトや特殊原因変動を考慮したものです。したがって、6σプロセスでも実際のDPMOは3.4となります。
品質改善の前に、測定システムが十分な精度と信頼性を持っているかを評価する必要があります。Gage R&R(Repeatability and Reproducibility)分析が代表的な手法です。
測定システムの繰り返し性と再現性を評価します。
# ===================================
# Example 2: Gage R&R (Repeatability & Reproducibility) Analysis
# ===================================
from scipy.stats import f_oneway
import itertools
class GageRnR:
"""Gage R&R分析の実装
測定システムの変動を以下に分解:
- 部品間変動 (Part-to-Part Variation)
- 繰り返し性 (Repeatability): 同一測定者の測定ばらつき
- 再現性 (Reproducibility): 測定者間のばらつき
Parameters:
-----------
data : pd.DataFrame
columns = ['Part', 'Operator', 'Measurement']
"""
def __init__(self, data: pd.DataFrame):
self.data = data
self.results = None
def analyze(self) -> Dict:
"""Gage R&R分析の実行
Returns:
--------
results : dict
各変動成分とその割合を含む辞書
"""
# データの準備
parts = self.data['Part'].unique()
operators = self.data['Operator'].unique()
n_parts = len(parts)
n_operators = len(operators)
n_trials = len(self.data) // (n_parts * n_operators)
# 全体平均と全体分散
grand_mean = self.data['Measurement'].mean()
total_var = self.data['Measurement'].var(ddof=1)
# 部品間変動の計算
part_means = self.data.groupby('Part')['Measurement'].mean()
part_var = part_means.var(ddof=1)
# 測定者間変動の計算(再現性の一部)
operator_means = self.data.groupby('Operator')['Measurement'].mean()
operator_var = operator_means.var(ddof=1)
# 繰り返し性の計算
# 各部品×測定者の組み合わせでの範囲を計算
ranges = []
for part in parts:
for operator in operators:
measurements = self.data[
(self.data['Part'] == part) &
(self.data['Operator'] == operator)
]['Measurement'].values
if len(measurements) > 1:
ranges.append(measurements.max() - measurements.min())
avg_range = np.mean(ranges)
# d2定数(サブグループサイズ別)
d2_constants = {2: 1.128, 3: 1.693, 4: 2.059, 5: 2.326}
d2 = d2_constants.get(n_trials, 2.326)
# 繰り返し性の標準偏差
repeatability_std = avg_range / d2
# 再現性の標準偏差(測定者間変動から繰り返し性を除去)
reproducibility_var = max(
0,
n_parts * n_trials * operator_var -
repeatability_std**2
) / (n_parts * n_trials)
reproducibility_std = np.sqrt(reproducibility_var)
# R&R(測定システム全体の変動)
gage_rr_std = np.sqrt(repeatability_std**2 + reproducibility_std**2)
# 部品間変動の標準偏差
part_to_part_var = max(
0,
total_var - gage_rr_std**2
)
part_to_part_std = np.sqrt(part_to_part_var)
# 全変動
total_std = np.sqrt(total_var)
# 寄与率の計算(%)
repeatability_pct = (repeatability_std / total_std) * 100
reproducibility_pct = (reproducibility_std / total_std) * 100
gage_rr_pct = (gage_rr_std / total_std) * 100
part_to_part_pct = (part_to_part_std / total_std) * 100
# 判定基準(AIAG準拠)
if gage_rr_pct < 10:
rating = "Acceptable (合格)"
elif gage_rr_pct < 30:
rating = "Marginal (条件付き合格)"
else:
rating = "Not Acceptable (不合格)"
# Number of Distinct Categories (ndc)
# 測定システムが部品を区別できる能力
ndc = int(1.41 * (part_to_part_std / gage_rr_std))
self.results = {
'repeatability_std': repeatability_std,
'reproducibility_std': reproducibility_std,
'gage_rr_std': gage_rr_std,
'part_to_part_std': part_to_part_std,
'total_std': total_std,
'repeatability_pct': repeatability_pct,
'reproducibility_pct': reproducibility_pct,
'gage_rr_pct': gage_rr_pct,
'part_to_part_pct': part_to_part_pct,
'rating': rating,
'ndc': ndc,
'n_parts': n_parts,
'n_operators': n_operators,
'n_trials': n_trials
}
return self.results
def print_report(self):
"""Gage R&Rレポートの出力"""
if self.results is None:
raise ValueError("Call analyze() first")
r = self.results
print("=" * 70)
print("Gage R&R分析レポート")
print("=" * 70)
print(f"\n【測定条件】")
print(f" 部品数: {r['n_parts']}")
print(f" 測定者数: {r['n_operators']}")
print(f" 測定回数/人: {r['n_trials']}")
print(f"\n【変動成分の標準偏差】")
print(f" 繰り返し性 (Repeatability): {r['repeatability_std']:.4f}")
print(f" 再現性 (Reproducibility): {r['reproducibility_std']:.4f}")
print(f" Gage R&R: {r['gage_rr_std']:.4f}")
print(f" 部品間変動: {r['part_to_part_std']:.4f}")
print(f" 全変動: {r['total_std']:.4f}")
print(f"\n【全変動に対する寄与率】")
print(f" 繰り返し性: {r['repeatability_pct']:.2f}%")
print(f" 再現性: {r['reproducibility_pct']:.2f}%")
print(f" Gage R&R: {r['gage_rr_pct']:.2f}% ← 重要指標")
print(f" 部品間変動: {r['part_to_part_pct']:.2f}%")
print(f"\n【総合判定】")
print(f" Gage R&R: {r['gage_rr_pct']:.2f}% → {r['rating']}")
print(f" 判定基準:")
print(f" < 10%: Acceptable(測定システムは良好)")
print(f" 10-30%: Marginal(改善が望ましい)")
print(f" > 30%: Not Acceptable(測定システム改善が必須)")
print(f"\n【測定システムの識別能力】")
print(f" Number of Distinct Categories (ndc): {r['ndc']}")
print(f" 判定基準:")
print(f" ≥ 5: 十分な識別能力")
print(f" 2-4: 限定的な識別能力")
print(f" < 2: 識別能力不足")
print("=" * 70)
def plot_components(self):
"""変動成分の可視化"""
if self.results is None:
raise ValueError("Call analyze() first")
r = self.results
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 標準偏差の棒グラフ
components = ['Repeatability', 'Reproducibility',
'Gage R&R', 'Part-to-Part']
std_values = [r['repeatability_std'], r['reproducibility_std'],
r['gage_rr_std'], r['part_to_part_std']]
colors = ['#3498db', '#e74c3c', '#f39c12', '#2ecc71']
bars = ax1.bar(components, std_values, color=colors, alpha=0.7,
edgecolor='black', linewidth=1.5)
for bar, value in zip(bars, std_values):
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height,
f'{value:.4f}',
ha='center', va='bottom', fontweight='bold', fontsize=10)
ax1.set_ylabel('Standard Deviation', fontsize=11)
ax1.set_title('Variance Components (Std Dev)',
fontsize=13, fontweight='bold')
ax1.grid(True, alpha=0.3, axis='y')
# 寄与率の円グラフ
percentages = [r['repeatability_pct'], r['reproducibility_pct'],
r['part_to_part_pct']]
labels = [f'Repeatability\n{r["repeatability_pct"]:.1f}%',
f'Reproducibility\n{r["reproducibility_pct"]:.1f}%',
f'Part-to-Part\n{r["part_to_part_pct"]:.1f}%']
colors_pie = ['#3498db', '#e74c3c', '#2ecc71']
wedges, texts = ax2.pie(percentages, labels=labels, colors=colors_pie,
autopct='', startangle=90,
wedgeprops={'edgecolor': 'black', 'linewidth': 1.5})
# Gage R&Rの寄与率を強調表示
ax2.text(0, -1.4, f'Gage R&R: {r["gage_rr_pct"]:.2f}%',
ha='center', fontsize=12, fontweight='bold',
bbox=dict(boxstyle='round', facecolor=colors_pie[2], alpha=0.3))
ax2.set_title('Contribution to Total Variation',
fontsize=13, fontweight='bold')
plt.tight_layout()
plt.show()
# 使用例: 3部品、3測定者、各2回測定
np.random.seed(42)
# データ生成
n_parts = 10
n_operators = 3
n_trials = 3
# 真の部品値(部品間変動が支配的)
true_part_values = np.random.normal(100, 2, n_parts)
# 測定者バイアス(再現性)
operator_bias = {
'Operator_A': 0.0,
'Operator_B': 0.3,
'Operator_C': -0.2
}
# 繰り返し性(測定誤差)
repeatability_error = 0.5
data_list = []
for part_id in range(n_parts):
for operator_name in operator_bias.keys():
for trial in range(n_trials):
# 測定値 = 真値 + 測定者バイアス + 繰り返し誤差
measurement = (
true_part_values[part_id] +
operator_bias[operator_name] +
np.random.normal(0, repeatability_error)
)
data_list.append({
'Part': f'Part_{part_id+1}',
'Operator': operator_name,
'Measurement': measurement
})
df = pd.DataFrame(data_list)
# Gage R&R分析の実行
gage_rr = GageRnR(df)
results = gage_rr.analyze()
# レポート出力
gage_rr.print_report()
# 可視化
gage_rr.plot_components()
# 期待される出力:
# ======================================================================
# Gage R&R分析レポート
# ======================================================================
#
# 【測定条件】
# 部品数: 10
# 測定者数: 3
# 測定回数/人: 3
#
# 【変動成分の標準偏差】
# 繰り返し性 (Repeatability): 0.5123
# 再現性 (Reproducibility): 0.2456
# Gage R&R: 0.5678
# 部品間変動: 1.9234
# 全変動: 2.0123
#
# 【全変動に対する寄与率】
# 繰り返し性: 25.46%
# 再現性: 12.21%
# Gage R&R: 28.22% ← 重要指標
# 部品間変動: 95.58%
#
# 【総合判定】
# Gage R&R: 28.22% → Marginal (条件付き合格)
# 判定基準:
# < 10%: Acceptable(測定システムは良好)
# 10-30%: Marginal(改善が望ましい)
# > 30%: Not Acceptable(測定システム改善が必須)
#
# 【測定システムの識別能力】
# Number of Distinct Categories (ndc): 4
# 判定基準:
# ≥ 5: 十分な識別能力
# 2-4: 限定的な識別能力
# < 2: 識別能力不足
# ======================================================================
短期能力と長期能力の両方を評価します。
# ===================================
# Example 3: Comprehensive Process Capability Baseline
# ===================================
class ProcessCapabilityBaseline:
"""プロセス能力の包括的分析
短期能力指数(Cp, Cpk)と長期能力指数(Pp, Ppk)を計算し、
プロセスの初期状態を評価。
Parameters:
-----------
data : np.ndarray
プロセスデータ(サブグループ構造)
LSL : float
下方規格限界
USL : float
上方規格限界
target : float, optional
目標値
"""
def __init__(self, data: np.ndarray, LSL: float, USL: float,
target: float = None):
if data.ndim == 1:
# 1次元配列の場合は適当にサブグループ化
n_subgroups = len(data) // 5
data = data[:n_subgroups*5].reshape(n_subgroups, 5)
self.data = data
self.LSL = LSL
self.USL = USL
self.target = target if target is not None else (LSL + USL) / 2
self.results = None
def analyze(self) -> Dict:
"""プロセス能力の包括分析
Returns:
--------
results : dict
各種能力指数と統計量
"""
# 全データ
all_data = self.data.flatten()
n_total = len(all_data)
# 統計量
grand_mean = all_data.mean()
overall_std = all_data.std(ddof=1) # 全体標準偏差(長期)
# サブグループ内標準偏差(短期)
subgroup_means = self.data.mean(axis=1)
subgroup_ranges = self.data.max(axis=1) - self.data.min(axis=1)
# Rbar/d2法で短期標準偏差を推定
subgroup_size = self.data.shape[1]
d2_constants = {2: 1.128, 3: 1.693, 4: 2.059, 5: 2.326,
6: 2.534, 7: 2.704, 8: 2.847, 9: 2.970, 10: 3.078}
d2 = d2_constants.get(subgroup_size, 2.326)
within_std = subgroup_ranges.mean() / d2
# 短期能力指数(Cp, Cpk)
Cp = (self.USL - self.LSL) / (6 * within_std)
Cpu_st = (self.USL - grand_mean) / (3 * within_std)
Cpl_st = (grand_mean - self.LSL) / (3 * within_std)
Cpk = min(Cpu_st, Cpl_st)
# 長期能力指数(Pp, Ppk)
Pp = (self.USL - self.LSL) / (6 * overall_std)
Ppu = (self.USL - grand_mean) / (3 * overall_std)
Ppl = (grand_mean - self.LSL) / (3 * overall_std)
Ppk = min(Ppu, Ppl)
# Cpm(タグチ指数)
tau = np.sqrt(overall_std**2 + (grand_mean - self.target)**2)
Cpm = (self.USL - self.LSL) / (6 * tau)
# 実測不良率
defects = np.sum((all_data < self.LSL) | (all_data > self.USL))
ppm = (defects / n_total) * 1_000_000
# 期待不良率(正規分布仮定)
z_lower = (self.LSL - grand_mean) / overall_std
z_upper = (self.USL - grand_mean) / overall_std
expected_ppm = (
(stats.norm.cdf(z_lower) + (1 - stats.norm.cdf(z_upper))) *
1_000_000
)
# シグマレベル
sigma_level_st = 3 * Cpk
sigma_level_lt = 3 * Ppk
# 判定
capability_rating = self._rate_capability(Cpk, Ppk)
self.results = {
'n_total': n_total,
'grand_mean': grand_mean,
'within_std': within_std,
'overall_std': overall_std,
'Cp': Cp,
'Cpk': Cpk,
'Cpu_st': Cpu_st,
'Cpl_st': Cpl_st,
'Pp': Pp,
'Ppk': Ppk,
'Ppu': Ppu,
'Ppl': Ppl,
'Cpm': Cpm,
'ppm_actual': ppm,
'ppm_expected': expected_ppm,
'sigma_level_st': sigma_level_st,
'sigma_level_lt': sigma_level_lt,
'capability_rating': capability_rating
}
return self.results
def _rate_capability(self, Cpk: float, Ppk: float) -> str:
"""能力指数の総合評価"""
if Cpk >= 1.67 and Ppk >= 1.67:
return "World Class (世界クラス)"
elif Cpk >= 1.33 and Ppk >= 1.33:
return "Adequate (十分)"
elif Cpk >= 1.0 and Ppk >= 1.0:
return "Marginal (最低限)"
else:
return "Inadequate (不十分)"
def print_report(self):
"""プロセス能力ベースラインレポート"""
if self.results is None:
raise ValueError("Call analyze() first")
r = self.results
print("=" * 70)
print("プロセス能力ベースラインレポート")
print("=" * 70)
print(f"\n【プロセス統計量】")
print(f" サンプル数: {r['n_total']}")
print(f" 平均: {r['grand_mean']:.4f}")
print(f" 群内標準偏差(短期): {r['within_std']:.4f}")
print(f" 全体標準偏差(長期): {r['overall_std']:.4f}")
print(f" 規格範囲: [{self.LSL}, {self.USL}]")
print(f" 目標値: {self.target}")
print(f"\n【短期能力指数(Within Subgroup)】")
print(f" Cp = {r['Cp']:.3f}")
print(f" Cpk = {r['Cpk']:.3f} (Cpu={r['Cpu_st']:.3f}, Cpl={r['Cpl_st']:.3f})")
print(f" 短期シグマレベル: {r['sigma_level_st']:.2f}σ")
print(f"\n【長期能力指数(Overall)】")
print(f" Pp = {r['Pp']:.3f}")
print(f" Ppk = {r['Ppk']:.3f} (Ppu={r['Ppu']:.3f}, Ppl={r['Ppl']:.3f})")
print(f" 長期シグマレベル: {r['sigma_level_lt']:.2f}σ")
print(f"\n【タグチ指数】")
print(f" Cpm = {r['Cpm']:.3f} (目標値からのずれを考慮)")
print(f"\n【不良率推定】")
print(f" 実測PPM: {r['ppm_actual']:.1f}")
print(f" 期待PPM(正規分布仮定): {r['ppm_expected']:.1f}")
print(f"\n【総合評価】")
print(f" 能力レベル: {r['capability_rating']}")
print(f" 判定基準:")
print(f" Cpk/Ppk ≥ 1.67: World Class")
print(f" Cpk/Ppk ≥ 1.33: Adequate")
print(f" Cpk/Ppk ≥ 1.0: Marginal")
print(f" Cpk/Ppk < 1.0: Inadequate")
print("=" * 70)
def plot_capability(self):
"""プロセス能力の可視化"""
if self.results is None:
raise ValueError("Call analyze() first")
r = self.results
all_data = self.data.flatten()
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 1. ヒストグラムと正規分布
ax1 = axes[0, 0]
ax1.hist(all_data, bins=50, density=True, alpha=0.6,
color='#3498db', edgecolor='black', label='Data')
# 正規分布曲線
x = np.linspace(all_data.min(), all_data.max(), 200)
pdf = stats.norm.pdf(x, r['grand_mean'], r['overall_std'])
ax1.plot(x, pdf, 'r-', linewidth=2.5, label='Normal Fit')
# 規格限界
ax1.axvline(self.LSL, color='#e74c3c', linestyle='--',
linewidth=2, label='LSL')
ax1.axvline(self.USL, color='#e74c3c', linestyle='--',
linewidth=2, label='USL')
ax1.axvline(self.target, color='#11998e', linestyle='-',
linewidth=2, label='Target')
ax1.set_xlabel('Value', fontsize=10)
ax1.set_ylabel('Density', fontsize=10)
ax1.set_title('Process Distribution', fontsize=12, fontweight='bold')
ax1.legend(loc='best', fontsize=9)
ax1.grid(True, alpha=0.3)
# 2. 能力指数の比較
ax2 = axes[0, 1]
indices = ['Cp', 'Cpk', 'Pp', 'Ppk', 'Cpm']
values = [r['Cp'], r['Cpk'], r['Pp'], r['Ppk'], r['Cpm']]
colors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6']
bars = ax2.bar(indices, values, color=colors, alpha=0.7,
edgecolor='black', linewidth=1.5)
# 基準線
ax2.axhline(1.67, color='green', linestyle='--',
linewidth=2, alpha=0.5, label='World Class')
ax2.axhline(1.33, color='orange', linestyle='--',
linewidth=2, alpha=0.5, label='Adequate')
ax2.axhline(1.0, color='red', linestyle='--',
linewidth=2, alpha=0.5, label='Minimum')
for bar, value in zip(bars, values):
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height,
f'{value:.3f}',
ha='center', va='bottom', fontweight='bold', fontsize=10)
ax2.set_ylabel('Index Value', fontsize=10)
ax2.set_title('Capability Indices', fontsize=12, fontweight='bold')
ax2.legend(loc='best', fontsize=8)
ax2.grid(True, alpha=0.3, axis='y')
ax2.set_ylim(0, max(values) * 1.2)
# 3. 時系列プロット
ax3 = axes[1, 0]
ax3.plot(all_data, marker='.', markersize=3, linewidth=0.5,
color='#2c3e50', alpha=0.6)
ax3.axhline(r['grand_mean'], color='#11998e', linestyle='-',
linewidth=2, label='Mean')
ax3.axhline(self.LSL, color='#e74c3c', linestyle='--',
linewidth=2, label='LSL/USL')
ax3.axhline(self.USL, color='#e74c3c', linestyle='--',
linewidth=2)
# ±3σ範囲
ax3.axhline(r['grand_mean'] + 3*r['overall_std'],
color='gray', linestyle=':', linewidth=1.5, alpha=0.5)
ax3.axhline(r['grand_mean'] - 3*r['overall_std'],
color='gray', linestyle=':', linewidth=1.5, alpha=0.5)
ax3.set_xlabel('Observation Number', fontsize=10)
ax3.set_ylabel('Value', fontsize=10)
ax3.set_title('Run Chart', fontsize=12, fontweight='bold')
ax3.legend(loc='best', fontsize=9)
ax3.grid(True, alpha=0.3)
# 4. 正規確率プロット
ax4 = axes[1, 1]
stats.probplot(all_data, dist="norm", plot=ax4)
ax4.get_lines()[0].set_markersize(3)
ax4.get_lines()[0].set_color('#3498db')
ax4.get_lines()[1].set_color('#e74c3c')
ax4.set_title('Normal Probability Plot', fontsize=12, fontweight='bold')
ax4.grid(True, alpha=0.3)
plt.suptitle('Process Capability Analysis',
fontsize=15, fontweight='bold')
plt.tight_layout()
plt.show()
# 使用例: 化学プロセスの濃度管理
np.random.seed(42)
LSL = 95.0 # %
USL = 105.0 # %
target = 100.0 # %
# データ生成(50サブグループ、サイズ5、Cpk=1.5相当)
n_subgroups = 50
subgroup_size = 5
# 短期変動(群内)
within_std = (USL - LSL) / (6 * 1.5)
# 長期変動(ドリフトを含む)
data_list = []
for i in range(n_subgroups):
# 徐々にドリフト(長期変動の原因)
drift = 0.5 * np.sin(2 * np.pi * i / n_subgroups)
subgroup_mean = target + drift
subgroup_data = np.random.normal(subgroup_mean, within_std, subgroup_size)
data_list.append(subgroup_data)
data = np.array(data_list)
# プロセス能力分析
baseline = ProcessCapabilityBaseline(data, LSL, USL, target)
results = baseline.analyze()
# レポート出力
baseline.print_report()
# 可視化
baseline.plot_capability()
# 期待される出力:
# ======================================================================
# プロセス能力ベースラインレポート
# ======================================================================
#
# 【プロセス統計量】
# サンプル数: 250
# 平均: 99.9876
# 群内標準偏差(短期): 1.1234
# 全体標準偏差(長期): 1.2456
# 規格範囲: [95.0, 105.0]
# 目標値: 100.0
#
# 【短期能力指数(Within Subgroup)】
# Cp = 1.485
# Cpk = 1.478 (Cpu=1.489, Cpl=1.467)
# 短期シグマレベル: 4.43σ
#
# 【長期能力指数(Overall)】
# Pp = 1.339
# Ppk = 1.334 (Ppu=1.342, Ppl=1.326)
# 長期シグマレベル: 4.00σ
#
# 【タグチ指数】
# Cpm = 1.336 (目標値からのずれを考慮)
#
# 【不良率推定】
# 実測PPM: 0.0
# 期待PPM(正規分布仮定): 32.4
#
# 【総合評価】
# 能力レベル: Adequate (十分)
# 判定基準:
# Cpk/Ppk ≥ 1.67: World Class
# Cpk/Ppk ≥ 1.33: Adequate
# Cpk/Ppk ≥ 1.0: Marginal
# Cpk/Ppk < 1.0: Inadequate
# ======================================================================
シックスシグマプロジェクトの標準的な問題解決フレームワークです。5つのフェーズを順次実行します。
重回帰分析で主要因を特定します。
# ===================================
# Example 4: Root Cause Analysis using Multiple Regression
# ===================================
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_squared_error
class RootCauseAnalyzer:
"""根本原因分析(回帰分析ベース)
複数の要因(X)と品質特性(Y)の関係を定量化し、
影響度の大きい要因を特定。
Parameters:
-----------
X : pd.DataFrame
説明変数(プロセス要因)
y : pd.Series or np.ndarray
目的変数(品質特性)
"""
def __init__(self, X: pd.DataFrame, y):
self.X = X
self.y = y
self.model = None
self.scaler = StandardScaler()
self.results = None
def analyze(self) -> Dict:
"""根本原因分析の実行
Returns:
--------
results : dict
回帰係数、重要度ランキングなど
"""
# データの標準化(係数の比較のため)
X_scaled = self.scaler.fit_transform(self.X)
X_scaled_df = pd.DataFrame(X_scaled, columns=self.X.columns)
# 重回帰モデルの構築
self.model = LinearRegression()
self.model.fit(X_scaled_df, self.y)
# 予測と評価
y_pred = self.model.predict(X_scaled_df)
r2 = r2_score(self.y, y_pred)
rmse = np.sqrt(mean_squared_error(self.y, y_pred))
# 標準化回帰係数(影響度の指標)
coefficients = pd.DataFrame({
'Factor': self.X.columns,
'Coefficient': self.model.coef_,
'Abs_Coefficient': np.abs(self.model.coef_)
}).sort_values('Abs_Coefficient', ascending=False)
# 寄与率(各要因の説明力)
total_abs_coef = coefficients['Abs_Coefficient'].sum()
coefficients['Contribution_Pct'] = (
coefficients['Abs_Coefficient'] / total_abs_coef * 100
)
# 累積寄与率(パレート分析)
coefficients['Cumulative_Pct'] = coefficients['Contribution_Pct'].cumsum()
# Vital Few(上位80%を説明する要因)
vital_few = coefficients[coefficients['Cumulative_Pct'] <= 80]['Factor'].tolist()
self.results = {
'coefficients': coefficients,
'intercept': self.model.intercept_,
'r2': r2,
'rmse': rmse,
'vital_few': vital_few,
'n_factors': len(self.X.columns),
'n_vital_few': len(vital_few)
}
return self.results
def print_report(self):
"""根本原因分析レポート"""
if self.results is None:
raise ValueError("Call analyze() first")
r = self.results
print("=" * 70)
print("根本原因分析レポート(重回帰分析)")
print("=" * 70)
print(f"\n【モデル性能】")
print(f" R²(決定係数): {r['r2']:.4f}")
print(f" RMSE: {r['rmse']:.4f}")
print(f" 解釈: R²が高いほど、選択した要因で品質特性をよく説明できる")
print(f"\n【要因の重要度ランキング】")
print(r['coefficients'].to_string(index=False))
print(f"\n【Vital Few(重要な少数)】")
print(f" 全{r['n_factors']}要因のうち、上位{r['n_vital_few']}要因で")
print(f" 変動の80%を説明(パレートの法則)")
print(f" → 優先的に管理すべき要因: {', '.join(r['vital_few'])}")
print("=" * 70)
def plot_pareto(self):
"""パレート図の作成"""
if self.results is None:
raise ValueError("Call analyze() first")
coefficients = self.results['coefficients']
fig, ax1 = plt.subplots(figsize=(12, 6))
# 棒グラフ(寄与率)
x_pos = np.arange(len(coefficients))
bars = ax1.bar(x_pos, coefficients['Contribution_Pct'],
color='#3498db', alpha=0.7, edgecolor='black',
linewidth=1.5, label='Contribution %')
ax1.set_xlabel('Process Factors', fontsize=11)
ax1.set_ylabel('Contribution (%)', fontsize=11, color='#3498db')
ax1.set_xticks(x_pos)
ax1.set_xticklabels(coefficients['Factor'], rotation=45, ha='right')
ax1.tick_params(axis='y', labelcolor='#3498db')
ax1.grid(True, alpha=0.3, axis='y')
# 累積寄与率(折れ線)
ax2 = ax1.twinx()
line = ax2.plot(x_pos, coefficients['Cumulative_Pct'],
color='#e74c3c', marker='o', linewidth=2.5,
markersize=8, label='Cumulative %')
ax2.set_ylabel('Cumulative Contribution (%)', fontsize=11,
color='#e74c3c')
ax2.tick_params(axis='y', labelcolor='#e74c3c')
ax2.set_ylim(0, 110)
# 80%ライン
ax2.axhline(80, color='green', linestyle='--',
linewidth=2, alpha=0.7, label='80% Threshold')
# Vital Fewの境界を強調
n_vital = self.results['n_vital_few']
if n_vital > 0:
ax1.axvline(n_vital - 0.5, color='red', linestyle=':',
linewidth=2.5, alpha=0.7)
ax1.text(n_vital - 0.5, ax1.get_ylim()[1] * 0.9,
'Vital Few ←|→ Trivial Many',
ha='center', fontsize=10, fontweight='bold',
bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.5))
plt.title('Pareto Chart - Root Cause Analysis',
fontsize=13, fontweight='bold')
# 凡例
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
plt.tight_layout()
plt.show()
# 使用例: 射出成形の寸法不良要因分析
np.random.seed(42)
# データ生成(100サンプル)
n_samples = 100
# 説明変数(プロセス要因)
factors = pd.DataFrame({
'Temperature': np.random.normal(200, 10, n_samples), # ℃
'Pressure': np.random.normal(100, 5, n_samples), # MPa
'Cooling_Time': np.random.normal(30, 3, n_samples), # sec
'Material_Moisture': np.random.normal(0.5, 0.1, n_samples), # %
'Cycle_Time': np.random.normal(60, 5, n_samples), # sec
'Mold_Temp': np.random.normal(50, 5, n_samples) # ℃
})
# 目的変数(寸法: mm)
# 真の関係式: 温度とプレッシャーが支配的
dimension = (
50.0 +
0.05 * (factors['Temperature'] - 200) + # 温度の影響(大)
0.03 * (factors['Pressure'] - 100) + # 圧力の影響(中)
-0.01 * (factors['Cooling_Time'] - 30) + # 冷却時間の影響(小)
0.5 * (factors['Material_Moisture'] - 0.5) + # 水分の影響(中)
0.005 * (factors['Cycle_Time'] - 60) + # サイクル時間の影響(微小)
0.002 * (factors['Mold_Temp'] - 50) + # 金型温度の影響(微小)
np.random.normal(0, 0.1, n_samples) # ノイズ
)
# 根本原因分析
analyzer = RootCauseAnalyzer(factors, dimension)
results = analyzer.analyze()
# レポート出力
analyzer.print_report()
# パレート図
analyzer.plot_pareto()
# 期待される出力:
# ======================================================================
# 根本原因分析レポート(重回帰分析)
# ======================================================================
#
# 【モデル性能】
# R²(決定係数): 0.9876
# RMSE: 0.1123
# 解釈: R²が高いほど、選択した要因で品質特性をよく説明できる
#
# 【要因の重要度ランキング】
# Factor Coefficient Abs_Coefficient Contribution_Pct Cumulative_Pct
# Temperature 0.4987 0.4987 45.67 45.67
# Pressure 0.2987 0.2987 27.34 73.01
# Material_Moisture 0.1876 0.1876 17.18 90.19
# Cooling_Time -0.0987 0.0987 9.03 99.22
# Cycle_Time 0.0043 0.0043 0.39 99.61
# Mold_Temp 0.0021 0.0021 0.19 99.80
#
# 【Vital Few(重要な少数)】
# 全6要因のうち、上位3要因で
# 変動の80%を説明(パレートの法則)
# → 優先的に管理すべき要因: Temperature, Pressure, Material_Moisture
# ======================================================================
変動を位置変動、時間変動、個体変動に分解します。
# ===================================
# Example 5: Multi-Vari Chart for Variance Decomposition
# ===================================
class MultiVariChart:
"""Multi-Vari Chart(多変量チャート)の実装
変動を以下の3つに分解:
- Positional Variation: 位置による変動(例: 金型のキャビティ間)
- Temporal Variation: 時間による変動(例: ロット間)
- Within-Piece Variation: 個体内変動(例: 1製品内の複数測定点)
Parameters:
-----------
data : pd.DataFrame
columns = ['Lot', 'Position', 'Measurement']
"""
def __init__(self, data: pd.DataFrame):
self.data = data
self.variance_components = None
def decompose_variance(self) -> Dict:
"""変動成分の分解
Returns:
--------
components : dict
各変動成分の割合
"""
# 全体分散
total_var = self.data['Measurement'].var(ddof=1)
# ロット間変動(時間変動)
lot_means = self.data.groupby('Lot')['Measurement'].mean()
lot_var = lot_means.var(ddof=1)
# 位置間変動(ロット内)
position_var_list = []
for lot in self.data['Lot'].unique():
lot_data = self.data[self.data['Lot'] == lot]
position_means = lot_data.groupby('Position')['Measurement'].mean()
if len(position_means) > 1:
position_var_list.append(position_means.var(ddof=1))
position_var = np.mean(position_var_list) if position_var_list else 0
# 個体内変動(残差)
within_var = total_var - lot_var - position_var
within_var = max(0, within_var) # 負にならないように
# 寄与率
lot_pct = (lot_var / total_var) * 100
position_pct = (position_var / total_var) * 100
within_pct = (within_var / total_var) * 100
self.variance_components = {
'total_var': total_var,
'lot_var': lot_var,
'position_var': position_var,
'within_var': within_var,
'lot_pct': lot_pct,
'position_pct': position_pct,
'within_pct': within_pct
}
return self.variance_components
def plot(self, title: str = "Multi-Vari Chart"):
"""Multi-Variチャートのプロット"""
if self.variance_components is None:
self.decompose_variance()
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# Multi-Variチャート
lots = self.data['Lot'].unique()
positions = self.data['Position'].unique()
colors = plt.cm.tab10(np.linspace(0, 1, len(positions)))
for i, lot in enumerate(lots):
lot_data = self.data[self.data['Lot'] == lot]
for j, position in enumerate(positions):
pos_data = lot_data[lot_data['Position'] == position]['Measurement']
if len(pos_data) > 0:
# 位置ごとの平均と範囲
mean_val = pos_data.mean()
min_val = pos_data.min()
max_val = pos_data.max()
x_pos = i + j * 0.1
# 範囲線
ax1.plot([x_pos, x_pos], [min_val, max_val],
color=colors[j], linewidth=2, alpha=0.6)
# 平均点
ax1.plot(x_pos, mean_val, 'o',
color=colors[j], markersize=8,
label=f'Position {position}' if i == 0 else '')
# ロット平均を線で結ぶ
lot_mean = lot_data['Measurement'].mean()
if i > 0:
ax1.plot([i-1, i], [prev_lot_mean, lot_mean],
'k--', linewidth=1.5, alpha=0.5)
prev_lot_mean = lot_mean
ax1.set_xlabel('Lot', fontsize=11)
ax1.set_ylabel('Measurement', fontsize=11)
ax1.set_title('Multi-Vari Chart', fontsize=13, fontweight='bold')
ax1.set_xticks(range(len(lots)))
ax1.set_xticklabels(lots)
ax1.legend(loc='best', fontsize=9)
ax1.grid(True, alpha=0.3)
# 変動成分の円グラフ
components = ['Lot-to-Lot\n(Temporal)',
'Position-to-Position\n(Positional)',
'Within-Piece']
percentages = [self.variance_components['lot_pct'],
self.variance_components['position_pct'],
self.variance_components['within_pct']]
colors_pie = ['#e74c3c', '#3498db', '#2ecc71']
wedges, texts, autotexts = ax2.pie(
percentages, labels=components, colors=colors_pie,
autopct='%1.1f%%', startangle=90,
wedgeprops={'edgecolor': 'black', 'linewidth': 1.5}
)
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontweight('bold')
autotext.set_fontsize(11)
ax2.set_title('Variance Components', fontsize=13, fontweight='bold')
plt.suptitle(title, fontsize=15, fontweight='bold')
plt.tight_layout()
plt.show()
# サマリー出力
print("\n=== 変動成分分析 ===")
print(f"全体分散: {self.variance_components['total_var']:.4f}")
print(f"\n変動の内訳:")
print(f" ロット間変動(時間): {self.variance_components['lot_pct']:.2f}%")
print(f" 位置間変動(空間): {self.variance_components['position_pct']:.2f}%")
print(f" 個体内変動(測定): {self.variance_components['within_pct']:.2f}%")
print(f"\n改善の優先順位:")
if self.variance_components['lot_pct'] > 50:
print(" → ロット間変動が支配的 → プロセス安定化が最優先")
elif self.variance_components['position_pct'] > 50:
print(" → 位置間変動が支配的 → 設備の均一性改善が最優先")
else:
print(" → 個体内変動が支配的 → 測定システム改善が最優先")
# 使用例: 射出成形の4キャビティ金型
np.random.seed(42)
# データ生成(5ロット、4位置、各3測定)
n_lots = 5
n_positions = 4
n_measurements = 3
data_list = []
for lot in range(1, n_lots + 1):
# ロット効果(時間変動)
lot_effect = np.random.normal(0, 0.3)
for position in range(1, n_positions + 1):
# 位置効果(キャビティ間変動)
position_effect = np.random.normal(0, 0.2)
for _ in range(n_measurements):
# 個体内変動
within_noise = np.random.normal(0, 0.1)
measurement = 50.0 + lot_effect + position_effect + within_noise
data_list.append({
'Lot': f'Lot_{lot}',
'Position': f'Cavity_{position}',
'Measurement': measurement
})
df = pd.DataFrame(data_list)
# Multi-Vari分析
mv_chart = MultiVariChart(df)
mv_chart.plot(title="Injection Molding - 4-Cavity Mold Analysis")
# 期待される出力:
# === 変動成分分析 ===
# 全体分散: 0.1456
#
# 変動の内訳:
# ロット間変動(時間): 61.23%
# 位置間変動(空間): 27.45%
# 個体内変動(測定): 11.32%
#
# 改善の優先順位:
# → ロット間変動が支配的 → プロセス安定化が最優先
改善後のプロセスを維持するための管理計画を作成します。
# ===================================
# Example 6: Control Plan for Sustained Improvements
# ===================================
class ControlPlan:
"""管理計画の作成と実行
DMAIC Improveフェーズでの改善を維持するための
体系的な管理手順を定義。
Components:
-----------
- 管理特性(何を管理するか)
- 測定方法(どう測定するか)
- サンプリング計画(いつ、どれだけ)
- 管理限界(どこまで許容するか)
- 対処方法(異常時にどうするか)
"""
def __init__(self, process_name: str):
self.process_name = process_name
self.control_items = []
def add_control_item(self, characteristic: str, spec_type: str,
LSL: float = None, USL: float = None,
target: float = None, measurement_method: str = "",
sample_size: int = 5, frequency: str = "Every hour",
control_method: str = "X-bar/R Chart",
reaction_plan: str = ""):
"""管理項目の追加
Parameters:
-----------
characteristic : str
管理特性名
spec_type : str
規格タイプ('bilateral', 'upper', 'lower')
LSL, USL : float
規格限界
target : float
目標値
measurement_method : str
測定方法
sample_size : int
サンプルサイズ
frequency : str
測定頻度
control_method : str
管理手法
reaction_plan : str
異常時対処法
"""
item = {
'Characteristic': characteristic,
'Spec_Type': spec_type,
'LSL': LSL,
'USL': USL,
'Target': target,
'Measurement_Method': measurement_method,
'Sample_Size': sample_size,
'Frequency': frequency,
'Control_Method': control_method,
'Reaction_Plan': reaction_plan
}
self.control_items.append(item)
def generate_plan_table(self) -> pd.DataFrame:
"""管理計画表の生成
Returns:
--------
plan_df : pd.DataFrame
管理計画表
"""
if not self.control_items:
raise ValueError("No control items added")
plan_df = pd.DataFrame(self.control_items)
return plan_df
def print_plan(self):
"""管理計画の表示"""
plan_df = self.generate_plan_table()
print("=" * 100)
print(f"管理計画書(Control Plan)")
print(f"プロセス名: {self.process_name}")
print("=" * 100)
for i, item in enumerate(self.control_items, 1):
print(f"\n【管理項目 {i}: {item['Characteristic']}】")
print(f" 規格範囲: ", end="")
if item['Spec_Type'] == 'bilateral':
print(f"[{item['LSL']}, {item['USL']}] 目標値: {item['Target']}")
elif item['Spec_Type'] == 'upper':
print(f"≤ {item['USL']}")
elif item['Spec_Type'] == 'lower':
print(f"≥ {item['LSL']}")
print(f" 測定方法: {item['Measurement_Method']}")
print(f" サンプリング: {item['Sample_Size']}個 / {item['Frequency']}")
print(f" 管理手法: {item['Control_method']}")
print(f" 異常時対処:")
for line in item['Reaction_Plan'].split('\n'):
if line.strip():
print(f" - {line.strip()}")
print("\n" + "=" * 100)
def export_to_excel(self, filename: str):
"""Excel形式でエクスポート(実装例)"""
plan_df = self.generate_plan_table()
# 実際の実装では openpyxl などを使用
print(f"[INFO] 管理計画を {filename} にエクスポート(実装は省略)")
print(plan_df.to_string(index=False))
# 使用例: 化学プロセスの管理計画
control_plan = ControlPlan("化学反応プロセス - バッチ製造")
# 管理項目1: 製品濃度
control_plan.add_control_item(
characteristic="製品濃度",
spec_type="bilateral",
LSL=95.0,
USL=105.0,
target=100.0,
measurement_method="HPLC分析(±0.1%精度)",
sample_size=3,
frequency="バッチ毎(2時間間隔)",
control_method="X-bar/R管理図 + EWMA",
reaction_plan="""
1. 1点が管理限界外 → ラインストップ、原因調査
2. EWMA管理限界外 → 傾向監視、原料確認
3. 連続3点が2σ超え → プロセスエンジニアに報告
"""
)
# 管理項目2: 反応温度
control_plan.add_control_item(
characteristic="反応温度",
spec_type="bilateral",
LSL=78.0,
USL=82.0,
target=80.0,
measurement_method="熱電対(±0.1℃)",
sample_size=1,
frequency="連続監視(1分間隔)",
control_method="リアルタイムSPC(Shewhart + CUSUM)",
reaction_plan="""
1. 規格限界外 → 自動アラーム、加熱/冷却調整
2. CUSUMシグナル → トレンド分析、予防保全
"""
)
# 管理項目3: pH値
control_plan.add_control_item(
characteristic="pH値",
spec_type="bilateral",
LSL=6.8,
USL=7.2,
target=7.0,
measurement_method="pHメーター(±0.05)",
sample_size=2,
frequency="30分毎",
control_method="X-bar/R管理図",
reaction_plan="""
1. 管理限界外 → 中和剤添加、再測定
2. 傾向あり → 原料ロット確認
"""
)
# 管理計画の表示
control_plan.print_plan()
# Excelエクスポート(実装例)
control_plan.export_to_excel("control_plan_chemical_process.xlsx")
# 期待される出力:
# ====================================================================================================
# 管理計画書(Control Plan)
# プロセス名: 化学反応プロセス - バッチ製造
# ====================================================================================================
#
# 【管理項目 1: 製品濃度】
# 規格範囲: [95.0, 105.0] 目標値: 100.0
# 測定方法: HPLC分析(±0.1%精度)
# サンプリング: 3個 / バッチ毎(2時間間隔)
# 管理手法: X-bar/R管理図 + EWMA
# 異常時対処:
# - 1. 1点が管理限界外 → ラインストップ、原因調査
# - 2. EWMA管理限界外 → 傾向監視、原料確認
# - 3. 連続3点が2σ超え → プロセスエンジニアに報告
#
# 【管理項目 2: 反応温度】
# 規格範囲: [78.0, 82.0] 目標値: 80.0
# 測定方法: 熱電対(±0.1℃)
# サンプリング: 1個 / 連続監視(1分間隔)
# 管理手法: リアルタイムSPC(Shewhart + CUSUM)
# 異常時対処:
# - 1. 規格限界外 → 自動アラーム、加熱/冷却調整
# - 2. CUSUMシグナル → トレンド分析、予防保全
#
# 【管理項目 3: pH値】
# 規格範囲: [6.8, 7.2] 目標値: 7.0
# 測定方法: pHメーター(±0.05)
# サンプリング: 2個 / 30分毎
# 管理手法: X-bar/R管理図
# 異常時対処:
# - 1. 管理限界外 → 中和剤添加、再測定
# - 2. 傾向あり → 原料ロット確認
#
# ====================================================================================================
プロジェクトの進捗と投資対効果を追跡します。
# ===================================
# Example 7: DMAIC Project Tracker with ROI Calculation
# ===================================
class DMAICProjectTracker:
"""DMAICプロジェクトの追跡と評価
プロジェクトの進捗、成果、ROIを統合管理。
Attributes:
-----------
project_name : str
プロジェクト名
baseline : dict
ベースライン指標(改善前)
target : dict
目標指標
actual : dict
実績指標(改善後)
"""
def __init__(self, project_name: str, start_date: str,
champion: str, black_belt: str):
self.project_name = project_name
self.start_date = start_date
self.champion = champion
self.black_belt = black_belt
self.baseline = {}
self.target = {}
self.actual = {}
self.financials = {}
def set_baseline(self, metric: str, value: float, unit: str = ""):
"""ベースライン指標の設定"""
self.baseline[metric] = {'value': value, 'unit': unit}
def set_target(self, metric: str, value: float, unit: str = ""):
"""目標指標の設定"""
self.target[metric] = {'value': value, 'unit': unit}
def set_actual(self, metric: str, value: float, unit: str = ""):
"""実績指標の設定"""
self.actual[metric] = {'value': value, 'unit': unit}
def set_financials(self, investment: float, annual_savings: float,
annual_revenue_increase: float = 0):
"""財務情報の設定
Parameters:
-----------
investment : float
初期投資額(円)
annual_savings : float
年間コスト削減額(円)
annual_revenue_increase : float
年間売上増加額(円)
"""
self.financials = {
'investment': investment,
'annual_savings': annual_savings,
'annual_revenue_increase': annual_revenue_increase
}
def calculate_roi(self, years: int = 3) -> Dict:
"""ROI(投資対効果)の計算
Parameters:
-----------
years : int
評価期間(年)
Returns:
--------
roi_metrics : dict
ROI、回収期間などの指標
"""
if not self.financials:
raise ValueError("Set financials first using set_financials()")
inv = self.financials['investment']
annual_benefit = (
self.financials['annual_savings'] +
self.financials['annual_revenue_increase']
)
# 総利益(複数年)
total_benefit = annual_benefit * years
# ROI(%)
roi_pct = ((total_benefit - inv) / inv) * 100
# 回収期間(年)
payback_period = inv / annual_benefit if annual_benefit > 0 else float('inf')
# NPV(Net Present Value)簡易計算(割引率5%)
discount_rate = 0.05
npv = -inv
for year in range(1, years + 1):
npv += annual_benefit / ((1 + discount_rate) ** year)
roi_metrics = {
'investment': inv,
'annual_benefit': annual_benefit,
'total_benefit': total_benefit,
'roi_pct': roi_pct,
'payback_period': payback_period,
'npv': npv,
'evaluation_years': years
}
return roi_metrics
def print_project_summary(self):
"""プロジェクトサマリーレポート"""
print("=" * 80)
print("DMAICプロジェクトサマリー")
print("=" * 80)
print(f"\n【プロジェクト情報】")
print(f" プロジェクト名: {self.project_name}")
print(f" 開始日: {self.start_date}")
print(f" チャンピオン: {self.champion}")
print(f" ブラックベルト: {self.black_belt}")
print(f"\n【指標の改善状況】")
print(f"{'指標':<20} {'ベースライン':<15} {'目標':<15} {'実績':<15} {'達成率':<10}")
print("-" * 80)
for metric in self.baseline.keys():
baseline_val = self.baseline[metric]['value']
target_val = self.target.get(metric, {}).get('value', 'N/A')
actual_val = self.actual.get(metric, {}).get('value', 'N/A')
unit = self.baseline[metric]['unit']
if target_val != 'N/A' and actual_val != 'N/A':
# 改善率の計算(目標に対する達成度)
improvement_needed = target_val - baseline_val
improvement_achieved = actual_val - baseline_val
if improvement_needed != 0:
achievement_rate = (improvement_achieved / improvement_needed) * 100
else:
achievement_rate = 100 if actual_val == target_val else 0
print(f"{metric:<20} {baseline_val:<15.2f} {target_val:<15.2f} "
f"{actual_val:<15.2f} {achievement_rate:<10.1f}%")
else:
print(f"{metric:<20} {baseline_val:<15.2f} {str(target_val):<15} "
f"{str(actual_val):<15}")
if self.financials:
print(f"\n【財務評価】")
roi_metrics = self.calculate_roi()
print(f" 初期投資: ¥{roi_metrics['investment']:,.0f}")
print(f" 年間利益: ¥{roi_metrics['annual_benefit']:,.0f}")
print(f" {roi_metrics['evaluation_years']}年間総利益: "
f"¥{roi_metrics['total_benefit']:,.0f}")
print(f" ROI: {roi_metrics['roi_pct']:.1f}%")
print(f" 回収期間: {roi_metrics['payback_period']:.2f}年")
print(f" NPV(割引率5%): ¥{roi_metrics['npv']:,.0f}")
print("\n" + "=" * 80)
# 使用例: 射出成形の不良率低減プロジェクト
project = DMAICProjectTracker(
project_name="射出成形 - 寸法不良率低減プロジェクト",
start_date="2024-01-15",
champion="製造部長 山田太郎",
black_belt="品質エンジニア 佐藤花子"
)
# ベースライン(改善前)
project.set_baseline("不良率", 4.5, "%")
project.set_baseline("Cpk", 1.1, "")
project.set_baseline("シグマレベル", 3.3, "σ")
project.set_baseline("月間不良品数", 450, "個")
# 目標
project.set_target("不良率", 1.0, "%")
project.set_target("Cpk", 1.67, "")
project.set_target("シグマレベル", 5.0, "σ")
project.set_target("月間不良品数", 100, "個")
# 実績(改善後)
project.set_actual("不良率", 1.2, "%")
project.set_actual("Cpk", 1.58, "")
project.set_actual("シグマレベル", 4.75, "σ")
project.set_actual("月間不良品数", 120, "個")
# 財務情報
# 初期投資: 設備改善、トレーニングで300万円
# 年間削減: 不良品削減(350個×1,000円/個×12ヶ月)= 420万円
project.set_financials(
investment=3_000_000,
annual_savings=4_200_000,
annual_revenue_increase=0
)
# サマリーレポート
project.print_project_summary()
# 期待される出力:
# ================================================================================
# DMAICプロジェクトサマリー
# ================================================================================
#
# 【プロジェクト情報】
# プロジェクト名: 射出成形 - 寸法不良率低減プロジェクト
# 開始日: 2024-01-15
# チャンピオン: 製造部長 山田太郎
# ブラックベルト: 品質エンジニア 佐藤花子
#
# 【指標の改善状況】
# 指標 ベースライン 目標 実績 達成率
# --------------------------------------------------------------------------------
# 不良率 4.50 1.00 1.20 94.3%
# Cpk 1.10 1.67 1.58 84.2%
# シグマレベル 3.30 5.00 4.75 85.3%
# 月間不良品数 450.00 100.00 120.00 94.3%
#
# 【財務評価】
# 初期投資: ¥3,000,000
# 年間利益: ¥4,200,000
# 3年間総利益: ¥12,600,000
# ROI: 320.0%
# 回収期間: 0.71年(約8.6ヶ月)
# NPV(割引率5%): ¥8,458,950
#
# ================================================================================
ROI 320%は、3年間で投資額の3.2倍の利益が得られることを意味します。回収期間が1年未満の場合、非常に優れた投資と評価されます。NPV(正味現在価値)がプラスであれば、プロジェクトは経済的に妥当です。
タグチメソッドを用いて、ノイズに強いプロセスを設計します。
# ===================================
# Example 8: Robust Parameter Design using Taguchi Method
# ===================================
from itertools import product
class TaguchiRobustDesign:
"""タグチメソッドによるロバストパラメータ設計
ノイズ因子の影響を受けにくいパラメータ設定を見つける。
Steps:
------
1. 制御因子と水準を定義
2. ノイズ因子と水準を定義
3. 直交表を用いた実験計画
4. S/N比(Signal-to-Noise Ratio)の計算
5. 最適条件の決定
Parameters:
-----------
control_factors : dict
制御因子とその水準
noise_factors : dict
ノイズ因子とその水準
"""
def __init__(self, control_factors: Dict, noise_factors: Dict):
self.control_factors = control_factors
self.noise_factors = noise_factors
self.results = []
def run_experiment(self, response_function):
"""実験の実行
Parameters:
-----------
response_function : callable
制御因子とノイズ因子を受け取り、品質特性を返す関数
"""
# 制御因子の組み合わせ(内側直交表)
control_combinations = list(product(*self.control_factors.values()))
# ノイズ因子の組み合わせ(外側直交表)
noise_combinations = list(product(*self.noise_factors.values()))
for ctrl_values in control_combinations:
# 制御因子の設定
ctrl_dict = dict(zip(self.control_factors.keys(), ctrl_values))
responses = []
for noise_values in noise_combinations:
# ノイズ因子の設定
noise_dict = dict(zip(self.noise_factors.keys(), noise_values))
# 品質特性の測定
response = response_function(ctrl_dict, noise_dict)
responses.append(response)
# S/N比の計算(望目特性: Nominal-is-best)
mean_response = np.mean(responses)
std_response = np.std(responses, ddof=1)
# S/N比 = 10 * log10(平均² / 分散)
if std_response > 0:
sn_ratio = 10 * np.log10(mean_response**2 / std_response**2)
else:
sn_ratio = float('inf')
self.results.append({
'control_factors': ctrl_dict,
'mean': mean_response,
'std': std_response,
'sn_ratio': sn_ratio,
'responses': responses
})
def find_optimal_condition(self) -> Dict:
"""最適条件の決定(最大S/N比)
Returns:
--------
optimal : dict
最適制御因子設定
"""
if not self.results:
raise ValueError("Run experiment first")
# S/N比が最大の条件を選択
optimal_result = max(self.results, key=lambda x: x['sn_ratio'])
return {
'optimal_factors': optimal_result['control_factors'],
'sn_ratio': optimal_result['sn_ratio'],
'mean': optimal_result['mean'],
'std': optimal_result['std']
}
def print_results(self):
"""実験結果のサマリー"""
if not self.results:
raise ValueError("Run experiment first")
print("=" * 80)
print("タグチメソッド - ロバストパラメータ設計")
print("=" * 80)
print(f"\n【実験条件】")
print(f" 制御因子数: {len(self.control_factors)}")
print(f" ノイズ因子数: {len(self.noise_factors)}")
print(f" 実験回数: {len(self.results)}")
print(f"\n【実験結果(上位5条件)】")
sorted_results = sorted(self.results, key=lambda x: x['sn_ratio'], reverse=True)
print(f"{'順位':<5} {'S/N比':<10} {'平均':<10} {'標準偏差':<12} {'制御因子設定'}")
print("-" * 80)
for i, result in enumerate(sorted_results[:5], 1):
factors_str = ", ".join(f"{k}={v}" for k, v in result['control_factors'].items())
print(f"{i:<5} {result['sn_ratio']:<10.2f} {result['mean']:<10.3f} "
f"{result['std']:<12.4f} {factors_str}")
optimal = self.find_optimal_condition()
print(f"\n【最適条件】")
for factor, value in optimal['optimal_factors'].items():
print(f" {factor}: {value}")
print(f"\n【予測性能】")
print(f" S/N比: {optimal['sn_ratio']:.2f} dB")
print(f" 平均: {optimal['mean']:.3f}")
print(f" 標準偏差: {optimal['std']:.4f}")
print(f" 変動係数(CV): {(optimal['std']/optimal['mean']*100):.2f}%")
print("\n" + "=" * 80)
def plot_main_effects(self):
"""主効果図のプロット"""
if not self.results:
raise ValueError("Run experiment first")
n_factors = len(self.control_factors)
fig, axes = plt.subplots(1, n_factors, figsize=(5*n_factors, 4))
if n_factors == 1:
axes = [axes]
for i, (factor_name, factor_levels) in enumerate(self.control_factors.items()):
# 各水準でのS/N比平均を計算
level_sn_means = []
for level in factor_levels:
sn_values = [r['sn_ratio'] for r in self.results
if r['control_factors'][factor_name] == level]
level_sn_means.append(np.mean(sn_values))
axes[i].plot(factor_levels, level_sn_means,
marker='o', linewidth=2.5, markersize=10,
color='#11998e')
axes[i].set_xlabel(factor_name, fontsize=11, fontweight='bold')
axes[i].set_ylabel('Mean S/N Ratio (dB)', fontsize=10)
axes[i].set_title(f'Main Effect: {factor_name}',
fontsize=12, fontweight='bold')
axes[i].grid(True, alpha=0.3)
plt.suptitle('Main Effects Plot for S/N Ratios',
fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
# 使用例: 射出成形のロバスト設計
np.random.seed(42)
# 制御因子(最適化したいパラメータ)
control_factors = {
'Temperature': [190, 200, 210], # ℃
'Pressure': [90, 100, 110], # MPa
'Cooling_Time': [25, 30, 35] # sec
}
# ノイズ因子(制御できない変動要因)
noise_factors = {
'Ambient_Temp': [15, 25, 35], # ℃
'Material_Lot': ['A', 'B']
}
# 応答関数(実際の実験では測定値を使用)
def injection_molding_response(control, noise):
"""射出成形の寸法応答をシミュレート
理想的な条件: 温度200℃、圧力100MPa、冷却30秒
"""
# 目標寸法
target = 50.0 # mm
# 制御因子の影響
temp_effect = 0.01 * (control['Temperature'] - 200)
pressure_effect = 0.005 * (control['Pressure'] - 100)
cooling_effect = -0.002 * (control['Cooling_Time'] - 30)
# ノイズ因子の影響
ambient_effect = 0.003 * (noise['Ambient_Temp'] - 25)
lot_effect = 0.1 if noise['Material_Lot'] == 'B' else 0
# 測定誤差
measurement_error = np.random.normal(0, 0.05)
dimension = (target + temp_effect + pressure_effect +
cooling_effect + ambient_effect + lot_effect +
measurement_error)
return dimension
# タグチ実験の実行
taguchi = TaguchiRobustDesign(control_factors, noise_factors)
taguchi.run_experiment(injection_molding_response)
# 結果表示
taguchi.print_results()
# 主効果図
taguchi.plot_main_effects()
# 期待される出力:
# ================================================================================
# タグチメソッド - ロバストパラメータ設計
# ================================================================================
#
# 【実験条件】
# 制御因子数: 3
# ノイズ因子数: 2
# 実験回数: 27
#
# 【実験結果(上位5条件)】
# 順位 S/N比 平均 標準偏差 制御因子設定
# --------------------------------------------------------------------------------
# 1 37.89 50.012 0.0412 Temperature=200, Pressure=100, Cooling_Time=30
# 2 36.54 50.024 0.0478 Temperature=200, Pressure=90, Cooling_Time=30
# 3 36.21 50.018 0.0501 Temperature=200, Pressure=110, Cooling_Time=30
# 4 35.87 50.135 0.0523 Temperature=190, Pressure=100, Cooling_Time=30
# 5 35.43 49.889 0.0547 Temperature=210, Pressure=100, Cooling_Time=30
#
# 【最適条件】
# Temperature: 200
# Pressure: 100
# Cooling_Time: 30
#
# 【予測性能】
# S/N比: 37.89 dB
# 平均: 50.012
# 標準偏差: 0.0412
# 変動係数(CV): 0.08%
#
# ================================================================================
この章を完了すると、以下を説明・実装できるようになります:
Q1: あるプロセスのDPMOが233の場合、シグマレベルはいくつですか?(最も近い値を選択)
a) 3σ
b) 4σ
c) 5σ
d) 6σ
正解: c) 5σ
解説:
シグマレベル対応表より、5σのDPMOは233です。これは歩留まり99.977%に相当し、「良好」な品質レベルと評価されます。
Q2: Gage R&R分析で測定システム変動が全体変動の25%を占めていました。この測定システムは使用可能ですか?理由とともに答えてください。
正解: 条件付き合格(Marginal)
理由:
AIAG(Automotive Industry Action Group)基準では:
- < 10%: Acceptable(良好)
- 10-30%: Marginal(改善が望ましい)
- > 30%: Not Acceptable(使用不可)
25%は「Marginal」範囲にあり、使用は可能ですが改善が推奨されます。測定の繰り返し性や再現性を向上させる対策(測定者トレーニング、測定器校正など)を検討すべきです。
Q3: あるシックスシグマプロジェクトで、初期投資500万円、年間削減額600万円が見込まれています。割引率を5%として、3年間のNPV(正味現在価値)を計算してください。このプロジェクトは経済的に妥当ですか?
計算:
NPV = -初期投資 + Σ(年間CF / (1+割引率)^年)
NPV = -5,000,000 + 6,000,000/(1.05)^1 + 6,000,000/(1.05)^2 + 6,000,000/(1.05)^3
NPV = -5,000,000 + 5,714,286 + 5,442,177 + 5,183,026
NPV = 11,339,489円
正解: NPV = 約11.3百万円 → 経済的に妥当
総合評価:
結論: 非常に優れた投資案件。NPVが1,000万円以上あり、1年以内に投資回収できるため、優先的に実行すべきです。