100本ノック / 数理モデリング / 数理モデリング100本ノック
需要変動と設備故障を「確率」で意思決定へ変える
需要変動と設備故障を「確率」で意思決定へ変える
重要交換部品で学ぶ不確実性とリスクのモデル化 No.061〜No.070
本記事では、架空の産業設備向け重要交換部品と熱処理設備を題材に、需要・納期・歩留まり・故障時期を確率変数として表します。期待値、標準偏差、確率分布、予測区間、欠品確率、故障確率、期待損失を使い、平均だけでは見えないリスクを在庫・保全判断へ接続します。
[!NOTE] 本資料は、数理工房 (もしくは代表である和山個人) が過去に企業研修において使用した notebook を企業様の許可を得て再構成・編集のうえ公開しています。 掲載データはすべて架空のものであり、実在する企業・工場・数値とは一切関係ありません。
はじめに:この記事で扱う製造業の実務課題
交換部品メーカーでは、需要急増による欠品と、熱処理設備の突発故障が納期を左右しています。平均需要と平均故障間隔だけで計画すると、通常時は効率的でも、変動が重なったときの損失を説明できません。
365日分の3製品需要と、設備寿命の架空モデルを使い、平均的成果と悪化時の損失を同時に比較します。
現場でよくある状況
- 平均需要だけで在庫を決め、需要のばらつきを見ていない
- 期待利益が同じ案を同等と扱い、損失の大きさを比較しない
- 個数・納期・歩留まりに同じ正規分布を機械的に当てる
- 売上予測を1本の数値で示し、予測幅を経営会議へ出さない
- 故障確率と故障時損失を分けて管理している
- 期待値最大案だけを選び、最悪ケースへの耐性を確認しない
なぜこの問題は判断が難しいのか
不確実性は「値が確定していないこと」、リスクは「不確実な結果が業務損失へつながること」です。発生確率が低くても影響が大きい故障と、頻繁だが影響が小さい需要変動を同じ基準で比較する必要があります。
平均だけでなく、分布、上位分位点、損失確率、期待損失、悪化時平均を確認します。
今回扱うノックの全体像
| No. | テーマ | 判断 |
|---|---|---|
| 061 | 確率変数 | 需要・納期・歩留まりをどう表すか |
| 062 | 期待値 | 平均的成果をどう比較するか |
| 063 | 分散・標準偏差 | ばらつきをどう測るか |
| 064 | 分布選択 | 業務量に合う分布は何か |
| 065 | 需要分布 | リードタイム需要をどう表すか |
| 066 | 売上予測幅 | 売上の上下限をどう示すか |
| 067 | 欠品確率 | 在庫水準ごとの欠品リスクは何%か |
| 068 | 故障確率 | 保全間隔内の故障確率は何%か |
| 069 | 期待損失 | 確率と影響をどう統合するか |
| 070 | 意思決定 | 期待値と悪化時リスクをどう両立するか |
Python 環境の準備
外部データは使いません。乱数シードを固定し、NumPy・pandas・SciPy・matplotlibでシミュレーションします。
%matplotlib inline
%config InlineBackend.figure_format = 'svg'
import platform, sys
import matplotlib, matplotlib.pyplot as plt
from matplotlib import font_manager
import numpy as np, pandas as pd, scipy
from scipy import stats
from IPython.display import display
SEED=42; rng=np.random.default_rng(SEED)
fonts={f.name for f in font_manager.fontManager.ttflist}
plot_font=next((f for f in ["Hiragino Sans","Yu Gothic","Noto Sans CJK JP"] if f in fonts),"sans-serif")
plt.rcParams["font.family"]=plot_font; plt.rcParams["axes.unicode_minus"]=False
print(f"Python {sys.version.split()[0]} / NumPy {np.__version__} / pandas {pd.__version__} / SciPy {scipy.__version__}")
print(f"matplotlib {matplotlib.__version__} / font {plot_font} / seed {SEED} / {platform.platform()}")
Python 3.13.1 / NumPy 2.5.1 / pandas 3.0.3 / SciPy 1.18.0
matplotlib 3.11.0 / font Hiragino Sans / seed 42 / macOS-26.3-arm64-arm-64bit-Mach-O
架空データの作成
製品A・B・Cの365日需要を負の二項分布から生成します。平均需要と分散は製品ごとに異なり、曜日・季節の倍率を加えます。設備寿命は形状母数2.2、尺度母数420日のワイブル分布とします。
dates=pd.date_range("2025-01-01",periods=365,freq="D")
specs=pd.DataFrame({"product":["A","B","C"],"mean_demand":[95,58,28],"dispersion":[18,10,5],"price":[5200,8400,14500],"unit_margin":[1800,3100,5600],"shortage_loss":[3500,6000,12000],"lead_days":[5,8,12]})
rows=[]
for i,date in enumerate(dates):
factor=(.48 if date.dayofweek>=5 else 1.0)*(1+.14*np.sin(2*np.pi*(i-50)/365))
for s in specs.itertuples(index=False):
mu=max(1,s.mean_demand*factor); k=s.dispersion; p=k/(k+mu)
rows.append({"date":date,"product":s.product,"demand":rng.negative_binomial(k,p),"expected":mu})
demand=pd.DataFrame(rows).merge(specs,on="product")
print(f"需要データ: {len(demand):,}行(365日×3製品)")
display(demand.head(9).style.format({"expected":"{:.1f}","price":"¥{:,.0f}"}))
需要データ: 1,095行(365日×3製品)
| date | product | demand | expected | mean_demand | dispersion | price | unit_margin | shortage_loss | lead_days | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2025-01-01 00:00:00 | A | 101 | 84.9 | 95 | 18 | ¥5,200 | 1800 | 3500 | 5 |
| 1 | 2025-01-01 00:00:00 | B | 18 | 51.8 | 58 | 10 | ¥8,400 | 3100 | 6000 | 8 |
| 2 | 2025-01-01 00:00:00 | C | 37 | 25.0 | 28 | 5 | ¥14,500 | 5600 | 12000 | 12 |
| 3 | 2025-01-02 00:00:00 | A | 95 | 85.1 | 95 | 18 | ¥5,200 | 1800 | 3500 | 5 |
| 4 | 2025-01-02 00:00:00 | B | 72 | 51.9 | 58 | 10 | ¥8,400 | 3100 | 6000 | 8 |
| 5 | 2025-01-02 00:00:00 | C | 45 | 25.1 | 28 | 5 | ¥14,500 | 5600 | 12000 | 12 |
| 6 | 2025-01-03 00:00:00 | A | 83 | 85.2 | 95 | 18 | ¥5,200 | 1800 | 3500 | 5 |
| 7 | 2025-01-03 00:00:00 | B | 88 | 52.0 | 58 | 10 | ¥8,400 | 3100 | 6000 | 8 |
| 8 | 2025-01-03 00:00:00 | C | 23 | 25.1 | 28 | 5 | ¥14,500 | 5600 | 12000 | 12 |
summary=demand.groupby("product",as_index=False).agg(平均需要=("demand","mean"),標準偏差=("demand","std"),最小=("demand","min"),最大=("demand","max"))
display(summary.style.format({"平均需要":"{:.1f}","標準偏差":"{:.1f}"}))
fig,axes=plt.subplots(1,2,figsize=(11,4.2))
for p,g in demand.groupby("product"): axes[0].plot(g["date"],g["demand"].rolling(14).mean(),label=p)
axes[0].set_title("製品別需要の14日移動平均"); axes[0].set_xlabel("日付"); axes[0].set_ylabel("需要(個/日)"); axes[0].grid(True,alpha=.3); axes[0].legend()
axes[1].boxplot([g["demand"] for _,g in demand.groupby("product")],tick_labels=["A","B","C"])
axes[1].set_title("製品別の日次需要分布"); axes[1].set_xlabel("製品"); axes[1].set_ylabel("需要(個/日)"); axes[1].grid(True,axis="y",alpha=.3)
plt.tight_layout(); plt.show()
| product | 平均需要 | 標準偏差 | 最小 | 最大 | |
|---|---|---|---|---|---|
| 0 | A | 82.4 | 32.5 | 25 | 184 |
| 1 | B | 48.9 | 23.5 | 10 | 153 |
| 2 | C | 23.7 | 13.9 | 1 | 77 |
No.061:不確実性を確率変数として表現する
実務での意味
需要、リードタイム、歩留まり、寿命を固定値ではなく確率変数として表すと、複数の結果と発生しやすさを扱えます。
分析・モデル化の考え方
を確率変数とし、需要 、納期 、歩留まり を分布で表します。単位と取り得る範囲を先に決めます。
Pythonで確認する
samples=pd.DataFrame({"日次需要":rng.negative_binomial(12,12/(12+80),10000),"リードタイム":np.maximum(1,rng.normal(8,1.5,10000)),"歩留まり":rng.beta(98,2,10000)})
display(samples.describe(percentiles=[.05,.5,.95]).T.style.format("{:.2f}"))
fig,axes=plt.subplots(1,3,figsize=(12,3.7))
for ax,col,color in zip(axes,samples.columns,["#2c7fb8","#756bb1","#2ca25f"]):
ax.hist(samples[col],bins=25,color=color,edgecolor="white"); ax.set_title(col+"の確率分布"); ax.set_xlabel(col); ax.set_ylabel("度数"); ax.grid(True,axis="y",alpha=.3)
plt.tight_layout(); plt.show()
| count | mean | std | min | 5% | 50% | 95% | max | |
|---|---|---|---|---|---|---|---|---|
| 日次需要 | 10000.00 | 80.02 | 24.70 | 13.00 | 43.00 | 77.00 | 124.00 | 224.00 |
| リードタイム | 10000.00 | 7.98 | 1.49 | 2.14 | 5.53 | 7.98 | 10.48 | 14.28 |
| 歩留まり | 10000.00 | 0.98 | 0.01 | 0.88 | 0.95 | 0.98 | 1.00 | 1.00 |
結果の読み取り
需要は非負整数、納期は正の連続値、歩留まりは0〜1です。業務量の範囲に合う分布を選ぶことが、現実的なシミュレーションの前提です。
No.062:期待値を使って平均的な結果を表現する
実務での意味
生産量候補ごとの平均利益を比較できます。ただし期待値だけでは損失の振れ幅は分かりません。
分析・モデル化の考え方
です。需要を満たした利益から売れ残り費と欠品損失を引いた利益をモンテカルロ評価します。
Pythonで確認する
d_sim=rng.choice(demand.query("product=='A'")["demand"],size=30000,replace=True)
plans=[]
for q in [60,80,100,120,140]:
sold=np.minimum(q,d_sim); leftover=np.maximum(q-d_sim,0); shortage=np.maximum(d_sim-q,0)
profit=sold*1800-leftover*250-shortage*3500
plans.append({"生産量":q,"期待利益":profit.mean(),"損失確率":(profit<0).mean(),"5%点":np.quantile(profit,.05)})
ev=pd.DataFrame(plans); display(ev.style.format({"期待利益":"¥{:,.0f}","損失確率":"{:.1%}","5%点":"¥{:,.0f}"}))
fig,ax=plt.subplots(); ax.plot(ev["生産量"],ev["期待利益"],marker="o"); ax.set_title("生産量と期待利益"); ax.set_xlabel("生産量(個/日)"); ax.set_ylabel("期待利益(円/日)"); ax.grid(True,alpha=.3); plt.tight_layout(); plt.show()
| 生産量 | 期待利益 | 損失確率 | 5%点 | |
|---|---|---|---|---|
| 0 | 60 | ¥4,188 | 39.9% | ¥-154,500 |
| 1 | 80 | ¥67,665 | 12.0% | ¥-48,500 |
| 2 | 100 | ¥108,383 | 2.3% | ¥32,400 |
| 3 | 120 | ¥126,173 | 0.3% | ¥35,600 |
| 4 | 140 | ¥129,515 | 0.0% | ¥32,650 |
結果の読み取り
期待利益最大案は平均的には有利ですが、5%点や損失確率も確認します。資金余力や顧客重要度で選択が変わります。
No.063:分散・標準偏差でばらつきを表現する
実務での意味
平均需要が同じでも標準偏差が大きい製品ほど、安全在庫や能力余裕が必要です。
分析・モデル化の考え方
、。変動係数 で規模を調整します。
Pythonで確認する
variability=demand.groupby("product")["demand"].agg(["mean","var","std"]).reset_index(); variability["CV"]=variability["std"]/variability["mean"]
display(variability.style.format({"mean":"{:.1f}","var":"{:.1f}","std":"{:.1f}","CV":"{:.2f}"}))
fig,ax=plt.subplots(); ax.bar(variability["product"],variability["CV"],color="#fdae6b"); ax.set_title("製品別の需要変動係数"); ax.set_xlabel("製品"); ax.set_ylabel("変動係数 CV"); ax.grid(True,axis="y",alpha=.3); plt.tight_layout(); plt.show()
| product | mean | var | std | CV | |
|---|---|---|---|---|---|
| 0 | A | 82.4 | 1057.0 | 32.5 | 0.39 |
| 1 | B | 48.9 | 552.5 | 23.5 | 0.48 |
| 2 | C | 23.7 | 192.7 | 13.9 | 0.59 |
結果の読み取り
標準偏差は元の個数単位、分散は個数の二乗、CVは無単位です。異なる需要規模の比較にはCVが便利です。
No.064:業務データに合う確率分布を選ぶ
実務での意味
個数、時間、割合、寿命では値域と生成過程が異なります。分布の誤選択は負の需要や100%超の歩留まりを生みます。
分析・モデル化の考え方
件数はポアソン・負の二項、正の時間はガンマ・対数正規、割合はベータ、寿命はワイブルが候補です。平均と分散、ヒストグラム、業務知識で選びます。
Pythonで確認する
dist_check=demand.groupby("product")["demand"].agg(["mean","var"]).reset_index(); dist_check["分散平均比"]=dist_check["var"]/dist_check["mean"]; dist_check["候補"] = np.where(dist_check["分散平均比"]>1.3,"負の二項分布","ポアソン分布")
display(dist_check.style.format({"mean":"{:.1f}","var":"{:.1f}","分散平均比":"{:.2f}"}))
fig,ax=plt.subplots(); ax.bar(dist_check["product"],dist_check["分散平均比"],color="#9ecae1"); ax.axhline(1,color="black",linestyle="--",label="ポアソンの目安"); ax.set_title("需要の分散平均比"); ax.set_xlabel("製品"); ax.set_ylabel("分散 ÷ 平均"); ax.grid(True,axis="y",alpha=.3); ax.legend(); plt.tight_layout(); plt.show()
| product | mean | var | 分散平均比 | 候補 | |
|---|---|---|---|---|---|
| 0 | A | 82.4 | 1057.0 | 12.84 | 負の二項分布 |
| 1 | B | 48.9 | 552.5 | 11.30 | 負の二項分布 |
| 2 | C | 23.7 | 192.7 | 8.13 | 負の二項分布 |
結果の読み取り
分散が平均を大きく上回り、単純ポアソンより過分散です。需要急増や顧客差を表せる負の二項分布が候補になります。
No.065:需要を確率分布として表現する
実務での意味
リードタイム需要の分布を作ると、発注点を平均ではなく分位点で決められます。
分析・モデル化の考え方
を履歴再抽出でシミュレーションし、平均・95%点・99%点を計算します。
Pythonで確認する
a_hist=demand.query("product=='A'")["demand"].to_numpy(); lead=5
lead_demand=rng.choice(a_hist,size=(30000,lead),replace=True).sum(axis=1)
lead_kpi=pd.DataFrame({"平均":[lead_demand.mean()],"標準偏差":[lead_demand.std()],"95%点":[np.quantile(lead_demand,.95)],"99%点":[np.quantile(lead_demand,.99)]})
display(lead_kpi.style.format("{:.0f}個"))
fig,ax=plt.subplots(); ax.hist(lead_demand,bins=35,color="#2c7fb8",edgecolor="white"); ax.axvline(np.quantile(lead_demand,.95),color="#de2d26",linestyle="--",label="95%点"); ax.set_title("製品A:5日間需要の確率分布"); ax.set_xlabel("5日間需要(個)"); ax.set_ylabel("度数"); ax.grid(True,axis="y",alpha=.3); ax.legend(); plt.tight_layout(); plt.show()
| 平均 | 標準偏差 | 95%点 | 99%点 | |
|---|---|---|---|---|
| 0 | 411個 | 72個 | 532個 | 583個 |
結果の読み取り
平均在庫では約半分のケースで上回られます。目標サービス水準に対応する分位点を発注点候補にします。
No.066:売上の予測幅をモデル化する
実務での意味
売上を一点で示さず、需要・価格・歩留まりを通じた予測幅を示すと資金・能力計画をシナリオ化できます。
分析・モデル化の考え方
月間売上 をモンテカルロ計算し、中央値と80%・95%区間を得ます。
Pythonで確認する
n=40000; monthly_demand=rng.choice(a_hist,size=(n,30),replace=True).sum(axis=1); price=rng.normal(5200,120,n); yield_rate=rng.beta(98,2,n); capacity=3000
revenue=price*np.minimum(monthly_demand,capacity*yield_rate)
qs=np.quantile(revenue,[.025,.1,.5,.9,.975]); interval=pd.DataFrame({"指標":["2.5%","10%","中央値","90%","97.5%"],"月間売上":qs}); display(interval.style.format({"月間売上":"¥{:,.0f}"}))
fig,ax=plt.subplots(); ax.hist(revenue/1e6,bins=35,color="#74c476",edgecolor="white"); ax.axvspan(qs[0]/1e6,qs[-1]/1e6,color="#fdae6b",alpha=.25,label="95%予測区間"); ax.set_title("月間売上の予測分布"); ax.set_xlabel("月間売上(百万円)"); ax.set_ylabel("度数"); ax.grid(True,axis="y",alpha=.3); ax.legend(); plt.tight_layout(); plt.show()
print(f"95%予測区間: ¥{qs[0]:,.0f}〜¥{qs[-1]:,.0f}")
| 指標 | 月間売上 | |
|---|---|---|
| 0 | 2.5% | ¥10,969,947 |
| 1 | 10% | ¥11,602,134 |
| 2 | 中央値 | ¥12,838,050 |
| 3 | 90% | ¥14,095,645 |
| 4 | 97.5% | ¥14,773,267 |
95%予測区間: ¥10,969,947〜¥14,773,267
結果の読み取り
予測幅は需要だけでなく価格と歩留まりの不確実性も含みます。下限側を資金繰り、上限側を能力計画へ使います。
No.067:欠品リスクを確率で表現する
実務での意味
在庫水準ごとにリードタイム中の欠品確率を示すと、在庫とサービスのトレードオフを説明できます。
分析・モデル化の考え方
をシミュレーションで推定します。
Pythonで確認する
stock_levels=np.arange(300,801,50); risk=pd.DataFrame({"在庫":stock_levels,"欠品確率":[np.mean(lead_demand>s) for s in stock_levels]}); display(risk.style.format({"欠品確率":"{:.1%}"}))
fig,ax=plt.subplots(); ax.plot(risk["在庫"],risk["欠品確率"]*100,marker="o",color="#de2d26"); ax.axhline(5,color="black",linestyle="--",label="5%目標"); ax.set_title("製品A:在庫水準と5日以内の欠品確率"); ax.set_xlabel("利用可能在庫(個)"); ax.set_ylabel("欠品確率(%)"); ax.grid(True,alpha=.3); ax.legend(); plt.tight_layout(); plt.show()
target_stock=risk.loc[risk["欠品確率"]<=.05,"在庫"].min(); print(f"欠品確率5%以下の最小候補: {target_stock:.0f}個")
| 在庫 | 欠品確率 | |
|---|---|---|
| 0 | 300 | 93.9% |
| 1 | 350 | 79.6% |
| 2 | 400 | 55.2% |
| 3 | 450 | 29.4% |
| 4 | 500 | 11.4% |
| 5 | 550 | 2.9% |
| 6 | 600 | 0.6% |
| 7 | 650 | 0.1% |
| 8 | 700 | 0.0% |
| 9 | 750 | 0.0% |
| 10 | 800 | 0.0% |
欠品確率5%以下の最小候補: 550個
結果の読み取り
在庫を増やすと欠品確率は低下しますが、限界改善は次第に小さくなります。重要度別の目標確率と保管費を組み合わせます。
No.068:故障リスクを確率で表現する
実務での意味
設備寿命を確率分布で表すと、次回保全までに故障する確率を定量化できます。
分析・モデル化の考え方
ワイブル分布の累積故障確率は 。 は経年劣化型です。
Pythonで確認する
beta,eta=2.2,420; days=np.arange(30,601,30); failure_prob=1-np.exp(-(days/eta)**beta); failure=pd.DataFrame({"保全間隔_日":days,"期間内故障確率":failure_prob}); display(failure.iloc[::2].style.format({"期間内故障確率":"{:.1%}"}))
fig,ax=plt.subplots(); ax.plot(days,failure_prob*100,marker="o",color="#756bb1"); ax.axhline(10,color="black",linestyle="--",label="10%目安"); ax.set_title("保全間隔と期間内故障確率"); ax.set_xlabel("保全間隔(日)"); ax.set_ylabel("故障確率(%)"); ax.grid(True,alpha=.3); ax.legend(); plt.tight_layout(); plt.show()
limit=failure.loc[failure["期間内故障確率"]<=.10,"保全間隔_日"].max(); print(f"故障確率10%以下の最大間隔候補: {limit}日")
| 保全間隔_日 | 期間内故障確率 | |
|---|---|---|
| 0 | 30 | 0.3% |
| 2 | 90 | 3.3% |
| 4 | 150 | 9.9% |
| 6 | 210 | 19.6% |
| 8 | 270 | 31.5% |
| 10 | 330 | 44.5% |
| 12 | 390 | 57.2% |
| 14 | 450 | 68.8% |
| 16 | 510 | 78.4% |
| 18 | 570 | 85.9% |
故障確率10%以下の最大間隔候補: 150日
結果の読み取り
保全間隔を延ばすほど故障確率が上がります。実績故障・交換履歴から製品群別に母数を更新し、予防交換費と比較します。
No.069:期待損失を使ってリスクを評価する
実務での意味
発生確率と影響額を掛けると、種類の異なるリスクを共通の金額尺度で優先順位付けできます。
分析・モデル化の考え方
。ただし低頻度巨大損失は期待値だけでなく最大損失や事業継続制約も確認します。
Pythonで確認する
risks=pd.DataFrame({"リスク":["部品A欠品","設備突発故障","品質流出","緊急輸送"],"発生確率":[.08,.12,.015,.20],"影響額":[3_500_000,18_000_000,45_000_000,1_200_000]}); risks["期待損失"]=risks["発生確率"]*risks["影響額"]; risks=risks.sort_values("期待損失",ascending=False); display(risks.style.format({"発生確率":"{:.1%}","影響額":"¥{:,.0f}","期待損失":"¥{:,.0f}"}))
fig,ax=plt.subplots(); ax.barh(risks["リスク"][::-1],risks["期待損失"][::-1]/1e6,color="#fdae6b"); ax.set_title("製造リスク別の期待損失"); ax.set_xlabel("期待損失(百万円/評価期間)"); ax.set_ylabel("リスク事象"); ax.grid(True,axis="x",alpha=.3); plt.tight_layout(); plt.show()
| リスク | 発生確率 | 影響額 | 期待損失 | |
|---|---|---|---|---|
| 1 | 設備突発故障 | 12.0% | ¥18,000,000 | ¥2,160,000 |
| 2 | 品質流出 | 1.5% | ¥45,000,000 | ¥675,000 |
| 0 | 部品A欠品 | 8.0% | ¥3,500,000 | ¥280,000 |
| 3 | 緊急輸送 | 20.0% | ¥1,200,000 | ¥240,000 |
結果の読み取り
高確率・中損失と低確率・巨大損失を同じ表で比較できます。品質流出のような許容不能リスクは期待損失順位にかかわらず制約として扱います。
No.070:不確実性を考慮した意思決定を整理する
実務での意味
期待費用が小さい案と、悪化時に強い案は一致しません。平均とテールリスクを併記して方針を選びます。
分析・モデル化の考え方
在庫・保全方針ごとの総損失をモンテカルロ計算し、期待損失、95%点、最悪5%の平均CVaRを比較します。
Pythonで確認する
decisions=[]; n=30000
for name,stock,interval,annual_cost in [("低コスト",450,360,1_200_000),("バランス",550,240,2_100_000),("高信頼",650,150,3_400_000)]:
dl=rng.choice(a_hist,size=(n,5),replace=True).sum(axis=1); shortage=np.maximum(dl-stock,0)*3500
fail=rng.random(n)<(1-np.exp(-(interval/eta)**beta)); failure_loss=fail*18_000_000
total=annual_cost+shortage+failure_loss; q95=np.quantile(total,.95); cvar=total[total>=q95].mean()
decisions.append({"方針":name,"期待総損失":total.mean(),"95%点":q95,"CVaR95":cvar,"損失500万円超確率":np.mean(total>5_000_000)})
decision=pd.DataFrame(decisions); display(decision.style.format({"期待総損失":"¥{:,.0f}","95%点":"¥{:,.0f}","CVaR95":"¥{:,.0f}","損失500万円超確率":"{:.1%}"}))
fig,ax=plt.subplots(); x=np.arange(3); ax.bar(x-.18,decision["期待総損失"]/1e6,.36,label="期待総損失"); ax.bar(x+.18,decision["CVaR95"]/1e6,.36,label="CVaR95"); ax.set_xticks(x,decision["方針"]); ax.set_title("方針別の平均損失とテールリスク"); ax.set_xlabel("意思決定方針"); ax.set_ylabel("損失(百万円)"); ax.grid(True,axis="y",alpha=.3); ax.legend(); plt.tight_layout(); plt.show()
best=decision.loc[decision["期待総損失"].idxmin()]; print(f"期待総損失最小: {best['方針']} / ¥{best['期待総損失']:,.0f}")
| 方針 | 期待総損失 | 95%点 | CVaR95 | 損失500万円超確率 | |
|---|---|---|---|---|---|
| 0 | 低コスト | ¥10,462,275 | ¥19,396,000 | ¥19,517,076 | 51.2% |
| 1 | バランス | ¥6,627,720 | ¥20,100,000 | ¥20,103,242 | 25.1% |
| 2 | 高信頼 | ¥5,203,663 | ¥21,400,000 | ¥21,400,070 | 10.0% |
期待総損失最小: 高信頼 / ¥5,203,663
結果の読み取り
低コスト案は固定費が小さくても、欠品・故障のテール損失が大きくなります。経営は期待値、許容損失確率、CVaR、顧客・安全制約を合わせて選びます。
対象ノックを通して見える実務上の示唆
- 不確実な業務量を値域に合う確率変数で表す
- 期待値と標準偏差・分位点を併記する
- 分布は業務の生成過程と平均分散から選ぶ
- 予測は一点でなく区間で示す
- 欠品・故障を確率として在庫・保全へ接続する
- 確率と影響額を期待損失で統合する
- 低頻度巨大損失にはCVaRや制約を使う
- 方針は平均的成果と悪化時耐性の両方で評価する
実務導入する場合に必要なこと
1. 不確実性の対象と単位を決める
需要、納期、歩留まり、故障、価格を混同せず定義します。
2. 欠品・故障・保全履歴を残す
正常データだけでなく、打切り寿命、遅延、失注、影響額を記録します。
3. 分布仮定を検証する
ヒストグラム、分位点、適合度、工程知識を確認し、定期更新します。
4. 損失パラメータを合意する
粗利、停止、緊急輸送、顧客影響、安全・法令を関係部署で整理します。
5. バックテストとストレステストを行う
通常期間に加え、需要急増・供給停止・連続故障で耐性を確認します。
6. 意思決定ルールを定める
期待値、サービス水準、最大許容損失、CVaRのどれを承認基準にするか決めます。
まとめ
No.061〜070では、需要・売上・欠品・故障を確率分布として表し、期待値、標準偏差、予測区間、期待損失、CVaRを意思決定へつなげました。不確実性を消すのではなく、結果の幅と損失の大きさを見える形にして、在庫・保全の選択肢を比較することが重要です。
法人向けのご相談
数理工房では、需要・納期・歩留まりの確率モデル、欠品・故障リスク、予測区間、モンテカルロシミュレーション、リスクを考慮した在庫・保全判断を支援しています。
📩 お問い合わせ: surikobo.co.jp/contact まずはお気軽にご相談ください。