03 결과 — 표·그림과 검증¶
02의 산출물만 읽어 본문 표와 그림을 만들고, 마지막 검증 셀에서 본문 수치·4장 공표 수치·보고서1 5장 수치와 대조한다.
In [1]:
import os, hashlib
import numpy as np, pandas as pd
import config as C
import ai_index as I, ai_varx as X, ai_forecast as F, varx_engine as V, alt_run as R
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 (한글 글꼴)
O = lambda f: os.path.join(C.OUTPUT, f) # noqa: E731
comp = pd.read_csv(O("ai_index_quarterly.csv"), index_col=0)
ex = pd.read_csv(O("ai_exposure_21.csv"), index_col=0)
rho = pd.read_csv(O("exposure_rank_corr.csv"), index_col=0).iloc[:, 0]
summ = pd.read_csv(O("summary_variants.csv"), index_col=0)
P62 = pd.read_csv(O("premium_62_variants.csv"), index_col=0)
diag2 = pd.read_csv(O("alt2_diagnostics.csv"), index_col=0)["값"]
bct = pd.read_csv(O("alt2_baseline_change.csv"), index_col=0)
dec = pd.read_csv(O("spillover_diag.csv"), index_col=0)
chk = pd.read_csv(O("model_check.csv"), index_col=0)["값"]
tri = pd.read_csv(O("triangulation_rank_corr.csv"), index_col=0)
ktot = pd.read_csv(O("keis_r14_total.csv"), index_col=0)["값"]
Python 3.12.10 · pandas 2.2.3 · numpy 2.4.6 · matplotlib 3.11.0 판: 2026-10 정비판 기준 · Λ 0.5 · 드리프트 목표 1.8% · ONLINE=0(스냅샷) · Event Registry 키 없음 설문 통계표(비공개) 있음 data\private\AI수용도조사_통계표_260417.xlsx 설문 원자료(비공개) 있음 data\private\AI수용도조사_원자료_n2297.xlsx
표 1. 사양별 AI 확산 효과(2035년, Λ = 0.5%p)¶
In [2]:
t1 = summ[["AI_2035_조", "수준차이_조", "차이_%p", "프리미엄_최소", "프리미엄_최대", "음의_산업수"]].copy()
t1.columns = ["AI 확산 2035(조 원)", "기준 대비(조 원)", "연평균 차이(%p)", "산업 프리미엄 최소(%p)", "산업 프리미엄 최대(%p)", "음(−)인 산업 수"]
t1.to_csv(O("table1_variants.csv"), encoding="utf-8-sig")
display(t1.round(3))
| AI 확산 2035(조 원) | 기준 대비(조 원) | 연평균 차이(%p) | 산업 프리미엄 최소(%p) | 산업 프리미엄 최대(%p) | 음(−)인 산업 수 | |
|---|---|---|---|---|---|---|
| 사양 | ||||||
| 현행(직접) | 2,692.703 | 83.675 | 0.322 | 0.400 | 0.620 | 0 |
| 대안1a 설문·장기 | 2,690.796 | 81.768 | 0.315 | 0.393 | 0.624 | 0 |
| 대안1b 노출·장기 | 2,690.588 | 81.560 | 0.314 | 0.385 | 0.652 | 0 |
| 대안1c 고용정보원·장기 | 2,690.570 | 81.542 | 0.314 | -0.040 | 0.856 | 1 |
| 참고 노출·직접 | 2,692.702 | 83.674 | 0.322 | 0.388 | 0.647 | 0 |
| 진단 설문·동시항 | 2,689.780 | 80.752 | 0.311 | -0.405 | 0.906 | 9 |
| 대안2 설문 표본오차 | 2,690.798 | 81.770 | 0.315 | 0.393 | 0.624 | 0 |
| 대안2 계층(경험적 베이즈) | 2,690.798 | 81.770 | 0.315 | 0.393 | 0.624 | 0 |
표 2. 기존 전망의 변화(기준 시나리오, 미래 AI 갭 0)¶
대안 1은 기존 계수를 그대로 쓰므로 변화가 0이다. 대안 2는 AI 갭을 넣어 다시 추정한 모형의 결과이다.
In [3]:
t2 = pd.concat([pd.DataFrame([{"사전분포": "대안 1(기존 계수 고정)", "바텀업_기존_조": chk["바텀업_2035_조"], "바텀업_재추정_조": chk["바텀업_2035_조"],
"바텀업_변화_%": 0.0, "벤치마킹후_최대_%": 0.0, "벤치마킹후_최소_%": 0.0, "벤치마킹후_절대중앙_%": 0.0}]).set_index("사전분포"),
bct.rename(index=lambda s: f"대안 2({s})")])
t2.to_csv(O("table2_baseline_change.csv"), encoding="utf-8-sig")
display(t2.round(3))
| 바텀업_기존_조 | 바텀업_재추정_조 | 바텀업_변화_% | 벤치마킹후_최대_% | 벤치마킹후_최소_% | 벤치마킹후_절대중앙_% | |
|---|---|---|---|---|---|---|
| 사전분포 | ||||||
| 대안 1(기존 계수 고정) | 2,793.745 | 2,793.745 | 0.000 | 0.000 | 0.000 | 0.000 |
| 대안 2(설문 표본오차) | 2,793.745 | 2,786.618 | -0.255 | 0.864 | -0.298 | 0.120 |
| 대안 2(계층(경험적 베이즈)) | 2,793.745 | 2,786.618 | -0.255 | 0.864 | -0.298 | 0.120 |
표 3. 산업별 프리미엄 — 현행과 대안 1b의 차이가 큰 산업(%p)¶
대안 1c(한국고용정보원 2017년 혁신 시나리오 배분)도 함께 싣는다. 업종 순위의 일치 정도는 표 4에 있다.
In [4]:
cols = ["현행(직접)", "대안1a 설문·장기", "대안1b 노출·장기", "대안1c 고용정보원·장기", "진단 설문·동시항"]
d = (P62["대안1b 노출·장기"] - P62["현행(직접)"]).sort_values()
t3 = P62.loc[list(d.index[:6]) + list(d.index[-6:]), cols].assign(**{"1b−현행": d})
t3.to_csv(O("table3_premium_diff.csv"), encoding="utf-8-sig")
display(t3.round(2))
| 현행(직접) | 대안1a 설문·장기 | 대안1b 노출·장기 | 대안1c 고용정보원·장기 | 진단 설문·동시항 | 1b−현행 | |
|---|---|---|---|---|---|---|
| 56.교육서비스업 | 0.580 | 0.580 | 0.450 | -0.040 | 0.770 | -0.140 |
| 21.금속가공제품 | 0.480 | 0.450 | 0.380 | 0.130 | 0.750 | -0.090 |
| 10.가죽제품 | 0.480 | 0.490 | 0.400 | 0.220 | 0.010 | -0.080 |
| 9.섬유ㆍ의복 | 0.480 | 0.490 | 0.400 | 0.220 | -0.050 | -0.080 |
| 17.의료ㆍ의약품 | 0.490 | 0.490 | 0.420 | 0.380 | 0.450 | -0.070 |
| 18.고무ㆍ플라스틱제품 | 0.490 | 0.490 | 0.420 | 0.390 | 0.470 | -0.070 |
| 36.수상운송업 | 0.460 | 0.480 | 0.520 | 0.410 | -0.260 | 0.060 |
| 38.창고ㆍ운송보조서비스업 | 0.460 | 0.480 | 0.520 | 0.410 | -0.340 | 0.060 |
| 37.항공운송업 | 0.460 | 0.480 | 0.520 | 0.410 | -0.400 | 0.060 |
| 49.부동산업 | 0.440 | 0.430 | 0.500 | 0.560 | 0.500 | 0.060 |
| 51.연구개발업 | 0.470 | 0.470 | 0.550 | 0.850 | 0.630 | 0.070 |
| 53.과학기술관련전문서비스업 | 0.470 | 0.470 | 0.550 | 0.860 | 0.630 | 0.080 |
표 4. 세 자료의 업종 순위상관(21개 업종, 스피어만)¶
In [5]:
display(tri.round(2))
| 설문 E4-1 | 객관 노출 지수 | 고용정보원(2017) ΔVA | |
|---|---|---|---|
| 설문 E4-1 | 1.000 | 0.490 | 0.050 |
| 객관 노출 지수 | 0.490 | 1.000 | 0.610 |
| 고용정보원(2017) ΔVA | 0.050 | 0.610 | 1.000 |
그림 1. AI 확산 총량 지수와 갭¶
In [6]:
fig, ax = plt.subplots(2, 1, figsize=(8, 5.6), sharex=True, gridspec_kw={"height_ratios": [2, 1]})
x = range(len(comp))
for c, s in [("특허", ":"), ("논문", "--"), ("뉴스", "-.")]:
ax[0].plot(x, comp[c], s, lw=1, label=c)
ax[0].plot(x, comp["총량지수"], lw=2.2, color="k", label="총량 지수")
ax[0].plot(x, comp["추세"], lw=1, color="0.5", label="HP 추세")
ax[0].set_ylabel("로그 편차(2015–2024 평균 = 0)"); ax[0].legend(ncol=5, fontsize=8, loc="upper left")
ax[1].bar(x, comp["갭"], color=["#2b6cb0" if v >= 0 else "#c05621" for v in comp["갭"]])
ax[1].axhline(0, color="k", lw=.6); ax[1].set_ylabel("갭")
ticks = [i for i, q in enumerate(comp.index) if q.endswith("Q1")]
ax[1].set_xticks(ticks); ax[1].set_xticklabels([comp.index[i][:4] for i in ticks], fontsize=8)
fig.tight_layout(); fig.savefig(O("fig1_ai_index.png"), dpi=150)
그림 2. 업종별 AI 노출 지수(z)¶
In [7]:
e = ex["노출지수_z"].sort_values()
fig, ax = plt.subplots(figsize=(7, 6))
ax.barh([s.split(". ")[1] for s in e.index], e, color=["#2b6cb0" if v >= 0 else "#a0aec0" for v in e])
ax.axvline(0, color="k", lw=.6); ax.set_xlabel("노출 지수(자료원별 z의 평균)")
fig.tight_layout(); fig.savefig(O("fig2_exposure.png"), dpi=150)
그림 3. 산업별 2035년 프리미엄 — 현행과 대안 비교(62개 산업)¶
In [8]:
o = P62.sort_values("현행(직접)").index
fig, ax = plt.subplots(figsize=(8, 11))
yy = range(len(o))
ax.scatter(P62.loc[o, "진단 설문·동시항"], yy, s=14, marker="x", color="#c05621", label="진단: 설문·동시항")
ax.scatter(P62.loc[o, "대안1c 고용정보원·장기"], yy, s=14, marker="^", color="#718096", label="대안 1c: 고용정보원(2017)·장기")
ax.scatter(P62.loc[o, "대안1b 노출·장기"], yy, s=16, color="#2b6cb0", label="대안 1b: 노출·장기")
ax.scatter(P62.loc[o, "현행(직접)"], yy, s=18, facecolors="none", edgecolors="k", label="현행(직접)")
ax.set_yticks(list(yy)); ax.set_yticklabels([s.split(".", 1)[1][:14] for s in o], fontsize=7)
ax.axvline(0, color="k", lw=.6); ax.set_xlabel("2035년 AI 프리미엄(연 %p)"); ax.legend(fontsize=8, loc="lower right")
fig.tight_layout(); fig.savefig(O("fig3_premium_variants.png"), dpi=150)
검증¶
- 4장과의 정합. '현행(직접)' 사양은 4장 방식과 같은 구조이므로 4장 결과(
config.S4_PUBLISHED)를 재현해야 한다. 시점 가중치를 분기로 보간하는 차이 때문에 2035년 수준은 1조 원 안에서, 연평균 차이와 산업 프리미엄은 공표 자릿수의 반올림 범위 안에서 같으면 통과로 본다. - 보고서1과의 정합. 기존 계수로 다시 낸 바텀업 합계(드리프트 재설정 후)가 보고서1 5장 표의 2,794조 원과 같아야 한다.
- 대안 1의 기존 전망 불변. 대안 1은 기준 시나리오에서 AI 항이 0이므로 바텀업 변화가 정확히 0이다(표 2 첫 행).
- 본문 수치. 이 장 본문의 수치(
config.PUBLISHED)가 계산 결과와 반올림 범위 안에서 같아야 한다.
In [9]:
def p62(prefix, col="현행(직접)"):
return float(P62.loc[[c for c in P62.index if c.startswith(prefix)], col].iloc[0])
s = summ
calc4 = {"AI_2035_조": s.loc["현행(직접)", "AI_2035_조"], "AI_차이_%p": s.loc["현행(직접)", "차이_%p"],
"정보통신_프리미엄_%p": p62("44."), "교육_프리미엄_%p": p62("56."), "문화_프리미엄_%p": p62("59.")}
rows = [{"구분": "4장 정합", "항목": k, "계산": calc4[k], "기준값": v, "허용오차": tol} for k, (v, tol) in C.S4_PUBLISHED.items()]
v5, dg5 = C.S5_BOTTOMUP_2035_조
rows.append({"구분": "보고서1 정합", "항목": "바텀업_2035_조", "계산": chk["바텀업_2035_조"], "기준값": v5, "허용오차": C.tolerance(dg5)})
calc = {
"1a_차이_%p": s.loc["대안1a 설문·장기", "차이_%p"], "1b_차이_%p": s.loc["대안1b 노출·장기", "차이_%p"],
"진단_차이_%p": s.loc["진단 설문·동시항", "차이_%p"], "진단_음의_산업수": s.loc["진단 설문·동시항", "음의_산업수"],
"진단_최소_%p": s.loc["진단 설문·동시항", "프리미엄_최소"], "진단_최대_%p": s.loc["진단 설문·동시항", "프리미엄_최대"],
"1b_최소_%p": s.loc["대안1b 노출·장기", "프리미엄_최소"], "1b_최대_%p": s.loc["대안1b 노출·장기", "프리미엄_최대"],
"1a_현행_최대차_%p": (P62["대안1a 설문·장기"] - P62["현행(직접)"]).abs().max(),
"1b_교육_차_%p": p62("56.", "대안1b 노출·장기") - p62("56."),
"노출_순위상관": rho["노출 지수"],
"대안2_차이_%p": s.loc["대안2 설문 표본오차", "차이_%p"],
"대안2_beta_t": diag2["beta_t"], "대안2_유의_산업수": diag2["유의(|t|>1.96)_산업수"], "대안2_tau2": diag2["tau2_계층"],
"대안2_바텀업_변화_%": bct.loc["설문 표본오차", "바텀업_변화_%"],
"대안2_벤치_최대_%": bct.loc["설문 표본오차", "벤치마킹후_최대_%"], "대안2_벤치_최소_%": bct.loc["설문 표본오차", "벤치마킹후_최소_%"],
"지수_2015Q1": comp["총량지수"].iloc[0], "지수_2025Q4": comp["총량지수"].iloc[-1],
"교차계수_음수비율": chk["교차계수_음수비율"],
"1c_차이_%p": s.loc["대안1c 고용정보원·장기", "차이_%p"], "1c_최소_%p": s.loc["대안1c 고용정보원·장기", "프리미엄_최소"],
"1c_최대_%p": s.loc["대안1c 고용정보원·장기", "프리미엄_최대"], "고용정보원_전산업_%p": float(ktot["전산업_KIET_3구간평균"]),
"순위상관_설문_고용정보원": tri.loc["설문 E4-1", "고용정보원(2017) ΔVA"], "순위상관_노출_고용정보원": tri.loc["객관 노출 지수", "고용정보원(2017) ΔVA"],
"1a_최소_%p": s.loc["대안1a 설문·장기", "프리미엄_최소"], "1a_최대_%p": s.loc["대안1a 설문·장기", "프리미엄_최대"],
"대안2_최소_%p": s.loc["대안2 설문 표본오차", "프리미엄_최소"], "대안2_최대_%p": s.loc["대안2 설문 표본오차", "프리미엄_최대"],
"1c_음의_산업수": s.loc["대안1c 고용정보원·장기", "음의_산업수"], "1c_연구개발_%p": p62("51.", "대안1c 고용정보원·장기"),
"노출_정보통신": ex.loc["13. 정보통신", "노출지수_z"], "노출_음식숙박": ex.loc["15. 음식숙박", "노출지수_z"], "노출_교육": ex.loc["19. 교육", "노출지수_z"],
"진단_항공운송_%p": p62("37.", "진단 설문·동시항"), "진단_장기음수_산업수": int((dec["합계_장기"] < 0).sum()),
"대안2_자료비중_최대": diag2["자료비중_최대[설문 표본오차]"],
"대안2_beta": diag2["beta"],
}
assert set(calc) == set(C.PUBLISHED), "검증 항목과 본문 수치 항목이 다르다: " + str(set(calc) ^ set(C.PUBLISHED))
for k, (v, dg) in C.PUBLISHED.items():
rows.append({"구분": "본문", "항목": k, "계산": calc[k], "기준값": v, "허용오차": C.tolerance(dg)})
res = pd.DataFrame(rows)
res["통과"] = (res["계산"] - res["기준값"]).abs() <= res["허용오차"] + 1e-12
res.to_csv(O("verification.csv"), index=False, encoding="utf-8-sig")
display(res)
assert res["통과"].all(), "공표 수치와 계산 결과가 다르다 — 위 표에서 통과=False 항목을 확인한다"
print(f"검증 통과: 4장 정합 {len(C.S4_PUBLISHED)}개, 보고서1 정합 1개, 본문 {len(C.PUBLISHED)}개 — 모두 {len(res)}개 항목이 반올림 범위 안에서 일치한다.")
| 구분 | 항목 | 계산 | 기준값 | 허용오차 | 통과 | |
|---|---|---|---|---|---|---|
| 0 | 4장 정합 | AI_2035_조 | 2,692.703 | 2,692.220 | 1.000 | True |
| 1 | 4장 정합 | AI_차이_%p | 0.322 | 0.320 | 0.005 | True |
| 2 | 4장 정합 | 정보통신_프리미엄_%p | 0.620 | 0.620 | 0.005 | True |
| 3 | 4장 정합 | 교육_프리미엄_%p | 0.584 | 0.580 | 0.005 | True |
| 4 | 4장 정합 | 문화_프리미엄_%p | 0.400 | 0.400 | 0.005 | True |
| 5 | 보고서1 정합 | 바텀업_2035_조 | 2,793.745 | 2,794.000 | 0.500 | True |
| 6 | 본문 | 1a_차이_%p | 0.315 | 0.310 | 0.005 | True |
| 7 | 본문 | 1b_차이_%p | 0.314 | 0.310 | 0.005 | True |
| 8 | 본문 | 진단_차이_%p | 0.311 | 0.310 | 0.005 | True |
| 9 | 본문 | 진단_음의_산업수 | 9.000 | 9.000 | 0.500 | True |
| 10 | 본문 | 진단_최소_%p | -0.405 | -0.400 | 0.005 | True |
| 11 | 본문 | 진단_최대_%p | 0.906 | 0.910 | 0.005 | True |
| 12 | 본문 | 1b_최소_%p | 0.385 | 0.380 | 0.005 | True |
| 13 | 본문 | 1b_최대_%p | 0.652 | 0.650 | 0.005 | True |
| 14 | 본문 | 1a_현행_최대차_%p | 0.052 | 0.050 | 0.005 | True |
| 15 | 본문 | 1b_교육_차_%p | -0.137 | -0.140 | 0.005 | True |
| 16 | 본문 | 노출_순위상관 | 0.491 | 0.490 | 0.005 | True |
| 17 | 본문 | 대안2_차이_%p | 0.315 | 0.310 | 0.005 | True |
| 18 | 본문 | 대안2_beta_t | 0.902 | 0.900 | 0.050 | True |
| 19 | 본문 | 대안2_유의_산업수 | 1.000 | 1.000 | 0.500 | True |
| 20 | 본문 | 대안2_tau2 | 0.000 | 0.000 | 0.005 | True |
| 21 | 본문 | 대안2_바텀업_변화_% | -0.255 | -0.260 | 0.005 | True |
| 22 | 본문 | 대안2_벤치_최대_% | 0.864 | 0.860 | 0.005 | True |
| 23 | 본문 | 대안2_벤치_최소_% | -0.298 | -0.300 | 0.005 | True |
| 24 | 본문 | 지수_2015Q1 | -1.534 | -1.530 | 0.005 | True |
| 25 | 본문 | 지수_2025Q4 | 1.242 | 1.240 | 0.005 | True |
| 26 | 본문 | 교차계수_음수비율 | 0.459 | 0.460 | 0.005 | True |
| 27 | 본문 | 1c_차이_%p | 0.314 | 0.310 | 0.005 | True |
| 28 | 본문 | 1c_최소_%p | -0.040 | -0.040 | 0.005 | True |
| 29 | 본문 | 1c_최대_%p | 0.856 | 0.860 | 0.005 | True |
| 30 | 본문 | 고용정보원_전산업_%p | 0.467 | 0.470 | 0.005 | True |
| 31 | 본문 | 순위상관_설문_고용정보원 | 0.051 | 0.050 | 0.005 | True |
| 32 | 본문 | 순위상관_노출_고용정보원 | 0.610 | 0.610 | 0.005 | True |
| 33 | 본문 | 1a_최소_%p | 0.393 | 0.390 | 0.005 | True |
| 34 | 본문 | 1a_최대_%p | 0.624 | 0.620 | 0.005 | True |
| 35 | 본문 | 대안2_최소_%p | 0.393 | 0.390 | 0.005 | True |
| 36 | 본문 | 대안2_최대_%p | 0.624 | 0.620 | 0.005 | True |
| 37 | 본문 | 1c_음의_산업수 | 1.000 | 1.000 | 0.500 | True |
| 38 | 본문 | 1c_연구개발_%p | 0.854 | 0.850 | 0.005 | True |
| 39 | 본문 | 노출_정보통신 | 2.023 | 2.020 | 0.005 | True |
| 40 | 본문 | 노출_음식숙박 | -1.305 | -1.310 | 0.005 | True |
| 41 | 본문 | 노출_교육 | -0.436 | -0.440 | 0.005 | True |
| 42 | 본문 | 진단_항공운송_%p | -0.405 | -0.400 | 0.005 | True |
| 43 | 본문 | 진단_장기음수_산업수 | 10.000 | 10.000 | 0.500 | True |
| 44 | 본문 | 대안2_자료비중_최대 | 0.000 | 0.000 | 0.001 | True |
| 45 | 본문 | 대안2_beta | 0.080 | 0.080 | 0.005 | True |
검증 통과: 4장 정합 5개, 보고서1 정합 1개, 본문 40개 — 모두 46개 항목이 반올림 범위 안에서 일치한다.