From 51dda7f3178542a1b901be85bc74a0220c49cebb Mon Sep 17 00:00:00 2001 From: SANGDONKIM Date: Fri, 4 Nov 2022 19:41:25 +0900 Subject: [PATCH] chapter 10 update --- 10-Matching.ipynb | 1581 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1581 insertions(+) create mode 100644 10-Matching.ipynb diff --git a/10-Matching.ipynb b/10-Matching.ipynb new file mode 100644 index 0000000..8610bc5 --- /dev/null +++ b/10-Matching.ipynb @@ -0,0 +1,1581 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 10 - Matching\n", + "\n", + "## 회귀는 결국 무엇을 하고 있습니까?\n", + "\n", + "지금까지 살펴본 것처럼 회귀는 실험군과 대조군 비교를 수행할 때 추가 변수를 제어하는데 놀라운 역할을 합니다. 조건부 독립 가정을 만족한다면, $(Y_0, Y_1)\\perp T | X$, 회귀는 X를 통제하여 ATE를 식별할 수 있습니다. The way regression does this is kind of magical. 회귀가 수행하는 방식은 일종의 마법과 같습니다. 이에 대한 직관을 얻기 위해 모든 변수 X가 더미 변수인 경우를 기억합시다. 이 경우 회귀 분석은 데이터를 더미 셀로 분할하고 실험군과 통제군 간의 평균 차이를 계산합니다. 이 평균의 차이는 X 더미의 고정 셀에서 수행하기 때문에 X를 일정하게 유지합니다. 마치 우리가 해왔던 것처럼 $E[Y|T=1] - E[Y|T=0] | X=x$로 구할 수 있고, $x$ 는 더미 셀을 의미합니다(예를 들어 모든 더미는 1로 고정된 경우). 그런 다음 회귀는 각 셀의 추정치를 결합하여 최종 ATE를 생성합니다. 이를 수행하는 방법은 해당 그룹의 처리 분산에 비례하여 셀에 가중치를 적용하는 것입니다." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings('ignore')\n", + "\n", + "import pandas as pd\n", + "import numpy as np\n", + "from matplotlib import style\n", + "from matplotlib import pyplot as plt\n", + "import statsmodels.formula.api as smf\n", + "\n", + "import graphviz as gr\n", + "\n", + "%matplotlib inline\n", + "\n", + "style.use(\"fivethirtyeight\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "예를 들어, 남자 6명과 여상 4명이 있을 때, 약의 효과를 추정하려고 합니다. 반응 변수는 입원 일수이고, 약을 섭취했을 때 입원 일수가 낮아질 것으로 기대합니다. 남성의 경우 진짜 인과효과가 -3이므로, 이 약은 입원 일수를 3일 단축시킵니다. 여성의 경우 -2입니다. 설상가상으로 남성은 이 질병의 영향을 훨씬 더 많이 받고 병원에 더 오래 머무르는 경향이 있으며, 훨씬 더 많은 약을 섭취합니다(6명 중 1명만 약물 섭취). 반면 여성은 이 질병에 내성이 있기 때문에 병원에 오래 머무르지 않는 경향이 있습니다. 또한 여성의 50%가 약을 섭취합니다." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "drug_example = pd.DataFrame(dict(\n", + " sex= [\"M\",\"M\",\"M\",\"M\",\"M\",\"M\", \"W\",\"W\",\"W\",\"W\"],\n", + " drug=[1,1,1,1,1,0, 1,0,1,0],\n", + " days=[5,5,5,5,5,8, 2,4,2,4]\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "실험군과 대조군의 단순한 비교는 부정적으로 편향된 효과, 즉 약이 실제보다 덜 효과적인 것처럼 보입니다. 이것은 우리가 성별 교란자를 생략했기 때문에 발생하는 것입니다. 이 경우 남성이 더 많은 약물을 복용하고 질병의 영향을 더 많이 받기 때문에 추정된 ATE는 실제보다 작습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1.1904761904761898" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "drug_example.query(\"drug==1\")[\"days\"].mean() - drug_example.query(\"drug==0\")[\"days\"].mean()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "남자에 대한 실제 효과는 -3이고 여자에 대한 실제 효과는 -2이므로 ATE는 다음과 같아야 합니다.\n", + "$\n", + "ATE=\\dfrac{(-3*6) + (-2*4)}{10}=-2.6\n", + "$\n", + "\n", + "이 추정치는 1) 데이터를 교란자 셀(이 경우 남성과 여성)로 분할하고, 2) 각 셀에 대한 영향을 추정하고, 3) 추정치를 가중 평균과 결합하여 수행됩니다. 여기서 가중치는 셀 또는 공변량 그룹의 표본 크기입니다. 데이터에 정확히 같은 크기의 남성과 여성이 있는 경우 ATE 추정치는 두 그룹의 ATE의 중간인 -2.5가 됩니다. 데이터셋에는 여성보다 남성이 더 많기 때문에 ATE 추정치는 남성의 ATE에 조금 더 가깝습니다. 이것은 데이터가 어떻게 생성되었는지에 대한 가정을 하지 않기 때문에 비모수 추정이라고 합니다.\n", + "\n", + "회귀를 사용하여 성별을 제어하는 ​​경우 선형성 가정을 추가합니다. 회귀는 데이터를 남성과 여성으로 분할하고 이 두 그룹에 대한 영향을 추정합니다. 지금까지는 그런대로 잘되었습니다. 그러나 각 그룹에 대한 효과를 결합할 때 표본 크기로 가중치를 두지 않습니다. 대신 회귀는 해당 그룹에서 treatment 분산에 비례하여 가중치를 설정합니다. 우리의 경우 남성의 treatment 분산은 여성보다 작습니다. 왜냐하면 대조군에는 남성이 한 명뿐이기 때문입니다. 정확하게 남성에 대한 T의 분산은 $0.139=1/6*(1 - 1/6)$이고, 여성의 경우 $0.25=2/4*(1 - 2/4)$ 입니다. 따라서 회귀는 이 예에서 여성에게 더 높은 가중치를 부여하고 ATE는 -2의 여성 ATE에 약간 더 가깝습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
Intercept 7.5455 0.188 40.093 0.000 7.100 7.990
C(sex)[T.W] -3.3182 0.176 -18.849 0.000 -3.734 -2.902
drug -2.4545 0.188 -13.042 0.000 -2.900 -2.010
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "smf.ols('days ~ drug + C(sex)', data=drug_example).fit().summary().tables[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "이 결과는 더미 변수에 대해 더 직관적이지만, 그 자체로 이상한 방식으로, 회귀는 효과를 추정하면서 연속 변수를 일정하게 유지합니다. 또한 연속 변수의 경우 ATE는 공변량이 더 많은 분산을 갖는 방향을 가리킵니다.\n", + "\n", + "따라서 우리는 회귀에 고유한 특성이 있음을 보았습니다. 선형적이며 매개변수적이며, 분산이 큰 특성을 좋아합니다... 이것은 컨텍스트에 따라 좋거나 나쁠 수 있습니다. 이 때문에 교란자를 제어하는 ​​데 사용할 수 있는 다른 기술을 알고 있는 것이 중요합니다. 그것들은 인과 분석 도구들 중 하나 일뿐만 아니라 교란자를 처리하는 다양한 방법을 이해하면 문제에 대한 이해가 확장됩니다. 이러한 이유로 이제 Subclassification Estimator를 소개합니다!\n", + "\n", + "\n", + "## The Subclassification Estimator\n", + "\n", + "![img](./data/img/matching/explain.png)\n", + "\n", + "직업 훈련이 개인의 수입에 미치는 영향과 같이 추정하고자 하는 인과 관계가 있고, 처치가 무작위로 할당되지 않은 경우 교란 요인을 주의해야 합니다. 더 동기가 부여된 사람들만이 훈련을 하고 훈련에 관계없이 더 높은 수입을 얻을 수 있습니다. 우리는 동기 수준과 우리가 가질 수 있는 다른 교란요인이 대략 동일한 개인의 소그룹 내에서 훈련 프로그램의 효과를 추정할 필요가 있습니다.\n", + "\n", + "더 일반적으로, 추정하고자 하는 인과 효과가 있지만, 교란자 X 때문에 추정이 어려운 경우, X가 동일한 소그룹 내에서 처치 대 대조군 비교를 수행해야 합니다. 조건부 독립 성질을 만족한다면($(Y_0, Y_1)\\perp T | X$), ATE는 다음과 같이 계산할 수 있습니다.\n", + "\n", + "$\n", + "ATE = \\int(E[Y|X, T=1] - E[Y|X, T=0])dP(x)\n", + "$\n", + "\n", + "이 적분은 특성 X 분포의 모든 공간을 통과하고 모든 작은 공간에 대한 평균의 차이를 계산하고 모든 것을 ATE에 결합합니다. 이것을 해석하는 또 다른 방법은 변수의 이산 집합에 대해 생각하는 것입니다. 이 경우, 변수 X가 K개의 서로 다른 셀$\\{X_1, X_2, ..., X_k\\}$을 취한다고 말할 수 있으며, 각 셀의 처치 효과를 계산하고 이를 ATE로 결합하는 것입니다. 이산 케이스의 경우 적분을 합으로 변환하면 하위 분류 추정기를 유도할 수 있습니다.\n", + "\n", + "\n", + "\n", + "$\n", + "\\hat{ATE} = \\sum^K_{i=0}(\\bar{Y}_{k1} - \\bar{Y}_{k0}) * \\dfrac{N_k}{N}\n", + "$\n", + "\n", + "여기서 막대는 셀 k에서 처치 그룹의 평균, $Y_{k1}$, 통제 그룹의 평균, $Y_{k0}$을 나타내며, $N_{k}$ 같은 셀에서의 관측치의 수를 나타냅니다. 보시다시피, 우리는 각 셀에 대한 로컬 ATE를 계산하고 가중치가 셀의 표본 크기인 가중 평균을 사용하여 결합합니다. 위의 의학 예에서 이것은 첫 번째 추정치가 되어 -2.6을 얻었습니다.\n", + "\n", + "## Matching Estimator\n", + "\n", + "![img](./data/img/matching/its-a-match.png)\n", + "\n", + "subclassification estimator는 실제로 많이 사용되지는 않지만(우리는 그것이 차원의 저주 때문이라는 것을 곧 알게 될 것입니다) 인과 추론 추정기가 무엇을 하는지, 교란자를 어떻게 제어하는지에 대한 좋은 직관을 제공합니다. 이를 통해 Matching Estimator와 같은 다른 종류의 추정기를 탐색할 수 있습니다.\n", + "\n", + "아이디어는 매우 비슷합니다. 일종의 교란자 X가 처치 그룹과 통제 그룹을 비교할 수 없도록 만들기 때문에, 각 처치그룹의 값을 유사한 통제그룹의 값과 일치 시켜 비교할 수 있습니다. 치료를 받을 때마다 치료받지 않은 쌍둥이를 찾는 것과 같습니다. 이러한 비교를 통해 처치된 것과 처치되지 않은 것을 다시 비교할 수 있게 됩니다.\n", + "\n", + "예를 들어, 연수 프로그램이 수입에 미치는 영향을 추정하려고 한다고 가정해 보겠습니다. 연수를 받은 경우 데이터는 다음과 같습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
unittraineesageearnings
0112817700
1213410200
2312914400
3412520800
451296100
5612328600
6713321900
7812728800
8913120300
91012628100
10111259400
111212714300
121312912500
131412419700
141512510100
151614310700
161712811500
171812710700
181912816300
\n", + "
" + ], + "text/plain": [ + " unit trainees age earnings\n", + "0 1 1 28 17700\n", + "1 2 1 34 10200\n", + "2 3 1 29 14400\n", + "3 4 1 25 20800\n", + "4 5 1 29 6100\n", + "5 6 1 23 28600\n", + "6 7 1 33 21900\n", + "7 8 1 27 28800\n", + "8 9 1 31 20300\n", + "9 10 1 26 28100\n", + "10 11 1 25 9400\n", + "11 12 1 27 14300\n", + "12 13 1 29 12500\n", + "13 14 1 24 19700\n", + "14 15 1 25 10100\n", + "15 16 1 43 10700\n", + "16 17 1 28 11500\n", + "17 18 1 27 10700\n", + "18 19 1 28 16300" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "trainee = pd.read_csv(\"./data/trainees.csv\")\n", + "trainee.query(\"trainees==1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "연수를 받지 않은 경우는 다음과 같습니다:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
unittraineesageearnings
192004320900
202105031000
212203021000
22230279300
232405441100
242504829800
252603942000
26270288800
272802425500
282903315500
2931026400
303203126600
313302616500
323403424200
333502523300
34360249700
35370296200
363803530200
373903217800
38400239500
394103225900
\n", + "
" + ], + "text/plain": [ + " unit trainees age earnings\n", + "19 20 0 43 20900\n", + "20 21 0 50 31000\n", + "21 22 0 30 21000\n", + "22 23 0 27 9300\n", + "23 24 0 54 41100\n", + "24 25 0 48 29800\n", + "25 26 0 39 42000\n", + "26 27 0 28 8800\n", + "27 28 0 24 25500\n", + "28 29 0 33 15500\n", + "29 31 0 26 400\n", + "30 32 0 31 26600\n", + "31 33 0 26 16500\n", + "32 34 0 34 24200\n", + "33 35 0 25 23300\n", + "34 36 0 24 9700\n", + "35 37 0 29 6200\n", + "36 38 0 35 30200\n", + "37 39 0 32 17800\n", + "38 40 0 23 9500\n", + "39 41 0 32 25900" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "trainee.query(\"trainees==0\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "평균적으로 단순 비교를 해보면 연수생이 연수를 받지 않은 사람보다 돈을 덜 번다는 것을 알 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-4297.49373433584" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "trainee.query(\"trainees==1\")[\"earnings\"].mean() - trainee.query(\"trainees==0\")[\"earnings\"].mean()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "그러나 위의 표를 보면 연수생이 연수생이 아닌 사람보다 훨씬 어리다는 것을 알 수 있습니다. 이를 수정하기 위해 나이를 일치시킵니다. 둘 다 28세이므로 처치 그룹에서 unit 1을 가져와 unit 27과 짝지을 것입니다. unit 2는 unit 34와, unit 3은 unit 37, unit 4는 unit 35와 짝을 지을 것입니다… unit 5는 통제그룹에서 29세의 사람을 찾아야 하지만 이미, unit 1은 unit 37과 짝을 이뤘습니다. 하지만 같은 단위를 여러 번 사용할 수 있으므로 문제가 되지 않습니다. 1개 이상의 unit이 일치하는 경우, 우리는 그들 중에서 무작위로 선택할 수 있습니다.\n", + "\n", + "다음은 처음 7개 unit에 대해 일치하는 데이터 세트의 모양입니다." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
unit_t_1trainees_t_1ageearnings_t_1unit_t_0trainees_t_0earnings_t_0t1_minuts_t0
011281770027088008900
121341020034024200-14000
231291440037062008200
341252080035023300-2500
4512961003706200-100
5612328600400950019100
6713321900290155006400
\n", + "
" + ], + "text/plain": [ + " unit_t_1 trainees_t_1 age earnings_t_1 unit_t_0 trainees_t_0 \\\n", + "0 1 1 28 17700 27 0 \n", + "1 2 1 34 10200 34 0 \n", + "2 3 1 29 14400 37 0 \n", + "3 4 1 25 20800 35 0 \n", + "4 5 1 29 6100 37 0 \n", + "5 6 1 23 28600 40 0 \n", + "6 7 1 33 21900 29 0 \n", + "\n", + " earnings_t_0 t1_minuts_t0 \n", + "0 8800 8900 \n", + "1 24200 -14000 \n", + "2 6200 8200 \n", + "3 23300 -2500 \n", + "4 6200 -100 \n", + "5 9500 19100 \n", + "6 15500 6400 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# make dataset where no one has the same age\n", + "unique_on_age = (trainee\n", + " .query(\"trainees==0\")\n", + " .drop_duplicates(\"age\"))\n", + "\n", + "matches = (trainee\n", + " .query(\"trainees==1\")\n", + " .merge(unique_on_age, on=\"age\", how=\"left\", suffixes=(\"_t_1\", \"_t_0\"))\n", + " .assign(t1_minuts_t0 = lambda d: d[\"earnings_t_1\"] - d[\"earnings_t_0\"]))\n", + "\n", + "matches.head(7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "마지막 열에 처치 unit과 일치하는 통제 unit 간의 수입 차이가 어떻게 되는지 주목하십시오. 마지막 열의 평균을 취하면 연령을 제어하면서 ATET 추정치를 얻을 수 있습니다. 단순 평균 차이를 사용한 이전 추정치와 비교하여 추정치가 이제 매우 긍정적인 점에 주목하십시오." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2457.8947368421054" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "matches[\"t1_minuts_t0\"].mean()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "그러나 이것은 매칭을 도입하기 위해 매우 인위적인 예였습니다. 실제로 우리는 일반적으로 하나 이상의 변수를 가지고 있으며 단위가 완벽하게 일치하지 않습니다. 이 경우 단위가 서로 얼마나 가까운지를 비교하기 위해 일부 근접도 측정을 정의해야 합니다. 이것에 대한 하나의 일반적인 메트릭은 유클리디안 거리입니다($||X_i - X_j||$). 그러나 이 차이는 변수의 스케일에 따라 변하지 않습니다. 이것은 10단위의 값인 나이와 수백 단위의 값인 수입을 이용해서 유클리디안 거리를 계산할 때, 나이 변수는 훨씬 덜 중요하다는 것을 의미합니다. 이러한 이유로 유클리디안 거리를 적용하기 전에 변수가 대략 같은 스케일이 되도록 스케일을 조정해야 합니다.\n", + "\n", + "거리 측도를 정의했으므로 이제 가장 가까운 이웃으로 매칭을 정의할 수 있습니다. 수학 용어로 일치 추정기를 다음과 같이 작성할 수 있습니다.\n", + "\n", + "$\n", + "\\hat{ATE} = \\frac{1}{N} \\sum^N_{i=0} (2T_i - 1)\\big(Y_i - Y_{jm}(i)\\big)\n", + "$\n", + "\n", + "여기서 $Y_{jm}(i)$ $Y_i$와 가장 유사한 다른 처치그룹의 표본을 의미합니다. 우리는 이것을 $2T_i - 1$로 하여 두 가지 방식으로 일치시킵니다: treated with controls and controls with the treatment. \n", + "\n", + "이 추정기를 테스트하기 위해 의학 예시를 살펴보겠습니다. 다시 한 번, 우리는 환자가 회복까지 며칠이 걸리는지를 통해 약물의 효과를 알아보고 싶습니다. 불행히도 이 효과는 질병의 심각도, 성별 및 연령에 따라 왜곡됩니다. 우리는 상태가 더 심각한 환자가 약을 받을 가능성이 더 높다고 믿습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sexageseveritymedicationrecovery
0035.0491340.887658131
1141.5803230.899784149
2128.1274910.486349038
3136.3750330.323091035
4025.0917170.209006015
\n", + "
" + ], + "text/plain": [ + " sex age severity medication recovery\n", + "0 0 35.049134 0.887658 1 31\n", + "1 1 41.580323 0.899784 1 49\n", + "2 1 28.127491 0.486349 0 38\n", + "3 1 36.375033 0.323091 0 35\n", + "4 0 25.091717 0.209006 0 15" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "med = pd.read_csv(\"./data/medicine_impact_recovery.csv\")\n", + "med.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "단순히 평균 차이를 계산한다면($E[Y|T=1]-E[Y|T=0]$), 약물을 투약한 환자가 약물을 투약하지 않은 환자에 비해 회복하는 데 평균 16.9일이 더 걸린다는 결과를 도출합니다. 약이 환자에게 해를 끼칠 것이기 때문에 이 결과는 아마도 교란 요인 때문일 것입니다." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "16.895799546498726" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "med.query(\"medication==1\")[\"recovery\"].mean() - med.query(\"medication==0\")[\"recovery\"].mean()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "이 편향을 수정하기 위해 매칭을 통해 X를 제어합니다. 먼저 변수의 스케일을 조정해야 한다는 것을 기억해야 합니다. 그렇지 않으면 관측치 사이의 거리를 계산할 때 나이와 같은 변수가 심각도와 같은 변수보다 더 중요할 것입니다. 이를 해결하기 위해 변수를 표준화할 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sexageseveritymedicationrecovery
0-0.9969800.2807871.459800131
11.0029790.8653751.502164149
21.002979-0.3387490.057796038
31.0029790.399465-0.512557035
4-0.996980-0.610473-0.911125015
\n", + "
" + ], + "text/plain": [ + " sex age severity medication recovery\n", + "0 -0.996980 0.280787 1.459800 1 31\n", + "1 1.002979 0.865375 1.502164 1 49\n", + "2 1.002979 -0.338749 0.057796 0 38\n", + "3 1.002979 0.399465 -0.512557 0 35\n", + "4 -0.996980 -0.610473 -0.911125 0 15" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# scale features\n", + "X = [\"severity\", \"age\", \"sex\"]\n", + "y = \"recovery\"\n", + "\n", + "med = med.assign(**{f: (med[f] - med[f].mean())/med[f].std() for f in X})\n", + "med.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "매칭함수를 직접 코딩하는 것 대신에 [Sklearn](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsRegressor.html)의 k-nearest-neighbor 알고리즘을 이용합니다. 이 알고리즘은 훈련 데이터셋에서 가장 가까운 데이터 포인트를 찾아 예측을 수행합니다.\n", + "\n", + "매칭을 위해서는 그 중 2개가 필요합니다. 하나 `mt0`는 처치되지 않은 관측치를 저장하고, 처치되지 않은 항목에서 일치하는 항목을 찾습니다. 다른 하나 `mt1`는 처치된 관측치를 저장하고 처치된 항목에서 일치하는 항목을 찾습니다. 이 피팅 단계 후에 우리는 이 KNN 모델을 사용하여 매칭하는 예측을 할 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sexageseveritymedicationrecoverymatch
0-0.9969800.2807871.45980013139.0
11.0029790.8653751.50216414952.0
7-0.9969801.4951341.26854013846.0
101.002979-0.1065340.54591113445.0
16-0.9969800.0430341.42873213039.0
\n", + "
" + ], + "text/plain": [ + " sex age severity medication recovery match\n", + "0 -0.996980 0.280787 1.459800 1 31 39.0\n", + "1 1.002979 0.865375 1.502164 1 49 52.0\n", + "7 -0.996980 1.495134 1.268540 1 38 46.0\n", + "10 1.002979 -0.106534 0.545911 1 34 45.0\n", + "16 -0.996980 0.043034 1.428732 1 30 39.0" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.neighbors import KNeighborsRegressor\n", + "\n", + "treated = med.query(\"medication==1\")\n", + "untreated = med.query(\"medication==0\")\n", + "\n", + "mt0 = KNeighborsRegressor(n_neighbors=1).fit(untreated[X], untreated[y])\n", + "mt1 = KNeighborsRegressor(n_neighbors=1).fit(treated[X], treated[y])\n", + "\n", + "predicted = pd.concat([\n", + " # find matches for the treated looking at the untreated knn model\n", + " treated.assign(match=mt0.predict(treated[X])),\n", + " \n", + " # find matches for the untreated looking at the treated knn model\n", + " untreated.assign(match=mt1.predict(untreated[X]))\n", + "])\n", + "\n", + "predicted.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "매칭을 통해 이제 매칭 추정기 공식을 적용할 수 있습니다.\n", + "\n", + "$\n", + "\\hat{ATE} = \\frac{1}{N} \\sum^N_{i=0} (2T_i - 1)\\big(Y_i - Y_{jm}(i)\\big)\n", + "$" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-0.9954" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.mean((2*predicted[\"medication\"] - 1)*(predicted[\"recovery\"] - predicted[\"match\"]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "이러한 매칭을 통해 약의 효과가 더 이상 긍정적이지 않음을 알 수 있습니다. 이는 X를 제어할 때 약이 평균적으로 약 1일 정도 회복 시간을 단축한다는 것을 의미합니다. 이는 회복 시간이 16.9 증가할 것으로 예측한 편향된 추정치보다 이미 큰 개선 사항입니다.\n", + "\n", + "그러나 우리는 여전히 더 잘할 수 있습니다.\n", + "\n", + "## Matching Bias\n", + "\n", + "위에서 설계한 매칭 추정기가 편향된 것으로 나타났습니다. 이것을 보기 위해 ATE 대신 ATET estimator를 고려해보겠습니다. 작성하기 더 간단하기 때문입니다. 직관은 ATE에도 동일하게 적용될 것입니다.\n", + "\n", + "$\n", + "\\hat{ATET} = \\frac{1}{N_1}\\sum (Y_i - Y_j(i))\n", + "$\n", + "\n", + "여기서 $N_1$ 처치된 관측치의 수이며, $Y_j(i)$ 처치된 unit i에 매칭되는 처치되지 않은 unit입니다. 편향을 확인하기 위해 우리가 하는 일은 중심 극한 정리를 적용하여 이것이 평균이 0인 정규 분포에 수렴하도록 하는 것입니다. \n", + "\n", + "$\n", + "\\sqrt{N_1}(\\hat{ATET} - ATET)\n", + "$\n", + "\n", + "그러나 이것이 항상 일어나는 것은 아닙니다. 주어진 X에 대한 처리되지 않은 그룹의 평균을 정의하면($\\mu_0(x)=E[Y|X=x, T=0]$), 우리는 이것을 계산할 수 있습니다.(여기서는 요점을 약간 벗어나기 때문에 증명을 생략했습니다).\n", + "\n", + "$\n", + "E[\\sqrt{N_1}(\\hat{ATET} - ATET)] = E[\\sqrt{N_1}(\\mu_0(X_i) - \\mu_0(X_j(i)))]\n", + "$\n", + "\n", + "지금, $\\mu_0(X_i) - \\mu_0(X_j(i))$ 이해하기 쉽지 않기 떄문에, 더 자세하게 살펴보겠습니다. $\\mu_0(X_i)$는 처치된 unit $i$가 처치되지 않았더라면 계산될 Y 값입니다. 그래서 이것은 반사실적 결과입니다(unit i에 대한 $Y_0$). $\\mu_0(X_j(i))$는 unit $i$에 매칭된 처치되지 않은 unit $j$의 결과입니다. 그래서 이것은 또한 $Y_0$이지만 지금 unit $j$입니다. 이번에야말로, $j$는 처치되지 않은 그룹의 unit이기 때문에 사실적인 결과입니다. 지금, $j$와 $i$는 유사하지만, 정확히 같지는 않기 때문에 0은 아닐 것입니다(즉, $X_i \\approx X_j $, $Y_{0i} \\approx Y_{0j}$). \n", + "\n", + "표본크기가 증가하면 매칭되는 unit이 많아지므로, unit $i$와 unit $j$의 차이는 더 작아집니다. 하지만 이 차이는 느리게 0으로 수렴합니다. 결과적으로 $E[\\sqrt{N_1}(\\mu_0(X_i) - \\mu_0(X_j(i)))]$ 0으로 수렴하지 않을 것입니다. 왜냐하면 $(\\mu_0(X_i) - \\mu_0(X_j(i)))$가 감소하는 속도에 비해 $\\sqrt{N_1}$ $(\\mu_0(X_i) - \\mu_0(X_j(i)))$가 더 빠르게 증가할 것이기 때문입니다.\n", + "\n", + "매칭 불일치가 클 때 편향이 발생합니다. 다행히도 우리는 그것을 수정하는 방법을 알고 있습니다. 각 관측치는 $(\\mu_0(X_i) - \\mu_0(X_j(i)))$에 기여하므로, 추정기의 매칭되는 비교에서 이 양을 직접 빼면 됩니다. 이것을 하기 위해, 우리는 $\\mu_0(X_j(i))$를 선형회귀와 같은 모델로 얻을 수 있는 $\\hat{\\mu_0}(X_j(i))$로 대체할 수 있습니다. 이것은 ATET 추정기를 다음 방정식으로 업데이트합니다.\n", + "\n", + "$\n", + "\\hat{ATET} = \\frac{1}{N_1}\\sum \\big((Y_i - Y_{j(i)}) - (\\hat{\\mu_0}(X_i) - \\hat{\\mu_0}(X_{j(i)}))\\big)\n", + "$\n", + "\n", + "여기서 $\\hat{\\mu_0}(x)$는 $E[Y|X, T=0]$로, 처치되지 않은 샘플에만 맞는 선형회귀모형과 같습니다. " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sexageseveritymedicationrecoverymatchbias_correct
0-0.9969800.2807871.45980013139.04.404034
11.0029790.8653751.50216414952.012.915348
7-0.9969801.4951341.26854013846.01.871428
101.002979-0.1065340.54591113445.0-0.496970
16-0.9969800.0430341.42873213039.02.610159
\n", + "
" + ], + "text/plain": [ + " sex age severity medication recovery match bias_correct\n", + "0 -0.996980 0.280787 1.459800 1 31 39.0 4.404034\n", + "1 1.002979 0.865375 1.502164 1 49 52.0 12.915348\n", + "7 -0.996980 1.495134 1.268540 1 38 46.0 1.871428\n", + "10 1.002979 -0.106534 0.545911 1 34 45.0 -0.496970\n", + "16 -0.996980 0.043034 1.428732 1 30 39.0 2.610159" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.linear_model import LinearRegression\n", + "\n", + "# fit the linear regression model to estimate mu_0(x)\n", + "ols0 = LinearRegression().fit(untreated[X], untreated[y])\n", + "ols1 = LinearRegression().fit(treated[X], treated[y])\n", + "\n", + "# find the units that match to the treated\n", + "treated_match_index = mt0.kneighbors(treated[X], n_neighbors=1)[1].ravel()\n", + "\n", + "# find the units that match to the untreatd\n", + "untreated_match_index = mt1.kneighbors(untreated[X], n_neighbors=1)[1].ravel()\n", + "\n", + "predicted = pd.concat([\n", + " (treated\n", + " # find the Y match on the other group\n", + " .assign(match=mt0.predict(treated[X])) \n", + " \n", + " # build the bias correction term\n", + " .assign(bias_correct=ols0.predict(treated[X]) - ols0.predict(untreated.iloc[treated_match_index][X]))),\n", + " (untreated\n", + " .assign(match=mt1.predict(untreated[X]))\n", + " .assign(bias_correct=ols1.predict(untreated[X]) - ols1.predict(treated.iloc[untreated_match_index][X])))\n", + "])\n", + "\n", + "predicted.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "한 가지 즉각적인 질문은 다음과 같습니다. 이것이 매칭의 요점을 무너뜨리지 않습니까? 어쨌든 선형 회귀를 실행해야 하는 경우 이 복잡한 모델 대신 해당 모델만 사용하는 것이 좋습니다. 그것은 공정한 지적이므로 답변하는 데 시간이 필요합니다.\n", + "\n", + "![img](./data/img/matching/ubiquitous-ols.png)\n", + "\n", + "우선, 우리가 맞추는 이 선형 회귀는 처치 효과를 얻기 위해 처치 차원을 외삽하지 않습니다. 대신, 그 목적은 편향을 수정하는 것입니다. 여기서 선형 회귀는 처치되지 않은 것처럼 보이는 경우 처치된 방법을 확인하려고 시도하지 않는다는 점에서 국소적입니다. 이것은 외삽을 하지 않습니다. 이것은 매칭되는 부분에 맡깁니다. 추정기의 핵심은 여전히 ​​매칭되는 구성 요소입니다. 여기서 말하고 싶은 요점은 OLS가 이 추정기에 부차적이라는 것입니다.\n", + "\n", + "두 번째 요점은 매칭이 비모수적 추정량이라는 것입니다. 선형성이나 어떤 종류의 매개변수 모델도 가정하지 않습니다. 따라서 선형 회귀보다 더 유연하며 선형 회귀가 아닌 상황, 즉 비선형성이 매우 강한 상황에서 작동할 수 있습니다.\n", + "\n", + "이것은 매칭만 사용해야 한다는 것을 의미합니까? 글쎄요, 그건 어려운 질문입니다. 알베르토 아바디(Alberto Abadie)는 예, 그렇게 해야 한다고 주장합니다. 더 유연하고 일단 코드가 있으면 똑같이 간단하게 실행할 수 있습니다. 나는 그것에 대해 완전히 확신하지 못합니다. 예를 들어, Abadie는 추정기를 연구하고 개발하는 데 많은 시간을 보냈습니다(예, 그는 매칭에 기여하는 과학자 중 한 명입니다). 따라서 그는 분명히 이 방법에 개인적으로 투자했습니다. 둘째, 선형 회귀의 단순성에 대해 매칭에서 볼 수 없는 무언가가 있습니다. \"다른 모든 것을 일정하게 유지\"하는 편도함수 수학은 매칭보다 선형 회귀로 파악하는 것이 훨씬 쉽습니다. 하지만 그건 내 취향일 뿐입니다. 솔직히 말해서 이 질문에 대한 명확한 답은 없습니다. 어쨌든, 우리의 예로 돌아갑니다.\n", + "\n", + "편향 보정 공식을 사용하면 다음과 같은 ATE 추정을 얻을 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-7.36266090614141" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.mean((2*predicted[\"medication\"] - 1)*((predicted[\"recovery\"] - predicted[\"match\"])-predicted[\"bias_correct\"]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "물론, 우리는 또한 이 측정 주위에 신뢰 구간을 배치해야 하지만, 이제 수학 이론으로 충분합니다. 실제로, 우리는 단순히 다른 사람의 코드를 사용하고 일치하는 추정기를 가져올 수 있습니다. 다음은 [causalinference](https://github.com/laurencium/causalinference) 라이브러리에서 가져온 것입니다." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Treatment Effect Estimates: Matching\n", + "\n", + " Est. S.e. z P>|z| [95% Conf. int.]\n", + "--------------------------------------------------------------------------------\n", + " ATE -7.709 0.609 -12.649 0.000 -8.903 -6.514\n", + " ATC -6.665 0.246 -27.047 0.000 -7.148 -6.182\n", + " ATT -9.679 1.693 -5.717 0.000 -12.997 -6.361\n", + "\n" + ] + } + ], + "source": [ + "from causalinference import CausalModel\n", + "\n", + "cm = CausalModel(\n", + " Y=med[\"recovery\"].values, \n", + " D=med[\"medication\"].values, \n", + " X=med[[\"severity\", \"age\", \"sex\"]].values\n", + ")\n", + "\n", + "cm.est_via_matching(matches=1, bias_adj=True)\n", + "\n", + "print(cm.estimates)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "마지막으로, 우리는 우리 약이 실제로 병원 체류기간을 줄여준다고 자신 있게 말할 수 있습니다. ATE 추정치는 직접 계산한 것보다 약간 낮으므로 아마도 직접 계산한 코드가 완벽하지 않을 것입니다. 따라서 여기에 직접 코드를 작성하는 대신 라이브러리 코드를 가져와야 하는 또 다른 이유가 있습니다.\n", + "\n", + "이 주제를 닫기 전에 매칭 항목에서 편향의 원인을 조금 더 설명하고 싶었습니다. 우리는 unit들과 매칭이 그렇게 유사하지 않을 때 매칭이 편향된다는 것을 보았습니다. 그러나 무엇이 그들을 그렇게 다르게 만듭니까?\n", + "\n", + "\n", + "## The Curse of Dimensionality\n", + "\n", + "알고 보면 답은 매우 간단하고 직관적입니다. 성별과 같은 몇 가지 특성이 매칭되는 사람들을 쉽게 찾을 수 있습니다. 그러나 나이, 소득, 출신지 등의 변수를 추가하면 매칭되는 항목을 찾기가 점점 더 어려워집니다. 더 일반적인 용어로, 더 많은 변수를 가질수록 unit과 매칭되는 unit 사이의 거리가 더 멀어집니다.\n", + "\n", + "이것은 매칭 추정기에만 영향을 주는 것이 아닙니다. 이전에 본 하위 분류 추정기와 다시 연결됩니다. 초기에 성별에 따라 하위 분류 추정기를 구축하는 것은 매우 쉬운 의약의 예입니다. 남성과 여성 2개의 셀만 있었기 때문입니다. 그러나 우리가 더 많은 것을 가지고 있다면 어떻게 될까요? 연령 및 소득과 같은 2개의 연속적인 특성이 있고 각각 5개의 버킷으로 이산화할 수 있다고 가정해 보겠습니다. 이것은 우리에게 25개의 셀을 줄 것입니다($5^2$). 그리고 각각 3개의 버킷이 있는 10개의 공변량이 있다면 어떻게 될까요? 많지 않은 것 같죠? 글쎄, 이것은 우리에게 59049개의 세포를 줄 것입니다($3^10$). 이것이 어떻게 빠르게 증가하는지 쉽게 알 수 있습니다. 이것은 **차원의 저주** 라고 불리는 모든 데이터 과학에 만연한 현상입니다 !!!\n", + "\n", + "![img](./data/img/curse-of-dimensionality.jpg)\n", + "Image Source: https://deepai.org/machine-learning-glossary-and-terms/curse-of-dimensionality\n", + "\n", + "무섭고 가식적인 이름에도 불구하고 이것은 변수 공간을 채우는 데 필요한 데이터 포인트의 수가 변수 또는 차원의 수에 따라 기하급수적으로 증가한다는 것을 의미합니다. 따라서 예를 들어 3개의 변수 공간을 채우는 데 X 데이터 포인트가 필요하면 4개의 기능 공간을 채우는 데 기하급수적으로 더 많은 포인트가 필요합니다.\n", + "\n", + "하위 분류 추정기의 맥락에서 차원의 저주는 변수가 많을 경우 문제가 발생한다는 것을 의미합니다. 많은 변수는 X의 여러 셀을 의미합니다. 여러 셀이 있는 경우 일부는 데이터가 거의 없습니다. 그들 중 일부는 처치만 하거나 통제만 했을 수도 있으므로 거기에서 ATE를 추정하는 것이 불가능할 것입니다. 일치하는 컨텍스트에서 이는 변수 공간이 매우 희소하고 관측치 간 서로 매우 멀리 떨어져 있음을 의미합니다. 이렇게 하면 매칭된 관측치 간의 거리가 늘어나고 편향 문제가 발생합니다.\n", + "\n", + "선형 회귀의 경우 실제로 이 문제를 아주 잘 처리합니다. 그것이 하는 일은 모든 변수 X를 단일 Y 차원으로 투영하는 것입니다. 그런 다음 해당 투영에 대한 처치 및 통제 비교를 수행합니다. 따라서 어떤 면에서 선형 회귀는 일종의 차원 축소를 수행하여 ATE를 추정합니다. 상당히 고급스럽습니다.\n", + "\n", + "대부분의 인과 모델은 차원의 저주를 처리하는 방법도 있습니다. 계속 반복하지는 않겠지만 그것들을 볼 때 염두에 두어야 할 사항입니다. 예를 들어 다음 섹션에서 성향 점수를 다룰 때 이 문제를 어떻게 해결하는지 살펴보십시오.\n", + "\n", + "## Key Ideas\n", + "\n", + "이 섹션은 선형 회귀가 하는 일과 인과 관계를 식별하는 데 어떻게 도움이 되는지 이해하기 위해 시작했습니다. 즉, 회귀는 데이터셋을 셀로 분할하고 각 셀에서 ATE를 계산한 다음 셀의 ATE를 전체 데이터셋에 대한 단일 ATE로 결합하는 것으로 볼 수 있음을 이해했습니다.\n", + "\n", + "거기에서 우리는 하위 분류가 있는 매우 일반적인 인과 추론 추정기를 도출했습니다. 우리는 그 추정기가 실제로 매우 유용하지 않다는 것을 보았지만 인과 추론 추정의 문제를 해결하는 방법에 대한 흥미로운 통찰력을 제공했습니다. 그것은 우리에게 매칭 추정기에 대해 이야기할 기회를 주었습니다.\n", + "\n", + "각 처치된 단위를 보고 매우 유사하고 처치되지 않은 단위에 대해 유사하게 처치되지 않은 쌍을 찾아 교란자에 대한 대조를 매칭시킵니다. 우리는 KNN 알고리즘을 사용하여 이 방법을 구현하는 방법과 회귀를 사용하여 편향을 줄이는 방법을 보았습니다. 마지막으로 매칭과 선형 회귀의 차이점에 대해 논의했습니다. 우리는 선형 회귀가 하는 방식으로 선형성에 의존하지 않는 비모수적 추정기(non-parametric estimator)인 매칭을 보았습니다.\n", + "\n", + "마지막으로, 우리는 고차원 데이터셋의 문제를 파헤쳤고 인과적 추론 방법이 어떻게 문제를 겪을 수 있는지 보았습니다.\n", + "\n", + "\n", + "## References\n", + "\n", + "저는 이 책을 Joshua Angrist, Alberto Abadie 및 Christopher Walters의 대단한 계량 경제학 수업에 대한 찬사라고 생각하고 싶습니다. 이 자료에 있는 대부분의 아이디어는 전미경제학회(American Economic Association)의 수업에서 가져왔어요. 이렇게 좋은 참고자료를 지켜보는 것이 저의 2020년의 힘든 한 해 동안, 온전한 정신을 유지하도록 도와주었어요.\n", + "\n", + "* [Cross-Section Econometrics](https://www.aeaweb.org/conference/cont-ed/2017-webcasts)\n", + "* [Mastering Mostly Harmless Econometrics](https://www.aeaweb.org/conference/cont-ed/2020-webcasts)\n", + "\n", + "\n", + "또한 Angrist의 정말 좋은 책들을 참고자료 목록에 담고 싶습니다. 이 책들은 계량경제학(Econometrics) 또는 '메트릭스(Metrics, 계량적 분석)'가 매우 유용할 뿐만 아니라 매우 재미있다는 것을 저에게 보여주었습니다.\n", + "\n", + "* [Mostly Harmless Econometrics](https://www.mostlyharmlesseconometrics.com/)\n", + "* [Mastering 'Metrics](https://www.masteringmetrics.com/)\n", + "\n", + "마지막으로 제가 참고한 자료는 Miguel Hernan과 Jamie Robins의 책입니다. 이 책들은 제가 대답해야 했던 까다로운 인과적인 질문에서 신뢰할 수 있는 동반자와 같은 존재였어요.\n", + "\n", + "* [Causal Inference Book](https://www.hsph.harvard.edu/miguel-hernan/causal-inference-book/)\n", + "\n", + "본문의 앞쪽의 맥주 비유는 JL Colins의 [주식 시리즈](https://jlcollinsnh.com/2012/04/15/stocks-part-1-theres-a-major-market-crash-coming-and-dr-lo)에서 가져왔습니다. 이 시리즈는 돈을 생산적으로 투자하는 방법을 배우려는 모든 분들께서 반드시 읽어야할 자료입니다.\n", + "\n", + "![img](./data/img/poetry.png)\n", + "\n", + "## Contribute\n", + "\n", + "Causal Inference for the Brave and True는 인과추론, 통계학에 대한 오픈소스 자료입니다. 이 자료는 금전적으로나 지적으로 접근이 가능할 수 있도록 하는 것이 목표입니다. 그리고, 이 책은 Python 기반의 무료 소프트웨어만 사용해요.\n", + "여러분들께서 이 자료가 가치 있다고 생각하시고, 금전적으로 지원을 원하신다면 [Patreon](https://www.patreon.com/causal_inference_for_the_brave_and_true)를 방문해주세요. \n", + "만약 여러분이 금전적으로 기여하기가 쉽지 않으시다면, 오타 수정, 수정 제안, 이해하기 난해한 부분에 대한 피드백 제공 등을 통해 도움을 주실 수 있어요. 이 책의 Github 저장소 [이슈 페이지](https://github.com/matheusfacure/python-causality-handbook/issues)를 방문해주세요. 마지막으로 이 자료가 여러분의 마음에 드셨다면 도움이 될 수 있는 다른 사람들과 공유해주시고, [이 책의 Github 자료에 star](https://github.com/matheusfacure/python-causality-handbook/stargazers) 부탁드립니다!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}