{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 논문 2 · 챕터 2 — MECE 70산업 carve-out\n",
    "\n",
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/sdkparkforbi/gva-monthly-ssm-70/blob/main/ch2/code/chP2_mece.ipynb)\n",
    "\n",
    "> **Google Colab에서 바로 실행**됩니다. 배지를 누른 뒤 `런타임 → 모두 실행`.\n",
    "> 데이터는 아래 셀에서 자동으로 내려받고, 그림은 셀 실행 시 바로 표시됩니다.\n",
    "\n",
    "각 셀에는 **셀 번호 · 셀 제목**이 달려 있고, 코드에는 줄별 **주석**을 넣었습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 0. 준비 — Colab 환경과 데이터"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 1 · 한글 폰트 (koreanize-matplotlib) =====\n",
    "# 그림의 한글이 깨지지 않도록 koreanize-matplotlib 를 설치하고 불러옵니다.\n",
    "# import 하는 순간 matplotlib 한글 폰트가 자동 설정됩니다.\n",
    "_font_ok = False\n",
    "try:\n",
    "    import koreanize_matplotlib                       # 이미 설치돼 있으면 바로 사용\n",
    "    _font_ok = True\n",
    "except Exception:\n",
    "    try:\n",
    "        import subprocess, sys\n",
    "        subprocess.run([sys.executable,'-m','pip','install','-q','koreanize-matplotlib'], check=False)\n",
    "        import koreanize_matplotlib                   # 설치 후 다시 불러오기\n",
    "        _font_ok = True\n",
    "    except Exception:\n",
    "        _font_ok = False\n",
    "if not _font_ok:                                      # 폴백: 나눔폰트 직접 설치·등록\n",
    "    import subprocess\n",
    "    subprocess.run(['apt-get','-qq','-y','install','fonts-nanum'], check=False)\n",
    "    import matplotlib.font_manager as fm, matplotlib.pyplot as plt\n",
    "    _p = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'\n",
    "    try:\n",
    "        fm.fontManager.addfont(_p)\n",
    "        plt.rcParams['font.family'] = fm.FontProperties(fname=_p).get_name()  # 실제 등록 이름 사용\n",
    "    except Exception: pass\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['axes.unicode_minus'] = False           # 마이너스 기호 깨짐 방지\n",
    "print('한글 폰트 준비 완료')"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 2 · 데이터 다운로드 =====\n",
    "# 이 챕터의 입력 데이터를 서버에서 로컬 data/ 폴더로 내려받습니다.\n",
    "import os, urllib.request, urllib.parse\n",
    "BASE = \"https://aiforalab.com/gva-monthly-ssm-70/ch2/data/\"          # 서버의 이 챕터 데이터 폴더\n",
    "FILES = [\"emerging_meta.csv\", \"host_va_q.csv\", \"proxies_m.csv\"]\n",
    "os.makedirs(\"data\", exist_ok=True)\n",
    "for f in FILES:                                      # 한글 파일명은 URL 인코딩 후 요청\n",
    "    urllib.request.urlretrieve(BASE + urllib.parse.quote(f), os.path.join('data', f))\n",
    "print('다운로드 완료:', len(FILES), '개 ->', sorted(os.listdir('data')))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 방법 구현과 계산"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 3 · 라이브러리 임포트·설정 =====\n",
    "import os, json\n",
    "import numpy as np, pandas as pd\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 4 · Colab 경로: 데이터=data/, 그림=figures/ =====\n",
    "# ----- Colab 경로: 데이터=data/, 그림=figures/ -----\n",
    "import os\n",
    "DATA = \"data\"; PREP = \"data\"; FIG = \"figures\"; OUT = \"figures\"\n",
    "os.makedirs(\"data\", exist_ok=True); os.makedirs(\"figures\", exist_ok=True)\n",
    "plt.rcParams[\"savefig.dpi\"]=130\n",
    "MON=pd.date_range(\"2000-01-01\",\"2025-12-01\",freq=\"MS\"); T=len(MON)\n",
    "QMIDX=[(i*3+2) for i in range(104)]"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 5 · 함수 정의: zscore_log, chowlin_mean, recover_parent =====\n",
    "def zscore_log(v):\n",
    "    v=np.asarray(v,float); out=np.full_like(v,np.nan); m=v>0\n",
    "    lg=np.log(v[m]); out[m]=(lg-lg.mean())/(lg.std()+1e-9); return out\n",
    "def chowlin_mean(q_level, X, rho_grid=None):\n",
    "    Tn,k=X.shape\n",
    "    if rho_grid is None: rho_grid=np.r_[np.linspace(0,0.95,20),0.97,0.99]\n",
    "    Q=len(q_level); A=np.zeros((Q,Tn))\n",
    "    for i,me in enumerate(QMIDX):\n",
    "        for d in range(3):\n",
    "            j=me-d\n",
    "            if 0<=j<Tn: A[i,j]=1/3\n",
    "    yq=np.asarray(q_level,float); vv=~np.isnan(yq); A=A[vv]; yq=yq[vv]; Q=len(yq)\n",
    "    best=None\n",
    "    for rho in rho_grid:\n",
    "        lag=np.abs(np.subtract.outer(np.arange(Tn),np.arange(Tn))); Vu=rho**lag/(1-rho**2+1e-12)\n",
    "        AX=A@X; Om=A@Vu@A.T+1e-8*np.eye(Q); Oi=np.linalg.pinv(Om)\n",
    "        beta=np.linalg.solve(AX.T@Oi@AX+1e-8*np.eye(k),AX.T@Oi@yq); r=yq-AX@beta\n",
    "        s2=float(r@Oi@r)/max(Q-k,1); _,ld=np.linalg.slogdet(Om*s2); ll=-0.5*(Q*np.log(2*np.pi)+ld+Q)\n",
    "        if best is None or ll>best[0]: best=(ll,rho,X@beta+Vu@A.T@Oi@r)\n",
    "    return best[2]\n",
    "def recover_parent(qva, proxy):\n",
    "    cols=[np.ones(T), np.linspace(0,1,T)]\n",
    "    if proxy is not None:\n",
    "        cols.append(pd.Series(zscore_log(proxy)).interpolate(limit_direction=\"both\").fillna(0).values)\n",
    "    m=chowlin_mean(np.log(np.asarray(qva,float)), np.column_stack(cols))\n",
    "    return np.exp(m)                      # 월별 부모 수준"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 6 · 신산업 → (신프록시, 모프록시, 2020비중, 소스) =====\n",
    "# 신산업 → (신프록시, 모프록시, 2020비중, 소스)\n",
    "CFG={\n",
    " \"반도체\":(\"C261\",\"C26\",0.500),\"이차전지\":(\"C282\",\"C28\",0.220),\"바이오의약\":(\"C212\",\"C20\",0.173),\n",
    " \"신재생에너지\":(None,None,0.080),\"AI·SW\":(\"J582\",\"J\",0.444),\"디지털플랫폼\":(\"G4791\",\"G\",0.120),\n",
    " \"콘텐츠\":(\"J591\",\"J\",0.451),\"디지털헬스케어\":(\"Q869\",\"Q86\",0.050)}\n",
    "HOST_KO={\"반도체\":\"전자\",\"이차전지\":\"전기장비\",\"바이오의약\":\"화학\",\"신재생에너지\":\"전기가스\",\n",
    "         \"AI·SW\":\"정보서비스\",\"디지털플랫폼\":\"도소매\",\"콘텐츠\":\"출판영상\",\"디지털헬스케어\":\"보건\"}"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 7 · 데이터 로드 =====\n",
    "hv=pd.read_csv(os.path.join(DATA,\"host_va_q.csv\"),encoding=\"utf-8-sig\")\n",
    "px=pd.read_csv(os.path.join(DATA,\"proxies_m.csv\"),encoding=\"utf-8-sig\")\n",
    "yr=MON.year.values; is2020=(yr==2020)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 8 · 함수 정의: tv_share =====\n",
    "def tv_share(newk, hostk, s0):\n",
    "    if newk is None: return np.full(T, s0)\n",
    "    nv=pd.Series(px[newk].values).interpolate(limit_direction=\"both\").bfill().ffill().values\n",
    "    hv_=pd.Series(px[hostk].values).interpolate(limit_direction=\"both\").bfill().ffill().values\n",
    "    rel=(nv/np.nanmean(nv[is2020]))/(hv_/np.nanmean(hv_[is2020]))\n",
    "    return np.clip(s0*rel, 0.01, 0.95)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 9 · 계산 =====\n",
    "new_series={}; new_growth={}; parent_cache={}; share_ts={}\n",
    "for nm,(nk,hk,s0) in CFG.items():\n",
    "    proxy=px[hk].values if hk else None\n",
    "    parent=recover_parent(hv[nm].values, proxy); parent_cache[nm]=parent\n",
    "    s=tv_share(nk,hk,s0); share_ts[nm]=s\n",
    "    new=parent*s; new_series[nm]=new\n",
    "    new_growth[nm]=float(np.nanmean(100*np.diff(np.log(new))))\n",
    "\n",
    "# --- carve-out 항등 검증 (반도체) ---\n",
    "p=parent_cache[\"반도체\"]; s=share_ts[\"반도체\"]; semi=p*s; resid=p*(1-s)\n",
    "ident_err=float(np.nanmax(np.abs((semi+resid)-p)/p))"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 10 · 결과 출력 =====\n",
    "print(\"논문2·챕터2 — MECE 70산업 분할 (carve-out 재현)\")\n",
    "print(f\"  carve-out 항등 최대오차 (반도체+잔여 vs 전자부모): {ident_err:.2e}\")\n",
    "print(f\"  구조: 62 기준분류 + 8 신산업 + 8 잔여 = 70 (합=경제 보존)\")\n",
    "print(f\"  {'신산업':12}{'모산업':8}{'2020비중':>8}{'월평균성장%':>11}\")\n",
    "for nm in CFG: print(f\"  {nm:12}{HOST_KO[nm]:8}{CFG[nm][2]*100:>7.1f}%{new_growth[nm]:>11.2f}\")"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 11 · 함수 정의: idx2020 =====\n",
    "def idx2020(v): return 100*v/np.nanmean(v[is2020])\n",
    "CLR=[\"#185FA5\",\"#9a3b2e\",\"#2e7d4f\",\"#b8860b\",\"#6a4c93\",\"#0e7c86\",\"#c0392b\",\"#555\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 결과와 그림"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 12 · fig1: 반도체 carve-out (전자 부모 = 반도체 + 전자잔여) — 공통기 =====\n",
    "# --- fig1: 반도체 carve-out (전자 부모 = 반도체 + 전자잔여) — 공통기준(부모 2020) ---\n",
    "fig,ax=plt.subplots(figsize=(10,4.6)); sel=MON>=pd.Timestamp(\"2008-01-01\")\n",
    "base=np.nanmean(p[is2020])                      # 부모 2020 = 100 공통기준(항등식 보존)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 13 · 함수 정의: bidx =====\n",
    "def bidx(v): return 100*v/base"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 14 · 그림 그리기 =====\n",
    "ax.stackplot(MON[sel],[bidx(semi)[sel],bidx(resid)[sel]],\n",
    "             labels=[\"반도체 (carve-out)\",\"전자·컴퓨터·통신 잔여\"],colors=[\"#185FA5\",\"#c9d6e5\"])\n",
    "ax.plot(MON[sel], bidx(p)[sel], color=\"#111\", lw=1.4, ls=\"--\", label=\"전자 부모 = 반도체 + 잔여 (합)\")\n",
    "ax.set_title(\"그림 1. carve-out 항등 — 전자 부모 = 반도체 + 잔여 (2020=100)\", fontweight=\"bold\")\n",
    "ax.set_ylabel(\"지수 (2020=100)\"); ax.legend(fontsize=9, loc=\"upper left\"); ax.grid(alpha=.25); ax.margins(x=.02)\n",
    "fig.tight_layout(); fig.savefig(os.path.join(FIG,\"fig1_carveout.png\"),bbox_inches=\"tight\"); plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 15 · fig2: 신규 8개 산업 월별 부가가치 지수 (2020=100) =====\n",
    "# --- fig2: 신규 8개 산업 월별 부가가치 지수 (2020=100) ---\n",
    "fig,ax=plt.subplots(figsize=(10,5)); sel=MON>=pd.Timestamp(\"2005-01-01\")\n",
    "dash=[\"-\",\"--\",\"-\",\"--\",\"-.\",\"--\",\"-\",\":\"]\n",
    "for i,nm in enumerate(CFG):\n",
    "    ax.plot(MON[sel], idx2020(new_series[nm])[sel], color=CLR[i], ls=dash[i], lw=1.6, label=nm)\n",
    "ax.set_title(\"그림 2. MECE 분할로 복원한 신규 8개 산업 월별 실질부가가치 (2020=100)\", fontweight=\"bold\")\n",
    "ax.set_ylabel(\"지수 (2020=100)\"); ax.legend(fontsize=8.5, ncol=2); ax.grid(alpha=.25); ax.margins(x=.02)\n",
    "fig.tight_layout(); fig.savefig(os.path.join(FIG,\"fig2_new8.png\"),bbox_inches=\"tight\"); plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 16 · fig3: 2020 분할비중 =====\n",
    "# --- fig3: 2020 분할비중 ---\n",
    "fig,ax=plt.subplots(figsize=(8,4.2))\n",
    "nms=list(CFG); sh=[CFG[n][2]*100 for n in nms]; order=np.argsort(sh)\n",
    "ax.barh([nms[i]+f\" ({HOST_KO[nms[i]]})\" for i in order],[sh[i] for i in order],color=\"#185FA5\")\n",
    "ax.set_title(\"그림 3. 신규 8개 산업의 2020 모산업 내 분할비중\", fontweight=\"bold\")\n",
    "ax.set_xlabel(\"모산업 내 비중 (%)\"); ax.grid(alpha=.2, axis=\"x\")\n",
    "for i,idx in enumerate(order): ax.text(sh[idx]+0.5,i,f\"{sh[idx]:.0f}%\",va=\"center\",fontsize=8.5)\n",
    "fig.tight_layout(); fig.savefig(os.path.join(FIG,\"fig3_shares.png\"),bbox_inches=\"tight\"); plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 17 · 결과 출력 =====\n",
    "json.dump({\"ident_err_semi\":ident_err,\"structure\":\"54 base + 8 emerging + 8 residual = 70\",\n",
    "           \"new8\":[{\"name\":nm,\"host\":HOST_KO[nm],\"share_2020_pct\":round(CFG[nm][2]*100,1),\n",
    "                    \"mean_growth_pct\":round(new_growth[nm],2)} for nm in CFG]},\n",
    "          open(os.path.join(DATA,\"chP2_results.json\"),\"w\",encoding=\"utf-8\"),ensure_ascii=False,indent=2)\n",
    "print(\"저장: ch2/figures/(fig1,fig2,fig3), ch2/data/chP2_results.json\")"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "chP2_mece.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}