100本ノック / 数理モデリング / 数理モデリング100本ノック
需要増に備える工場施策を「利益・納期・リスク」で選ぶ
需要増に備える工場施策を「利益・納期・リスク」で選ぶ
シミュレーションと意思決定 No.091〜No.100
本記事では、需要増が見込まれる精密部品工場について、現状維持、予防保全、増産シフト、複合施策をモンテカルロシミュレーションで比較します。期待利益だけでなく、需要充足率、下方利益、投資回収、損失確率、モデル前提を整理し、最終的な意思決定支援モデルへまとめます。
[!NOTE] 本資料は、数理工房 (もしくは代表である和山個人) が過去に企業研修において使用した notebook を企業様の許可を得て再構成・編集のうえ公開しています。 掲載データはすべて架空のものであり、実在する企業・工場・数値とは一切関係ありません。
はじめに:この記事で扱う製造業の実務課題
架空工場では来年度の需要増に備え、施策A「予防保全強化」と施策B「第2シフト増設」を比較しています。Aは停止を減らしますが能力増は限定的、Bは能力を増やしますが固定費と品質リスクが増えます。複合施策も候補です。
12か月×8,000試行をシミュレーションし、平均と悪化時の両面から意思決定します。
現場でよくある状況
- 楽観ケースだけで投資を承認する
- 施策案ごとに需要前提が異なり比較できない
- ROIは出すが欠品・下方利益を示さない
- 感度分析がなく、結論を左右する前提が不明
- モデルの限界と適用範囲が資料に残らない
- 分析結果が経営指標だけで、実行計画へつながらない
なぜこの問題は判断が難しいのか
需要、停止率、歩留まり、原価は同時に変動します。投資額が大きい案は平均利益を上げても、需要が伸びない場合に固定費負担が残ります。
シナリオ間で同じ乱数を使う共通乱数法により比較ノイズを減らし、期待値・分位点・確率・費用対効果を同じ表へまとめます。
今回扱うノックの全体像
| No. | テーマ | 判断 |
|---|---|---|
| 091 | シナリオ分析 | 将来の複数像を比較する |
| 092 | 感度分析 | 結論を左右する前提を探す |
| 093 | 現状維持 | 比較基準を固定する |
| 094 | A/B比較 | 予防保全と増産を比較する |
| 095 | 費用対効果 | ROI・回収期間を測る |
| 096 | リスク表 | 平均と下方リスクを統合する |
| 097 | 可視化 | 経営へ比較を伝える |
| 098 | 限界・前提 | 適用範囲を明示する |
| 099 | 進め方 | PoCから運用まで設計する |
| 100 | 統合設計 | 業務課題から推奨案を作る |
Python 環境の準備
外部データは使いません。乱数シードを固定し、NumPy・pandas・matplotlibで12か月の事業シミュレーションを実行します。
%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
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__} / matplotlib {matplotlib.__version__}")
print(f"font {plot_font} / seed {SEED} / {platform.platform()}")
Python 3.13.1 / NumPy 2.5.1 / pandas 3.0.3 / matplotlib 3.11.0
font Hiragino Sans / seed 42 / macOS-26.3-arm64-arm-64bit-Mach-O
架空データの作成
基準月間需要12万個、単価5,200円、変動費3,100円、初期在庫1.2万個を設定します。需要・停止率・歩留まり・原価を確率変数とし、4シナリオを同じ8,000組の乱数で評価します。
n=8000; months=12
z_d=rng.normal(size=(n,months)); z_y=rng.normal(size=(n,months)); z_c=rng.normal(size=(n,months)); u_down=rng.beta(2,18,size=(n,months))
scenarios=pd.DataFrame({
"scenario":["現状維持","A_予防保全","B_増産シフト","AB_複合"],
"capacity":[128000,130000,150000,151000],"downtime_scale":[1.0,.55,1.05,.58],"yield_mean":[.965,.972,.958,.970],
"annual_fixed":[150e6,158e6,174e6,184e6],"investment":[0,24e6,38e6,56e6]})
def simulate(row,demand_multiplier=1.0,cost_multiplier=1.0):
demand=np.maximum(0,120000*demand_multiplier*(1+.025*np.arange(months))[None,:]*(1+.10*z_d))
downtime=np.clip(u_down*row.downtime_scale,0,.35); yield_rate=np.clip(row.yield_mean+.008*z_y,.90,.995)
production=row.capacity*(1-downtime)*yield_rate
inventory=np.full(n,12000.); sales_total=np.zeros(n); demand_total=demand.sum(axis=1); prod_total=np.zeros(n)
for m in range(months):
available=inventory+production[:,m]; sold=np.minimum(available,demand[:,m]); inventory=available-sold; sales_total+=sold; prod_total+=production[:,m]
unit_cost=np.maximum(2500,3100*cost_multiplier*(1+.025*z_c.mean(axis=1)))
profit=sales_total*5200-prod_total*unit_cost-row.annual_fixed-row.investment
return pd.DataFrame({"profit":profit,"sales":sales_total,"demand":demand_total,"ending_inventory":inventory,"service_rate":sales_total/demand_total})
results={row.scenario:simulate(row) for row in scenarios.itertuples(index=False)}
print(f"シナリオ数: {len(results)} / 各{n:,}試行 × {months}か月")
display(scenarios.style.format({"capacity":"{:,.0f}","annual_fixed":"¥{:,.0f}","investment":"¥{:,.0f}"}))
シナリオ数: 4 / 各8,000試行 × 12か月
| scenario | capacity | downtime_scale | yield_mean | annual_fixed | investment | |
|---|---|---|---|---|---|---|
| 0 | 現状維持 | 128,000 | 1.000000 | 0.965000 | ¥150,000,000 | ¥0 |
| 1 | A_予防保全 | 130,000 | 0.550000 | 0.972000 | ¥158,000,000 | ¥24,000,000 |
| 2 | B_増産シフト | 150,000 | 1.050000 | 0.958000 | ¥174,000,000 | ¥38,000,000 |
| 3 | AB_複合 | 151,000 | 0.580000 | 0.970000 | ¥184,000,000 | ¥56,000,000 |
def summarize(name,df):
return {"シナリオ":name,"期待利益":df.profit.mean(),"利益5%点":df.profit.quantile(.05),"損失確率":(df.profit<0).mean(),"平均充足率":df.service_rate.mean(),"充足率95%未満確率":(df.service_rate<.95).mean(),"平均期末在庫":df.ending_inventory.mean()}
summary=pd.DataFrame([summarize(k,v) for k,v in results.items()])
display(summary.style.format({"期待利益":"¥{:,.0f}","利益5%点":"¥{:,.0f}","損失確率":"{:.1%}","平均充足率":"{:.2%}","充足率95%未満確率":"{:.1%}","平均期末在庫":"{:,.0f}"}))
fig,axes=plt.subplots(1,2,figsize=(11,4.2)); axes[0].bar(summary["シナリオ"],summary["期待利益"]/1e6,color="#2c7fb8"); axes[0].set_title("シナリオ別の期待利益"); axes[0].set_xlabel("シナリオ"); axes[0].set_ylabel("期待利益(百万円/年)"); axes[0].grid(True,axis="y",alpha=.3); axes[1].bar(summary["シナリオ"],summary["平均充足率"]*100,color="#2ca25f"); axes[1].set_title("シナリオ別の需要充足率"); axes[1].set_xlabel("シナリオ"); axes[1].set_ylabel("平均充足率(%)"); axes[1].grid(True,axis="y",alpha=.3); plt.xticks(rotation=15); plt.tight_layout(); plt.show()
| シナリオ | 期待利益 | 利益5%点 | 損失確率 | 平均充足率 | 充足率95%未満確率 | 平均期末在庫 | |
|---|---|---|---|---|---|---|---|
| 0 | 現状維持 | ¥2,713,302,647 | ¥2,602,642,065 | 0.0% | 82.27% | 100.0% | 44 |
| 1 | A_予防保全 | ¥2,888,359,561 | ¥2,809,656,317 | 0.0% | 88.30% | 99.1% | 129 |
| 2 | B_増産シフト | ¥3,076,598,139 | ¥2,934,411,407 | 0.0% | 94.89% | 50.4% | 2,828 |
| 3 | AB_複合 | ¥3,093,538,649 | ¥2,717,132,026 | 0.0% | 99.45% | 1.4% | 39,468 |
No.091:シナリオ分析の考え方を理解する
実務での意味
一点予測ではなく、現状・保全・増産・複合という実行可能な将来像を同じ前提で比較します。
分析・モデル化の考え方
各シナリオは能力、停止、歩留まり、固定費、投資額の組として定義します。需要乱数を共通化し、施策差を見やすくします。
Pythonで確認する
scenario_view=summary[["シナリオ","期待利益","利益5%点","平均充足率","平均期末在庫"]]; display(scenario_view.style.format({"期待利益":"¥{:,.0f}","利益5%点":"¥{:,.0f}","平均充足率":"{:.2%}","平均期末在庫":"{:,.0f}"}))
fig,ax=plt.subplots();
for name,df in results.items(): ax.hist(df.profit/1e6,bins=35,alpha=.35,label=name)
ax.set_title("シナリオ別の年間利益分布"); ax.set_xlabel("年間利益(百万円)"); ax.set_ylabel("度数"); ax.grid(True,axis="y",alpha=.3); ax.legend(); plt.tight_layout(); plt.show()
| シナリオ | 期待利益 | 利益5%点 | 平均充足率 | 平均期末在庫 | |
|---|---|---|---|---|---|
| 0 | 現状維持 | ¥2,713,302,647 | ¥2,602,642,065 | 82.27% | 44 |
| 1 | A_予防保全 | ¥2,888,359,561 | ¥2,809,656,317 | 88.30% | 129 |
| 2 | B_増産シフト | ¥3,076,598,139 | ¥2,934,411,407 | 94.89% | 2,828 |
| 3 | AB_複合 | ¥3,093,538,649 | ¥2,717,132,026 | 99.45% | 39,468 |
結果の読み取り
分布の位置と幅から、平均利益だけでなく下方リスクを比較できます。複合施策は高費用でも供給余力が大きいなど、複数KPIで特徴が異なります。
No.092:感度分析で重要なパラメータを調べる
実務での意味
結論を左右する前提を特定し、追加調査や契約交渉の優先順位を決めます。
分析・モデル化の考え方
基準シナリオの需要倍率、停止率倍率、原価倍率を一つずつ変え、期待利益の変化を測ります。
Pythonで確認する
base_row=scenarios.iloc[0]; base_profit=results["現状維持"].profit.mean(); sens=[]
for param,values in [("需要倍率",[.9,1.1]),("停止率倍率",[.7,1.3]),("原価倍率",[.95,1.05])]:
for value in values:
row=base_row.copy(); dm=cm=1.0
if param=="需要倍率": dm=value
elif param=="停止率倍率": row["downtime_scale"]=value
else: cm=value
sens.append({"パラメータ":param,"設定":value,"期待利益差":simulate(row,dm,cm).profit.mean()-base_profit})
sensitivity=pd.DataFrame(sens); display(sensitivity.style.format({"設定":"{:.2f}","期待利益差":"¥{:+,.0f}"}))
fig,ax=plt.subplots(); piv=sensitivity.pivot(index="パラメータ",columns="設定",values="期待利益差"); piv.plot.barh(ax=ax); ax.set_title("基準利益に対する感度分析"); ax.set_xlabel("期待利益差(円/年)"); ax.set_ylabel("パラメータ"); ax.grid(True,axis="x",alpha=.3); plt.tight_layout(); plt.show()
| パラメータ | 設定 | 期待利益差 | |
|---|---|---|---|
| 0 | 需要倍率 | 0.90 | ¥-2,149,227 |
| 1 | 需要倍率 | 1.10 | ¥+198,663 |
| 2 | 停止率倍率 | 0.70 | ¥+93,041,622 |
| 3 | 停止率倍率 | 1.30 | ¥-90,232,879 |
| 4 | 原価倍率 | 0.95 | ¥+206,783,556 |
| 5 | 原価倍率 | 1.05 | ¥-206,783,556 |
結果の読み取り
利益感度が大きい前提ほど意思決定価値が高く、需要調査、原価契約、停止実績の精緻化を優先します。一変数感度は相互作用を示さない点に注意します。
No.093:現状維持シナリオを基準として設定する
実務での意味
施策効果は「何もしない場合」との差で測ります。現状維持にも需要増・老朽化リスクが含まれます。
分析・モデル化の考え方
基準ケースの能力、停止、歩留まり、費用を凍結し、全施策の増分KPIを同じ基準で計算します。
Pythonで確認する
baseline=summary.query("シナリオ=='現状維持'").iloc[0]; incremental=summary.copy(); incremental["増分利益"]=incremental["期待利益"]-baseline["期待利益"]; incremental["充足率改善_pp"]=(incremental["平均充足率"]-baseline["平均充足率"])*100
display(incremental[["シナリオ","増分利益","充足率改善_pp"]].style.format({"増分利益":"¥{:+,.0f}","充足率改善_pp":"{:+.2f}pt"}))
fig,ax=plt.subplots(); ax.bar(incremental["シナリオ"],incremental["増分利益"]/1e6,color="#6baed6"); ax.axhline(0,color="black",linewidth=.8); ax.set_title("現状維持に対する増分利益"); ax.set_xlabel("シナリオ"); ax.set_ylabel("増分利益(百万円/年)"); ax.grid(True,axis="y",alpha=.3); plt.tight_layout(); plt.show()
| シナリオ | 増分利益 | 充足率改善_pp | |
|---|---|---|---|
| 0 | 現状維持 | ¥+0 | +0.00pt |
| 1 | A_予防保全 | ¥+175,056,914 | +6.03pt |
| 2 | B_増産シフト | ¥+363,295,492 | +12.62pt |
| 3 | AB_複合 | ¥+380,236,002 | +17.19pt |
結果の読み取り
増分表示により施策が現状より何を改善・悪化させるか明確になります。比較基準の定義変更は全結果へ影響するため版管理します。
No.094:施策Aと施策Bを比較する
実務での意味
予防保全Aと増産シフトBを、利益、納期、下方リスク、在庫で直接比較します。
分析・モデル化の考え方
共通乱数による試行ごとの差 を計算すると、BがAを上回る確率も評価できます。
Pythonで確認する
a=results["A_予防保全"]; bres=results["B_増産シフト"]; diff=bres.profit-a.profit
ab=pd.DataFrame({"比較":["B-A"],"平均利益差":[diff.mean()],"Bが高利益の確率":[(diff>0).mean()],"充足率差":[bres.service_rate.mean()-a.service_rate.mean()]})
display(ab.style.format({"平均利益差":"¥{:,.0f}","Bが高利益の確率":"{:.1%}","充足率差":"{:+.2%}"}))
fig,ax=plt.subplots(); ax.hist(diff/1e6,bins=35,color="#756bb1",edgecolor="white"); ax.axvline(0,color="black",linestyle="--"); ax.set_title("施策BとAの年間利益差分布"); ax.set_xlabel("B-A利益(百万円)"); ax.set_ylabel("度数"); ax.grid(True,axis="y",alpha=.3); plt.tight_layout(); plt.show()
| 比較 | 平均利益差 | Bが高利益の確率 | 充足率差 | |
|---|---|---|---|---|
| 0 | B-A | ¥188,238,578 | 98.8% | +6.59% |
結果の読み取り
Bの平均利益が高くても全試行で勝つとは限りません。利益差確率と充足率改善を合わせ、経営のリスク許容度で選びます。
No.095:費用対効果をモデル化する
実務での意味
増分利益を投資額で割り、ROIと単純回収期間を比較します。
分析・モデル化の考え方
、回収期間 。便益が継続する期間と割引率を明示します。
Pythonで確認する
roi=incremental.merge(scenarios[["scenario","investment"]],left_on="シナリオ",right_on="scenario"); roi=roi.query("investment>0").copy(); roi["ROI"]=roi["増分利益"]/roi["investment"]; roi["回収月数"]=roi["investment"]/(roi["増分利益"]/12).replace(0,np.nan)
display(roi[["シナリオ","増分利益","investment","ROI","回収月数"]].style.format({"増分利益":"¥{:,.0f}","investment":"¥{:,.0f}","ROI":"{:.1%}","回収月数":"{:.1f}か月"}))
fig,ax=plt.subplots(); ax.bar(roi["シナリオ"],roi["ROI"]*100,color="#2ca25f"); ax.axhline(0,color="black",linewidth=.8); ax.set_title("施策別の単年度ROI"); ax.set_xlabel("施策"); ax.set_ylabel("ROI(%)"); ax.grid(True,axis="y",alpha=.3); plt.tight_layout(); plt.show()
| シナリオ | 増分利益 | investment | ROI | 回収月数 | |
|---|---|---|---|---|---|
| 1 | A_予防保全 | ¥175,056,914 | ¥24,000,000 | 729.4% | 1.6か月 |
| 2 | B_増産シフト | ¥363,295,492 | ¥38,000,000 | 956.0% | 1.3か月 |
| 3 | AB_複合 | ¥380,236,002 | ¥56,000,000 | 679.0% | 1.8か月 |
結果の読み取り
ROIが高くても供給目標を満たさない案は採用できません。複数年案件ではNPV、残存価値、税効果も加えます。
No.096:リスクを考慮した意思決定表を作る
実務での意味
期待値、5%点、損失確率、サービス未達を一表にし、リターンとリスクを同時に審議します。
分析・モデル化の考え方
意思決定表に制約条件を追加し、平均充足率97%以上・損失確率5%以下などの採用条件を明示します。
Pythonで確認する
decision=summary.copy(); decision["サービス条件"]=decision["平均充足率"]>=.97; decision["損失条件"]=decision["損失確率"]<=.05; decision["採用候補"]=decision["サービス条件"]&decision["損失条件"]
display(decision.style.format({"期待利益":"¥{:,.0f}","利益5%点":"¥{:,.0f}","損失確率":"{:.1%}","平均充足率":"{:.2%}","充足率95%未満確率":"{:.1%}","平均期末在庫":"{:,.0f}"}))
| シナリオ | 期待利益 | 利益5%点 | 損失確率 | 平均充足率 | 充足率95%未満確率 | 平均期末在庫 | サービス条件 | 損失条件 | 採用候補 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 現状維持 | ¥2,713,302,647 | ¥2,602,642,065 | 0.0% | 82.27% | 100.0% | 44 | False | True | False |
| 1 | A_予防保全 | ¥2,888,359,561 | ¥2,809,656,317 | 0.0% | 88.30% | 99.1% | 129 | False | True | False |
| 2 | B_増産シフト | ¥3,076,598,139 | ¥2,934,411,407 | 0.0% | 94.89% | 50.4% | 2,828 | False | True | False |
| 3 | AB_複合 | ¥3,093,538,649 | ¥2,717,132,026 | 0.0% | 99.45% | 1.4% | 39,468 | True | True | True |
結果の読み取り
採用条件を先に置くと、期待利益だけでリスクの高い案を選ぶことを防げます。条件値は顧客契約と財務余力から決めます。
No.097:モデル結果を可視化して説明する
実務での意味
経営層には期待利益と下方リスク、現場には充足率と在庫を、同じ結果から伝え分けます。
分析・モデル化の考え方
リスク・リターン散布図と、基準化した複数KPIで施策の特徴を可視化します。
Pythonで確認する
fig,axes=plt.subplots(1,2,figsize=(11,4.2))
axes[0].scatter(summary["利益5%点"]/1e6,summary["期待利益"]/1e6,s=100)
for _,r in summary.iterrows(): axes[0].annotate(r["シナリオ"],(r["利益5%点"]/1e6,r["期待利益"]/1e6),xytext=(4,4),textcoords="offset points")
axes[0].set_title("利益のリスク・リターン"); axes[0].set_xlabel("利益5%点(百万円)"); axes[0].set_ylabel("期待利益(百万円)"); axes[0].grid(True,alpha=.3)
axes[1].scatter(summary["平均期末在庫"],summary["平均充足率"]*100,s=100,color="#de2d26")
for _,r in summary.iterrows(): axes[1].annotate(r["シナリオ"],(r["平均期末在庫"],r["平均充足率"]*100),xytext=(4,4),textcoords="offset points")
axes[1].set_title("在庫とサービス水準"); axes[1].set_xlabel("平均期末在庫(個)"); axes[1].set_ylabel("平均充足率(%)"); axes[1].grid(True,alpha=.3); plt.tight_layout(); plt.show()
結果の読み取り
右上ほど利益面、左上ほど在庫効率とサービス面で望ましい図です。目的別に軸を選び、都合の良い指標だけを示さないようにします。
No.098:モデルの限界と前提条件を整理する
実務での意味
モデルが使える範囲と、外れたときの対応を明示し、誤った自動判断を防ぎます。
分析・モデル化の考え方
需要分布、能力、価格、原価、独立性、投資効果、データ期間を仮定台帳にし、ストレス条件で結果を確認します。
Pythonで確認する
assumptions=pd.DataFrame({"前提":["需要成長","停止率","歩留まり","販売価格","原価","能力上限"],"基準":["月2.5%","過去分布","施策別平均","5,200円","3,100円","施策別固定"],"外れた場合":["能力不足","納期悪化","不良増","利益減","利益減","残業・外注"]}); display(assumptions)
stress=[]
for name,row in scenarios.set_index("scenario").iterrows(): stress.append({"シナリオ":name,"需要+25%・原価+10%利益":simulate(row,1.25,1.10).profit.mean()})
stress=pd.DataFrame(stress); display(stress.style.format({"需要+25%・原価+10%利益":"¥{:,.0f}"}))
fig,ax=plt.subplots(); ax.bar(stress["シナリオ"],stress["需要+25%・原価+10%利益"]/1e6,color="#fdae6b"); ax.axhline(0,color="black"); ax.set_title("ストレス条件下の期待利益"); ax.set_xlabel("シナリオ"); ax.set_ylabel("期待利益(百万円)"); ax.grid(True,axis="y",alpha=.3); plt.tight_layout(); plt.show()
| 前提 | 基準 | 外れた場合 | |
|---|---|---|---|
| 0 | 需要成長 | 月2.5% | 能力不足 |
| 1 | 停止率 | 過去分布 | 納期悪化 |
| 2 | 歩留まり | 施策別平均 | 不良増 |
| 3 | 販売価格 | 5,200円 | 利益減 |
| 4 | 原価 | 3,100円 | 利益減 |
| 5 | 能力上限 | 施策別固定 | 残業・外注 |
| シナリオ | 需要+25%・原価+10%利益 | |
|---|---|---|
| 0 | 現状維持 | ¥2,299,955,841 |
| 1 | A_予防保全 | ¥2,444,817,687 |
| 2 | B_増産シフト | ¥2,612,757,372 |
| 3 | AB_複合 | ¥2,785,372,850 |
結果の読み取り
通常ケースの推奨がストレス下で維持されるか確認します。前提範囲を超えたら再計算・手動判断へ切り替えるルールが必要です。
No.099:数理モデリングプロジェクトの進め方を整理する
実務での意味
分析だけで終わらず、課題定義、データ、PoC、並行運用、定着までを計画します。
分析・モデル化の考え方
意思決定・KPI・責任者・成果物・判定基準を各段階へ設定し、モデル精度と業務効果を分けて評価します。
Pythonで確認する
project=pd.DataFrame({"工程":["課題定義","データ整備","モデルPoC","並行運用","本番定着"],"開始週":[0,2,5,9,13],"期間週":[2,3,4,4,5],"成果物":["意思決定/KPI","データ辞書","比較モデル","運用評価","監視・更新手順"]}); display(project)
fig,ax=plt.subplots();
for i,r in project.iterrows(): ax.barh(r["工程"],r["期間週"],left=r["開始週"],color="#6baed6")
ax.set_title("数理モデリングプロジェクトの進行例"); ax.set_xlabel("プロジェクト週"); ax.set_ylabel("工程"); ax.grid(True,axis="x",alpha=.3); ax.invert_yaxis(); plt.tight_layout(); plt.show()
| 工程 | 開始週 | 期間週 | 成果物 | |
|---|---|---|---|---|
| 0 | 課題定義 | 0 | 2 | 意思決定/KPI |
| 1 | データ整備 | 2 | 3 | データ辞書 |
| 2 | モデルPoC | 5 | 4 | 比較モデル |
| 3 | 並行運用 | 9 | 4 | 運用評価 |
| 4 | 本番定着 | 13 | 5 | 監視・更新手順 |
結果の読み取り
課題定義とデータ整備を省くと、精度が高くても使われないモデルになります。並行運用で現行判断との差をレビューします。
No.100:業務課題から意思決定支援モデルを設計する
実務での意味
最後に、課題、入力、モデル、出力、判断基準、運用を一枚の意思決定設計へ統合します。
分析・モデル化の考え方
推奨ルールは、サービス・損失条件を満たす案の中で期待利益最大とします。該当なしなら制約緩和を経営へ戻します。
Pythonで確認する
candidates=decision.query("採用候補"); recommended=(candidates.loc[candidates["期待利益"].idxmax()] if len(candidates) else decision.loc[decision["利益5%点"].idxmax()])
design=pd.DataFrame({"要素":["業務課題","入力","モデル","出力","判断基準","推奨","運用"],"内容":["需要増への能力・保全投資","需要・停止・歩留まり・価格・原価・費用","12か月モンテカルロ","利益分布・充足率・在庫・ROI","充足率≥97%、損失確率≤5%、期待利益最大",recommended["シナリオ"],"月次更新・四半期再評価・前提逸脱時再計算"]}); display(design)
fig,ax=plt.subplots(); order=["入力","モデル","出力","判断基準","推奨"]; ax.plot(range(len(order)),range(len(order)),marker="o",linewidth=2); ax.set_xticks(range(len(order)),order); ax.set_yticks(range(len(order)),["データ","計算","KPI","ルール","実行"]); ax.set_title("業務課題から意思決定までの接続"); ax.set_xlabel("意思決定支援フロー"); ax.set_ylabel("成果物のレイヤー"); ax.grid(True,alpha=.3); plt.tight_layout(); plt.show(); print(f"推奨シナリオ: {recommended['シナリオ']} / 期待利益 ¥{recommended['期待利益']:,.0f} / 充足率 {recommended['平均充足率']:.2%}")
| 要素 | 内容 | |
|---|---|---|
| 0 | 業務課題 | 需要増への能力・保全投資 |
| 1 | 入力 | 需要・停止・歩留まり・価格・原価・費用 |
| 2 | モデル | 12か月モンテカルロ |
| 3 | 出力 | 利益分布・充足率・在庫・ROI |
| 4 | 判断基準 | 充足率≥97%、損失確率≤5%、期待利益最大 |
| 5 | 推奨 | AB_複合 |
| 6 | 運用 | 月次更新・四半期再評価・前提逸脱時再計算 |
推奨シナリオ: AB_複合 / 期待利益 ¥3,093,538,649 / 充足率 99.45%
結果の読み取り
推奨案だけでなく、採用条件、入力、更新頻度、再計算条件まで含めて意思決定支援モデルです。最終承認は現場知識と経営責任の下で行います。
対象ノックを通して見える実務上の示唆
シナリオは同じ基準で比較し、現状維持を明示します。感度分析で重要前提を特定し、ROIとリスクを同時に示します。可視化は受け手の判断へ合わせ、限界・適用範囲を隠しません。モデル構築はPoCで終わらず、並行運用、監視、更新、再承認まで設計する必要があります。
実務導入する場合に必要なこと
1. 意思決定と承認条件を先に決める
利益、サービス、損失許容度、投資上限を合意します。
2. シナリオ前提を統一・版管理する
需要、価格、能力、費用、施策効果の出典を残します。
3. 共通乱数とバックテストで比較する
施策差と乱数差を分け、過去期間で再現性を確認します。
4. モデル限界と手動判断を定義する
供給停止、大口受注、重大品質事故などを例外運用へ回します。
5. 並行運用で業務効果を測る
精度だけでなく欠品、在庫、残業、意思決定時間を測ります。
6. 更新・監視・責任者を決める
月次入力、四半期再推定、前提逸脱アラートを運用します。
まとめ
No.091〜100では、シナリオ、感度、基準ケース、施策比較、ROI、リスク、可視化、限界、プロジェクト進行を統合し、業務課題から意思決定支援モデルを設計しました。数理モデルの完成は計算結果ではなく、組織が前提とトレードオフを共有し、継続的に判断できる状態です。
法人向けのご相談
数理工房では、シナリオ分析、モンテカルロシミュレーション、投資・生産・在庫・保全の意思決定支援モデル、PoCから運用定着までを支援しています。
📩 お問い合わせ: surikobo.co.jp/contact まずはお気軽にご相談ください。