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)
No description has been provided for this image

그림 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)
No description has been provided for this image

그림 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)
No description has been provided for this image

검증¶

  1. 4장과의 정합. '현행(직접)' 사양은 4장 방식과 같은 구조이므로 4장 결과(config.S4_PUBLISHED)를 재현해야 한다. 시점 가중치를 분기로 보간하는 차이 때문에 2035년 수준은 1조 원 안에서, 연평균 차이와 산업 프리미엄은 공표 자릿수의 반올림 범위 안에서 같으면 통과로 본다.
  2. 보고서1과의 정합. 기존 계수로 다시 낸 바텀업 합계(드리프트 재설정 후)가 보고서1 5장 표의 2,794조 원과 같아야 한다.
  3. 대안 1의 기존 전망 불변. 대안 1은 기준 시나리오에서 AI 항이 0이므로 바텀업 변화가 정확히 0이다(표 2 첫 행).
  4. 본문 수치. 이 장 본문의 수치(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개 항목이 반올림 범위 안에서 일치한다.