01 데이터 입수 — 국민계정 36개 산업, 산업연관표, 신산업 대용지표¶
목적. 62·69개 산업 분기 실질부가가치 패널과 연쇄가중 총량 재현에 필요한 공개 자료를 불러오고 검증한다.
기본 실행은 data/inputs/의 스냅샷(2026년 6–8월 수집, 2026-10-08 원천 재수집 대조 — 6절 참조)만 읽으므로 인증키와 인터넷 연결이 필요 없다.
| 입력 | 내용 | 원천(통계표·항목) | 기간 |
|---|---|---|---|
bok36_va_q.csv |
36개 산업 분기 실질·명목 부가가치(계절조정, 십억원) | 한국은행 ECOS 200Y104(실질)·200Y103(명목) | 1980Q1–2025Q4 |
gva_total_q.csv |
총부가가치(기초가격, 실질, 계절조정) | ECOS 200Y104 항목 1200 | 1980Q1–2025Q4 |
gdp_quarterly_sa.csv |
국내총생산(지출, 계절조정, 명목·실질) | ECOS 200Y107·200Y108 항목 10601 | 1980Q1–2025Q4 |
io_benchmark_va_83ind.csv |
상품중분류(83) 부가가치(백만원) | ECOS 271Y120 부가가치계(9690) | 2015·2020 |
io_M_mid_2020.csv |
중분류 생산유발계수(85×85) | ECOS 271Y122 | 2020 |
io_small_37x_va.csv |
통합소분류 37x(전기장비) 부가가치(백만원) | 한국은행 산업연관표 2020 실측표·2021–2023 연장표(xlsx) | 2020–2023 |
mfg_prod_c28_c282.csv |
생산지수(원지수) 전기장비(C28)·일차전지 및 축전지(C282) | KOSIS 101/DT_1F02001 항목 T10 | 2000.01–2026.04 |
power_gen_kpx.csv |
발전량: 신재생 및 기타·합계(GWh) | KOSIS 388/TX_38803_A016A(한국전력거래소) | 2006–2016 |
power_gen_kepco.csv |
발전원별 발전량 6종(GWh) | 한국전력공사 전력통계(e-나라지표 1339) | 2016–2025 |
설계표 data/crosswalk_62.json(KEIT62 ↔ BOK36 ↔ IO 상품중분류 83)과 data/carveout_rules.csv(62→69 분화 규칙)는 본 연구가 작성한 표이다.
산출물. data/interim/va36_real_q.csv, va36_nominal_q.csv(36개 산업 광형), snapshot_check.csv, (ONLINE=1일 때) online_diff.csv
스위치. 환경변수 ONLINE=1이면 6절에서 ECOS·KOSIS를 다시 조회해 스냅샷과 비교한다. 인증키는 API_KEY_BOK, API_KEY_KOSIS 환경변수에서만 읽는다.
import os, glob
import numpy as np, pandas as pd
import config as C
import gva_data as F
pd.set_option("display.float_format", lambda v: f"{v:,.4f}")
pd.set_option("display.width", 160)
C.setup()
Python 3.12.10 · pandas 2.2.3 · numpy 2.4.6 ONLINE=0 (원천 재수집) FULL=0 ECOS 키 없음 · KOSIS 키 없음 36개 산업 분기 있음 data\inputs\bok36_va_q.csv 총부가가치 있음 data\inputs\gva_total_q.csv 실질 GDP 있음 data\inputs\gdp_quarterly_sa.csv IO 중분류 부가가치 있음 data\inputs\io_benchmark_va_83ind.csv IO 생산유발계수 있음 data\inputs\io_M_mid_2020.csv IO 소분류 37x 있음 data\inputs\io_small_37x_va.csv 생산지수 C28·C282 있음 data\inputs\mfg_prod_c28_c282.csv 발전량 KPX 있음 data\inputs\power_gen_kpx.csv 발전량 KEPCO 있음 data\inputs\power_gen_kepco.csv 대조표 62 있음 data\crosswalk_62.json 분화 규칙 69 있음 data\carveout_rules.csv
1. 스냅샷 무결성¶
각 입력 파일의 SHA-256을 config.INPUT_SHA256에 기록한 값과 대조한다. 하나라도 다르면 이후 계산 결과가 공표본과 달라질 수 있으므로 여기서 멈춘다.
files = {"36개 산업 분기": C.VA36, "총부가가치": C.GVA_TOTAL, "실질 GDP": C.GDP_Q, "IO 중분류 부가가치": C.IO_VA83,
"IO 생산유발계수": C.IO_M, "IO 소분류 37x": C.IO_SMALL, "생산지수 C28·C282": C.MFG_PROD,
"발전량 KPX": C.POWER_KPX, "발전량 KEPCO": C.POWER_KEPCO, "대조표 62": C.CROSSWALK, "분화 규칙 69": C.CARVE_RULES}
snap = F.snapshot_table(files, C.INPUT_SHA256)
snap.to_csv(os.path.join(C.INTERIM, "snapshot_check.csv"), index=False, encoding="utf-8-sig")
assert snap["기록값과 일치"].all(), "스냅샷 SHA-256이 기록값과 다르다 — data/inputs/ 파일을 확인한다"
snap
| 파일 | 내용 | 바이트 | 행수 | SHA-256(앞 16자) | 기록값과 일치 | |
|---|---|---|---|---|---|---|
| 0 | bok36_va_q.csv | 36개 산업 분기 | 377192 | 6624 | be93329c14b1ea65 | True |
| 1 | gva_total_q.csv | 총부가가치 | 3135 | 184 | 4087e14e8d461c59 | True |
| 2 | gdp_quarterly_sa.csv | 실질 GDP | 6242 | 184 | 3adbc832b93488c3 | True |
| 3 | io_benchmark_va_83ind.csv | IO 중분류 부가가치 | 7463 | 206 | 24a07dfdbc388f6e | True |
| 4 | io_M_mid_2020.csv | IO 생산유발계수 | 111783 | 7225 | af8a6514e0ddd90c | True |
| 5 | io_small_37x_va.csv | IO 소분류 37x | 3081 | 24 | 46f72bff6388a9ee | True |
| 6 | mfg_prod_c28_c282.csv | 생산지수 C28·C282 | 33618 | 632 | 1af50c079edd888e | True |
| 7 | power_gen_kpx.csv | 발전량 KPX | 249 | 11 | 4c05ae3809eb8879 | True |
| 8 | power_gen_kepco.csv | 발전량 KEPCO | 490 | 10 | 81fb52b2499a2ce3 | True |
| 9 | crosswalk_62.json | 대조표 62 | 4674 | <NA> | 09c137303ab59970 | True |
| 10 | carveout_rules.csv | 분화 규칙 69 | 879 | 7 | 59b7fb2688cec105 | True |
2. 36개 산업 분기 실질·명목 부가가치¶
한국은행 「경제활동별 GDP 및 GNI」(계절조정, 분기)의 말단 산업 36개이다. 실질은 2020년 기준 연쇄가격이다.
상위 집계(제조업·서비스업 등)와 소득 항목(총부가가치·GDP·GNI)은 제외하였다. 36개 산업이 모두 존재하는 첫 분기가 1980Q1이므로 결측이 없는 균형 패널을 이룬다.
수집 당시 2026Q1 계절조정 명목 GDP가 직전 분기보다 10.5% 높은 비정상값을 보여 표본을 2025Q4에서 끊었다(원 수집 코드 fetch_gdp_quarterly.py의 기록).
62·69 패널은 이 가운데 2000Q1–2025Q4(104분기)의 실질값만 쓰고, 연쇄가중 재현(02의 7절)은 1980Q1부터의 실질·명목을 모두 쓴다.
real, nom, names = F.read_va36(C.VA36)
print(f"실질 {real.shape} · 명목 {nom.shape} · 기간 {real.index[0]}–{real.index[-1]} · 결측 {int(real.isna().sum().sum() + nom.isna().sum().sum())}")
assert real.shape == (184, 36) and nom.shape == (184, 36) and not real.isna().any().any()
real.to_csv(os.path.join(C.INTERIM, "va36_real_q.csv"), encoding="utf-8-sig")
nom.to_csv(os.path.join(C.INTERIM, "va36_nominal_q.csv"), encoding="utf-8-sig")
last = pd.DataFrame({"산업명": pd.Series(names), "실질 2025Q4": real.loc["2025Q4"], "명목 2025Q4": nom.loc["2025Q4"],
"실질 2000Q1": real.loc["2000Q1"]})
last.index.name = "ECOS 코드"
last.head(12)
실질 (184, 36) · 명목 (184, 36) · 기간 1980Q1–2025Q4 · 결측 0
| 산업명 | 실질 2025Q4 | 명목 2025Q4 | 실질 2000Q1 | |
|---|---|---|---|---|
| ECOS 코드 | ||||
| 1101 | 농림어업 | 8,373.6000 | 9,686.2000 | 7,369.2000 |
| 1102 | 광업 | 339.3000 | 402.3000 | 778.0000 |
| 110301 | 음식료품 제조업 | 7,990.9000 | 9,148.5000 | 4,215.3000 |
| 110302 | 섬유 및 가죽제품 제조업 | 3,732.1000 | 4,073.9000 | 4,651.3000 |
| 110303 | 목재, 종이, 인쇄 및 복제업 | 3,273.7000 | 3,952.5000 | 2,868.4000 |
| 110304 | 코크스 및 석유정제품 제조업 | 2,427.6000 | 4,780.0000 | 6,051.7000 |
| 110305 | 화학물질 및 화학제품 제조업 | 24,256.2000 | 27,003.9000 | 9,878.1000 |
| 110306 | 비금속광물제품 제조업 | 2,271.9000 | 3,051.3000 | 1,822.3000 |
| 110307 | 1차금속 제조업 | 5,973.8000 | 9,942.3000 | 4,991.5000 |
| 110308 | 금속가공제품 제조업 | 6,310.6000 | 11,815.7000 | 4,579.5000 |
| 110309 | 컴퓨터, 전자 및 광학기기 제조업 | 61,268.0000 | 72,310.8000 | 5,389.9000 |
| 110310 | 전기장비 제조업 | 8,512.3000 | 10,015.8000 | 3,233.5000 |
3. 총량 계열 — 총부가가치와 국내총생산¶
연쇄가중 실질값은 가법성이 없다. 36개 산업 실질값을 단순히 더한 값은 기준연도(2020) 부근에서만 총부가가치와 같고, 기준연도에서 멀어질수록 차이가 커진다.
국내총생산은 총부가가치에 순생산물세를 더한 값이다. 실질 GDP는 총량 모형의 입력 패널(levels_agg.csv)이 된다.
gva = F.read_gva_total(C.GVA_TOTAL)
gdp = F.read_gdp(C.GDP_Q)
show = ["1980Q1", "2000Q1", "2010Q1", "2020Q1", "2025Q4"]
pd.DataFrame({"36개 단순합": real.sum(axis=1), "총부가가치(1200)": gva, "실질 GDP(10601)": gdp["실질GDP_십억원"]}).loc[show]
| 36개 단순합 | 총부가가치(1200) | 실질 GDP(10601) | |
|---|---|---|---|
| 분기 | |||
| 1980Q1 | 52,579.1000 | 43,120.7000 | 47,272.0000 |
| 2000Q1 | 233,441.0000 | 220,819.8000 | 243,573.1000 |
| 2010Q1 | 358,568.4000 | 354,510.9000 | 386,238.5000 |
| 2020Q1 | 475,917.8000 | 475,788.6000 | 517,226.9000 |
| 2025Q4 | 549,415.1000 | 543,908.0000 | 585,964.0000 |
4. 산업연관표(2020 실측표)¶
- 상품중분류 83개 부가가치(271Y120): 36→62 배분과 62→69 고정 몫의 기준이다. 표의 열(수요부문 1–83)별 '부가가치계'(9690) 행 값이다.
- 중분류 생산유발계수(271Y122): 산업모형의 산업연관 사전(prior)에 쓰는 62·69 연계강도 행렬의 원자료이다.
- 통합소분류 37x 부가가치: 전기장비(37) 안의 전지(373) 비중을 구해 이차전지 몫의 수준을 맞춘다. ECOS Open API는 소분류 표를 제공하지 않으므로 한국은행 산업연관표 xlsx에서 추출한 값을 스냅샷으로 둔다.
io = F.read_io_va83(C.IO_VA83)
M = pd.read_csv(C.IO_M)
small = pd.read_csv(C.IO_SMALL, dtype={"소분류코드": str})
print(f"IO83 부가가치(2020): {len(io)}개 상품 · 합계 {io.sum()/1e6:,.1f}조원 · 0인 상품 {list(io[io == 0].index)}")
print(f"생산유발계수: {M['i'].nunique()}×{M['j'].nunique()} · 대각 평균 {M[M.i == M.j]['v'].mean():.3f}")
small.pivot(index=["소분류코드", "소분류명"], columns="연도", values="부가가치_백만원")
IO83 부가가치(2020): 83개 상품 · 합계 2,036.2조원 · 0인 상품 [83] 생산유발계수: 85×85 · 대각 평균 1.071
| 연도 | 2020 | 2021 | 2022 | 2023 | |
|---|---|---|---|---|---|
| 소분류코드 | 소분류명 | ||||
| 371 | 발전기 및 전동기 | 2553718 | 2928477 | 3467250 | 3272707 |
| 372 | 전기변환.공급제어장치 | 8899299 | 8782292 | 9408848 | 10641695 |
| 373 | 전지 | 5752407 | 6424953 | 7674703 | 7441610 |
| 374 | 전선 및 케이블 | 2241154 | 2706742 | 2845949 | 2641273 |
| 375 | 가정용 전기기기 | 4954564 | 4490541 | 5012941 | 5278263 |
| 379 | 기타 전기장비 | 7272675 | 6803946 | 7329982 | 8389264 |
5. 신산업 대용지표 — 생산지수와 발전량¶
이차전지와 신재생에너지는 산업연관표 상품중분류에 독립 코드가 없어 시계열 몫을 따로 만든다.
- 이차전지: 광업제조업동향조사 생산지수(원지수) C282(일차전지 및 축전지)/C28(전기장비) 비율이 시간적 형태를 준다.
- 신재생에너지: 신재생 발전량/총발전량. 2006–2015년은 한국전력거래소(KOSIS 388/TX_38803_A016A)의 '신재생 및 기타'/'합계', 2016–2025년은 한국전력공사 전력통계(e-나라지표 1339)의 발전원별 발전량 6종 합계를 쓴다. 두 원천은 2016년 신재생 25,837 GWh(KPX)와 25,836 GWh(KEPCO)로 거의 같다.
mfg = F.read_mfg_prod(C.MFG_PROD)
kpx, kepco = pd.read_csv(C.POWER_KPX), pd.read_csv(C.POWER_KEPCO)
print(f"생산지수: {mfg.index[0]}–{mfg.index[-1]} ({len(mfg)}개월) · 결측 {int(mfg.isna().sum().sum())}")
k16, e16 = kpx.set_index("연도").loc[2016], kepco.set_index("연도").loc[2016]
print(f"2016년 접합 점검: KPX 신재생 {k16['신재생및기타_GWh']:,} / 합계 {k16['합계_GWh']:,} GWh · "
f"KEPCO 신재생 {e16['신재생']:,} / 합계 {int(e16[['원자력','양수','석탄','유류','가스','신재생']].sum()):,} GWh")
mfg.loc[["2000-01-01", "2010-01-01", "2020-01-01", "2025-12-01"]]
생산지수: 2000-01-01–2026-04-01 (316개월) · 결측 0 2016년 접합 점검: KPX 신재생 25,837 / 합계 540,441 GWh · KEPCO 신재생 25,836 / 합계 540,440 GWh
| ksic | C28 | C282 |
|---|---|---|
| date | ||
| 2000-01-01 | 59.0560 | 8.9690 |
| 2010-01-01 | 86.6710 | 75.0080 |
| 2020-01-01 | 92.6000 | 96.4000 |
| 2025-12-01 | 114.3000 | 95.1000 |
6. 원천 재수집과 대조(ONLINE=1)¶
ECOS·KOSIS Open API로 같은 통계표를 다시 받아 스냅샷과 공통 관측을 비교한다. 한국은행·통계청은 과거 계열을 수정 공표하므로 차이가 생길 수 있으며,
차이는 보고만 하고 실행을 멈추지 않는다. 전력통계(e-나라지표 1339)는 Open API가 없어 대조 대상에서 제외한다.
재수집 자료는 data/interim/online_*.csv로 저장하며, 02의 8절이 이 자료로 62 패널과 이차전지 몫을 다시 계산해 차이를 보인다.
if not C.ONLINE:
print("ONLINE=0 — 스냅샷만 사용한다. 원천과 대조하려면 ONLINE=1과 API_KEY_BOK·API_KEY_KOSIS 환경변수를 설정한 뒤 다시 실행한다.")
else:
rows = []
def attempt(label, fn):
try:
rows.append(fn())
except Exception as e:
rows.append({"항목": label, "비고": f"재수집 실패: {str(e)[:80]}"})
if not C.API_KEY_BOK:
rows.append({"항목": "ECOS 전체", "비고": "API_KEY_BOK 없음 — 건너뜀"})
else:
def va36():
r2, n2, g2 = F.fetch_va36_online(C.API_KEY_BOK, list(real.columns))
r2.to_csv(os.path.join(C.INTERIM, "online_va36_real_q.csv"), encoding="utf-8-sig")
rows.append(F.diff_summary("36개 산업 명목(200Y103)", nom, n2))
rows.append(F.diff_summary("총부가가치(200Y104·1200)", gva.to_frame("v"), g2.to_frame("v")))
return F.diff_summary("36개 산업 실질(200Y104)", real, r2)
attempt("36개 산업(200Y103·200Y104)", va36)
attempt("실질 GDP(200Y108)", lambda: F.diff_summary("실질 GDP(200Y108)", gdp[["실질GDP_십억원"]], F.fetch_gdp_online(C.API_KEY_BOK).to_frame()))
attempt("IO83 부가가치(271Y120)", lambda: F.diff_summary("IO83 부가가치(271Y120)", io.to_frame("v"), F.fetch_io_va83_online(C.API_KEY_BOK).to_frame("v")))
attempt("생산유발계수(271Y122)", lambda: F.diff_summary("생산유발계수(271Y122)", M.pivot(index="i", columns="j", values="v"),
F.fetch_io_M_online(C.API_KEY_BOK).pivot(index="i", columns="j", values="v")))
if not C.API_KEY_KOSIS:
rows.append({"항목": "KOSIS 전체", "비고": "API_KEY_KOSIS 없음 — 건너뜀"})
else:
def mfg_online():
m2 = F.fetch_mfg_prod_online(C.API_KEY_KOSIS)
m2.to_csv(os.path.join(C.INTERIM, "online_mfg_prod.csv"), encoding="utf-8-sig")
return F.diff_summary("생산지수 C28·C282(DT_1F02001)", mfg, m2)
attempt("생산지수(DT_1F02001)", mfg_online)
attempt("발전량 KPX(TX_38803_A016A)", lambda: F.diff_summary("발전량 KPX(TX_38803_A016A)", kpx.set_index("연도"),
F.fetch_kpx_online(C.API_KEY_KOSIS).set_index("연도")))
diff = pd.DataFrame(rows)
diff.to_csv(os.path.join(C.INTERIM, "online_diff.csv"), index=False, encoding="utf-8-sig")
display(diff)
ONLINE=0 — 스냅샷만 사용한다. 원천과 대조하려면 ONLINE=1과 API_KEY_BOK·API_KEY_KOSIS 환경변수를 설정한 뒤 다시 실행한다.
7. (선택) 산업연관표 통합소분류 원본 재추출¶
한국은행 산업연관표 「투입산출표(생산자가격) 통합소분류」 xlsx 4개(2020 실측, 2021–2023 연장)를 data/external/에 두면 37x 부가가치를 다시 추출해 스냅샷과 대조한다.
파일이 없으면 이 절을 건너뛴다. 파일명 형식은 config.IO_SMALL_XLSX에 있다.
found = {y: sorted(glob.glob(os.path.join(C.EXTERNAL, pat))) for y, pat in C.IO_SMALL_XLSX.items()}
if not all(found.values()):
print("data/external/ 에 통합소분류 xlsx가 없어 건너뛴다(스냅샷 io_small_37x_va.csv 사용).")
else:
ext = F.extract_io_small_xlsx({y: f[0] for y, f in found.items()})
m = ext.merge(small, on=["연도", "소분류코드"], suffixes=("_원본", "_스냅샷"))
print(f"대조 {len(m)}행 · 최대 차이 {(m['부가가치_백만원_원본'] - m['부가가치_백만원_스냅샷']).abs().max()} 백만원")
data/external/ 에 통합소분류 xlsx가 없어 건너뛴다(스냅샷 io_small_37x_va.csv 사용).
8. 산출물¶
다음 노트북(02_분석.ipynb)은 data/interim/의 36개 산업 광형 파일과 data/inputs/의 나머지 스냅샷을 읽는다.
for f in sorted(os.listdir(C.INTERIM)):
p = os.path.join(C.INTERIM, f)
print(f"{f:<28} {os.path.getsize(p):>9,d} bytes")
snapshot_check.csv 830 bytes va36_nominal_q.csv 47,341 bytes va36_real_q.csv 48,741 bytes