03 결과 도출 — 옵션 비교표·그림, 이전 판 대비 변화, 검증¶
02_분석.ipynb의 산출물로 4장의 옵션 비교표와 그림을 만든다. 마지막 셀은 다음을 대조한다(반올림 범위 안에서 일치해야 통과).
- 정비판 그리드(정비판 외생) ↔ 2026-10 정비 실행에서 동결한 기록 그리드(
data/published/grid_summary_*.csv)와 핵심 수치(config.PUBLISHED) - 이전 판 외생으로 다시 계산한 그리드 ↔ 이전 공표 그리드(
data/published_prev/, 2026-08-28·09-01 실행)와 논문 1–3 본문 인용값(config.PREVIOUS)
In [1]:
import config as C # 선형대수 스레드를 1개로 고정하므로 numpy보다 먼저 불러온다
import os, hashlib, time
import numpy as np, pandas as pd
import varx_grid as V
pd.set_option("display.float_format", lambda v: f"{v:,.3f}")
pd.set_option("display.max_columns", 30)
C.setup()
import matplotlib.pyplot as plt
import koreanize_matplotlib # noqa: F401 (한글 글꼴)
grids = {g: pd.read_csv(os.path.join(C.OUTPUT, f"grid_{g}.csv"), encoding="utf-8-sig") for g in C.GRIDS}
prev = {g: pd.read_csv(os.path.join(C.OUTPUT, f"grid_prev_{g}.csv"), encoding="utf-8-sig") for g in C.GRIDS}
ORDER = [o["옵션"] for o in V.options()]
M = ["RMSE_train", "RMSE_valid", "RMSE_test"]
Python 3.12.10 · pandas 2.2.3 · numpy 2.4.6 · matplotlib 3.11.0 · BLAS 스레드 1 표본 2000Q1–2025Q4 · train ~2021Q4 · valid ~2023Q4 · test 이후 · 옵션 12개 · P=4, Q=2, λ=0.2 있음 data\inputs\levels_sa.csv 있음 data\inputs\levels_62.csv 있음 data\inputs\levels_69.csv 있음 data\inputs\levels_agg.csv 있음 data\inputs\io_linkage_W.csv 있음 data\inputs\io_linkage_62.csv 있음 data\inputs\io_linkage_69.csv 있음 data\inputs\exog_hist_v2_20260828.csv 있음 data\inputs\exog_hist_v2.csv
표 1. 12개 옵션의 예측오차(RMSE, 100·log 수준) — 분류별 valid·test(정비판)¶
62·69분류는 36개 공표 산업을 고정 비중으로 나눈 패널이어서 서로 다른 성장률 계열이 각각 32개·34개뿐이다(01 2절). 따라서 분류 간 RMSE 차이는 산업 동학의 차이보다 산업별 가중의 차이를 많이 반영하며, 비교는 같은 분류 안의 옵션끼리 하는 것이 맞다.
In [2]:
t1 = pd.concat({g: grids[g].set_index("옵션")[["RMSE_valid", "RMSE_test"]] for g in ["36", "62", "69"]}, axis=1).loc[ORDER]
t1.to_csv(os.path.join(C.OUTPUT, "table1_grid_rmse.csv"), encoding="utf-8-sig")
t1.style.format("{:.3f}").highlight_min(axis=0, props="font-weight:bold")
Out[2]:
| 36 | 62 | 69 | ||||
|---|---|---|---|---|---|---|
| RMSE_valid | RMSE_test | RMSE_valid | RMSE_test | RMSE_valid | RMSE_test | |
| 옵션 | ||||||
| 차분_minnesota_varx | 7.296 | 3.626 | 6.188 | 3.332 | 6.232 | 3.525 |
| 차분_minnesota_var | 7.405 | 3.597 | 6.355 | 3.322 | 6.389 | 3.581 |
| 차분_io_varx | 6.744 | 3.106 | 6.181 | 3.306 | 6.230 | 3.449 |
| 차분_io_var | 6.885 | 2.916 | 6.316 | 3.274 | 6.358 | 3.443 |
| 수준_minnesota_varx | 13.939 | 4.755 | 13.426 | 4.539 | 12.944 | 5.128 |
| 수준_minnesota_var | 14.823 | 5.178 | 14.118 | 4.866 | 13.795 | 5.279 |
| 수준_io_varx | 10.325 | 3.804 | 12.476 | 4.485 | 12.475 | 4.442 |
| 수준_io_var | 10.417 | 3.827 | 13.566 | 4.907 | 13.491 | 4.735 |
| VECM_minnesota_varx | 8.111 | 4.173 | 7.600 | 4.155 | 8.067 | 4.963 |
| VECM_minnesota_var | 8.387 | 4.166 | 7.906 | 4.286 | 8.395 | 5.008 |
| VECM_io_varx | 8.660 | 4.153 | 7.490 | 4.091 | 7.929 | 4.969 |
| VECM_io_var | 8.739 | 4.196 | 7.837 | 4.215 | 8.344 | 4.867 |
표 2. 분류별 선택 옵션(valid 최소)과 시험 구간 성능(정비판)¶
선택 옵션과 시험 구간 최소 옵션을 함께 보인다. 총량은 수준 명세가 선택되며 두 사전분포는 같은 모형이다(동률). 5장은 총량의 경로를 BOKDPM 기준선에서 가져오고, 총량 VARX(수준·산업연관표·VARX)는 시나리오 간 차이를 내는 데만 쓴다.
In [3]:
t2 = []
for g, d in grids.items():
best = V.select(d)
r = d.set_index("옵션").loc[best[0]]
tmin = d.loc[d["RMSE_test"].idxmin()]
t2.append({"분류": g, "선택 옵션(valid 최소)": " = ".join(best), **{m: r[m] for m in M},
"test 최소 옵션": tmin["옵션"], "test 최소": tmin["RMSE_test"],
"이전 판 선택": " = ".join(V.select(prev[g]))})
t2 = pd.DataFrame(t2).set_index("분류")
t2.to_csv(os.path.join(C.OUTPUT, "table2_selected.csv"), encoding="utf-8-sig")
t2.style.format({m: "{:.3f}" for m in M} | {"test 최소": "{:.3f}"})
Out[3]:
| 선택 옵션(valid 최소) | RMSE_train | RMSE_valid | RMSE_test | test 최소 옵션 | test 최소 | 이전 판 선택 | |
|---|---|---|---|---|---|---|---|
| 분류 | |||||||
| 36 | 차분_io_varx | 3.691 | 6.744 | 3.106 | 차분_io_var | 2.916 | 차분_io_varx |
| 62 | 차분_io_varx | 2.023 | 6.181 | 3.306 | 차분_io_var | 3.274 | 차분_io_varx |
| 69 | 차분_io_varx | 2.098 | 6.230 | 3.449 | 차분_io_var | 3.443 | 차분_io_varx |
| AGG | 수준_minnesota_var = 수준_io_var | 0.883 | 0.414 | 0.554 | 수준_minnesota_var | 0.554 | 수준_minnesota_var = 수준_io_var |
표 3. 축별 평균 RMSE — 명세·사전분포·외생(정비판)¶
36·62·69분류 각각에서 해당 축의 수준을 공유하는 옵션(4개 또는 6개)의 평균이다. 총량은 사전분포 축이 의미가 없으므로 뺀다.
In [4]:
three = {g: grids[g] for g in ["36", "62", "69"]}
t3 = pd.concat({"valid": V.axis_means(three, "RMSE_valid"), "test": V.axis_means(three, "RMSE_test")}, axis=1)
t3.to_csv(os.path.join(C.OUTPUT, "table3_axis_means.csv"), encoding="utf-8-sig")
t3.style.format("{:.3f}")
Out[4]:
| valid | test | ||||||
|---|---|---|---|---|---|---|---|
| 분류 | 36 | 62 | 69 | 36 | 62 | 69 | |
| 축 | 수준 | ||||||
| 명세 | VECM | 8.474 | 7.708 | 8.184 | 4.172 | 4.187 | 4.952 |
| 수준 | 12.376 | 13.397 | 13.176 | 4.391 | 4.699 | 4.896 | |
| 차분 | 7.083 | 6.260 | 6.302 | 3.311 | 3.308 | 3.500 | |
| 사전 | io | 8.629 | 8.978 | 9.138 | 3.667 | 4.046 | 4.317 |
| minnesota | 9.994 | 9.266 | 9.304 | 4.249 | 4.083 | 4.581 | |
| 외생 | var | 9.443 | 9.350 | 9.462 | 3.980 | 4.145 | 4.485 |
| varx | 9.179 | 8.894 | 8.980 | 3.936 | 3.985 | 4.413 | |
그림 1. 69분류 12개 옵션의 구간별 RMSE(valid 오름차순, 정비판)¶
In [5]:
d = grids["69"].sort_values("RMSE_valid")
x = np.arange(len(d))
fig, ax = plt.subplots(figsize=(10, 4.2))
for col, mk, lab in [("RMSE_train", "o", "train"), ("RMSE_valid", "s", "valid"), ("RMSE_test", "^", "test")]:
ax.plot(x, d[col], mk + "-", label=lab)
ax.set_xticks(x); ax.set_xticklabels(d["옵션"], rotation=45, ha="right", fontsize=8)
ax.set_ylabel("RMSE(100·log 수준)"); ax.grid(alpha=.3); ax.legend(frameon=False)
ax.set_title("69분류 · 12개 옵션의 1기 앞 예측오차", loc="left")
fig.tight_layout(); fig.savefig(os.path.join(C.OUTPUT, "fig1_rmse_69.png"), dpi=150)
그림 2. 분류 × 옵션 valid RMSE(정비판)¶
In [6]:
h = pd.DataFrame({g: grids[g].set_index("옵션")["RMSE_valid"] for g in ["36", "62", "69"]}).loc[ORDER]
fig, ax = plt.subplots(figsize=(6.2, 6.4))
im = ax.imshow(h.values, cmap="Blues", aspect="auto")
ax.set_xticks(range(3)); ax.set_xticklabels([f"{g}분류" for g in h.columns])
ax.set_yticks(range(len(h))); ax.set_yticklabels(h.index, fontsize=8)
for i in range(h.shape[0]):
for j in range(h.shape[1]):
v = h.values[i, j]
ax.text(j, i, f"{v:.2f}", ha="center", va="center", fontsize=8, color="white" if v > np.nanmedian(h.values) * 1.4 else "black")
fig.colorbar(im, ax=ax, shrink=.7, label="valid RMSE")
ax.set_title("valid RMSE(낮을수록 정확)", loc="left")
fig.tight_layout(); fig.savefig(os.path.join(C.OUTPUT, "fig2_valid_heatmap.png"), dpi=150)
표 4. 점검과 민감도 요약¶
02 4–6절의 결과를 모은다. 'A 정비 영향'은 실질금리 정의 변경 전후의 RMSE 차이와 선택이다. 비고의 'io−미네소타'는 차분·VARX에서 두 사전분포의 valid RMSE 차이이며, 음수이면 산업연관표가 더 정확하다.
In [7]:
ex = pd.read_csv(os.path.join(C.OUTPUT, "check_prev_vs_new_summary.csv"), index_col=0, encoding="utf-8-sig")
g36n = pd.read_csv(os.path.join(C.OUTPUT, "check_io36_normalized.csv"), encoding="utf-8-sig")
lam = pd.read_csv(os.path.join(C.OUTPUT, "lambda_sensitivity.csv"), encoding="utf-8-sig")
t4 = [{"점검": "A 정비 영향(실질금리 수준→차분)", "분류": g, "선택": ex.loc[g, "선택(정비판)"],
"비고": f"이전 판 선택 {ex.loc[g, '선택(이전 판)']}, 최대 |Δvalid| {ex.loc[g, '최대 |Δvalid|']:.3f}, |Δtest| {ex.loc[g, '최대 |Δtest|']:.3f}"} for g in ex.index]
t4.append({"점검": "B 36 행렬 정규화", "분류": "36", "선택": " = ".join(V.select(g36n)),
"비고": f"valid {g36n['RMSE_valid'].min():.3f}"})
for _, r in lam[lam["λ"] != C.LAM].iterrows():
t4.append({"점검": f"λ={r['λ']}", "분류": str(r["분류"]), "선택": r["valid 최소 옵션"],
"비고": f"valid {r['RMSE_valid']:.3f}, test {r['RMSE_test']:.3f}, 차분·VARX의 io−미네소타 {r['io − 미네소타']:+.3f}"})
t4 = pd.DataFrame(t4)
t4.to_csv(os.path.join(C.OUTPUT, "table4_checks.csv"), index=False, encoding="utf-8-sig")
t4
Out[7]:
| 점검 | 분류 | 선택 | 비고 | |
|---|---|---|---|---|
| 0 | A 정비 영향(실질금리 수준→차분) | 36 | 차분_io_varx | 이전 판 선택 차분_io_varx, 최대 |Δvalid| 0.049, |Δtest|... |
| 1 | A 정비 영향(실질금리 수준→차분) | 62 | 차분_io_varx | 이전 판 선택 차분_io_varx, 최대 |Δvalid| 0.013, |Δtest|... |
| 2 | A 정비 영향(실질금리 수준→차분) | 69 | 차분_io_varx | 이전 판 선택 차분_io_varx, 최대 |Δvalid| 0.022, |Δtest|... |
| 3 | A 정비 영향(실질금리 수준→차분) | AGG | 수준_minnesota_var = 수준_io_var | 이전 판 선택 수준_minnesota_var = 수준_io_var, 최대 |Δval... |
| 4 | B 36 행렬 정규화 | 36 | 차분_io_varx | valid 6.983 |
| 5 | λ=0.05 | 36 | 차분_minnesota_varx | valid 6.192, test 3.081, 차분·VARX의 io−미네소타 +0.079 |
| 6 | λ=0.05 | 62 | 차분_minnesota_varx | valid 5.166, test 2.654, 차분·VARX의 io−미네소타 +0.051 |
| 7 | λ=0.05 | 69 | 차분_minnesota_varx | valid 5.314, test 2.849, 차분·VARX의 io−미네소타 +0.086 |
| 8 | λ=0.05 | AGG | 수준_minnesota_varx = 수준_io_varx | valid 0.322, test 0.571, 차분·VARX의 io−미네소타 +0.000 |
| 9 | λ=0.1 | 36 | 차분_io_varx | valid 6.384, test 3.119, 차분·VARX의 io−미네소타 -0.009 |
| 10 | λ=0.1 | 62 | 차분_minnesota_varx | valid 5.406, test 2.804, 차분·VARX의 io−미네소타 +0.021 |
| 11 | λ=0.1 | 69 | 차분_minnesota_varx | valid 5.517, test 3.011, 차분·VARX의 io−미네소타 +0.059 |
| 12 | λ=0.1 | AGG | 수준_minnesota_varx = 수준_io_varx | valid 0.363, test 0.564, 차분·VARX의 io−미네소타 +0.000 |
| 13 | λ=0.4 | 36 | 차분_io_varx | valid 7.477, test 3.200, 차분·VARX의 io−미네소타 -1.298 |
| 14 | λ=0.4 | 62 | 차분_minnesota_varx | valid 7.374, test 4.241, 차분·VARX의 io−미네소타 +0.170 |
| 15 | λ=0.4 | 69 | 차분_minnesota_varx | valid 7.331, test 4.283, 차분·VARX의 io−미네소타 +0.109 |
| 16 | λ=0.4 | AGG | 수준_minnesota_var = 수준_io_var | valid 0.394, test 0.558, 차분·VARX의 io−미네소타 +0.000 |
표 5. 이전 판 대비 변화 — 선택 옵션과 VARX 옵션의 RMSE¶
실질금리 정의가 바뀌면 외생을 쓰는 VARX·VECM-X 옵션의 RMSE만 달라진다. 아래 표는 분류별 선택 옵션과 주요 VARX 옵션의 valid·test RMSE를 두 판에서 비교한다.
In [8]:
rows = []
for g in C.GRIDS:
n, p_ = grids[g].set_index("옵션"), prev[g].set_index("옵션")
for o in sorted(set(V.select(grids[g])[:1] + ["차분_io_varx", "수준_io_varx"])):
rows.append({"분류": g, "옵션": o, "valid(정비판)": n.loc[o, "RMSE_valid"], "valid(이전 판)": p_.loc[o, "RMSE_valid"],
"test(정비판)": n.loc[o, "RMSE_test"], "test(이전 판)": p_.loc[o, "RMSE_test"]})
t5 = pd.DataFrame(rows).set_index(["분류", "옵션"])
t5.to_csv(os.path.join(C.OUTPUT, "table5_vs_prev.csv"), encoding="utf-8-sig")
t5.style.format("{:.3f}")
Out[8]:
| valid(정비판) | valid(이전 판) | test(정비판) | test(이전 판) | ||
|---|---|---|---|---|---|
| 분류 | 옵션 | ||||
| 36 | 수준_io_varx | 10.325 | 10.327 | 3.804 | 3.859 |
| 차분_io_varx | 6.744 | 6.793 | 3.106 | 3.093 | |
| 62 | 수준_io_varx | 12.476 | 12.479 | 4.485 | 4.569 |
| 차분_io_varx | 6.181 | 6.179 | 3.306 | 3.293 | |
| 69 | 수준_io_varx | 12.475 | 12.476 | 4.442 | 4.488 |
| 차분_io_varx | 6.230 | 6.235 | 3.449 | 3.430 | |
| AGG | 수준_io_varx | 0.419 | 0.417 | 0.554 | 0.543 |
| 수준_minnesota_var | 0.414 | 0.414 | 0.554 | 0.554 | |
| 차분_io_varx | 0.489 | 0.425 | 0.632 | 0.586 |
표 6. 실질금리 계수의 부호(02 4-1절)¶
In [9]:
t6 = pd.read_csv(os.path.join(C.OUTPUT, "real_rate_sign.csv"), index_col=[0, 1], encoding="utf-8-sig")
t6.to_csv(os.path.join(C.OUTPUT, "table6_real_rate_sign.csv"), encoding="utf-8-sig")
t6.style.format({c: "{:.1f}" for c in t6.columns if "비율" in c} | {c: "{:+.4f}" for c in t6.columns if "가중" in c})
Out[9]:
| 산업 수 | 동시항 음수 비율(%) | 합계 음수 비율(%) | 가중평균 동시항 | 가중평균 합계 | ||
|---|---|---|---|---|---|---|
| 분류 | 판 | |||||
| 36 | 정비판(1차 차분) | 36 | 52.8 | 50.0 | +0.0426 | +0.1192 |
| 이전 판(수준) | 36 | 27.8 | 19.4 | +0.1478 | +0.2126 | |
| 62 | 정비판(1차 차분) | 62 | 43.5 | 45.2 | +0.0136 | +0.0148 |
| 이전 판(수준) | 62 | 25.8 | 19.4 | +0.1008 | +0.1948 | |
| 69 | 정비판(1차 차분) | 69 | 47.8 | 44.9 | +0.0127 | +0.0096 |
| 이전 판(수준) | 69 | 27.5 | 24.6 | +0.0995 | +0.1979 | |
| AGG(수준·io·varx) | 정비판(1차 차분) | 1 | 100.0 | 0.0 | -0.1257 | +0.0651 |
| 이전 판(수준) | 1 | 0.0 | 0.0 | +0.0245 | +0.1401 |
검증 — 정비판 기록 그리드, 이전 판 공표 그리드¶
- 그리드 RMSE는 소수 셋째 자리로 반올림해 기록하므로 허용오차는 0.0005이다(판마다 4개 분류 × 12개 옵션 × 3개 구간 = 144항목).
- 선택 옵션은 valid RMSE(소수 셋째 자리)가 가장 작은 옵션이다. 총량처럼 같은 모형이 동률이면 기록 선택이 동률 집합에 들어 있는지 확인한다.
- 핵심 수치(선택 옵션의 valid·test)는 소수 둘째 자리이므로 허용오차는 0.005이다. 이전 판은 논문 1–3 본문 인용값과 대조한다.
In [10]:
rows = []
def grid_rows(label, calc_grids, pub_paths, sel):
for g in C.GRIDS:
pub = pd.read_csv(pub_paths[g], encoding="utf-8-sig").set_index("옵션")
cal = calc_grids[g].set_index("옵션")
assert set(pub.index) == set(cal.index) == set(ORDER)
for o in ORDER:
for m in M:
rows.append({"판": label, "항목": f"{g} {o} {m}", "계산": cal.loc[o, m], "기록": pub.loc[o, m], "허용오차": 0.0005})
assert pub.index[0] == sel[f"선택_{g}"], "기록 그리드 첫 행과 config의 선택 옵션이 다르다"
rows.append({"판": label, "항목": f"{g} 선택 옵션(valid 최소)", "계산": " = ".join(V.select(calc_grids[g])),
"기록": sel[f"선택_{g}"], "허용오차": "동률 포함"})
grid_rows("정비판", grids, C.PUBLISHED_GRID, C.PUBLISHED)
grid_rows("이전 판", prev, C.PUBLISHED_GRID_PREV, C.PREVIOUS)
for g in ["36", "62", "69"]:
for label, src, P, pre in [("정비판", grids, C.PUBLISHED, "정비"), ("이전 판", prev, C.PREVIOUS, "논문")]:
cal = src[g].set_index("옵션").loc["차분_io_varx"]
for m, key in [("RMSE_valid", "valid"), ("RMSE_test", "test")]:
rows.append({"판": label, "항목": f"{pre}({g}분류) 차분·io·varx {key}", "계산": cal[m], "기록": P[f"{pre}_{g}_{key}"], "허용오차": 0.005})
def passed(r):
if r["허용오차"] == "동률 포함":
return r["기록"] in r["계산"].split(" = ")
return abs(r["계산"] - r["기록"]) <= r["허용오차"] + 1e-9
res = pd.DataFrame(rows)
res["통과"] = res.apply(passed, axis=1)
res.to_csv(os.path.join(C.OUTPUT, "verification.csv"), index=False, encoding="utf-8-sig")
num = res[res["항목"].str.contains("RMSE_")].copy()
num["분류"] = num["항목"].str.split(" ").str[0]
summary = num.assign(차이=lambda x: (x["계산"].astype(float) - x["기록"].astype(float)).abs()) \
.groupby(["판", "분류"]).agg(항목수=("통과", "size"), 최대차이=("차이", "max"), 통과=("통과", "all"))
display(summary)
display(res[~res["항목"].str.contains("RMSE_")])
assert res["통과"].all(), "기록 수치와 계산 결과가 다르다 — output/verification.csv에서 통과=False 항목을 확인한다"
print(f"검증 통과: 정비판·이전 판 그리드 각 144항목, 선택 옵션 각 {len(C.GRIDS)}항목, 핵심 수치 각 6항목, 모두 {len(res)}항목.")
| 항목수 | 최대차이 | 통과 | ||
|---|---|---|---|---|
| 판 | 분류 | |||
| 이전 판 | 36 | 36 | 0.000 | True |
| 62 | 36 | 0.000 | True | |
| 69 | 36 | 0.000 | True | |
| AGG | 36 | 0.000 | True | |
| 정비판 | 36 | 36 | 0.000 | True |
| 62 | 36 | 0.000 | True | |
| 69 | 36 | 0.000 | True | |
| AGG | 36 | 0.000 | True |
| 판 | 항목 | 계산 | 기록 | 허용오차 | 통과 | |
|---|---|---|---|---|---|---|
| 36 | 정비판 | 36 선택 옵션(valid 최소) | 차분_io_varx | 차분_io_varx | 동률 포함 | True |
| 73 | 정비판 | 62 선택 옵션(valid 최소) | 차분_io_varx | 차분_io_varx | 동률 포함 | True |
| 110 | 정비판 | 69 선택 옵션(valid 최소) | 차분_io_varx | 차분_io_varx | 동률 포함 | True |
| 147 | 정비판 | AGG 선택 옵션(valid 최소) | 수준_minnesota_var = 수준_io_var | 수준_io_var | 동률 포함 | True |
| 184 | 이전 판 | 36 선택 옵션(valid 최소) | 차분_io_varx | 차분_io_varx | 동률 포함 | True |
| 221 | 이전 판 | 62 선택 옵션(valid 최소) | 차분_io_varx | 차분_io_varx | 동률 포함 | True |
| 258 | 이전 판 | 69 선택 옵션(valid 최소) | 차분_io_varx | 차분_io_varx | 동률 포함 | True |
| 295 | 이전 판 | AGG 선택 옵션(valid 최소) | 수준_minnesota_var = 수준_io_var | 수준_io_var | 동률 포함 | True |
| 296 | 정비판 | 정비(36분류) 차분·io·varx valid | 6.744 | 6.740 | 0.005 | True |
| 297 | 정비판 | 정비(36분류) 차분·io·varx test | 3.106 | 3.110 | 0.005 | True |
| 298 | 이전 판 | 논문(36분류) 차분·io·varx valid | 6.793 | 6.790 | 0.005 | True |
| 299 | 이전 판 | 논문(36분류) 차분·io·varx test | 3.093 | 3.090 | 0.005 | True |
| 300 | 정비판 | 정비(62분류) 차분·io·varx valid | 6.181 | 6.180 | 0.005 | True |
| 301 | 정비판 | 정비(62분류) 차분·io·varx test | 3.306 | 3.310 | 0.005 | True |
| 302 | 이전 판 | 논문(62분류) 차분·io·varx valid | 6.179 | 6.180 | 0.005 | True |
| 303 | 이전 판 | 논문(62분류) 차분·io·varx test | 3.293 | 3.290 | 0.005 | True |
| 304 | 정비판 | 정비(69분류) 차분·io·varx valid | 6.230 | 6.230 | 0.005 | True |
| 305 | 정비판 | 정비(69분류) 차분·io·varx test | 3.449 | 3.450 | 0.005 | True |
| 306 | 이전 판 | 논문(69분류) 차분·io·varx valid | 6.235 | 6.240 | 0.005 | True |
| 307 | 이전 판 | 논문(69분류) 차분·io·varx test | 3.430 | 3.430 | 0.005 | True |
검증 통과: 정비판·이전 판 그리드 각 144항목, 선택 옵션 각 4항목, 핵심 수치 각 6항목, 모두 308항목.