01 데이터 입수 — 취업자·실질부가가치·시나리오 전망¶
목적. 취업계수 방식 노동수요 전망에 필요한 입력을 불러오고 검증한다.
2026-10 정비판. 보고서1은 거시 기준선을 다시 작성하였다. BOKDPM의 장기 정상상태를 현재 여건에 맞추어 고치고(잠재성장률 1.8%, 물가목표 2%, 실질유가 일정),
실질금리 외생 투입을 1차 차분으로 바꾸었으며, 외생 경로에 전달되지 않던 시나리오 레버를 연결한 뒤 산업 전망과 정합화를 다시 계산하였다.
이 패키지는 그 결과인 5장 정비판 최종 전망으로 주 투입을 바꾸었다. 취업계수의 기준 수준·추세·계수 경로를 정하는 방법과 취업자·부가가치 이력 자료는 바꾸지 않았다.
이전 판(2026-10-08 판)의 투입·산출 파일은 data/published_prev/에 보존하여 재현 점검과 비교에 쓴다.
| 입력 | 내용 | 출처 | 쓰임 |
|---|---|---|---|
data/inputs/emp_section_q.csv |
산업 대분류(A–U)별 취업자, 분기, 계절조정·원계열, 2013Q1–2025Q4 | 국가데이터처(옛 통계청) 경제활동인구조사(ECOS 901Y027) | 취업계수 |
data/inputs/va36_hist_q.csv |
36개 산업 실질부가가치, 계절조정, 분기, 2013Q1–2025Q4 | 한국은행 국민계정(ECOS 200Y104) | 취업계수 |
data/inputs/va36_reconciled_annual.csv |
36개 산업 시나리오(S1–S5) 실질부가가치 전망, 연간, 2026–2035 | 보고서1 5장 정비판 최종 전망(5장 재현 패키지 forecast_annual_36.csv) |
주 산출물 투입 |
data/published_prev/va36_reconciled_annual_prev.csv |
같은 형식, 정비 이전 5장 최종 전망(2026-09-03 판) | 이전 판(2026-10-08 판) 패키지의 주 투입 | 이전 판 재현·비교 |
data/published_prev/labor_demand_viewer_data_prev.json |
이전 판 노동수요 산출 파일(포털 뷰어 형식) | 이전 판(2026-10-08 판) 패키지의 주 산출물 | 이전 판 재현 대조 |
data/inputs/scenario_viewer_39q_data.json |
36개 산업 시나리오 실질부가가치 전망, 분기, 2026Q1–2035Q4(정합화 이전) | 포털 「산업별 시나리오 전망」 게시 파일(2026-08-04) | 최초 판(v0) 재현 투입 |
data/section_map_36_19.csv |
36개 산업 → 대분류 19개 대응표 | 본 연구 설계 | 취업계수 |
모든 입력은 공개 통계이거나 본 연구 모형의 공개 산출물이다.
산출물. data/interim/emp_section_annual_sa.csv, emp_section_annual_nsa.csv, va36_hist_annual.csv, va36_recon_annual.csv, va36_recon_prev_annual.csv, va36_scen_annual.csv
기본 실행은 고정 입력만 쓴다. ONLINE=1로 실행하면 6절에서 ECOS와 포털의 공개 원천을 다시 받아 고정 입력과 대조한다(ECOS 인증키는 환경변수 API_KEY_BOK).
import os, json
import numpy as np, pandas as pd
import config as C
import labor_demand as F
pd.set_option("display.float_format", lambda v: f"{v:,.3f}")
C.setup()
Python 3.12.10 · pandas 2.2.3 · numpy 2.4.6 · matplotlib 3.11.0 스위치: ONLINE=0 · FULL=0 · API_KEY_BOK 없음 취업자(분기) 있음 data\inputs\emp_section_q.csv 실질VA 이력(분기) 있음 data\inputs\va36_hist_q.csv 주 투입: 정비판 VA 있음 data\inputs\va36_reconciled_annual.csv 이전 판 투입: VA 있음 data\published_prev\va36_reconciled_annual_prev.csv 이전 판 산출 JSON 있음 data\published_prev\labor_demand_viewer_data_prev.json 최초 판 투입: 분기 VA 있음 data\inputs\scenario_viewer_39q_data.json 최초 판 게시 JSON 있음 data\published\labor_demand_viewer_data_v0_pre-reconciliation.json 산업 대응표 있음 data\section_map_36_19.csv
1. 고정 입력 확인¶
각 파일의 SHA-256을 config.INPUT_SHA256에 기록된 값과 대조한다. 하나라도 다르면 실행을 멈춘다.
rows = []
for f, want in C.INPUT_SHA256.items():
p = os.path.join(C.DATA, f)
h = F.sha256(p)
rows.append({"파일(data/ 기준)": f, "바이트": os.path.getsize(p), "SHA-256(앞 16자)": h[:16], "기록값 일치": h == want})
chk = pd.DataFrame(rows)
display(chk)
assert chk["기록값 일치"].all(), "고정 입력이 기록된 SHA-256과 다르다"
| 파일(data/ 기준) | 바이트 | SHA-256(앞 16자) | 기록값 일치 | |
|---|---|---|---|---|
| 0 | inputs/emp_section_q.csv | 64507 | d631cdcc9aec591e | True |
| 1 | inputs/va36_hist_q.csv | 94279 | 465641eba29c51a8 | True |
| 2 | inputs/va36_reconciled_annual.csv | 85765 | 4085f853bbc9ee18 | True |
| 3 | inputs/scenario_viewer_39q_data.json | 66341 | 3873c3c575a6c1b1 | True |
| 4 | section_map_36_19.csv | 2231 | b02dbd44aaa03371 | True |
| 5 | published_prev/va36_reconciled_annual_prev.csv | 85770 | 779114acd0168928 | True |
| 6 | published_prev/labor_demand_viewer_data_prev.json | 17222 | f3346aee10c08f89 | True |
2. 취업자 — 경제활동인구조사 산업 대분류¶
ECOS 901Y027은 한국표준산업분류 대분류 21개(A–U)의 취업자를 2013년부터 제공한다. 계산에는 분기 계절조정 계열(구분코드 I28B)을 쓰며, 연간 값은 4개 분기의 평균이다. 원계열(I28A)은 02의 명세 비교에만 쓴다. T(가구 내 고용활동)와 U(국제 및 외국기관)는 36개 산업 부가가치 분류에 대응시키지 않으므로 계산에서 제외한다. 두 대분류의 비중은 2013년 0.7%, 2016년 이후 0.3% 수준이다. 계절조정은 대분류별로 따로 하므로 대분류 합계와 전체 취업자 계절조정 계열 사이에는 수천 명 이내의 차이가 있다.
emp_q = {adj: F.read_employment(C.EMP_Q, adj) for adj in ["계절조정", "원계열"]}
for adj, q in emp_q.items():
print(f"{adj}: {q.index.min()}–{q.index.max()} · {q.shape[0]}개 분기 · 대분류 {q.shape[1] - 1}개 + 전체")
emp_a = {adj: F.annual_employment(q) for adj, q in emp_q.items()}
a = emp_a["계절조정"]
tu = a[["T", "U"]].sum(axis=1)
pd.DataFrame({"전체(천명)": a["전체"], "A–S 합계(천명)": a[F.SECTIONS].sum(axis=1),
"T+U(천명)": tu, "T+U 비중(%)": 100 * tu / a["전체"],
"A–U 합계 − 전체(천명)": a[F.SECTIONS + ["T", "U"]].sum(axis=1) - a["전체"]}).loc[[2013, 2016, 2019, 2022, 2023, 2024, 2025]]
계절조정: 2013Q1–2025Q4 · 52개 분기 · 대분류 21개 + 전체 원계열: 2013Q1–2025Q4 · 52개 분기 · 대분류 21개 + 전체
| 전체(천명) | A–S 합계(천명) | T+U(천명) | T+U 비중(%) | A–U 합계 − 전체(천명) | |
|---|---|---|---|---|---|
| 연도 | |||||
| 2013 | 25,298.250 | 25,113.500 | 180.250 | 0.712 | -4.500 |
| 2016 | 26,408.750 | 26,321.500 | 86.250 | 0.327 | -1.000 |
| 2019 | 27,123.750 | 27,039.750 | 86.500 | 0.319 | 2.500 |
| 2022 | 28,091.250 | 27,993.000 | 96.250 | 0.343 | -2.000 |
| 2023 | 28,418.750 | 28,321.250 | 94.500 | 0.333 | -3.000 |
| 2024 | 28,577.250 | 28,482.250 | 94.500 | 0.331 | -0.500 |
| 2025 | 28,769.000 | 28,681.500 | 91.000 | 0.316 | 3.500 |
3. 실질부가가치 이력 — 36개 산업¶
한국은행 경제활동별 GDP(계절조정, 실질, 분기) 36개 말단 산업이다. 단위는 십억 원(2020년 연쇄가격)이고, 연간 값은 4개 분기의 합계이다. 대응표는 각 산업을 하나의 대분류에 배정한다. 제조업(C)은 13개, 건설업(F)은 4개, 전기·가스(D)와 정보통신(J)은 각 2개 산업을 묶는다.
va_q = F.read_va_hist(C.VA_HIST_Q)
smap = pd.read_csv(C.SECTION_MAP, dtype={"산업코드": str})
sec_of = dict(zip(smap["산업명"], smap["대분류"]))
assert set(va_q.columns) == set(sec_of), "대응표와 부가가치 자료의 산업 이름이 다르다"
assert sorted(set(sec_of.values())) == F.SECTIONS, "대응표의 대분류가 A–S 19개가 아니다"
va_a = F.annual_va(va_q)
print(f"실질부가가치: {va_q.index.min()}–{va_q.index.max()} · {va_q.shape[0]}개 분기 · 산업 {va_q.shape[1]}개")
print(f"36개 산업 합계: 2013년 {va_a.loc[2013].sum() / 1e3:,.1f}조 원 → 2025년 {va_a.loc[2025].sum() / 1e3:,.1f}조 원")
smap.groupby(["대분류", "대분류명"])["산업명"].apply(lambda s: " · ".join(s)).to_frame("36개 산업")
실질부가가치: 2013Q1–2025Q4 · 52개 분기 · 산업 36개 36개 산업 합계: 2013년 1,599.2조 원 → 2025년 2,178.5조 원
| 36개 산업 | ||
|---|---|---|
| 대분류 | 대분류명 | |
| A | 농업, 임업 및 어업 | 농림어업 |
| B | 광업 | 광업 |
| C | 제조업 | 음식료품 제조업 · 섬유 및 가죽제품 제조업 · 목재, 종이, 인쇄 및 복제업 · ... |
| D | 전기, 가스, 증기 및 공기조절 공급업 | 전기업 · 가스, 증기 및 공기조절 공급업 |
| E | 수도, 하수 및 폐기물 처리, 원료 재생업 | 수도, 하수 및 폐기물 처리, 원료 재생업 |
| F | 건설업 | 주거용 건물 건설업 · 비주거용 건물 건설업 · 토목건설업 · 건축보수업 |
| G | 도매 및 소매업 | 도소매업 |
| H | 운수 및 창고업 | 운수업 |
| I | 숙박 및 음식점업 | 숙박 및 음식점업 |
| J | 정보통신업 | 통신업 · 출판, 방송, 영상, 정보서비스 |
| K | 금융 및 보험업 | 금융 및 보험업 |
| L | 부동산업 | 부동산업 |
| M | 전문, 과학 및 기술 서비스업 | 전문, 과학 및 기술관련 서비스업 |
| N | 사업시설 관리, 사업 지원 및 임대 서비스업 | 사업지원 서비스업 |
| O | 공공행정, 국방 및 사회보장 행정 | 공공행정, 국방 및 사회보장 |
| P | 교육 서비스업 | 교육서비스업 |
| Q | 보건업 및 사회복지 서비스업 | 의료, 보건업 및 사회복지서비스업 |
| R | 예술, 스포츠 및 여가관련 서비스업 | 예술, 스포츠 및 여가관련 서비스업 |
| S | 협회 및 단체, 수리 및 기타 개인 서비스업 | 기타 서비스업 |
4. 시나리오 실질부가가치 전망 — 주 투입, 이전 판 투입, 최초 판 투입¶
**주 투입(정비판)**은 보고서1 5장의 2026-10 정비판 최종 전망이다. 산업 VARX 합산 경로를 총량 전망에 맞추어 비례배분(산업 구성비 유지)하고 기존 전망과 결합한 연간 값이다.
이전 판 투입은 정비 이전의 5장 최종 전망(2026-09-03 판)이며, 이전 판(2026-10-08 판) 노동수요가 투입한 값이다. 파일 형식과 산업·연도·시나리오 구성은 주 투입과 같다.
최초 판 투입은 2026-08-04 포털 「산업별 시나리오 전망」에 게시된 정합화 이전 분기 전망(연간 값은 4개 분기 합계)이다. 같은 날 게시된 노동수요 자료(최초 판, v0)가 이 값을 투입했으며, 이 패키지는 그 파일을 바이트 단위까지 재현하는 데에만 이 자료를 쓴다.
표의 연평균 증가율은 2025년 실적(36개 산업 합계)에서 2035년 전망까지의 값이다.
recon = F.read_reconciled(C.RECON_CSV)
recon_prev = F.read_reconciled(C.PREV_RECON_CSV)
scen_a = F.scenario_annual(F.read_scenario_json(C.SCEN_JSON))
for s in C.SCENARIOS:
for d in (recon, recon_prev, scen_a):
assert set(d[s].columns) == set(sec_of) and list(d[s].index) == C.FORECAST_YEARS
b25 = va_a.loc[2025].sum()
print(f"2025년 실적(36개 산업 합계): {b25 / 1e3:,.1f}조 원")
pd.DataFrame({s: {"정비판 2026(조원)": recon[s].loc[2026].sum() / 1e3,
"정비판 2035(조원)": recon[s].loc[2035].sum() / 1e3,
"정비판 연평균(%)": F.cagr(b25, recon[s].loc[2035].sum(), 10),
"이전 판 2035(조원)": recon_prev[s].loc[2035].sum() / 1e3,
"이전 판 연평균(%)": F.cagr(b25, recon_prev[s].loc[2035].sum(), 10),
"정비판 − 이전 판 2035(조원)": (recon[s].loc[2035].sum() - recon_prev[s].loc[2035].sum()) / 1e3,
"최초 판 2035(조원)": scen_a[s].loc[2035].sum() / 1e3,
"최초 판 연평균(%)": F.cagr(b25, scen_a[s].loc[2035].sum(), 10)} for s in C.SCENARIOS}).T.round(2)
2025년 실적(36개 산업 합계): 2,178.5조 원
| 정비판 2026(조원) | 정비판 2035(조원) | 정비판 연평균(%) | 이전 판 2035(조원) | 이전 판 연평균(%) | 정비판 − 이전 판 2035(조원) | 최초 판 2035(조원) | 최초 판 연평균(%) | |
|---|---|---|---|---|---|---|---|---|
| S1 | 2,221.070 | 2,609.030 | 1.820 | 2,688.520 | 2.130 | -79.490 | 3,285.330 | 4.190 |
| S2 | 2,198.320 | 2,605.860 | 1.810 | 2,650.290 | 1.980 | -44.430 | 3,218.600 | 3.980 |
| S3 | 2,233.440 | 2,612.180 | 1.830 | 2,708.630 | 2.200 | -96.450 | 3,312.210 | 4.280 |
| S4 | 2,233.580 | 2,607.700 | 1.810 | 2,740.690 | 2.320 | -132.990 | 3,307.090 | 4.260 |
| S5 | 2,203.210 | 2,610.230 | 1.820 | 2,664.240 | 2.030 | -54.010 | 3,259.990 | 4.110 |
5. 주 투입의 원본 대조 — 5장 재현 패키지¶
주 투입은 5장 재현 패키지의 동결 산출물 s5-results/data/published/forecast_annual_36.csv(실적 2000–2025년, S1–S5 2026–2035년)에서
S1–S5·2026–2035년 행만 문자 그대로 옮긴 파일이다. 이 패키지가 5장 패키지와 같은 폴더 구조(repro/report-gva/)에 있으면 다음을 확인한다.
- 원본의 SHA-256이
config.SOURCE_SHA256과 같은지 - 원본에서 추린 행이 고정 입력과 바이트 단위로 같은지
- 원본의 2025년 실적 행이
va36_hist_q.csv의 2025년 4개 분기 합계와 같은지
배포 묶음처럼 원본이 없으면 이 절을 건너뛴다. 결과는 보고만 하고 실행을 멈추지 않는다. 고정 입력 자체의 동일성은 1절의 SHA-256 대조가 보장한다.
if not os.path.exists(C.S5_SOURCE):
print(f"5장 원본이 없어 건너뛴다({os.path.relpath(C.S5_SOURCE, C.HERE)}). 기록된 원본 SHA-256: {C.SOURCE_SHA256[:16]}…")
else:
raw = open(C.S5_SOURCE, "rb").read()
hs = F.sha256_bytes(raw)
same = F.reconciled_subset_bytes(raw) == open(C.RECON_CSV, "rb").read()
src = pd.read_csv(C.S5_SOURCE)
a25 = src[(src["시나리오"] == "실적") & (src["연도"] == 2025)].set_index("산업")["실질VA_십억원"]
d25 = (a25 - va_a.loc[2025, a25.index]).abs().max()
tag = ("기록값(정비판)과 일치" if hs == C.SOURCE_SHA256 else
"이전 판 원본(2026-09-03 판)과 일치" if hs == C.PREV_SOURCE_SHA256 else "기록값과 다름 — 5장 산출물이 바뀌었으므로 주 투입을 다시 만들어야 한다")
display(pd.DataFrame([
{"항목": "원본 SHA-256", "결과": f"{hs[:16]}… · {tag}"},
{"항목": "원본의 S1–S5·2026–2035 행 = 고정 입력", "결과": "바이트 단위 일치" if same else "다름"},
{"항목": "원본 2025년 실적 = va36_hist_q 2025년 합계", "결과": f"{len(a25)}개 산업 · 최대 차이 {d25:.2e}십억 원"}]))
| 항목 | 결과 | |
|---|---|---|
| 0 | 원본 SHA-256 | ae396b0eead6d696… · 기록값(정비판)과 일치 |
| 1 | 원본의 S1–S5·2026–2035 행 = 고정 입력 | 바이트 단위 일치 |
| 2 | 원본 2025년 실적 = va36_hist_q 2025년 합계 | 36개 산업 · 최대 차이 7.28e-12십억 원 |
6. 공개 원천과 대조(ONLINE=1)¶
- 통합 번들(
gva-scenario-bundle.zip)을 내려받아 주 투입의 원본(industry/output/forecast_annual_36.csv)이 고정 입력과 같은지 확인한다. 번들이 아직 이전 판 원본(2026-09-03 판)을 담고 있으면 그 사실을 보고한다. - 포털의 최초 판 투입 자료(
scenario_viewer_39q_data.json)의 SHA-256을 확인한다. - 포털의 노동수요 JSON(현행 주소와 최초 판 주소)을
data/interim/portal/에 내려받는다. 산출물과의 비교는03_결과.ipynb에서 한다. - ECOS에서 취업자(901Y027)와 실질부가가치(200Y104)를 다시 받아 비교한다. 계절조정 계열은 새 자료가 들어올 때마다 과거 값이 다시 추정되므로 수집 시점에 따라 작은 차이가 생길 수 있다.
차이는 보고만 하고 실행을 멈추지 않는다. 취업자·부가가치 고정 입력은 2026-10-08에 수집했다.
if not C.ONLINE:
print("ONLINE=0: 고정 입력을 그대로 쓴다. 공개 원천과 다시 대조하려면 ONLINE=1로 실행한다.")
else:
os.makedirs(C.PORTAL_DL, exist_ok=True)
res = []
zp = os.path.join(C.PORTAL_DL, "gva-scenario-bundle.zip")
h, st = F.try_download(C.URL_BUNDLE, zp)
if h is None:
res.append({"항목": "통합 번들", "결과": st})
else:
raw = F.bundle_member(zp, C.BUNDLE_MEMBER)
hm = F.sha256_bytes(raw)
sub = F.reconciled_subset(raw)
fix = pd.read_csv(C.RECON_CSV)
m = fix.merge(sub, on=["시나리오", "연도", "산업"], how="outer", suffixes=("_고정", "_번들"))
dmax = (m["실질VA_십억원_고정"] - m["실질VA_십억원_번들"]).abs().max()
tag = ("정비판 원본과 일치" if hm == C.SOURCE_SHA256 else
"이전 판 원본(2026-09-03 판)과 일치 — 포털 번들이 아직 갱신되지 않았다" if hm == C.PREV_SOURCE_SHA256 else
"기록값과 다름(" + hm[:12] + "…)")
res.append({"항목": f"번들 {C.BUNDLE_MEMBER}",
"결과": f"SHA-256 {tag} · S1–S5·2026–2035 {len(sub)}행, 고정 입력과 최대 차이 {dmax:.3f}십억 원"})
h, st = F.try_download(C.URL_SCEN, os.path.join(C.PORTAL_DL, "scenario_viewer_39q_data.json"))
res.append({"항목": "포털 scenario_viewer_39q_data.json",
"결과": st if h is None else ("SHA-256 일치" if h == C.INPUT_SHA256["inputs/scenario_viewer_39q_data.json"] else f"SHA-256 다름({h[:12]}…)")})
for name, url in [("labor_demand_viewer_data.json", C.URL_MAIN),
("labor_demand_viewer_data_v0_pre-reconciliation.json", C.URL_V0)]:
h, st = F.try_download(url, os.path.join(C.PORTAL_DL, name))
res.append({"항목": f"포털 {name}", "결과": st if h is None else f"내려받음(SHA-256 {h[:12]}…), 03에서 비교"})
key = os.environ.get(C.KEY_ENV)
if not key:
res.append({"항목": "ECOS", "결과": f"환경변수 {C.KEY_ENV}가 없어 건너뛴다"})
else:
e_new = F.fetch_employment(key, "2013Q1", "2025Q4")
e_old = pd.read_csv(C.EMP_Q, dtype={"분기": str})
m = e_old.merge(e_new, on=["분기", "대분류", "구분"], how="outer", suffixes=("_고정", "_재수집"))
for adj in ["계절조정", "원계열"]:
d = m[m["구분"] == adj]
diff = (d["취업자_천명_고정"] - d["취업자_천명_재수집"]).abs()
res.append({"항목": f"ECOS 901Y027 취업자({adj})",
"결과": f"{len(d)}개 값 · 최대 차이 {diff.max():.1f}천 명 · 결측 {int(diff.isna().sum())}개"})
v_new = F.fetch_va36(key, smap["산업코드"].tolist())
v_old = pd.read_csv(C.VA_HIST_Q, dtype={"분기": str, "산업코드": str})
mv = v_old.merge(v_new, on=["분기", "산업코드"], how="outer", suffixes=("_고정", "_재수집"))
dv = (mv["실질VA_십억원_고정"] - mv["실질VA_십억원_재수집"]).abs()
res.append({"항목": "ECOS 200Y104 실질부가가치",
"결과": f"{len(mv)}개 값 · 최대 차이 {dv.max():.1f}십억 원 · 결측 {int(dv.isna().sum())}개"})
display(pd.DataFrame(res))
ONLINE=0: 고정 입력을 그대로 쓴다. 공개 원천과 다시 대조하려면 ONLINE=1로 실행한다.
7. 산출물¶
다음 노트북(02_분석.ipynb)은 아래 중간 산출물과 대응표를 읽는다.
emp_a["계절조정"].to_csv(os.path.join(C.INTERIM, "emp_section_annual_sa.csv"), encoding="utf-8-sig")
emp_a["원계열"].to_csv(os.path.join(C.INTERIM, "emp_section_annual_nsa.csv"), encoding="utf-8-sig")
va_a.to_csv(os.path.join(C.INTERIM, "va36_hist_annual.csv"), encoding="utf-8-sig")
def long(d):
return pd.concat([v.stack().rename("실질VA_십억원").reset_index().set_axis(["연도", "산업", "실질VA_십억원"], axis=1).assign(시나리오=s)
for s, v in d.items()])[["시나리오", "연도", "산업", "실질VA_십억원"]]
long(recon).to_csv(os.path.join(C.INTERIM, "va36_recon_annual.csv"), index=False, encoding="utf-8-sig")
long(recon_prev).to_csv(os.path.join(C.INTERIM, "va36_recon_prev_annual.csv"), index=False, encoding="utf-8-sig")
long(scen_a).to_csv(os.path.join(C.INTERIM, "va36_scen_annual.csv"), index=False, encoding="utf-8-sig")
for f in sorted(os.listdir(C.INTERIM)):
p = os.path.join(C.INTERIM, f)
if os.path.isfile(p):
print(f"{f:<28} {os.path.getsize(p):>8,d} bytes")
emp_section_annual_nsa.csv 2,120 bytes emp_section_annual_sa.csv 2,130 bytes va36_hist_annual.csv 5,184 bytes va36_recon_annual.csv 85,765 bytes va36_recon_prev_annual.csv 85,770 bytes va36_scen_annual.csv 86,440 bytes