100本ノック / 確率統計 / 確率・統計理論100本ノック

製造業の品質管理で学ぶ確率入門|不良率分析のPython 10本ノック

不良率の「偶然」と「構造」を見分ける確率入門:製造品質データで学ぶ10本ノック

本記事は、製造業の品質管理を題材に、確率論の土台である事象、確率公理、条件付き確率、独立性、ベイズの定理、大数の法則を、ひと続きの意思決定として学ぶ実務ノートです。3つの生産ラインから得られた架空の検査記録を使い、「全体不良率は何を隠すのか」「不良品が見つかったとき、どのラインを優先調査するか」「標本数が少ない速報値をどこまで信じるか」をPythonで確認します。

この「確率・統計理論100本ノック」は、公式を暗記するのではなく、製造・品質・保全・需給の判断を数理的に説明できる状態を目指す全100問のシリーズです。最初の10問では、以後の確率分布、推定、検定を支える共通言語を整えます。

[!NOTE] 本資料は、数理工房 (もしくは代表である和山個人) が過去に企業研修において使用した notebook を企業様の許可を得て再構成・編集のうえ公開しています。
掲載データはすべて架空のものであり、実在する企業・工場・数値とは一切関係ありません。

はじめに:この記事で扱う製造業の実務課題

品質会議では「今月の不良率は3.1%だった」という全体値が最初に報告されます。しかし、必要な意思決定はその先にあります。夜勤で不良が増えたのか、特定ラインの構成比が変わったのか、不良品の原因調査をどこから始めるべきか、そして観測された差が少数データによる偶然ではないかを区別しなければなりません。

本記事の到達点は、確率を単なる割合ではなく、対象範囲(標本空間)と条件を明記した意思決定の尺度として扱えるようになることです。

現場でよくある状況

  • ライン別・シフト別の生産数が異なるのに、単純な不良件数だけで優先順位を決めている
  • 「夜勤」と「不良」の重なりを見ず、それぞれの割合だけを比較している
  • 不良品の多くがラインA由来だから、ラインAの品質が最悪だと判断している
  • 朝一番の数十個の検査結果で、その日の不良率を断定している
  • ダッシュボードの分母、除外条件、欠測の扱いが部署ごとに異なる

これらは、集合、条件付き確率、全確率、ベイズ更新という基礎概念で整理できます。

なぜこの問題は判断が難しいのか

第一に、不良率は「不良品数 / 検査数」という比率であり、分母となる製品集合を変えると値も変わります。第二に、ラインやシフトの構成比と、それぞれの条件下の不良率が同時に全体値へ影響します。第三に、有限個の検査では標本変動が避けられません。

したがって、分析前に OmegaOmega(対象となる全製品)、事象 AA(関心のある製品集合)、条件 BB(比較対象)を定義し、最後に標本数による不確実性を確認する必要があります。

今回扱うノックの全体像

No.テーマ製造業での問い
001集合と事象不良・夜勤・ラインをどう定義するか
002標本空間どの製品群を確率計算の分母にするか
003確率公理KPIとしての確率が満たすべき整合性は何か
004加法定理「不良または夜勤」の重複をどう除くか
005条件付き確率夜勤に限定した不良率はいくつか
006乗法定理条件を順に分解して同時発生を求められるか
007独立事象夜勤と不良を無関係とみなしてよいか
008全確率の定理ライン別不良率から全体不良率を再構成できるか
009ベイズの定理不良品を観測した後、製造ラインの可能性はどう変わるか
010モンテカルロ法と大数の法則検査数を増やすと速報値はどう安定するか

Python環境の準備

外部データには依存せず、numpypandasmatplotlibだけを使います。乱数シードを固定するため、再実行しても同じ架空データと図が得られます。

import sys
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import japanize_matplotlib
from IPython.display import display

SEED = 20260711
rng = np.random.default_rng(SEED)
plt.rcParams["figure.figsize"] = (8, 4.5)
plt.rcParams["axes.unicode_minus"] = False

print(f"Python     : {sys.version.split()[0]}")
print(f"NumPy      : {np.__version__}")
print(f"pandas     : {pd.__version__}")
print(f"Matplotlib : {matplotlib.__version__}")
print(f"random seed: {SEED}")
Python     : 3.13.1
NumPy      : 2.5.1
pandas     : 3.0.3
Matplotlib : 3.11.0
random seed: 20260711

架空データの作成

12,000個の製品について、製造ライン、シフト、設備温度帯、不良有無を生成します。ラインごとに基礎不良確率が異なり、夜勤と高温時には不良確率が上昇する設定です。これは因果関係を証明するデータではなく、確率概念を確認するための架空シナリオです。

n_units = 12_000
line = rng.choice(["A", "B", "C"], size=n_units, p=[0.45, 0.35, 0.20])
shift = rng.choice(["日勤", "夜勤"], size=n_units, p=[0.70, 0.30])
temperature = rng.choice(["通常", "高温"], size=n_units, p=[0.88, 0.12])

base_rate = pd.Series(line).map({"A": 0.015, "B": 0.030, "C": 0.050}).to_numpy()
defect_probability = base_rate + (shift == "夜勤") * 0.012 + (temperature == "高温") * 0.018
is_defect = rng.random(n_units) < defect_probability

df = pd.DataFrame({
    "product_id": [f"P{i:05d}" for i in range(1, n_units + 1)],
    "line": line,
    "shift": shift,
    "temperature": temperature,
    "is_defect": is_defect,
})
df["result"] = np.where(df["is_defect"], "不良", "良品")

display(df.head())
summary = (df.groupby(["line", "shift"], observed=True)
             .agg(検査数=("product_id", "size"), 不良数=("is_defect", "sum"), 不良率=("is_defect", "mean")))
display(summary.style.format({"不良率": "{:.2%}"}))
product_id line shift temperature is_defect result
0 P00001 A 日勤 高温 False 良品
1 P00002 C 日勤 通常 False 良品
2 P00003 B 日勤 通常 False 良品
3 P00004 B 日勤 高温 False 良品
4 P00005 C 日勤 通常 False 良品
    検査数 不良数 不良率
line shift      
A 夜勤 1618 36 2.22%
日勤 3786 58 1.53%
B 夜勤 1281 52 4.06%
日勤 2955 108 3.65%
C 夜勤 673 45 6.69%
日勤 1687 73 4.33%

生成結果から、ラインとシフトによって検査数も不良率も異なることが分かります。以降は同じデータを異なる確率概念で読み替え、各概念がどの判断を支えるのかを確認します。

No.001:集合と事象

実務での意味

品質条件を集合として定義すると、「不良かつ夜勤」「不良または高温」といった複合条件を曖昧さなく表現できます。データ抽出条件の仕様書にも、そのまま対応します。

分析・モデル化の考え方

全検査製品の集合を OmegaOmega、不良品の事象を DD、夜勤品を NN とします。共通部分 DND\cap N は夜勤の不良品、和集合 DND\cup N は不良品または夜勤品、補集合 DcD^c は良品です。事象は「起きる・起きない」の結果ではなく、条件を満たす標本の集合です。

Pythonで確認する

D = set(df.loc[df["is_defect"], "product_id"])
N = set(df.loc[df["shift"] == "夜勤", "product_id"])
omega = set(df["product_id"])

set_counts = pd.Series({
    "不良 D": len(D),
    "夜勤 N": len(N),
    "不良かつ夜勤 D∩N": len(D & N),
    "不良または夜勤 D∪N": len(D | N),
    "良品 D^c": len(omega - D),
})
display(set_counts.to_frame("製品数"))

set_counts.plot(kind="bar", color="#4472C4")
plt.title("品質事象を集合として数えた結果")
plt.xlabel("事象")
plt.ylabel("製品数")
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
製品数
不良 D 372
夜勤 N 3572
不良かつ夜勤 D∩N 133
不良または夜勤 D∪N 3811
良品 D^c 11628

png

結果の読み取り

「不良」と「夜勤」は重なりを持つため、両者の件数を足した値は「不良または夜勤」の件数になりません。KPIの抽出条件は、AND・OR・NOTと分母を同時に定義する必要があります。

No.002:標本空間とは何か

実務での意味

標本空間は、確率計算で起こり得る結果をすべて集めた集合です。対象月、対象工場、再検品を含むかなどが違えば標本空間も変わり、比較する不良率の意味も変わります。

分析・モデル化の考え方

ここでは1製品の結果を (line,shift,result)(\text{line},\text{shift},\text{result}) で表します。ライン3通り、シフト2通り、検査結果2通りなので、理論上の標本空間は 3×2×2=123\times2\times2=12 通りです。各製品はいずれか1つの要素に属します。

Pythonで確認する

from itertools import product

sample_space = list(product(["A", "B", "C"], ["日勤", "夜勤"], ["良品", "不良"]))
observed = (df.groupby(["line", "shift", "result"], observed=False)
              .size().reindex(pd.MultiIndex.from_tuples(sample_space), fill_value=0))
space_table = observed.rename("製品数").reset_index()
space_table.columns = ["ライン", "シフト", "結果", "製品数"]
display(space_table)

labels = space_table["ライン"] + "・" + space_table["シフト"] + "・" + space_table["結果"]
plt.bar(labels, space_table["製品数"], color="#70AD47")
plt.title("標本空間12通りの観測度数")
plt.xlabel("(ライン, シフト, 検査結果)")
plt.ylabel("製品数")
plt.xticks(rotation=60, ha="right")
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
ライン シフト 結果 製品数
0 A 日勤 良品 3728
1 A 日勤 不良 58
2 A 夜勤 良品 1582
3 A 夜勤 不良 36
4 B 日勤 良品 2847
5 B 日勤 不良 108
6 B 夜勤 良品 1229
7 B 夜勤 不良 52
8 C 日勤 良品 1614
9 C 日勤 不良 73
10 C 夜勤 良品 628
11 C 夜勤 不良 45

png

結果の読み取り

12通りを明示すると、データに存在しない組合せも含めて監視できます。実務では「観測件数0」が、起こり得ないのか、今月たまたま無かったのか、収集漏れなのかを区別することが重要です。

No.003:確率公理

実務での意味

複数の品質KPIを組み合わせる前に、確率として整合しているかを確認します。割合が負、全分類の合計が100%でない、重複しない分類の合計と全体が一致しない場合は、集計仕様やデータ品質に問題があります。

分析・モデル化の考え方

確率 PP は、(1) P(A)0P(A)\geq0、(2) P(Ω)=1P(\Omega)=1、(3) 互いに排反な AiA_i に対して P(iAi)=iP(Ai)P(\cup_i A_i)=\sum_i P(A_i) を満たします。良品 DcD^c と不良 DD は排反で、その和集合は OmegaOmega です。

Pythonで確認する

p_defect = df["is_defect"].mean()
p_pass = 1 - p_defect
p_omega = len(omega) / len(omega)

axioms = pd.DataFrame({
    "確認項目": ["非負性 P(D) ≥ 0", "全事象 P(Ω) = 1", "排反加法 P(D)+P(D^c)=P(Ω)"],
    "左辺": [p_defect, p_omega, p_defect + p_pass],
    "期待値": [0.0, 1.0, 1.0],
    "判定": [p_defect >= 0, np.isclose(p_omega, 1), np.isclose(p_defect + p_pass, 1)],
})
display(axioms.style.format({"左辺": "{:.4f}", "期待値": "{:.4f}"}))

plt.bar(["不良 P(D)", "良品 P(D^c)"], [p_defect, p_pass], color=["#C00000", "#70AD47"])
plt.title("排反な検査結果の確率")
plt.xlabel("検査結果")
plt.ylabel("確率")
plt.ylim(0, 1.05)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
  確認項目 左辺 期待値 判定
0 非負性 P(D) ≥ 0 0.0310 0.0000 True
1 全事象 P(Ω) = 1 1.0000 1.0000 True
2 排反加法 P(D)+P(D^c)=P(Ω) 1.0000 1.0000 True

png

結果の読み取り

良品と不良の確率は非負で、合計は1です。公理は当然に見えますが、分類の重複や欠測を発見する検算ルールとして、ETL処理や品質ダッシュボードの自動テストに利用できます。

No.004:加法定理の導出

実務での意味

「不良品または夜勤品を重点確認する」のように対象条件をORで結ぶとき、重複対象を二重計上しないための式です。必要工数や隔離スペースの見積りに直結します。

分析・モデル化の考え方

2事象の加法定理は次式です。

P(DN)=P(D)+P(N)P(DN)P(D\cup N)=P(D)+P(N)-P(D\cap N)

DDNN を単純に足すと共通部分を2回数えるため、1回分を引きます。排反なら共通部分は0となり、確率の和だけで計算できます。

Pythonで確認する

p_D = len(D) / n_units
p_N = len(N) / n_units
p_D_and_N = len(D & N) / n_units
p_union_direct = len(D | N) / n_units
p_union_formula = p_D + p_N - p_D_and_N

addition = pd.DataFrame({
    "計算": ["集合から直接", "加法定理", "誤った単純加算"],
    "確率": [p_union_direct, p_union_formula, p_D + p_N],
})
display(addition.style.format({"確率": "{:.2%}"}))

plt.bar(addition["計算"], addition["確率"], color=["#4472C4", "#70AD47", "#C00000"])
plt.title("OR条件の確率:重複控除の効果")
plt.xlabel("計算方法")
plt.ylabel("確率")
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
  計算 確率
0 集合から直接 31.76%
1 加法定理 31.76%
2 誤った単純加算 32.87%

png

結果の読み取り

直接集計と加法定理は一致し、単純加算だけが共通部分の分だけ過大になります。対象製品の隔離数や再検査工数を見積もる際には、条件間の重複を必ず控除します。

No.005:条件付き確率

実務での意味

全体不良率ではなく「夜勤で製造されたという条件の下での不良率」を求めると、シフト固有のリスクを比較できます。条件を付けることは、分析対象の分母を夜勤品へ絞り込むことです。

分析・モデル化の考え方

P(N)>0P(N)>0 のとき、条件付き確率は

P(DN)=P(DN)P(N)P(D\mid N)=\frac{P(D\cap N)}{P(N)}

です。分子は夜勤かつ不良、分母は夜勤の全製品です。P(DN)P(D\mid N)P(ND)P(N\mid D) は分母が異なるため、取り違えてはいけません。

Pythonで確認する

conditional_rates = (df.groupby("shift", observed=True)["is_defect"]
                       .agg([("検査数", "size"), ("不良数", "sum"), ("条件付き不良率", "mean")]))
display(conditional_rates.style.format({"条件付き不良率": "{:.2%}"}))

p_D_given_N_formula = p_D_and_N / p_N
print(f"定義式による P(不良 | 夜勤) = {p_D_given_N_formula:.2%}")

conditional_rates["条件付き不良率"].plot(kind="bar", color=["#5B9BD5", "#ED7D31"])
plt.title("シフトを条件とした不良率")
plt.xlabel("シフト")
plt.ylabel("不良率")
plt.xticks(rotation=0)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
  検査数 不良数 条件付き不良率
shift      
夜勤 3572 133 3.72%
日勤 8428 239 2.84%
定義式による P(不良 | 夜勤) = 3.72%


png

結果の読み取り

夜勤の条件付き不良率は日勤より高くなっています。ただし、これは優先調査の根拠にはなっても、夜勤が不良の原因だという証明ではありません。ライン構成、温度、作業条件などの交絡要因を追加確認します。

No.006:乗法定理

実務での意味

「夜勤品であり、かつ不良である」割合は、工程を段階的に分けて見積もれます。生産計画上の夜勤比率と夜勤不良率から、夜勤不良品の発生数や損失額を見積もる場合に使います。

分析・モデル化の考え方

条件付き確率の定義を変形すると、乗法定理

P(ND)=P(N)P(DN)P(N\cap D)=P(N)P(D\mid N)

を得ます。一般に順序を入れ替えた P(D)P(ND)P(D)P(N\mid D) でも同じ同時確率になります。

Pythonで確認する

p_N_given_D = p_D_and_N / p_D
multiplication = pd.DataFrame({
    "求め方": ["直接集計", "P(夜勤)×P(不良|夜勤)", "P(不良)×P(夜勤|不良)"],
    "同時確率": [p_D_and_N, p_N * p_D_given_N_formula, p_D * p_N_given_D],
})
display(multiplication.style.format({"同時確率": "{:.4%}"}))

plt.bar(multiplication["求め方"], multiplication["同時確率"], color=["#4472C4", "#70AD47", "#A5A5A5"])
plt.title("乗法定理による夜勤かつ不良の確率")
plt.xlabel("計算経路")
plt.ylabel("同時確率")
plt.xticks(rotation=12)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
  求め方 同時確率
0 直接集計 1.1083%
1 P(夜勤)×P(不良|夜勤) 1.1083%
2 P(不良)×P(夜勤|不良) 1.1083%

png

結果の読み取り

3つの経路は同じ値になります。実務では、直接の同時件数がまだ得られない計画段階でも、生産構成比と条件付き不良率があれば、発生見込みを分解して説明できます。

No.007:独立事象

実務での意味

2事象を独立と仮定できればモデルは簡単になりますが、誤った独立仮定は不良見込みを過小評価します。シフトと不良が独立かを確認することは、人員配置や設備条件の調査優先度に関わります。

分析・モデル化の考え方

DDNN が独立なら、

P(DN)=P(D)P(N)P(D\cap N)=P(D)P(N)

かつ P(DN)=P(D)P(D\mid N)=P(D) が成り立ちます。有限データでは完全一致しないため、ここでは差と比を診断量として確認します。正式な判断には、後続章で扱う信頼区間や仮説検定が必要です。

Pythonで確認する

independence = pd.DataFrame({
    "指標": ["観測 P(D∩N)", "独立仮定 P(D)P(N)", "全体 P(D)", "夜勤 P(D|N)"],
    "確率": [p_D_and_N, p_D * p_N, p_D, p_D_given_N_formula],
})
display(independence.style.format({"確率": "{:.3%}"}))
print(f"夜勤の相対リスク P(D|N) / P(D) = {p_D_given_N_formula / p_D:.2f} 倍")

plt.bar(independence["指標"], independence["確率"], color=["#ED7D31", "#A5A5A5", "#4472C4", "#C00000"])
plt.title("独立仮定と観測確率の比較")
plt.xlabel("確率指標")
plt.ylabel("確率")
plt.xticks(rotation=18)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
  指標 確率
0 観測 P(D∩N) 1.108%
1 独立仮定 P(D)P(N) 0.923%
2 全体 P(D) 3.100%
3 夜勤 P(D|N) 3.723%
夜勤の相対リスク P(D|N) / P(D) = 1.20 倍


png

結果の読み取り

夜勤の不良率は全体不良率と一致せず、観測された同時確率も独立仮定の積より大きくなっています。このデータでは独立を前提にせず、少なくともシフト別に管理するのが安全です。ただし、差の原因特定には追加分析が必要です。

No.008:全確率の定理

実務での意味

全体不良率は、ライン別不良率の単純平均ではありません。各ラインの生産構成比で重み付けした平均です。そのため、設備を改善して各ラインの不良率が変わらなくても、生産移管だけで全体不良率は変化します。

分析・モデル化の考え方

ライン LiL_i が標本空間を重複なく分割するとき、

P(D)=iP(DLi)P(Li)P(D)=\sum_i P(D\mid L_i)P(L_i)

です。各項は「そのラインから不良品が出る同時確率」であり、全体不良率への寄与度でもあります。

Pythonで確認する

line_stats = (df.groupby("line", observed=True)["is_defect"]
                .agg([("検査数", "size"), ("不良数", "sum"), ("ライン内不良率", "mean")]))
line_stats["生産構成比"] = line_stats["検査数"] / n_units
line_stats["全体不良率への寄与"] = line_stats["生産構成比"] * line_stats["ライン内不良率"]
display(line_stats.style.format({
    "ライン内不良率": "{:.2%}", "生産構成比": "{:.2%}", "全体不良率への寄与": "{:.2%}"
}))
print(f"寄与度の合計 = {line_stats['全体不良率への寄与'].sum():.2%}")
print(f"直接集計 P(D) = {p_D:.2%}")

line_stats["全体不良率への寄与"].plot(kind="bar", color="#5B9BD5")
plt.title("ライン別の全体不良率への寄与")
plt.xlabel("ライン")
plt.ylabel("寄与度")
plt.xticks(rotation=0)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
  検査数 不良数 ライン内不良率 生産構成比 全体不良率への寄与
line          
A 5404 94 1.74% 45.03% 0.78%
B 4236 160 3.78% 35.30% 1.33%
C 2360 118 5.00% 19.67% 0.98%
寄与度の合計 = 3.10%
直接集計 P(D) = 3.10%


png

結果の読み取り

寄与度の合計は直接集計した全体不良率と一致します。改善優先順位はライン内不良率だけでなく、生産構成比を掛けた寄与度、改善可能性、損失単価を合わせて決めるべきです。

No.009:ベイズの定理

実務での意味

不良品が1個見つかったとき、どのラインから来た可能性が高いかを更新します。ライン内不良率が最も高いラインと、不良品全体の発生源として最も多いラインは、生産量の違いによって一致しないことがあります。

分析・モデル化の考え方

不良品を観測した後のライン LiL_i の確率は、

P(LiD)=P(DLi)P(Li)P(D)P(L_i\mid D)=\frac{P(D\mid L_i)P(L_i)}{P(D)}

です。P(Li)P(L_i) は事前確率(生産構成比)、P(DLi)P(D\mid L_i) は尤度、P(LiD)P(L_i\mid D) は事後確率です。分子はNo.008の寄与度に一致します。

Pythonで確認する

bayes = line_stats[["生産構成比", "ライン内不良率", "全体不良率への寄与"]].copy()
bayes["不良観測後のライン確率"] = bayes["全体不良率への寄与"] / p_D
bayes["不良品から直接集計"] = df.loc[df["is_defect"], "line"].value_counts(normalize=True).sort_index()
display(bayes.style.format("{:.2%}"))

x = np.arange(len(bayes))
width = 0.35
plt.bar(x - width/2, bayes["生産構成比"], width, label="観測前(生産構成比)", color="#A5A5A5")
plt.bar(x + width/2, bayes["不良観測後のライン確率"], width, label="不良観測後", color="#ED7D31")
plt.title("不良観測による製造ライン確率の更新")
plt.xlabel("ライン")
plt.ylabel("確率")
plt.xticks(x, bayes.index)
plt.legend()
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()
  生産構成比 ライン内不良率 全体不良率への寄与 不良観測後のライン確率 不良品から直接集計
line          
A 45.03% 1.74% 0.78% 25.27% 25.27%
B 35.30% 3.78% 1.33% 43.01% 43.01%
C 19.67% 5.00% 0.98% 31.72% 31.72%

png

結果の読み取り

不良観測後は、高い不良率を持つラインの確率が生産構成比より上昇します。ベイズの定理は原因を確定する式ではなく、観測前の構成比と各条件下の起こりやすさを統合して、調査順序を合理的に更新する式です。

No.010:モンテカルロ法と大数の法則

実務での意味

少数の抜取検査では、不良率の速報値が大きく振れます。検査数を増やしたときに推定値がどの程度安定するかをシミュレーションすれば、速報値の扱い、検査コスト、必要標本数を議論できます。

分析・モデル化の考え方

独立で同じ分布に従うベルヌーイ変数 Xi{0,1}X_i\in\{0,1\} の平均

Xˉn=1ni=1nXi\bar{X}_n=\frac{1}{n}\sum_{i=1}^n X_i

は、nn が大きくなると真の不良確率 pp に近づきます。これが大数の法則です。モンテカルロ法では乱数による試行を繰り返し、推定量のばらつきや判断誤差を数値的に把握します。

Pythonで確認する

true_rate = p_D
mc_rng = np.random.default_rng(SEED + 1)
sample_sizes = [20, 50, 100, 500, 2_000]
n_repeats = 2_000

mc_rows = []
for n in sample_sizes:
    estimates = mc_rng.binomial(n, true_rate, size=n_repeats) / n
    mc_rows.append({
        "検査数 n": n,
        "推定不良率の平均": estimates.mean(),
        "標準偏差": estimates.std(ddof=1),
        "平均絶対誤差": np.mean(np.abs(estimates - true_rate)),
    })
mc_summary = pd.DataFrame(mc_rows)
display(mc_summary.style.format({
    "推定不良率の平均": "{:.3%}", "標準偏差": "{:.3%}", "平均絶対誤差": "{:.3%}"
}))

stream = mc_rng.random(20_000) < true_rate
running_rate = np.cumsum(stream) / np.arange(1, len(stream) + 1)
plt.plot(np.arange(1, len(stream) + 1), running_rate, label="累積推定不良率", color="#4472C4")
plt.axhline(true_rate, color="#C00000", linestyle="--", label=f"基準不良率 {true_rate:.2%}")
plt.xscale("log")
plt.title("検査数の増加と推定不良率の収束")
plt.xlabel("累積検査数(対数目盛)")
plt.ylabel("推定不良率")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
  検査数 n 推定不良率の平均 標準偏差 平均絶対誤差
0 20 3.143% 3.962% 3.366%
1 50 3.070% 2.393% 1.984%
2 100 3.066% 1.761% 1.378%
3 500 3.094% 0.767% 0.610%
4 2000 3.117% 0.389% 0.307%

png

結果の読み取り

反復シミュレーションでは、検査数が増えるほど推定値の標準偏差と平均絶対誤差が小さくなります。累積不良率も序盤は大きく振れますが、次第に基準値の近くへ安定します。大数の法則は「少数でも正しい」ことを保証しないため、速報値には分母と不確実性を併記します。

対象ノックを通して見える実務上の示唆

  1. 定義が分析品質を決める:標本空間、事象、期間、除外条件を最初に固定しなければ、正しい計算でも比較不能になります。
  2. 全体値は構造に分解する:全確率の定理で、構成比と条件付き不良率を分けると、悪化が生産移管によるものか工程内変化によるものかを説明できます。
  3. 原因候補の優先順位はベイズ更新する:高不良率だけでなく生産量も含め、不良品全体への寄与から調査順序を決めます。
  4. 独立は仮定ではなく検証対象である:簡略化のために独立を置く場合、その影響と診断結果を明示します。
  5. 小標本の速報値には幅がある:点推定だけでアラートを発報せず、標本数、管理限界、誤報コストを設計します。

実務導入する場合に必要なこと

  • product_id、設備、ライン、シフト、時刻、検査結果を追跡できるデータモデル
  • 不良・再検・手直し・廃棄の業務定義と、欠測・重複・再測定の処理規則
  • ライン別条件付き不良率と生産構成比を同時表示するダッシュボード
  • 分母急減や分類合計不一致を検知するデータ品質テスト
  • 統計的な差と、停止・調整に値する実務上の差を分けたアラート基準
  • 原因調査、対策実施、効果検証までを結ぶ責任者と運用手順

確率計算だけでは改善は完了しません。現場知識、測定システム、損失額、対策可能性と結び付けて初めて意思決定に使えます。

まとめ

No.001〜No.010では、製造品質データを使い、集合から大数の法則までを一つの流れで確認しました。重要なのは、全体不良率という答えを急いで出すことではなく、何を分母にし、何を条件とし、どの仮定で計算し、標本変動をどう扱ったかを説明できることです。

次の分析では、この土台の上に確率分布、期待値・分散、統計推定、仮説検定を積み上げることで、改善効果や将来リスクを定量化できます。

法人向けのご相談

数理工房では、製造業の品質管理・異常検知・需要予測・数理最適化に関するデータ分析支援、PoC設計、社内研修をご支援しています。現場のKPI定義から分析基盤、意思決定プロセスへの実装まで、課題に合わせて整理します。

📩 お問い合わせ: surikobo.co.jp/contact
まずはお気軽にご相談ください。