03 결과 도출 — 표·그림과 검증¶

02_분석.ipynb의 산출물로 포털 카드 「여섯 외생을 나란히 놓고 보니」(exog-deepdive.html)와 「유가 한 변수를 파보니」(oil-deepdive.html)의 표·그림을 다시 만든다. 공표 문구 가운데 계산과 맞지 않거나 근거가 달라진 항목은 별도 표로 정리한다. 표 7은 정비 전후의 외생 반사실(참고), 표 8–11·그림 4는 2026-10 정비판의 선형 SHAP·유가 진단·시나리오 폭(02 10절)이다. 마지막 셀은 이전 판 공표 수치와 정비판 기록 수치를 계산 결과와 대조한다 (반올림 범위 안에서 일치해야 통과).

In [1]:
import os, json
import config as C            # BLAS 스레드 설정이 적용되도록 numpy보다 먼저 불러온다
import numpy as np, pandas as pd
import exog_diagnosis as F
pd.set_option("display.float_format", lambda v: f"{v:,.3f}")
pd.set_option("display.width", 200)
C.setup()
import matplotlib.pyplot as plt
import koreanize_matplotlib  # noqa: F401  (한글 글꼴)
plt.rcParams["axes.unicode_minus"] = False
rd = lambda name, **kw: pd.read_csv(os.path.join(C.OUTPUT, name), encoding="utf-8-sig", **kw)   # noqa: E731
eff = rd("exog_effects.csv", index_col=0)
tot = rd("model_totals.csv", index_col=0)
paths = rd("exog_paths_summary.csv", index_col=0)
per = rd("exog_persistence.csv", index_col=0)
orank = rd("oil_rank.csv", dtype={"분류": str})
oil_cmp = rd("oil_effect_compare.csv", index_col=0)
dollars = rd("oil_dollar_paths.csv", index_col=0)
gap = rd("oil_level_gap.csv", index_col=0)
var = rd("oil_source_variants.csv", index_col=0)
src_chk = rd("oil_source_check.csv", index_col=0)
bm = rd("oil_benchmark.csv", index_col=0)
dub = rd("dubai_sim.csv", index_col=0)
shap_chk = rd("shap_check.csv", index_col=0)
bat = rd("battery_parent_check.csv", index_col=0)
SH = {r: rd(f"shap_{r}.csv", index_col=0) for r in ("36", "62", "69")}
NAME = {"유가": "유가", "세계수요": "세계수요", "실질금리": "실질금리", "실질환율": "실질환율", "고용_취업자": "고용(취업자)", "우리경기": "우리경기"}
INK, MUTED, S1C, S2C = "#1a1f26", "#5c6570", "#2a78d6", "#eb6834"   # 글자색·보조 글자색·계열 1·계열 2
Python 3.12.10 · pandas 2.2.3 · numpy 2.4.6
  ONLINE=0 (FRED 유가 재수집)  FULL=0 (확장 점검)  FRED 키 없음(공개 CSV 사용)
  외생 과거(정상형)     있음  data\inputs\exog_hist_v2.csv
  외생 시나리오(정상형)   있음  data\inputs\exog_scen_v2.csv
  외생 수준          있음  data\inputs\exog_scenarios_v2.csv
  시나리오 충격        있음  data\inputs\dsge_exog_paths.csv
  BOKDPM 관측      있음  data\inputs\bokdpm_obs_2000_2025.csv
  FRED WTI       있음  data\inputs\fred_POILWTIUSDM.csv
  FRED 두바이       있음  data\inputs\fred_POILDUBUSDM.csv
  동결 계수          있음  data\inputs\varx_coef_frozen.npz
  패널 총량          있음  data\inputs\levels_agg.csv
  패널 36          있음  data\inputs\levels_sa.csv
  패널 62          있음  data\inputs\levels_62.csv
  패널 69          있음  data\inputs\levels_69.csv

표 1. 외생 6종의 실질부가가치 순효과(2035년, S1) — 반사실(해당 외생 고정) 재전망¶

In [2]:
t1 = eff.copy()
t1["장기"] = per["판정"]
t1 = t1.sort_values("총량_CAGR기여_%p", ascending=False)
t1.index = [NAME[k] for k in t1.index]
t1 = t1[["변환", "장기", "총량_효과_조원", "총량_CAGR기여_%p", "바텀업_효과_조원", "바텀업_CAGR기여_%p"]]
t1.to_csv(os.path.join(C.OUTPUT, "table1_exog_effects.csv"), encoding="utf-8-sig")
print(f"기준 전망(S1): 총량 {tot.loc['총량', '2025(조원)']:,.0f} → {tot.loc['총량', '2035_S1(조원)']:,.0f}조원(연 {tot.loc['총량', 'CAGR(%)']:.2f}%), "
      f"62 바텀업 {tot.loc['62', '2025(조원)']:,.0f} → {tot.loc['62', '2035_S1(조원)']:,.0f}조원(연 {tot.loc['62', 'CAGR(%)']:.2f}%)")
t1.style.format({"총량_효과_조원": "{:+,.0f}", "바텀업_효과_조원": "{:+,.0f}", "총량_CAGR기여_%p": "{:+.2f}", "바텀업_CAGR기여_%p": "{:+.2f}"})
기준 전망(S1): 총량 2,326 → 2,970조원(연 2.47%), 62 바텀업 2,178 → 3,507조원(연 4.88%)
Out[2]:
  변환 장기 총량_효과_조원 총량_CAGR기여_%p 바텀업_효과_조원 바텀업_CAGR기여_%p
실질금리 level 일시 +162 +0.57 +354 +1.11
고용(취업자) dlog 일시 +62 +0.22 +32 +0.10
유가 diff 영구 +15 +0.05 +65 +0.20
실질환율 diff 일시 +5 +0.02 +10 +0.03
세계수요 level 일시 +2 +0.01 -12 -0.04
우리경기 level 일시 -1 -0.00 +8 +0.02

그림 1. 외생별 10년 CAGR 기여 — 총량 모형과 62 바텀업¶

In [3]:
o = eff.sort_values("바텀업_CAGR기여_%p")
y = np.arange(len(o))
fig, ax = plt.subplots(figsize=(8, 4.2))
ax.barh(y + 0.19, o["총량_CAGR기여_%p"], height=0.36, color=S1C, label="총량 예측모형")
ax.barh(y - 0.19, o["바텀업_CAGR기여_%p"], height=0.36, color=S2C, label="62 바텀업")
for yi, (a, b) in enumerate(zip(o["총량_CAGR기여_%p"], o["바텀업_CAGR기여_%p"])):
    ax.text(max(a, 0) + 0.02, yi + 0.19, f"{a:+.2f}", va="center", fontsize=8, color=MUTED)
    ax.text(max(b, 0) + 0.02, yi - 0.19, f"{b:+.2f}", va="center", fontsize=8, color=MUTED)
ax.set_yticks(y, [NAME[k] for k in o.index], color=INK)
ax.axvline(0, color="#9aa3ad", lw=0.8)
ax.set_xlabel("2026–2035 연평균 성장률 기여(%p)", color=MUTED)
ax.grid(axis="x", alpha=.25); ax.spines[["top", "right"]].set_visible(False)
ax.legend(frameon=False, loc="lower right")
ax.set_title("외생별 실질부가가치 CAGR 기여(2035, S1)", loc="left", color=INK)
fig.tight_layout(); fig.savefig(os.path.join(C.OUTPUT, "fig1_exog_effects.png"), dpi=150)
No description has been provided for this image

표 2. 시나리오 폭(max−min, 연평균) — 2026년과 2035년¶

In [4]:
t2 = per.copy()
t2.index = [NAME[k] for k in t2.index]
t2.to_csv(os.path.join(C.OUTPUT, "table2_persistence.csv"), encoding="utf-8-sig")
t2.style.format({"폭_2026": "{:,.2f}", "폭_2035": "{:,.3f}"})
Out[4]:
  폭_2026 폭_2035 판정
유가 25.16 51.750 영구
세계수요 1.79 0.000 일시
실질금리 4.80 0.000 일시
실질환율 11.87 0.004 일시
고용(취업자) 531.61 8.630 일시
우리경기 2.94 0.000 일시

그림 2. 유가 경로와 시나리오 충격의 누적¶

왼쪽은 연평균 유가(달러/배럴)이다. 과거 구간은 VARX에 들어간 값(BOKDPM 평활 LOIL_us)으로, 두바이유 기준이다. 오른쪽은 S2·S3·S5의 유가 충격(oil_g 편차, 분기)과 그 누적합인 수준차(S − S1, 100·ln)를 나눠 보인다. 충격은 8분기 뒤 사라지지만 수준차는 남는다.

In [5]:
dsge = pd.read_csv(C.DSGE_EXOG, encoding="utf-8-sig")
SC = {"S1": S1C, "S2": S2C, "S3": "#1baf7a", "S4": "#eda100", "S5": "#e87ba4"}
fig, axes = plt.subplots(1, 3, figsize=(12, 4.0), gridspec_kw={"width_ratios": [1.6, 1, 1]})
ax = axes[0]
h = dollars.loc[:2025, "S1"]
ax.plot(h.index, h.values, color=INK, lw=2, label="과거(두바이 기준)")
for s in ["S1", "S2", "S3", "S4", "S5"]:
    f = dollars.loc[2025:, s]
    ax.plot(f.index, f.values, color=SC[s], lw=2, ls="--" if s == "S4" else "-", label=s)
for s, dy in [("S5", 0), ("S1", 4), ("S3", -4), ("S2", 0)]:
    v = dollars.loc[2035, s]
    ax.text(2035.4, v + dy, ("S1·S4" if s == "S1" else s) + f" ${v:.0f}", va="center", fontsize=8, color=INK)
ax.set_xlim(2000, 2039); ax.set_ylabel("달러/배럴(연평균)", color=MUTED)
ax.grid(alpha=.25); ax.spines[["top", "right"]].set_visible(False); ax.legend(frameon=False, fontsize=8, loc="upper left")
ax.set_title("(가) 유가 수준", loc="left", color=INK)
q = np.arange(1, 41)
g1 = dsge[dsge["시나리오"] == "S1"]["oil_g"].to_numpy()[:40]
for s in ["S2", "S3", "S5"]:
    dev = dsge[dsge["시나리오"] == s]["oil_g"].to_numpy()[:40] - g1
    axes[1].plot(q, dev, color=SC[s], lw=2, label=s)
    axes[2].plot(q, np.cumsum(dev), color=SC[s], lw=2, label=s)
for a, t, yl in [(axes[1], "(나) 충격: oil_g 편차", "분기 성장률 편차(100·Δln)"), (axes[2], "(다) 수준차: 편차 누적합", "S - S1(100·ln)")]:
    a.axhline(0, color="#9aa3ad", lw=0.8); a.set_xlabel("2026Q1 이후 분기", color=MUTED); a.set_ylabel(yl, color=MUTED)
    a.grid(alpha=.25); a.spines[["top", "right"]].set_visible(False); a.legend(frameon=False, fontsize=8); a.set_title(t, loc="left", color=INK)
fig.tight_layout(); fig.savefig(os.path.join(C.OUTPUT, "fig2_oil_paths.png"), dpi=150)
No description has been provided for this image

표 3. 유가의 실질부가가치 순효과(2035년, S1) — 총량 모형과 62 바텀업¶

In [6]:
oil_cmp.to_csv(os.path.join(C.OUTPUT, "table3_oil_effect.csv"), encoding="utf-8-sig")
oil_cmp.style.format({c: "{:,.0f}" for c in ["2025(조원)", "2035(조원)", "유가 고정 2035(조원)"]} |
                     {"유가 효과(조원)": "{:+,.1f}", "유가 효과(%)": "{:+.2f}", "CAGR(%)": "{:.2f}", "유가 고정 CAGR(%)": "{:.2f}", "CAGR 기여(%p)": "{:+.3f}"})
Out[6]:
  2025(조원) 2035(조원) 유가 고정 2035(조원) 유가 효과(조원) 유가 효과(%) CAGR(%) 유가 고정 CAGR(%) CAGR 기여(%p)
모형                
총량 예측모형 2,326 2,970 2,956 +14.6 +0.49 2.47 2.42 +0.050
62 바텀업 2,178 3,507 3,442 +65.5 +1.87 4.88 4.68 +0.197

표 4. 유가에 가장 민감한 산업 — 36·62·69분류(10년 로그성장 기여, %p)¶

In [7]:
rows = []
for r in ("36", "62", "69"):
    g = orank[orank["분류"] == r]
    top = g[g["|기여| 최대"]].iloc[0]
    plus = g[(g["구분"] == "상위(+)") & ~g["|기여| 최대"]].head(3)
    minus = g[g["구분"] == "하위(−)"].head(3)
    rows.append({"분류": r, "유가 영향 1위": top["산업"], "기여(%p)": top["유가 기여(%p)"],
                 "수혜(+) 상위 3": " · ".join(f"{a}({v:+.1f})" for a, v in zip(plus["산업"], plus["유가 기여(%p)"])),
                 "피해(−) 상위 3": " · ".join(f"{a}({v:+.1f})" for a, v in zip(minus["산업"], minus["유가 기여(%p)"]))})
t4 = pd.DataFrame(rows).set_index("분류")
t4.to_csv(os.path.join(C.OUTPUT, "table4_oil_sensitive.csv"), encoding="utf-8-sig")
t4.style.format({"기여(%p)": "{:+.2f}"})
Out[7]:
  유가 영향 1위 기여(%p) 수혜(+) 상위 3 피해(−) 상위 3
분류        
36 코크스 및 석유정제품 제조업 +41.06 기계 및 장비 제조업(+14.0) · 예술, 스포츠 및 여가관련 서비스업(+12.1) · 운송장비 제조업(+10.8) 주거용 건물 건설업(-6.1) · 토목건설업(-6.0) · 통신업(-4.7)
62 14.석탄ㆍ석유제품 +31.36 25.기타기계ㆍ장비(+11.3) · 59.문화서비스업(+11.2) · 27.기타 운송장비(+10.8) 43.통신업(-4.4) · 29.전기ㆍ가스ㆍ증기ㆍ공기조절공급업(-4.0) · 33.건설업(-3.9)
69 14.석탄ㆍ석유제품 +30.83 이차전지(+15.8) · 25.기타기계ㆍ장비(+12.0) · 27.기타 운송장비(잔여)(+10.7) 토목건설(-5.1) · 건물건설건축보수(-4.8) · 43.통신업(-4.2)

그림 3. 62개 산업의 유가 기여(S1, 10년 로그성장 %p) — 상·하위 10개¶

In [8]:
s = SH["62"]["유가"].sort_values()
s = pd.concat([s.head(10), s.tail(10)])
fig, ax = plt.subplots(figsize=(8, 6.2))
ax.barh(range(len(s)), s.values, color=[S1C if v > 0 else S2C for v in s.values], height=0.7)
ax.set_yticks(range(len(s)), [i.split(".", 1)[1] for i in s.index], fontsize=8, color=INK)
for i, v in enumerate(s.values):
    ax.text(v + (0.4 if v > 0 else -0.4), i, f"{v:+.1f}", va="center", ha="left" if v > 0 else "right", fontsize=7.5, color=MUTED)
ax.axvline(0, color="#9aa3ad", lw=0.8)
ax.set_xlim(s.min() - 3, s.max() + 4)
ax.set_xlabel("유가 기여(10년 로그성장, %p) — 파랑: 상승 기여, 주황: 하락 기여", color=MUTED)
ax.grid(axis="x", alpha=.25); ax.spines[["top", "right"]].set_visible(False)
ax.set_title("62분류 산업별 유가 기여(선형 SHAP, S1)", loc="left", color=INK)
fig.tight_layout(); fig.savefig(os.path.join(C.OUTPUT, "fig3_oil_shap_62.png"), dpi=150)
No description has been provided for this image

표 5. 유가 과거값의 원천별 결과 — 공표 입력·FRED 두바이·FRED WTI¶

공표 입력의 유가 과거값은 두바이유와 일치하므로 첫째·둘째 행이 같다. 셋째 행은 문서상 정의(WTI)대로 추정했을 때의 결과이다.

In [9]:
var.to_csv(os.path.join(C.OUTPUT, "table5_oil_source.csv"), encoding="utf-8-sig")
var.style.format("{:,.2f}")
Out[9]:
  총량 2035(조원) 총량 유가효과(조원) 총량 유가효과(%) 62 2035(조원) 62 유가효과(조원) 62 유가효과(%) 62 실질금리효과(조원) 석유제품 유가 SHAP(%p)
유가 과거값                
공표 입력(exog_hist) 2,970.38 14.60 0.49 3,507.00 65.47 1.87 354.21 31.36
FRED 두바이 100·Δln 2,970.38 14.60 0.49 3,507.00 65.47 1.87 354.21 31.36
FRED WTI 100·Δln 2,923.70 5.73 0.20 3,511.48 57.11 1.63 342.82 34.71

표 6. 두바이유 교체 시산(축약 OLS, 원 시산 재현)¶

In [10]:
t6 = dub.copy()
t6.to_csv(os.path.join(C.OUTPUT, "table6_dubai.csv"), encoding="utf-8-sig")
print(f"로그차분 상관 corr(ΔWTI, Δ두바이): 원 시산 표본 {bm.loc['원 시산 표본(파일 전체)', 'corr']:.4f} "
      f"({bm.loc['원 시산 표본(파일 전체)', '첫분기']}–{bm.loc['원 시산 표본(파일 전체)', '끝분기']}) · "
      f"2000Q1–2025Q4 {bm.loc['2000Q1–2025Q4', 'corr']:.4f}")
t6.style.format({"WTI계수": "{:.3f}", "DUB계수": "{:.3f}", "WTI기여": "{:+.1f}", "DUB기여": "{:+.1f}", "변화": "{:+.1f}"})
로그차분 상관 corr(ΔWTI, Δ두바이): 원 시산 표본 0.9540 (2000Q1–2026Q3) · 2000Q1–2025Q4 0.9610
Out[10]:
  WTI계수 DUB계수 WTI기여 DUB기여 변화
산업          
14.석탄ㆍ석유제품 0.290 0.308 +24.9 +26.5 +1.6
25.기타기계ㆍ장비 0.104 0.109 +8.9 +9.3 +0.4
27.기타 운송장비 0.085 0.086 +7.3 +7.4 +0.1
33.건설업 -0.012 -0.003 -1.0 -0.3 +0.7
43.통신업 -0.001 -0.010 -0.1 -0.9 -0.8
46.금융업 0.016 0.011 +1.4 +0.9 -0.5

공표 문구 점검 — 정정·쟁점¶

카드 본문의 서술 가운데 계산과 맞지 않거나 근거가 달라진 항목이다. 아래 검증 셀에는 계산과 일치하는 공표 수치만 넣고, 이 표의 '불일치'·'쟁점' 항목은 README의 정정 기록에 같은 내용으로 적었다.

In [11]:
e = eff
rr_tot, rr_bu = e.loc["실질금리", "총량_효과_조원"] / e.loc["유가", "총량_효과_조원"], e.loc["실질금리", "바텀업_효과_조원"] / e.loc["유가", "바텀업_효과_조원"]
amp_oil, amp_rr = e.loc["유가", "바텀업_효과_조원"] / e.loc["유가", "총량_효과_조원"], e.loc["실질금리", "바텀업_효과_조원"] / e.loc["실질금리", "총량_효과_조원"]
g36 = orank[(orank["분류"] == "36") & (orank["구분"] == "상위(+)")]
g69 = orank[(orank["분류"] == "69") & (orank["구분"] == "하위(−)")]
b_full, b_25 = bm.loc["원 시산 표본(파일 전체)"], bm.loc["2000Q1–2025Q4"]
claims = pd.DataFrame([
    ("유가 원천(유가 카드 ①·각주, 그림 축)", "유가 = LOIL_us = FRED POILWTIUSDM(WTI)",
     f"VARX 유가 과거값 = 100·ln(두바이유), 최대 차이 {src_chk.loc['VARX 유가 − 100·ln(두바이)', '최대 |차이|']:.4f}; WTI와는 {src_chk.loc['VARX 유가 − 100·ln(WTI)', '최소']:+.1f}~{src_chk.loc['VARX 유가 − 100·ln(WTI)', '최대']:+.1f}(100·ln) 차이",
     "불일치(정정)"),
    ("두바이유 교체 권고(유가 카드 ⑧-5)", "POILWTIUSDM → POILDUBUSDM 한 줄 교체",
     f"추정 입력이 이미 두바이유이다. WTI로 추정하면 총량 유가효과 {var.iloc[0]['총량 유가효과(조원)']:+.1f} → {var.iloc[2]['총량 유가효과(조원)']:+.1f}조원",
     "전제 불성립(정정)"),
    ("유가 과거 수준(유가 카드 ② 부제)", "2008년 $109 → 2025년 $69",
     f"연평균 2008년 ${dollars.loc[2008, 'S1']:.1f}, $109는 2012년(${dollars.loc[2012, 'S1']:.1f}); 2025년 ${dollars.loc[2025, 'S1']:.1f}", "불일치(연도 표기)"),
    ("바텀업 증폭(외생 카드 ⑤)", "유가·실질금리는 바텀업이 총량의 약 2배(유가 65 vs 15조)",
     f"실질금리 {amp_rr:.1f}배, 유가 {amp_oil:.1f}배", "유가 부분 불일치"),
    ("바텀업 증폭(유가 카드 ④)", "바텀업이 유가 효과를 약 4배 증폭", f"{amp_oil:.2f}배", "부합"),
    ("실질금리 대 유가(외생 카드 ②)", "실질금리가 유가의 열 배", f"총량 {rr_tot:.1f}배, 바텀업 {rr_bu:.1f}배", "총량 기준 부합"),
    ("WTI·두바이 상관(유가 카드 ⑥)", "로그성장 상관 0.954",
     f"{b_full['corr']:.4f}({b_full['첫분기']}–{b_full['끝분기']}, 끝 분기는 7월 한 달); 원 출력 표기 기간 2000Q1–2025Q4로는 {b_25['corr']:.4f}",
     "값 재현, 기간 표기 쟁점"),
    ("스프레드(유가 카드 ⑥)", "최근 스프레드 약 $3",
     f"2026년 7월 WTI ${b_full['WTI_$']:.1f} − 두바이 ${b_full['DUB_$']:.1f} = ${b_full['WTI_$'] - b_full['DUB_$']:.1f}; 2025Q4는 두바이가 ${b_25['DUB_$'] - b_25['WTI_$']:.1f} 높음",
     "시점 의존(쟁점)"),
    ("36 수혜(+) 산업(유가 카드 ③)", "기계·운송·전기장비",
     "상위(+) 2–5위: " + " · ".join(f"{a}({v:+.1f})" for a, v in zip(g36["산업"].iloc[1:], g36["유가 기여(%p)"].iloc[1:])), "선택 표기(예술·스포츠 생략)"),
    ("69 피해(−) 산업(유가 카드 ③)", "토목·통신·전기가스",
     "하위(−) 1–4위: " + " · ".join(f"{a}({v:+.1f})" for a, v in zip(g69["산업"].iloc[:4], g69["유가 기여(%p)"].iloc[:4])), "선택 표기(건물건설 생략)"),
    ("69 이차전지(유가 카드 ③)", "모산업(전기장비) 계수를 IO 몫으로 물려받은 결과",
     f"별도 방정식으로 추정; 유가 기여 {bat.loc['이차전지', '유가 기여(%p)']:+.1f} vs 전기장비(잔여) {bat.loc['24.전기장비(잔여)', '유가 기여(%p)']:+.1f}, 분기 성장률 상관 {bat.loc['24.전기장비(잔여)', '분기 성장률 상관(이차전지와)']:.2f}",
     "근거 미확인(쟁점)"),
    ("실질금리 비중(외생 카드 ②)", "실질금리 하나가 바텀업 성장(연 4.88%)의 약 1/4",
     f"{e.loc['실질금리', '바텀업_CAGR기여_%p']:.2f} / {tot.loc['62', 'CAGR(%)']:.2f} = {e.loc['실질금리', '바텀업_CAGR기여_%p'] / tot.loc['62', 'CAGR(%)']:.2f}", "부합"),
], columns=["항목", "공표 문구", "계산 결과", "판정"])
claims.to_csv(os.path.join(C.OUTPUT, "claims_check.csv"), index=False, encoding="utf-8-sig")
claims.style.hide(axis="index")
Out[11]:
항목 공표 문구 계산 결과 판정
유가 원천(유가 카드 ①·각주, 그림 축) 유가 = LOIL_us = FRED POILWTIUSDM(WTI) VARX 유가 과거값 = 100·ln(두바이유), 최대 차이 0.0001; WTI와는 -30.3~+19.6(100·ln) 차이 불일치(정정)
두바이유 교체 권고(유가 카드 ⑧-5) POILWTIUSDM → POILDUBUSDM 한 줄 교체 추정 입력이 이미 두바이유이다. WTI로 추정하면 총량 유가효과 +14.6 → +5.7조원 전제 불성립(정정)
유가 과거 수준(유가 카드 ② 부제) 2008년 $109 → 2025년 $69 연평균 2008년 $89.1, $109는 2012년($109.0); 2025년 $69.2 불일치(연도 표기)
바텀업 증폭(외생 카드 ⑤) 유가·실질금리는 바텀업이 총량의 약 2배(유가 65 vs 15조) 실질금리 2.2배, 유가 4.5배 유가 부분 불일치
바텀업 증폭(유가 카드 ④) 바텀업이 유가 효과를 약 4배 증폭 4.49배 부합
실질금리 대 유가(외생 카드 ②) 실질금리가 유가의 열 배 총량 11.1배, 바텀업 5.4배 총량 기준 부합
WTI·두바이 상관(유가 카드 ⑥) 로그성장 상관 0.954 0.9540(2000Q1–2026Q3, 끝 분기는 7월 한 달); 원 출력 표기 기간 2000Q1–2025Q4로는 0.9610 값 재현, 기간 표기 쟁점
스프레드(유가 카드 ⑥) 최근 스프레드 약 $3 2026년 7월 WTI $79.7 − 두바이 $77.1 = $2.6; 2025Q4는 두바이가 $3.9 높음 시점 의존(쟁점)
36 수혜(+) 산업(유가 카드 ③) 기계·운송·전기장비 상위(+) 2–5위: 기계 및 장비 제조업(+14.0) · 예술, 스포츠 및 여가관련 서비스업(+12.1) · 운송장비 제조업(+10.8) · 전기장비 제조업(+9.9) 선택 표기(예술·스포츠 생략)
69 피해(−) 산업(유가 카드 ③) 토목·통신·전기가스 하위(−) 1–4위: 토목건설(-5.1) · 건물건설건축보수(-4.8) · 43.통신업(-4.2) · 29.전기ㆍ가스ㆍ증기ㆍ공기조절공급업(잔여)(-3.8) 선택 표기(건물건설 생략)
69 이차전지(유가 카드 ③) 모산업(전기장비) 계수를 IO 몫으로 물려받은 결과 별도 방정식으로 추정; 유가 기여 +15.8 vs 전기장비(잔여) +10.0, 분기 성장률 상관 0.12 근거 미확인(쟁점)
실질금리 비중(외생 카드 ②) 실질금리 하나가 바텀업 성장(연 4.88%)의 약 1/4 1.11 / 4.88 = 0.23 부합

표 7. 정비 전후의 외생 기여(참고, 검증 대상 아님)¶

02 9절의 결과이다. 2035년 S1 순효과(조 원)로, 이전 판은 공표 진단(표 1)과 같고 정비판은 2026-10 정비 외생으로 다시 계산한 값이다.

In [12]:
t7 = pd.read_csv(os.path.join(C.OUTPUT, "exog_effects_before_after.csv"), header=[0, 1], index_col=0, encoding="utf-8-sig")
t7.style.format("{:+.1f}")
Out[12]:
  이전 판 정비판
  총량_효과_조원 바텀업_효과_조원 총량_효과_조원 바텀업_효과_조원
외생        
유가 +14.6 +65.5 +1.1 +4.7
세계수요 +1.6 -12.0 +3.2 -1.3
실질금리 +162.5 +354.2 +0.4 +1.4
실질환율 +4.8 +9.9 -4.5 -13.2
고용_취업자 +62.3 +31.8 -0.9 -0.1
우리경기 -0.8 +7.6 -3.5 +0.6

표 8. 정비판 선형 SHAP 요약 — 36·62·69분류(S1, 10년 로그성장 기여 %p)¶

02 10절의 결과이다. 정비판은 정비 외생(실질금리 1차 차분 등)과 드리프트 재설정 계수로 분해하였고, 이전 판은 4절(표 4·그림 3의 근거)이다. 평균 절대 기여는 산업별 기여의 절댓값 평균이며, 외생은 여섯 외생 기여의 절댓값 합의 산업 평균이다.

In [13]:
ancj = rd("drift_anchor_jeongbi.csv", index_col=0)
scj = rd("shap_check_jeongbi.csv", index_col=0, dtype={"분류": str})
ssj = rd("shap_summary_jeongbi.csv", index_col=[0, 1], dtype={"분류": str})
orj = rd("oil_rank_jeongbi.csv", dtype={"분류": str})
ofj = rd("oil_factor_jeongbi.csv", index_col=0, dtype={"분류": str})
svj = rd("oil_shap_vs_cf_62_jeongbi.csv", index_col=0)
ocj = rd("oil_effect_compare_jeongbi.csv", index_col=0)
SHJ = {r: rd(f"shap_{r}_jeongbi.csv", index_col=0) for r in ("36", "62", "69")}
t8 = ssj.unstack("판").swaplevel(axis=1)[["이전 판", "정비판"]]
t8.insert(0, ("정비판", "δ(연 %p)"), ancj.loc[["36", "62", "69"], "δ(연 %p)"].values)
t8.to_csv(os.path.join(C.OUTPUT, "table8_shap_jeongbi.csv"), encoding="utf-8-sig")
print(f"62분류 정비판 분해 = 5장 분해(전망 갱신 카드): 최대 |차이| {scj.loc['62', '(라) 5장 기록과 최대 |차이|']:.1e}")
t8.style.format("{:.2f}")
62분류 정비판 분해 = 5장 분해(전망 갱신 카드): 최대 |차이| 1.4e-14
Out[13]:
판 정비판 이전 판 정비판
  δ(연 %p) 합계 산업 평균 과거추세 산업 동학 외생(6종 절댓값 합) 실질금리 최대 |기여| 유가 최대 |기여| 합계 산업 평균 과거추세 산업 동학 외생(6종 절댓값 합) 실질금리 최대 |기여| 유가 최대 |기여|
분류                          
36 1.52 29.08 24.61 8.27 34.62 56.84 41.06 4.14 21.04 5.00 3.88 0.25 8.47
62 1.52 34.79 29.02 18.61 33.42 53.81 31.36 10.37 23.53 14.21 3.30 0.16 6.21
69 1.52 37.15 30.76 22.48 33.23 54.02 30.83 12.57 25.75 15.74 3.39 0.16 6.08

표 9. 정비판 유가에 가장 민감한 산업 — 36·62·69분류(10년 로그성장 기여, %p)¶

In [14]:
rows = []
for r in ("36", "62", "69"):
    g = orj[orj["분류"] == r]
    top = g[g["|기여| 최대"]].iloc[0]
    plus = g[(g["구분"] == "상위(+)") & ~g["|기여| 최대"]].head(3)
    minus = g[g["구분"] == "하위(−)"].head(3)
    rows.append({"분류": r, "유가 영향 1위": top["산업"], "기여(%p)": top["유가 기여(%p)"], "이전 판 기여(%p)": ofj.loc[r, "이전 판 유가 기여(%p)"],
                 "수혜(+) 상위 3": " · ".join(f"{a}({v:+.2f})" for a, v in zip(plus["산업"], plus["유가 기여(%p)"])),
                 "피해(−) 상위 3": " · ".join(f"{a}({v:+.2f})" for a, v in zip(minus["산업"], minus["유가 기여(%p)"]))})
t9 = pd.DataFrame(rows).set_index("분류")
t9.to_csv(os.path.join(C.OUTPUT, "table9_oil_sensitive_jeongbi.csv"), encoding="utf-8-sig")
r0 = ofj.loc["62"]
print(f"S1 유가 누적 변화(100·ln) {r0['이전 판 유가 누적(100·ln)']:.2f} → {r0['정비판 유가 누적(100·ln)']:.2f}, "
      f"석유제품(62) 유가 계수 합 {r0['이전 판 유가 계수 합']:.3f} → {r0['정비판 유가 계수 합']:.3f}")
t9.style.format({"기여(%p)": "{:+.2f}", "이전 판 기여(%p)": "{:+.2f}"})
S1 유가 누적 변화(100·ln) 98.05 → 22.43, 석유제품(62) 유가 계수 합 0.337 → 0.339
Out[14]:
  유가 영향 1위 기여(%p) 이전 판 기여(%p) 수혜(+) 상위 3 피해(−) 상위 3
분류          
36 코크스 및 석유정제품 제조업 +8.47 +41.06 기계 및 장비 제조업(+2.69) · 예술, 스포츠 및 여가관련 서비스업(+2.31) · 운송장비 제조업(+2.25) 토목건설업(-1.31) · 통신업(-0.92) · 주거용 건물 건설업(-0.81)
62 14.석탄ㆍ석유제품 +6.21 +31.36 25.기타기계ㆍ장비(+2.28) · 27.기타 운송장비(+2.17) · 26.자동차(+1.95) 43.통신업(-0.90) · 52.사업관련전문서비스업(-0.83) · 53.과학기술관련전문서비스업(-0.68)
69 14.석탄ㆍ석유제품 +6.08 +30.83 25.기타기계ㆍ장비(+2.43) · 이차전지(+2.26) · 27.기타 운송장비(잔여)(+2.14) 43.통신업(-0.87) · 52.사업관련전문서비스업(-0.87) · 토목건설(-0.76)

그림 4. 62개 산업의 유가 기여 — 정비판(S1, 10년 로그성장 %p, 상·하위 10개)¶

In [15]:
s = SHJ["62"]["유가"].sort_values()
s = pd.concat([s.head(10), s.tail(10)])
fig, ax = plt.subplots(figsize=(8, 6.2))
ax.barh(range(len(s)), s.values, color=[S1C if v > 0 else S2C for v in s.values], height=0.7)
ax.set_yticks(range(len(s)), [i.split(".", 1)[1] for i in s.index], fontsize=8, color=INK)
for i, v in enumerate(s.values):
    ax.text(v + (0.08 if v > 0 else -0.08), i, f"{v:+.2f}", va="center", ha="left" if v > 0 else "right", fontsize=7.5, color=MUTED)
ax.axvline(0, color="#9aa3ad", lw=0.8)
ax.set_xlim(s.min() - 0.8, s.max() + 1.0)
ax.set_xlabel("유가 기여(10년 로그성장, %p) — 파랑: 상승 기여, 주황: 하락 기여", color=MUTED)
ax.grid(axis="x", alpha=.25); ax.spines[["top", "right"]].set_visible(False)
ax.set_title("62분류 산업별 유가 기여(선형 SHAP, S1, 정비판)", loc="left", color=INK)
fig.tight_layout(); fig.savefig(os.path.join(C.OUTPUT, "fig4_oil_shap_62_jeongbi.png"), dpi=150)
No description has been provided for this image

표 10. 정비판 유가의 실질부가가치 순효과(2035년, S1) — 총량 모형과 62 바텀업¶

반사실(유가 고정 재전망)으로 계산한다. 정비판 62 바텀업은 드리프트 재설정 모형이며, 재설정 전 모형(02 9절)과 이전 판(표 3)을 함께 둔다.

In [16]:
t10 = pd.concat({"정비판": ocj, "이전 판": oil_cmp})
t10.to_csv(os.path.join(C.OUTPUT, "table10_oil_effect_jeongbi.csv"), encoding="utf-8-sig")
print(f"62 유가 SHAP(직접)·반사실(총효과) 산업 간 상관: 정비판 {svj.iloc[:, 0].corr(svj.iloc[:, 1]):.3f}")
t10.style.format({c: "{:,.1f}" for c in ["2025(조원)", "2035(조원)", "유가 고정 2035(조원)"]} |
                 {"유가 효과(조원)": "{:+,.2f}", "유가 효과(%)": "{:+.3f}", "CAGR(%)": "{:.2f}", "유가 고정 CAGR(%)": "{:.2f}", "CAGR 기여(%p)": "{:+.4f}"})
62 유가 SHAP(직접)·반사실(총효과) 산업 간 상관: 정비판 0.898
Out[16]:
    2025(조원) 2035(조원) 유가 고정 2035(조원) 유가 효과(조원) 유가 효과(%) CAGR(%) 유가 고정 CAGR(%) CAGR 기여(%p)
  모형                
정비판 총량 예측모형 2,326.3 2,671.4 2,670.3 +1.07 +0.040 1.39 1.39 +0.0041
62 바텀업(드리프트 재설정) 2,178.5 2,793.7 2,789.8 +3.97 +0.142 2.52 2.50 +0.0146
62 바텀업(재설정 전, 9절) 2,178.5 3,223.4 3,218.7 +4.67 +0.145 4.00 3.98 +0.0151
이전 판 총량 예측모형 2,326.3 2,970.4 2,955.8 +14.60 +0.491 2.47 2.42 +0.0505
62 바텀업 2,178.5 3,507.0 3,441.5 +65.47 +1.867 4.88 4.68 +0.1975

표 11. 정비판 시나리오 폭(max−min, 연평균)과 유가 경로 — 2026년과 2035년¶

In [17]:
per_j = rd("exog_persistence_jeongbi.csv", index_col=0)
dol_j = rd("oil_dollar_paths_jeongbi.csv", index_col=0)
pth_j = rd("exog_paths_summary_jeongbi.csv", index_col=0)
t11 = per_j.join(per[["폭_2026", "폭_2035", "판정"]].add_prefix("이전 판 "))
t11.index = [NAME[k] for k in t11.index]
t11.to_csv(os.path.join(C.OUTPUT, "table11_persistence_jeongbi.csv"), encoding="utf-8-sig")
display(t11.style.format({"폭_2026": "{:,.2f}", "폭_2035": "{:,.3f}", "이전 판 폭_2026": "{:,.2f}", "이전 판 폭_2035": "{:,.3f}"}))
pd.concat({"정비판": dol_j.loc[[2025, 2030, 2035]], "이전 판": dollars.loc[[2025, 2030, 2035]]}, axis=1).style.format("{:.1f}")
  폭_2026 폭_2035 판정 이전 판 폭_2026 이전 판 폭_2035 이전 판 판정
유가 25.16 51.750 영구 25.16 51.750 영구
세계수요 1.79 0.000 일시 1.79 0.000 일시
실질금리 5.63 0.000 일시 4.80 0.000 일시
실질환율 13.65 0.004 일시 11.87 0.004 일시
고용(취업자) 681.87 943.421 영구 531.61 8.630 일시
우리경기 3.44 0.000 일시 2.94 0.000 일시
Out[17]:
  정비판 이전 판
  S1 S2 S3 S4 S5 S1 S2 S3 S4 S5
연도                    
2025 69.2 69.2 69.2 69.2 69.2 69.2 69.2 69.2 69.2 69.2
2030 70.4 58.8 65.8 70.4 98.6 99.7 83.3 93.2 99.7 139.7
2035 78.9 65.9 73.8 78.9 110.6 163.4 136.5 152.7 163.4 228.9

검증 — 공표 수치와 대조¶

외생 카드의 표·본문 수치, 유가 카드의 표·본문 수치, 공표 SHAP 산출물(shap_36/62/69.json)을 계산 결과와 대조한다(이전 판). 이어서 정비판(02 10절)의 분해 점검, 5장 기록(62분류 분해·드리프트 재설정 δ·바텀업 연평균)과의 대조, 정비판 진단 기록 수치(config.JEONGBI)를 대조한다. 허용오차는 공표·기록 자릿수의 반올림 폭이다(정수 조원 ±0.5, 소수 둘째 자리 ±0.005 등). 범주 항목은 일치 여부를 본다.

In [18]:
P = C.PUBLISHED
norm = lambda s: str(s).replace(" ", "").replace("·", "").replace("ㆍ", "").replace("및", "")   # noqa: E731
checks = []
def num(item, calc, pub, tol):
    checks.append({"항목": item, "계산": round(float(calc), 6), "공표": pub, "허용오차": f"±{tol:g}", "통과": abs(float(calc) - pub) <= tol + 1e-12})
def same(item, calc, pub, how="일치"):
    ok = (norm(pub) in norm(calc)) if how == "포함" else (calc == pub)
    checks.append({"항목": item, "계산": calc, "공표": pub, "허용오차": how, "통과": bool(ok)})
def le(item, calc, pub):
    checks.append({"항목": item, "계산": round(float(calc), 6), "공표": pub, "허용오차": "이하", "통과": float(calc) <= pub})

for k in F.EXOG:
    num(f"[외생] 총량 순효과 {NAME[k]}(조원)", eff.loc[k, "총량_효과_조원"], P["총량_조원"][k], 0.5)
for k in F.EXOG:
    num(f"[외생] 총량 CAGR 기여 {NAME[k]}(%p)", eff.loc[k, "총량_CAGR기여_%p"], P["총량_%p"][k], 0.005)
for k in F.EXOG:
    num(f"[외생] 바텀업 순효과 {NAME[k]}(조원)", eff.loc[k, "바텀업_효과_조원"], P["바텀업_조원"][k], 0.5)
for k in F.EXOG:
    num(f"[외생] 바텀업 CAGR 기여 {NAME[k]}(%p)", eff.loc[k, "바텀업_CAGR기여_%p"], P["바텀업_%p"][k], 0.005)
same("[외생] 지배 변수(총량·바텀업 모두 1위)",
     eff["총량_효과_조원"].abs().idxmax() if eff["총량_효과_조원"].abs().idxmax() == eff["바텀업_효과_조원"].abs().idxmax() else "불일치", P["지배_외생"])
same("[외생] 총량 CAGR 기여 순위", " > ".join(eff.sort_values("총량_CAGR기여_%p", ascending=False).index), " > ".join(P["총량_순위"]))
num("[외생] 실질금리 2025 연평균(%)", paths.loc["실질금리", "2025 연평균"], P["실질금리_2025"], 0.005)
num("[외생] 실질금리 2035 연평균(S1, %)", paths.loc["실질금리", "2035 연평균(S1)"], P["실질금리_2035"], 0.005)
num("[외생] 바텀업 CAGR 2025–2035(%)", tot.loc["62", "CAGR(%)"], P["바텀업_CAGR"], 0.005)
for k in ["유가", "고용_취업자", "실질환율", "실질금리", "우리경기", "세계수요"]:
    num(f"[외생] 시나리오 폭 2026 {NAME[k]}", per.loc[k, "폭_2026"], P["폭_2026"][k], 0.05)
for k in ["유가", "고용_취업자", "실질금리", "우리경기", "세계수요"]:
    num(f"[외생] 시나리오 폭 2035 {NAME[k]}", per.loc[k, "폭_2035"], P["폭_2035"][k], 0.05)
num("[외생] 시나리오 폭 2035 실질환율", per.loc["실질환율", "폭_2035"], P["폭_2035"]["실질환율"], 0.0005)
same("[외생] 영구 수준차 외생", " · ".join(per.index[per["판정"] == "영구"]), P["영구_외생"])

o_agg, o_bu = oil_cmp.loc["총량 예측모형"], oil_cmp.loc["62 바텀업"]
num("[유가] 총량 2035(조원)", o_agg["2035(조원)"], P["총량_2035_조"], 0.5)
num("[유가] 총량 유가 고정 2035(조원)", o_agg["유가 고정 2035(조원)"], P["총량_유가없음_조"], 0.5)
num("[유가] 총량 유가 효과(%)", o_agg["유가 효과(%)"], P["총량_유가효과_%"], 0.05)
num("[유가] 총량 CAGR 기여(%p)", o_agg["CAGR 기여(%p)"], P["총량_유가_%p"], 0.005)
num("[유가] 바텀업 2035(조원)", o_bu["2035(조원)"], P["바텀업_2035_조"], 0.5)
num("[유가] 바텀업 유가 고정 2035(조원)", o_bu["유가 고정 2035(조원)"], P["바텀업_유가없음_조"], 0.5)
num("[유가] 바텀업 유가 효과(%)", o_bu["유가 효과(%)"], P["바텀업_유가효과_%"], 0.05)
num("[유가] 바텀업 CAGR 기여(%p)", o_bu["CAGR 기여(%p)"], P["바텀업_유가_%p"], 0.005)
for r in ("36", "62", "69"):
    g = orank[(orank["분류"] == r) & orank["|기여| 최대"]].iloc[0]
    same(f"[유가] {r}분류 유가 영향 1위 산업", g["산업"], P["유가1위"][r][0], "포함")
    num(f"[유가] {r}분류 1위 기여(%p)", g["유가 기여(%p)"], P["유가1위"][r][1], 0.05)
num("[유가] 2025 연평균($/배럴)", dollars.loc[2025, "S1"], P["유가_2025_$"], 0.5)
num("[유가] S1 2035 연평균($/배럴)", dollars.loc[2035, "S1"], P["유가_S1_2035_$"], 0.5)
num("[유가] 2035 시나리오 최저($/배럴)", dollars.loc[2035].min(), P["유가_2035_최저_$"], 0.5)
num("[유가] 2035 시나리오 최고($/배럴)", dollars.loc[2035].max(), P["유가_2035_최고_$"], 0.5)
num("[두바이] WTI·두바이 로그차분 상관(원 시산 표본)", bm.loc["원 시산 표본(파일 전체)", "corr"], P["WTI_두바이_상관"], 0.0005)
num("[두바이] 최근 스프레드($, 2026-07)", bm.loc["원 시산 표본(파일 전체)", "WTI_$"] - bm.loc["원 시산 표본(파일 전체)", "DUB_$"], P["스프레드_$"], 0.5)
num("[두바이] 석유제품 기여 변화(%p)", dub.loc["14.석탄ㆍ석유제품", "변화"], P["정유_변화_%p"], 0.05)
le("[두바이] 나머지 5개 산업 |변화| 최대(%p)", dub.drop(index="14.석탄ㆍ석유제품")["변화"].abs().max(), P["나머지_변화_한도_%p"])
for r in ("36", "62", "69"):
    num(f"[SHAP] shap_{r}.json {int(shap_chk.loc[int(r), '공표 대조 값 수'])}개 값 최대 |차이|", shap_chk.loc[int(r), "반올림 전 최대 |차이|"], 0.0, 0.005)
n_prev = len(checks)

# 정비판(02 10절): 5장 기록과의 대조, 정비판 진단 기록 수치(C.JEONGBI)
J = C.JEONGBI
da5 = pd.read_csv(C.S5_DRIFT, index_col=0, encoding="utf-8-sig")
da5.index = da5.index.astype(str)
for r in ("36", "62", "69"):
    le(f"[정비판] {r}분류 SHAP 점검 (가)–(다) 최대 오차", scj.loc[r, ["(가) 성분 합 − 합계", "(나) 합계 − 전망 로그성장", "(다) 재설정분 − (−10(1−ΣA)δ)"]].max(), 1e-8)
le("[정비판] 62분류 SHAP − 5장 분해(shap_62_s5.csv) 최대 |차이|", scj.loc["62", "(라) 5장 기록과 최대 |차이|"], 1e-8)
for r in ("36", "62", "69"):
    num(f"[정비판] {r}분류 δ(연 %p) — 5장 drift_anchor", ancj.loc[r, "δ(연 %p)"], float(da5.loc[r, "δ(연 %p)"]), 1e-9)
    num(f"[정비판] {r}분류 바텀업 S1 연평균(%) — 5장 drift_anchor", ancj.loc[r, "S1 연평균 재설정 후(%)"], float(da5.loc[r, "바텀업 S1 연평균 — 재설정 후(%)"]), 1e-6)
num("[정비판] S1 유가 누적 변화 2025Q4→2035Q4(100·ln)", ofj.loc["62", "정비판 유가 누적(100·ln)"], J["유가_누적_S1"], 0.005)
for r in ("36", "62", "69"):
    g = orj[(orj["분류"] == r) & orj["|기여| 최대"]].iloc[0]
    same(f"[정비판] {r}분류 유가 영향 1위 산업", g["산업"], J["유가1위"][r][0], "포함")
    num(f"[정비판] {r}분류 1위 유가 기여(%p)", g["유가 기여(%p)"], J["유가1위"][r][1], 0.005)
    for c, key in (("과거추세", "과거추세"), ("산업 동학", "산업 동학"), ("외생(6종 절댓값 합)", "외생")):
        num(f"[정비판] {r}분류 평균 절대 기여 {key}(%p)", ssj.loc[(r, "정비판"), c], J["평균절대"][r][key], 0.005)
    num(f"[정비판] {r}분류 실질금리 최대 |기여|(%p)", ssj.loc[(r, "정비판"), "실질금리 최대 |기여|"], J["실질금리_최대절대"][r], 0.005)
num("[정비판] 62 유가 SHAP·반사실 산업 간 상관", svj.iloc[:, 0].corr(svj.iloc[:, 1]), J["유가_SHAP_반사실_상관"], 0.0005)
o_agg_j, o_bu_j = ocj.loc["총량 예측모형"], ocj.loc["62 바텀업(드리프트 재설정)"]
for tag, o in (("총량", o_agg_j), ("바텀업", o_bu_j)):
    num(f"[정비판] {tag} 2035(조원)", o["2035(조원)"], J[f"{tag}_2035_조"], 0.005)
    num(f"[정비판] {tag} 유가 효과(조원)", o["유가 효과(조원)"], J[f"{tag}_유가효과_조"], 0.005)
    num(f"[정비판] {tag} 유가 효과(%)", o["유가 효과(%)"], J[f"{tag}_유가효과_%"], 0.0005)
    num(f"[정비판] {tag} 유가 CAGR 기여(%p)", o["CAGR 기여(%p)"], J[f"{tag}_유가_%p"], 0.00005)
num("[정비판] 바텀업/총량 유가 효과 배율", o_bu_j["유가 효과(조원)"] / o_agg_j["유가 효과(조원)"], J["배율"], 0.005)
same("[정비판] 영구 수준차 외생(2035 시나리오 폭)", " · ".join(per_j.index[per_j["판정"] == "영구"]), J["영구_외생"])
num("[정비판] 시나리오 폭 2035 유가(100·ln)", per_j.loc["유가", "폭_2035"], J["유가_폭_2035"], 0.005)
num("[정비판] 유가 S1 2025 연평균($/배럴)", dol_j.loc[2025, "S1"], J["유가_2025_$"], 0.05)
num("[정비판] 유가 S1 2035 연평균($/배럴)", dol_j.loc[2035, "S1"], J["유가_S1_2035_$"], 0.05)
num("[정비판] 유가 2035 시나리오 최저($/배럴)", dol_j.loc[2035].min(), J["유가_2035_최저_$"], 0.05)
num("[정비판] 유가 2035 시나리오 최고($/배럴)", dol_j.loc[2035].max(), J["유가_2035_최고_$"], 0.05)
num("[정비판] 실질금리 S1 2035 연평균(%)", pth_j.loc["실질금리", "2035 연평균(S1)"], J["실질금리_2035"], 0.005)

res = pd.DataFrame(checks)
res.to_csv(os.path.join(C.OUTPUT, "verification.csv"), index=False, encoding="utf-8-sig")
with pd.option_context("display.max_rows", 200, "display.float_format", lambda v: f"{v:,.4f}"):
    display(res)
assert res["통과"].all(), "공표·기록 수치와 계산 결과가 다르다 — 위 표에서 통과=False 항목을 확인한다"
print(f"검증 통과: 이전 판 공표 수치 {n_prev}개(외생 카드·유가 카드·SHAP 산출물)와 정비판 항목 {len(res) - n_prev}개"
      f"(5장 분해 대조·정비판 기록 수치)를 반올림 범위 안에서 재현했다(합계 {len(res)}개).")
항목 계산 공표 허용오차 통과
0 [외생] 총량 순효과 유가(조원) 14.5957 15 ±0.5 True
1 [외생] 총량 순효과 세계수요(조원) 1.5975 2 ±0.5 True
2 [외생] 총량 순효과 실질금리(조원) 162.4946 162 ±0.5 True
3 [외생] 총량 순효과 실질환율(조원) 4.7973 5 ±0.5 True
4 [외생] 총량 순효과 고용(취업자)(조원) 62.3091 62 ±0.5 True
5 [외생] 총량 순효과 우리경기(조원) -0.8160 -1 ±0.5 True
6 [외생] 총량 CAGR 기여 유가(%p) 0.0505 0.0500 ±0.005 True
7 [외생] 총량 CAGR 기여 세계수요(%p) 0.0055 0.0100 ±0.005 True
8 [외생] 총량 CAGR 기여 실질금리(%p) 0.5749 0.5700 ±0.005 True
9 [외생] 총량 CAGR 기여 실질환율(%p) 0.0166 0.0200 ±0.005 True
10 [외생] 총량 CAGR 기여 고용(취업자)(%p) 0.2170 0.2200 ±0.005 True
11 [외생] 총량 CAGR 기여 우리경기(%p) -0.0028 -0.0000 ±0.005 True
12 [외생] 바텀업 순효과 유가(조원) 65.4734 65 ±0.5 True
13 [외생] 바텀업 순효과 세계수요(조원) -12.0228 -12 ±0.5 True
14 [외생] 바텀업 순효과 실질금리(조원) 354.2112 354 ±0.5 True
15 [외생] 바텀업 순효과 실질환율(조원) 9.9489 10 ±0.5 True
16 [외생] 바텀업 순효과 고용(취업자)(조원) 31.7926 32 ±0.5 True
17 [외생] 바텀업 순효과 우리경기(조원) 7.6240 8 ±0.5 True
18 [외생] 바텀업 CAGR 기여 유가(%p) 0.1975 0.2000 ±0.005 True
19 [외생] 바텀업 CAGR 기여 세계수요(%p) -0.0359 -0.0400 ±0.005 True
20 [외생] 바텀업 CAGR 기여 실질금리(%p) 1.1107 1.1100 ±0.005 True
21 [외생] 바텀업 CAGR 기여 실질환율(%p) 0.0298 0.0300 ±0.005 True
22 [외생] 바텀업 CAGR 기여 고용(취업자)(%p) 0.0955 0.1000 ±0.005 True
23 [외생] 바텀업 CAGR 기여 우리경기(%p) 0.0228 0.0200 ±0.005 True
24 [외생] 지배 변수(총량·바텀업 모두 1위) 실질금리 실질금리 일치 True
25 [외생] 총량 CAGR 기여 순위 실질금리 > 고용_취업자 > 유가 > 실질환율 > 세계수요 > 우리경기 실질금리 > 고용_취업자 > 유가 > 실질환율 > 세계수요 > 우리경기 일치 True
26 [외생] 실질금리 2025 연평균(%) 0.9945 0.9900 ±0.005 True
27 [외생] 실질금리 2035 연평균(S1, %) 2.7760 2.7800 ±0.005 True
28 [외생] 바텀업 CAGR 2025–2035(%) 4.8765 4.8800 ±0.005 True
29 [외생] 시나리오 폭 2026 유가 25.1560 25.2000 ±0.05 True
30 [외생] 시나리오 폭 2026 고용(취업자) 531.6052 531.6000 ±0.05 True
31 [외생] 시나리오 폭 2026 실질환율 11.8690 11.9000 ±0.05 True
32 [외생] 시나리오 폭 2026 실질금리 4.8030 4.8000 ±0.05 True
33 [외생] 시나리오 폭 2026 우리경기 2.9428 2.9000 ±0.05 True
34 [외생] 시나리오 폭 2026 세계수요 1.7900 1.8000 ±0.05 True
35 [외생] 시나리오 폭 2035 유가 51.7500 51.8000 ±0.05 True
36 [외생] 시나리오 폭 2035 고용(취업자) 8.6302 8.6000 ±0.05 True
37 [외생] 시나리오 폭 2035 실질금리 0.0000 0.0000 ±0.05 True
38 [외생] 시나리오 폭 2035 우리경기 0.0000 0.0000 ±0.05 True
39 [외생] 시나리오 폭 2035 세계수요 0.0000 0.0000 ±0.05 True
40 [외생] 시나리오 폭 2035 실질환율 0.0043 0.0040 ±0.0005 True
41 [외생] 영구 수준차 외생 유가 유가 일치 True
42 [유가] 총량 2035(조원) 2,970.3786 2970 ±0.5 True
43 [유가] 총량 유가 고정 2035(조원) 2,955.7830 2956 ±0.5 True
44 [유가] 총량 유가 효과(%) 0.4914 0.5000 ±0.05 True
45 [유가] 총량 CAGR 기여(%p) 0.0505 0.0500 ±0.005 True
46 [유가] 바텀업 2035(조원) 3,506.9957 3507 ±0.5 True
47 [유가] 바텀업 유가 고정 2035(조원) 3,441.5224 3442 ±0.5 True
48 [유가] 바텀업 유가 효과(%) 1.8669 1.9000 ±0.05 True
49 [유가] 바텀업 CAGR 기여(%p) 0.1975 0.2000 ±0.005 True
50 [유가] 36분류 유가 영향 1위 산업 코크스 및 석유정제품 제조업 코크스·석유정제품 포함 True
51 [유가] 36분류 1위 기여(%p) 41.0629 41.1000 ±0.05 True
52 [유가] 62분류 유가 영향 1위 산업 14.석탄ㆍ석유제품 석탄·석유제품 포함 True
53 [유가] 62분류 1위 기여(%p) 31.3593 31.4000 ±0.05 True
54 [유가] 69분류 유가 영향 1위 산업 14.석탄ㆍ석유제품 석탄·석유제품 포함 True
55 [유가] 69분류 1위 기여(%p) 30.8334 30.8000 ±0.05 True
56 [유가] 2025 연평균($/배럴) 69.2274 69 ±0.5 True
57 [유가] S1 2035 연평균($/배럴) 163.3638 163 ±0.5 True
58 [유가] 2035 시나리오 최저($/배럴) 136.4529 136 ±0.5 True
59 [유가] 2035 시나리오 최고($/배럴) 228.9445 229 ±0.5 True
60 [두바이] WTI·두바이 로그차분 상관(원 시산 표본) 0.9540 0.9540 ±0.0005 True
61 [두바이] 최근 스프레드($, 2026-07) 2.5777 3 ±0.5 True
62 [두바이] 석유제품 기여 변화(%p) 1.5852 1.6000 ±0.05 True
63 [두바이] 나머지 5개 산업 |변화| 최대(%p) 0.7853 1.0000 이하 True
64 [SHAP] shap_36.json 324개 값 최대 |차이| 0.0050 0.0000 ±0.005 True
65 [SHAP] shap_62.json 558개 값 최대 |차이| 0.0050 0.0000 ±0.005 True
66 [SHAP] shap_69.json 621개 값 최대 |차이| 0.0050 0.0000 ±0.005 True
67 [정비판] 36분류 SHAP 점검 (가)–(다) 최대 오차 0.0000 0.0000 이하 True
68 [정비판] 62분류 SHAP 점검 (가)–(다) 최대 오차 0.0000 0.0000 이하 True
69 [정비판] 69분류 SHAP 점검 (가)–(다) 최대 오차 0.0000 0.0000 이하 True
70 [정비판] 62분류 SHAP − 5장 분해(shap_62_s5.csv) 최대 |차이| 0.0000 0.0000 이하 True
71 [정비판] 36분류 δ(연 %p) — 5장 drift_anchor 1.5240 1.5240 ±1e-09 True
72 [정비판] 36분류 바텀업 S1 연평균(%) — 5장 drift_anchor 2.4570 2.4570 ±1e-06 True
73 [정비판] 62분류 δ(연 %p) — 5장 drift_anchor 1.5240 1.5240 ±1e-09 True
74 [정비판] 62분류 바텀업 S1 연평균(%) — 5장 drift_anchor 2.5187 2.5187 ±1e-06 True
75 [정비판] 69분류 δ(연 %p) — 5장 drift_anchor 1.5240 1.5240 ±1e-09 True
76 [정비판] 69분류 바텀업 S1 연평균(%) — 5장 drift_anchor 2.5250 2.5250 ±1e-06 True
77 [정비판] S1 유가 누적 변화 2025Q4→2035Q4(100·ln) 22.4299 22.4300 ±0.005 True
78 [정비판] 36분류 유가 영향 1위 산업 코크스 및 석유정제품 제조업 코크스·석유정제품 포함 True
79 [정비판] 36분류 1위 유가 기여(%p) 8.4737 8.4700 ±0.005 True
80 [정비판] 36분류 평균 절대 기여 과거추세(%p) 21.0409 21.0400 ±0.005 True
81 [정비판] 36분류 평균 절대 기여 산업 동학(%p) 4.9958 5.0000 ±0.005 True
82 [정비판] 36분류 평균 절대 기여 외생(%p) 3.8793 3.8800 ±0.005 True
83 [정비판] 36분류 실질금리 최대 |기여|(%p) 0.2493 0.2500 ±0.005 True
84 [정비판] 62분류 유가 영향 1위 산업 14.석탄ㆍ석유제품 석탄·석유제품 포함 True
85 [정비판] 62분류 1위 유가 기여(%p) 6.2084 6.2100 ±0.005 True
86 [정비판] 62분류 평균 절대 기여 과거추세(%p) 23.5288 23.5300 ±0.005 True
87 [정비판] 62분류 평균 절대 기여 산업 동학(%p) 14.2144 14.2100 ±0.005 True
88 [정비판] 62분류 평균 절대 기여 외생(%p) 3.2986 3.3000 ±0.005 True
89 [정비판] 62분류 실질금리 최대 |기여|(%p) 0.1572 0.1600 ±0.005 True
90 [정비판] 69분류 유가 영향 1위 산업 14.석탄ㆍ석유제품 석탄·석유제품 포함 True
91 [정비판] 69분류 1위 유가 기여(%p) 6.0811 6.0800 ±0.005 True
92 [정비판] 69분류 평균 절대 기여 과거추세(%p) 25.7494 25.7500 ±0.005 True
93 [정비판] 69분류 평균 절대 기여 산업 동학(%p) 15.7439 15.7400 ±0.005 True
94 [정비판] 69분류 평균 절대 기여 외생(%p) 3.3945 3.3900 ±0.005 True
95 [정비판] 69분류 실질금리 최대 |기여|(%p) 0.1557 0.1600 ±0.005 True
96 [정비판] 62 유가 SHAP·반사실 산업 간 상관 0.8979 0.8980 ±0.0005 True
97 [정비판] 총량 2035(조원) 2,671.4229 2,671.4200 ±0.005 True
98 [정비판] 총량 유가 효과(조원) 1.0742 1.0700 ±0.005 True
99 [정비판] 총량 유가 효과(%) 0.0402 0.0400 ±0.0005 True
100 [정비판] 총량 유가 CAGR 기여(%p) 0.0041 0.0041 ±5e-05 True
101 [정비판] 바텀업 2035(조원) 2,793.7447 2,793.7400 ±0.005 True
102 [정비판] 바텀업 유가 효과(조원) 3.9658 3.9700 ±0.005 True
103 [정비판] 바텀업 유가 효과(%) 0.1420 0.1420 ±0.0005 True
104 [정비판] 바텀업 유가 CAGR 기여(%p) 0.0146 0.0146 ±5e-05 True
105 [정비판] 바텀업/총량 유가 효과 배율 3.6917 3.6900 ±0.005 True
106 [정비판] 영구 수준차 외생(2035 시나리오 폭) 유가 · 고용_취업자 유가 · 고용_취업자 일치 True
107 [정비판] 시나리오 폭 2035 유가(100·ln) 51.7500 51.7500 ±0.005 True
108 [정비판] 유가 S1 2025 연평균($/배럴) 69.2274 69.2000 ±0.05 True
109 [정비판] 유가 S1 2035 연평균($/배럴) 78.9022 78.9000 ±0.05 True
110 [정비판] 유가 2035 시나리오 최저($/배럴) 65.9046 65.9000 ±0.05 True
111 [정비판] 유가 2035 시나리오 최고($/배럴) 110.5766 110.6000 ±0.05 True
112 [정비판] 실질금리 S1 2035 연평균(%) 1.3119 1.3100 ±0.005 True
검증 통과: 이전 판 공표 수치 67개(외생 카드·유가 카드·SHAP 산출물)와 정비판 항목 46개(5장 분해 대조·정비판 기록 수치)를 반올림 범위 안에서 재현했다(합계 113개).